Compare commits
93
Commits
v1.49.0
...
exv/glslkomi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0709f7d59a | ||
|
|
2027c9e237 | ||
|
|
86456c9e34 | ||
|
|
fad58ba892 | ||
|
|
8ae296974c | ||
|
|
f9575675d9 | ||
|
|
71025cf25a | ||
|
|
a067fb02b1 | ||
|
|
ffde51ee6f | ||
|
|
35f99ed168 | ||
|
|
a939ad03f8 | ||
|
|
05e628fff9 | ||
|
|
ad8473760a | ||
|
|
cb672eb063 | ||
|
|
9811b785cb | ||
|
|
ff6d4d721e | ||
|
|
c8a5094c41 | ||
|
|
0f34603cfc | ||
|
|
300f80448d | ||
|
|
a41d343a75 | ||
|
|
02f5903b67 | ||
|
|
c39a870abc | ||
|
|
09b8008e17 | ||
|
|
59b59cf6be | ||
|
|
0df6013263 | ||
|
|
d5041fced7 | ||
|
|
9c54b8a777 | ||
|
|
8a534d0940 | ||
|
|
130825422e | ||
|
|
984006ee25 | ||
|
|
b676002521 | ||
|
|
8bdf7bd1e5 | ||
|
|
1262cb286c | ||
|
|
ee6f3fb1dc | ||
|
|
b9a9586abb | ||
|
|
6ee20a57aa | ||
|
|
368fa2bf39 | ||
|
|
8f642892b4 | ||
|
|
5435a8ed3b | ||
|
|
5fd7a4e153 | ||
|
|
20ff230b92 | ||
|
|
44ff79ad34 | ||
|
|
102d2db008 | ||
|
|
5c9039e650 | ||
|
|
1203c24f06 | ||
|
|
9704d27aeb | ||
|
|
c6f2c3fc1c | ||
|
|
2faf868341 | ||
|
|
81f6260843 | ||
|
|
1a9063d53a | ||
|
|
6062b3c8c6 | ||
|
|
d9186c44ba | ||
|
|
6d06e00934 | ||
|
|
b4021a1fbc | ||
|
|
2c9559f9b8 | ||
|
|
e2da13f817 | ||
|
|
addae314b4 | ||
|
|
5e3c51822d | ||
|
|
9d817a0cc9 | ||
|
|
95ecc3d1e4 | ||
|
|
cabaa4323e | ||
|
|
d6b2ec49b0 | ||
|
|
93004aa5bc | ||
|
|
27fa8ab46d | ||
|
|
614990f413 | ||
|
|
da058028fd | ||
|
|
55c75453df | ||
|
|
f5eff10532 | ||
|
|
56a0364534 | ||
|
|
c8aae3cdaa | ||
|
|
4f94c575c9 | ||
|
|
1a29f020c9 | ||
|
|
d5847a3c6a | ||
|
|
3dbae482d3 | ||
|
|
27572e3be6 | ||
|
|
dedf276e26 | ||
|
|
111ad96134 | ||
|
|
2a67152dda | ||
|
|
2f62978e4c | ||
|
|
fdb90f2f49 | ||
|
|
5418b8c4cc | ||
|
|
724cab3623 | ||
|
|
d9faea264a | ||
|
|
6116c2f9eb | ||
|
|
95fdba0c5d | ||
|
|
9c56517370 | ||
|
|
08f06f4298 | ||
|
|
b1794a82ca | ||
|
|
56bd2871ec | ||
|
|
5a9c47bcb5 | ||
|
|
51c2fa2728 | ||
|
|
334c71de52 | ||
|
|
001098098b |
+2
-1
@@ -1,7 +1,8 @@
|
||||
;;; Directory Local Variables -*- no-byte-compile: t -*-
|
||||
;;; For more information see (info "(emacs) Directory Variables")
|
||||
|
||||
((c++-mode . ((c-file-style . "filament")
|
||||
((c++-mode . ((eglot-ignored-server-capabilities . (:documentFormattingProvider))
|
||||
(c-file-style . "filament")
|
||||
(apheleia-inhibit . t)))
|
||||
(c-mode . ((c-file-style . "filament")
|
||||
(apheleia-inhibit . t))))
|
||||
|
||||
+32
-4
@@ -41,6 +41,8 @@ option(FILAMENT_LINUX_IS_MOBILE "Treat Linux as Mobile" OFF)
|
||||
|
||||
option(FILAMENT_ENABLE_ASAN_UBSAN "Enable Address and Undefined Behavior Sanitizers" OFF)
|
||||
|
||||
option(FILAMENT_ENABLE_TSAN "Enable Thread Sanitizer" OFF)
|
||||
|
||||
option(FILAMENT_ENABLE_FEATURE_LEVEL_0 "Enable Feature Level 0" ON)
|
||||
|
||||
set(FILAMENT_NDK_VERSION "" CACHE STRING
|
||||
@@ -230,6 +232,21 @@ if (WIN32)
|
||||
# we don't need them on CI.
|
||||
string(REPLACE "/INCREMENTAL" "/INCREMENTAL:NO" ${LinkerFlag} ${${LinkerFlag}})
|
||||
endforeach()
|
||||
|
||||
# We turn off compile-time optimizations for CI, as options that speed up the compile-time
|
||||
# (e.g. /MP) might increase memory usage, leading to instabilities on limited CI machines.
|
||||
option(FILAMENT_SHORTEN_MSVC_COMPILATION "Shorten compile-time in Visual Studio" OFF)
|
||||
else()
|
||||
option(FILAMENT_SHORTEN_MSVC_COMPILATION "Shorten compile-time in Visual Studio" ON)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
if (FILAMENT_SHORTEN_MSVC_COMPILATION)
|
||||
# enable multi-processor compilation
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
|
||||
# disable run-time STL checks to improve tools (e.g. matc) performance
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_ITERATOR_DEBUG_LEVEL=0")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -411,7 +428,13 @@ endif()
|
||||
if (FILAMENT_ENABLE_ASAN_UBSAN)
|
||||
set(EXTRA_SANITIZE_OPTIONS "-fsanitize=address -fsanitize=undefined")
|
||||
endif()
|
||||
|
||||
if (FILAMENT_ENABLE_TSAN)
|
||||
set(EXTRA_SANITIZE_OPTIONS "-fsanitize=thread")
|
||||
endif()
|
||||
if (ANDROID)
|
||||
# keep STL debug infos (mimics what the NDK does)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-limit-debug-info")
|
||||
endif()
|
||||
if (NOT MSVC AND NOT WEBGL)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fstack-protector")
|
||||
endif()
|
||||
@@ -611,9 +634,9 @@ function(combine_static_libs TARGET OUTPUT DEPS)
|
||||
# Loop through the dependent libraries and query their location on disk.
|
||||
set(DEPS_FILES )
|
||||
foreach(DEPENDENCY ${DEPS})
|
||||
if(TARGET ${DEPENDENCY})
|
||||
if (TARGET ${DEPENDENCY})
|
||||
get_property(dep_type TARGET ${DEPENDENCY} PROPERTY TYPE)
|
||||
if(dep_type STREQUAL "STATIC_LIBRARY")
|
||||
if (dep_type STREQUAL "STATIC_LIBRARY")
|
||||
list(APPEND DEPS_FILES "$<TARGET_FILE:${DEPENDENCY}>")
|
||||
endif()
|
||||
endif()
|
||||
@@ -698,7 +721,6 @@ add_subdirectory(${LIBRARIES}/filabridge)
|
||||
add_subdirectory(${LIBRARIES}/filaflat)
|
||||
add_subdirectory(${LIBRARIES}/filagui)
|
||||
add_subdirectory(${LIBRARIES}/filameshio)
|
||||
add_subdirectory(${LIBRARIES}/geometry)
|
||||
add_subdirectory(${LIBRARIES}/gltfio)
|
||||
add_subdirectory(${LIBRARIES}/ibl)
|
||||
add_subdirectory(${LIBRARIES}/iblprefilter)
|
||||
@@ -726,12 +748,16 @@ add_subdirectory(${EXTERNAL}/jsmn/tnt)
|
||||
add_subdirectory(${EXTERNAL}/stb/tnt)
|
||||
add_subdirectory(${EXTERNAL}/getopt)
|
||||
|
||||
# Note that this has to be placed after mikktspace in order for combine_static_libs to work.
|
||||
add_subdirectory(${LIBRARIES}/geometry)
|
||||
|
||||
if (FILAMENT_BUILD_FILAMAT OR IS_HOST_PLATFORM)
|
||||
# spirv-tools must come before filamat, as filamat relies on the presence of the
|
||||
# spirv-tools_SOURCE_DIR variable.
|
||||
add_subdirectory(${EXTERNAL}/spirv-tools)
|
||||
add_subdirectory(${EXTERNAL}/glslang/tnt)
|
||||
add_subdirectory(${EXTERNAL}/spirv-cross/tnt)
|
||||
add_subdirectory(${LIBRARIES}/astrict)
|
||||
add_subdirectory(${LIBRARIES}/filamat)
|
||||
|
||||
# the material debugger requires filamat
|
||||
@@ -743,6 +769,8 @@ endif()
|
||||
if (FILAMENT_SUPPORTS_VULKAN)
|
||||
add_subdirectory(${LIBRARIES}/bluevk)
|
||||
add_subdirectory(${EXTERNAL}/vkmemalloc/tnt)
|
||||
set(SPIRV_HEADERS_SKIP_EXAMPLES ON)
|
||||
add_subdirectory(${EXTERNAL}/spirv-headers)
|
||||
endif()
|
||||
|
||||
set(FILAMENT_SAMPLES_BINARY_DIR ${PROJECT_BINARY_DIR}/samples)
|
||||
|
||||
@@ -7,4 +7,3 @@ for next branch cut* header.
|
||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.48.0'
|
||||
implementation 'com.google.android.filament:filament-android:1.50.0'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.48.0'
|
||||
pod 'Filament', '~> 1.50.0'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
|
||||
@@ -7,6 +7,25 @@ A new header is inserted each time a *tag* is created.
|
||||
Instead, if you are authoring a PR for the main branch, add your release note to
|
||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||
|
||||
## v1.50.1
|
||||
|
||||
- Metal: fix some shader artifacts by disabling fast math optimizations.
|
||||
- backend: remove `atan2` overload which had a typo and wasn't useful. Fixes b/320856413.
|
||||
- utils: remove usages of `SpinLock`. Fixes b/321101014.
|
||||
|
||||
## v1.50.0
|
||||
- engine: TAA now supports 4x upscaling [BETA] [⚠️ **New Material Version**]
|
||||
|
||||
## v1.49.3
|
||||
|
||||
- matc: Generate stereo variants for FL0 materials [⚠️ **Recompile materials**]
|
||||
|
||||
## v1.49.2
|
||||
|
||||
|
||||
## v1.49.1
|
||||
|
||||
|
||||
## v1.49.0
|
||||
|
||||
- matc: Fix ESSL 1.0 codegen when using external samplers [⚠️ **Recompile materials**]
|
||||
|
||||
@@ -81,14 +81,14 @@ buildscript {
|
||||
ext.versions = [
|
||||
'jdk': 17,
|
||||
'minSdk': 19,
|
||||
'targetSdk': 33,
|
||||
'compileSdk': 33,
|
||||
'kotlin': '1.9.0',
|
||||
'kotlin_coroutines': '1.7.2',
|
||||
'targetSdk': 34,
|
||||
'compileSdk': 34,
|
||||
'kotlin': '1.9.21',
|
||||
'kotlin_coroutines': '1.7.3',
|
||||
'buildTools': '34.0.0',
|
||||
'ndk': '25.1.8937393',
|
||||
'androidx_core': '1.10.1',
|
||||
'androidx_annotations': '1.6.0'
|
||||
'ndk': '26.1.10909125',
|
||||
'androidx_core': '1.12.0',
|
||||
'androidx_annotations': '1.7.0'
|
||||
]
|
||||
|
||||
ext.deps = [
|
||||
@@ -104,7 +104,7 @@ buildscript {
|
||||
]
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.1.0'
|
||||
classpath 'com.android.tools.build:gradle:8.2.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
|
||||
}
|
||||
|
||||
|
||||
@@ -849,6 +849,10 @@ public class Texture {
|
||||
public static final int SAMPLEABLE = 0x10;
|
||||
/** Texture can be used as a subpass input */
|
||||
public static final int SUBPASS_INPUT = 0x20;
|
||||
/** Texture can be used the source of a blit() */
|
||||
public static final int BLIT_SRC = 0x40;
|
||||
/** Texture can be used the destination of a blit() */
|
||||
public static final int BLIT_DST = 0x80;
|
||||
/** by default textures are <code>UPLOADABLE</code> and <code>SAMPLEABLE</code>*/
|
||||
public static final int DEFAULT = UPLOADABLE | SAMPLEABLE;
|
||||
}
|
||||
|
||||
@@ -1093,7 +1093,7 @@ public class View {
|
||||
* @see #setStereoscopicOptions
|
||||
*/
|
||||
@NonNull
|
||||
public StereoscopicOptions getStereoscoopicOptions() {
|
||||
public StereoscopicOptions getStereoscopicOptions() {
|
||||
if (mStereoscopicOptions == null) {
|
||||
mStereoscopicOptions = new StereoscopicOptions();
|
||||
}
|
||||
@@ -1637,6 +1637,10 @@ public class View {
|
||||
* circle of confusion scale factor (amount of blur)
|
||||
*/
|
||||
public float cocScale = 1.0f;
|
||||
/**
|
||||
* width/height aspect ratio of the circle of confusion (simulate anamorphic lenses)
|
||||
*/
|
||||
public float cocAspectRatio = 1.0f;
|
||||
/**
|
||||
* maximum aperture diameter in meters (zero to disable rotation)
|
||||
*/
|
||||
@@ -1852,7 +1856,7 @@ public class View {
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for Temporal Multi-Sample Anti-aliasing (MSAA)
|
||||
* Options for Multi-Sample Anti-aliasing (MSAA)
|
||||
* @see setMultiSampleAntiAliasingOptions()
|
||||
*/
|
||||
public static class MultiSampleAntiAliasingOptions {
|
||||
@@ -1876,21 +1880,111 @@ public class View {
|
||||
|
||||
/**
|
||||
* Options for Temporal Anti-aliasing (TAA)
|
||||
* Most TAA parameters are extremely costly to change, as they will trigger the TAA post-process
|
||||
* shaders to be recompiled. These options should be changed or set during initialization.
|
||||
* `filterWidth`, `feedback` and `jitterPattern`, however, can be changed at any time.
|
||||
*
|
||||
* `feedback` of 0.1 effectively accumulates a maximum of 19 samples in steady state.
|
||||
* see "A Survey of Temporal Antialiasing Techniques" by Lei Yang and all for more information.
|
||||
*
|
||||
* @see setTemporalAntiAliasingOptions()
|
||||
*/
|
||||
public static class TemporalAntiAliasingOptions {
|
||||
public enum BoxType {
|
||||
/**
|
||||
* use an AABB neighborhood
|
||||
*/
|
||||
AABB,
|
||||
/**
|
||||
* use the variance of the neighborhood (not recommended)
|
||||
*/
|
||||
VARIANCE,
|
||||
/**
|
||||
* use both AABB and variance
|
||||
*/
|
||||
AABB_VARIANCE,
|
||||
}
|
||||
|
||||
public enum BoxClipping {
|
||||
/**
|
||||
* Accurate box clipping
|
||||
*/
|
||||
ACCURATE,
|
||||
/**
|
||||
* clamping
|
||||
*/
|
||||
CLAMP,
|
||||
/**
|
||||
* no rejections (use for debugging)
|
||||
*/
|
||||
NONE,
|
||||
}
|
||||
|
||||
public enum JitterPattern {
|
||||
RGSS_X4,
|
||||
UNIFORM_HELIX_X4,
|
||||
HALTON_23_X8,
|
||||
HALTON_23_X16,
|
||||
HALTON_23_X32,
|
||||
}
|
||||
|
||||
/**
|
||||
* reconstruction filter width typically between 0 (sharper, aliased) and 1 (smoother)
|
||||
* reconstruction filter width typically between 0.2 (sharper, aliased) and 1.5 (smoother)
|
||||
*/
|
||||
public float filterWidth = 1.0f;
|
||||
/**
|
||||
* history feedback, between 0 (maximum temporal AA) and 1 (no temporal AA).
|
||||
*/
|
||||
public float feedback = 0.04f;
|
||||
public float feedback = 0.12f;
|
||||
/**
|
||||
* texturing lod bias (typically -1 or -2)
|
||||
*/
|
||||
public float lodBias = -1.0f;
|
||||
/**
|
||||
* post-TAA sharpen, especially useful when upscaling is true.
|
||||
*/
|
||||
public float sharpness = 0.0f;
|
||||
/**
|
||||
* enables or disables temporal anti-aliasing
|
||||
*/
|
||||
public boolean enabled = false;
|
||||
/**
|
||||
* 4x TAA upscaling. Disables Dynamic Resolution. [BETA]
|
||||
*/
|
||||
public boolean upscaling = false;
|
||||
/**
|
||||
* whether to filter the history buffer
|
||||
*/
|
||||
public boolean filterHistory = true;
|
||||
/**
|
||||
* whether to apply the reconstruction filter to the input
|
||||
*/
|
||||
public boolean filterInput = true;
|
||||
/**
|
||||
* whether to use the YcoCg color-space for history rejection
|
||||
*/
|
||||
public boolean useYCoCg = false;
|
||||
/**
|
||||
* type of color gamut box
|
||||
*/
|
||||
@NonNull
|
||||
public TemporalAntiAliasingOptions.BoxType boxType = TemporalAntiAliasingOptions.BoxType.AABB;
|
||||
/**
|
||||
* clipping algorithm
|
||||
*/
|
||||
@NonNull
|
||||
public TemporalAntiAliasingOptions.BoxClipping boxClipping = TemporalAntiAliasingOptions.BoxClipping.ACCURATE;
|
||||
@NonNull
|
||||
public TemporalAntiAliasingOptions.JitterPattern jitterPattern = TemporalAntiAliasingOptions.JitterPattern.HALTON_23_X16;
|
||||
public float varianceGamma = 1.0f;
|
||||
/**
|
||||
* adjust the feedback dynamically to reduce flickering
|
||||
*/
|
||||
public boolean preventFlickering = false;
|
||||
/**
|
||||
* whether to apply history reprojection (debug option)
|
||||
*/
|
||||
public boolean historyReprojection = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+8
-4
@@ -329,7 +329,6 @@ public class UiHelper {
|
||||
@Override
|
||||
public void detach() {
|
||||
mTextureView.setSurfaceTextureListener(null);
|
||||
setSurface(null);
|
||||
}
|
||||
|
||||
|
||||
@@ -382,6 +381,7 @@ public class UiHelper {
|
||||
@Override
|
||||
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureDestroyed()");
|
||||
setSurface(null);
|
||||
destroySwapChain();
|
||||
return true;
|
||||
}
|
||||
@@ -445,6 +445,9 @@ public class UiHelper {
|
||||
* {@link #attachTo(TextureView)}, or {@link #attachTo(SurfaceHolder)}.
|
||||
*/
|
||||
public void detach() {
|
||||
if (mRenderSurface != null) {
|
||||
mRenderSurface.detach();
|
||||
}
|
||||
destroySwapChain();
|
||||
mNativeWindow = null;
|
||||
mRenderSurface = null;
|
||||
@@ -590,6 +593,10 @@ public class UiHelper {
|
||||
// nothing to do
|
||||
return false;
|
||||
}
|
||||
if (mRenderSurface != null) {
|
||||
mRenderSurface.detach();
|
||||
mRenderSurface = null;
|
||||
}
|
||||
destroySwapChain();
|
||||
}
|
||||
mNativeWindow = nativeWindow;
|
||||
@@ -604,9 +611,6 @@ public class UiHelper {
|
||||
}
|
||||
|
||||
private void destroySwapChain() {
|
||||
if (mRenderSurface != null) {
|
||||
mRenderSurface.detach();
|
||||
}
|
||||
if (mRenderCallback != null) {
|
||||
mRenderCallback.onDetachedFromSurface();
|
||||
}
|
||||
|
||||
+1
-1
@@ -144,7 +144,7 @@ public class FilamentInstance {
|
||||
*
|
||||
* Ignored if variantIndex is out of bounds.
|
||||
*/
|
||||
void applyMaterialVariant(@IntRange(from = 0) int variantIndex) {
|
||||
public void applyMaterialVariant(@IntRange(from = 0) int variantIndex) {
|
||||
nApplyMaterialVariant(mNativeObject, variantIndex);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.48.0
|
||||
VERSION_NAME=1.50.0
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#Wed Nov 17 10:40:18 PST 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -50,7 +50,7 @@ function replace {
|
||||
FIND_STR="${1//\{\{VERSION\}\}/${VERSION_REGEX}}"
|
||||
REPLACE_STR="${1//\{\{VERSION\}\}/${NEW_VERSION}}"
|
||||
local FILE_NAME="$2"
|
||||
if [ IS_DARWIN == 1 ]; then
|
||||
if [ $IS_DARWIN == 1 ]; then
|
||||
sed -i '' -E "s/${FIND_STR}/${REPLACE_STR}/" "${FILE_NAME}"
|
||||
else
|
||||
sed -i -E "s/${FIND_STR}/${REPLACE_STR}/" "${FILE_NAME}"
|
||||
|
||||
+21
-2
File diff suppressed because one or more lines are too long
Binary file not shown.
+14
-1
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -587,6 +587,7 @@
|
||||
<li><em>overrides</em> Dictionary with one or more of the following properties: mapSize, shadowCascades, constantBias, normalBias, shadowFar, shadowNearHint, shadowFarHint, stable, polygonOffsetConstant, polygonOffsetSlope, \</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>engine.setStereoscopicOptions()</strong></li>
|
||||
<li><strong>engine.setTemporalAntiAliasingOptions()</strong></li>
|
||||
<li><strong>engine.setVignetteOptions()</strong></li>
|
||||
</ul>
|
||||
@@ -1462,6 +1463,8 @@ This defines the following functions:</p>
|
||||
<li>STENCIL_ATTACHMENT</li>
|
||||
<li>UPLOADABLE</li>
|
||||
<li>SAMPLEABLE</li>
|
||||
<li>BLIT_SRC</li>
|
||||
<li>BLIT_DST</li>
|
||||
<li>SUBPASS_INPUT</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+21
-19
@@ -209,10 +209,13 @@ set(PRIVATE_HDRS
|
||||
)
|
||||
|
||||
set(MATERIAL_SRCS
|
||||
src/materials/fsr/fsr_easu.mat
|
||||
src/materials/fsr/fsr_easu_mobile.mat
|
||||
src/materials/fsr/fsr_easu_mobileF.mat
|
||||
src/materials/fsr/fsr_rcas.mat
|
||||
src/materials/antiAliasing/fxaa.mat
|
||||
src/materials/antiAliasing/taa.mat
|
||||
src/materials/blitLow.mat
|
||||
src/materials/bloom/bloomDownsample.mat
|
||||
src/materials/bloom/bloomDownsample2x.mat
|
||||
src/materials/bloom/bloomDownsample9.mat
|
||||
src/materials/bloom/bloomUpsample.mat
|
||||
src/materials/colorGrading/colorGrading.mat
|
||||
src/materials/colorGrading/colorGradingAsSubpass.mat
|
||||
src/materials/colorGrading/customResolveAsSubpass.mat
|
||||
@@ -220,29 +223,28 @@ set(MATERIAL_SRCS
|
||||
src/materials/defaultMaterial.mat
|
||||
src/materials/dof/dof.mat
|
||||
src/materials/dof/dofCoc.mat
|
||||
src/materials/dof/dofDownsample.mat
|
||||
src/materials/dof/dofCombine.mat
|
||||
src/materials/dof/dofDilate.mat
|
||||
src/materials/dof/dofDownsample.mat
|
||||
src/materials/dof/dofMedian.mat
|
||||
src/materials/dof/dofMipmap.mat
|
||||
src/materials/dof/dofTiles.mat
|
||||
src/materials/dof/dofTilesSwizzle.mat
|
||||
src/materials/dof/dofDilate.mat
|
||||
src/materials/dof/dofMipmap.mat
|
||||
src/materials/dof/dofMedian.mat
|
||||
src/materials/flare/flare.mat
|
||||
src/materials/bloom/bloomDownsample.mat
|
||||
src/materials/bloom/bloomDownsample2x.mat
|
||||
src/materials/bloom/bloomDownsample9.mat
|
||||
src/materials/bloom/bloomUpsample.mat
|
||||
src/materials/fsr/fsr_easu.mat
|
||||
src/materials/fsr/fsr_easu_mobile.mat
|
||||
src/materials/fsr/fsr_easu_mobileF.mat
|
||||
src/materials/fsr/fsr_rcas.mat
|
||||
src/materials/resolveDepth.mat
|
||||
src/materials/separableGaussianBlur.mat
|
||||
src/materials/skybox.mat
|
||||
src/materials/shadowmap.mat
|
||||
src/materials/ssao/bilateralBlur.mat
|
||||
src/materials/ssao/bilateralBlurBentNormals.mat
|
||||
src/materials/ssao/mipmapDepth.mat
|
||||
src/materials/skybox.mat
|
||||
src/materials/ssao/sao.mat
|
||||
src/materials/ssao/saoBentNormals.mat
|
||||
src/materials/separableGaussianBlur.mat
|
||||
src/materials/antiAliasing/fxaa.mat
|
||||
src/materials/antiAliasing/taa.mat
|
||||
src/materials/vsmMipmap.mat
|
||||
src/materials/blitLow.mat
|
||||
)
|
||||
|
||||
set(MATERIAL_FL0_SRCS
|
||||
@@ -320,7 +322,7 @@ foreach (mat_src ${MATERIAL_SRCS})
|
||||
list(APPEND MATERIAL_BINS ${output_path})
|
||||
endforeach()
|
||||
|
||||
if (FILAMENT_ENABLE_FEATURE_LEVEL_0 AND FILAMENT_SUPPORTS_OPENGL)
|
||||
if (FILAMENT_ENABLE_FEATURE_LEVEL_0)
|
||||
foreach (mat_src ${MATERIAL_FL0_SRCS})
|
||||
get_filename_component(localname "${mat_src}" NAME_WE)
|
||||
get_filename_component(fullname "${mat_src}" ABSOLUTE)
|
||||
@@ -328,7 +330,7 @@ if (FILAMENT_ENABLE_FEATURE_LEVEL_0 AND FILAMENT_SUPPORTS_OPENGL)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${output_path}
|
||||
COMMAND matc -a opengl -p ${MATC_TARGET} ${MATC_OPT_FLAGS} -o ${output_path} ${fullname}
|
||||
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
|
||||
MAIN_DEPENDENCY ${fullname}
|
||||
DEPENDS matc
|
||||
COMMENT "Compiling material ${mat_src} to ${output_path}"
|
||||
|
||||
@@ -169,6 +169,8 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
||||
src/vulkan/platform/VulkanPlatform.cpp
|
||||
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
|
||||
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
|
||||
src/vulkan/spirv/VulkanSpirvUtils.cpp
|
||||
src/vulkan/spirv/VulkanSpirvUtils.h
|
||||
src/vulkan/VulkanBlitter.cpp
|
||||
src/vulkan/VulkanBlitter.h
|
||||
src/vulkan/VulkanBuffer.cpp
|
||||
@@ -313,6 +315,7 @@ endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_VULKAN)
|
||||
target_link_libraries(${TARGET} PUBLIC bluevk vkmemalloc vkshaders smol-v)
|
||||
target_link_libraries(${TARGET} PRIVATE SPIRV-Headers)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_METAL)
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_CALLBACKHANDLER_H
|
||||
#define TNT_FILAMENT_BACKEND_CALLBACKHANDLER_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <array> // FIXME: STL headers are not allowed in public headers
|
||||
#include <array> // FIXME: STL headers are not allowed in public headers
|
||||
#include <type_traits> // FIXME: STL headers are not allowed in public headers
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -227,6 +228,7 @@ struct Viewport {
|
||||
int32_t top() const noexcept { return bottom + int32_t(height); }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Specifies the mapping of the near and far clipping plane to window coordinates.
|
||||
*/
|
||||
@@ -658,13 +660,15 @@ enum class TextureFormat : uint16_t {
|
||||
|
||||
//! Bitmask describing the intended Texture Usage
|
||||
enum class TextureUsage : uint8_t {
|
||||
NONE = 0x0,
|
||||
COLOR_ATTACHMENT = 0x1, //!< Texture can be used as a color attachment
|
||||
DEPTH_ATTACHMENT = 0x2, //!< Texture can be used as a depth attachment
|
||||
STENCIL_ATTACHMENT = 0x4, //!< Texture can be used as a stencil attachment
|
||||
UPLOADABLE = 0x8, //!< Data can be uploaded into this texture (default)
|
||||
NONE = 0x00,
|
||||
COLOR_ATTACHMENT = 0x01, //!< Texture can be used as a color attachment
|
||||
DEPTH_ATTACHMENT = 0x02, //!< Texture can be used as a depth attachment
|
||||
STENCIL_ATTACHMENT = 0x04, //!< Texture can be used as a stencil attachment
|
||||
UPLOADABLE = 0x08, //!< Data can be uploaded into this texture (default)
|
||||
SAMPLEABLE = 0x10, //!< Texture can be sampled (default)
|
||||
SUBPASS_INPUT = 0x20, //!< Texture can be used as a subpass input
|
||||
BLIT_SRC = 0x40, //!< Texture can be used the source of a blit()
|
||||
BLIT_DST = 0x80, //!< Texture can be used the destination of a blit()
|
||||
DEFAULT = UPLOADABLE | SAMPLEABLE //!< Default texture usage
|
||||
};
|
||||
|
||||
@@ -692,6 +696,17 @@ static constexpr bool isDepthFormat(TextureFormat format) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr bool isStencilFormat(TextureFormat format) noexcept {
|
||||
switch (format) {
|
||||
case TextureFormat::STENCIL8:
|
||||
case TextureFormat::DEPTH24_STENCIL8:
|
||||
case TextureFormat::DEPTH32F_STENCIL8:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr bool isUnsignedIntFormat(TextureFormat format) {
|
||||
switch (format) {
|
||||
case TextureFormat::R8UI:
|
||||
@@ -1180,8 +1195,8 @@ using FrameScheduledCallback = void(*)(PresentCallable callable, void* user);
|
||||
enum class Workaround : uint16_t {
|
||||
// The EASU pass must split because shader compiler flattens early-exit branch
|
||||
SPLIT_EASU,
|
||||
// Backend allows feedback loop with ancillary buffers (depth/stencil) as long as they're read-only for
|
||||
// the whole render pass.
|
||||
// Backend allows feedback loop with ancillary buffers (depth/stencil) as long as they're
|
||||
// read-only for 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,
|
||||
|
||||
@@ -17,16 +17,14 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_HANDLE_H
|
||||
#define TNT_FILAMENT_BACKEND_HANDLE_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#if !defined(NDEBUG)
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
#endif
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <type_traits> // FIXME: STL headers are not allowed in public headers
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -54,7 +52,7 @@ struct HwVertexBuffer;
|
||||
class HandleBase {
|
||||
public:
|
||||
using HandleId = uint32_t;
|
||||
static constexpr const HandleId nullid = HandleId{ std::numeric_limits<HandleId>::max() };
|
||||
static constexpr const HandleId nullid = HandleId{ UINT32_MAX };
|
||||
|
||||
constexpr HandleBase() noexcept: object(nullid) {}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <limits>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -33,10 +33,7 @@ struct PipelineState {
|
||||
RasterState rasterState;
|
||||
StencilState stencilState;
|
||||
PolygonOffset polygonOffset;
|
||||
Viewport scissor{ 0, 0,
|
||||
(uint32_t)std::numeric_limits<int32_t>::max(),
|
||||
(uint32_t)std::numeric_limits<int32_t>::max()
|
||||
};
|
||||
Viewport scissor{ 0, 0, (uint32_t)INT32_MAX, (uint32_t)INT32_MAX };
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_PLATFORM_H
|
||||
#define TNT_FILAMENT_BACKEND_PLATFORM_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Invocable.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class Driver;
|
||||
@@ -41,18 +41,24 @@ public:
|
||||
struct Stream {};
|
||||
|
||||
struct DriverConfig {
|
||||
/*
|
||||
* size of handle arena in bytes. Setting to 0 indicates default value is to be used.
|
||||
/**
|
||||
* Size of handle arena in bytes. Setting to 0 indicates default value is to be used.
|
||||
* Driver clamps to valid values.
|
||||
*/
|
||||
size_t handleArenaSize = 0;
|
||||
|
||||
/*
|
||||
* this number of most-recently destroyed textures will be tracked for use-after-free.
|
||||
/**
|
||||
* This number of most-recently destroyed textures will be tracked for use-after-free.
|
||||
* Throws an exception when a texture is freed but still bound to a SamplerGroup and used in
|
||||
* a draw call. 0 disables completely. Currently only respected by the Metal backend.
|
||||
*/
|
||||
size_t textureUseAfterFreePoolSize = 0;
|
||||
|
||||
/**
|
||||
* Set to `true` to forcibly disable parallel shader compilation in the backend.
|
||||
* Currently only honored by the GL backend.
|
||||
*/
|
||||
bool disableParallelShaderCompile = false;
|
||||
};
|
||||
|
||||
Platform() noexcept;
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
namespace filament::backend {
|
||||
|
||||
/**
|
||||
* A PresentCallable is a callable object that, when called, schedules a frame for presentation on
|
||||
@@ -98,7 +97,6 @@ private:
|
||||
*/
|
||||
using FrameFinishedCallback UTILS_DEPRECATED = void(*)(PresentCallable callable, void* user);
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_PRESENTCALLABLE
|
||||
|
||||
@@ -17,17 +17,19 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_PROGRAM_H
|
||||
#define TNT_FILAMENT_BACKEND_PRIVATE_PROGRAM_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <array>
|
||||
#include <variant>
|
||||
#include <array> // FIXME: STL headers are not allowed in public headers
|
||||
#include <utility> // FIXME: STL headers are not allowed in public headers
|
||||
#include <variant> // FIXME: STL headers are not allowed in public headers
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
|
||||
@@ -24,9 +24,6 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
struct UTILS_PUBLIC SamplerDescriptor {
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_TARGETBUFFERINFO_H
|
||||
#define TNT_FILAMENT_BACKEND_TARGETBUFFERINFO_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -18,8 +18,11 @@
|
||||
#define TNT_FILAMENT_BACKEND_PRIVATE_OPENGLPLATFORM_H
|
||||
|
||||
#include <backend/AcquiredImage.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class Driver;
|
||||
|
||||
@@ -17,11 +17,10 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_COCOA_GL_H
|
||||
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_COCOA_GL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ struct PlatformCocoaTouchGLImpl;
|
||||
class PlatformCocoaTouchGL : public OpenGLPlatform {
|
||||
public:
|
||||
PlatformCocoaTouchGL();
|
||||
~PlatformCocoaTouchGL() noexcept;
|
||||
~PlatformCocoaTouchGL() noexcept override;
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Platform Interface
|
||||
|
||||
@@ -17,18 +17,21 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_H
|
||||
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Platform.h>
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <EGL/eglplatform.h>
|
||||
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,8 +17,14 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
|
||||
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
|
||||
|
||||
#include <backend/AcquiredImage.h>
|
||||
#include <backend/Platform.h>
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
#include <backend/platforms/PlatformEGL.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class ExternalStreamManagerAndroid;
|
||||
|
||||
@@ -20,13 +20,18 @@
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/PrivateImplementation.h>
|
||||
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using SwapChain = Platform::SwapChain;
|
||||
|
||||
@@ -17,10 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_CIRCULARBUFFER_H
|
||||
#define TNT_FILAMENT_BACKEND_PRIVATE_CIRCULARBUFFER_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
|
||||
@@ -19,12 +19,14 @@
|
||||
|
||||
#include "private/backend/CircularBuffer.h"
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Condition.h>
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
/*
|
||||
|
||||
@@ -32,11 +32,13 @@
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ThreadUtils.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -152,14 +154,14 @@ struct CommandType<void (Driver::*)(ARGS...)> {
|
||||
}
|
||||
|
||||
// placement new declared as "throw" to avoid the compiler's null-check
|
||||
inline void* operator new(std::size_t size, void* ptr) {
|
||||
inline void* operator new(std::size_t, void* ptr) {
|
||||
assert_invariant(ptr);
|
||||
return ptr;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// convert an method of "class Driver" into a Command<> type
|
||||
// convert a method of "class Driver" into a Command<> type
|
||||
#define COMMAND_TYPE(method) CommandType<decltype(&Driver::method)>::Command<&Driver::method>
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Command debugging off. debugging virtuals are not called.
|
||||
|
||||
@@ -300,10 +300,10 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isAutoDepthResolveSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isSRGBSwapChainSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isStereoSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthStencilResolveSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, canGenerateMipmaps)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage, void*, image)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, getTimerQueryValue, backend::TimerQueryHandle, query, uint64_t*, elapsedTime)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isWorkaroundNeeded, backend::Workaround, workaround)
|
||||
@@ -468,7 +468,7 @@ DECL_DRIVER_API_N(readBufferSubData,
|
||||
* --------------------
|
||||
*/
|
||||
|
||||
DECL_DRIVER_API_N(blit,
|
||||
DECL_DRIVER_API_N(blitDEPRECATED,
|
||||
backend::TargetBufferFlags, buffers,
|
||||
backend::RenderTargetHandle, dst,
|
||||
backend::Viewport, dstRect,
|
||||
@@ -476,6 +476,21 @@ DECL_DRIVER_API_N(blit,
|
||||
backend::Viewport, srcRect,
|
||||
backend::SamplerMagFilter, filter)
|
||||
|
||||
DECL_DRIVER_API_N(resolve,
|
||||
backend::TextureHandle, dst,
|
||||
uint8_t, dstLevel, uint8_t, dstLayer,
|
||||
backend::TextureHandle, src,
|
||||
uint8_t, srcLevel, uint8_t, srcLayer)
|
||||
|
||||
DECL_DRIVER_API_N(blit,
|
||||
backend::TextureHandle, dst,
|
||||
uint8_t, dstLevel, uint8_t, dstLayer,
|
||||
math::uint2, dstOrigin,
|
||||
backend::TextureHandle, src,
|
||||
uint8_t, srcLevel, uint8_t, srcLayer,
|
||||
math::uint2, srcOrigin,
|
||||
math::uint2, size)
|
||||
|
||||
DECL_DRIVER_API_N(draw,
|
||||
backend::PipelineState, state,
|
||||
backend::RenderPrimitiveHandle, rph,
|
||||
|
||||
@@ -22,9 +22,18 @@
|
||||
#include <utils/Allocator.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
#include <exception>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if !defined(NDEBUG) && UTILS_HAS_RTTI
|
||||
# define HANDLE_TYPE_SAFETY 1
|
||||
#else
|
||||
@@ -222,7 +231,7 @@ private:
|
||||
explicit Allocator(const utils::AreaPolicy::HeapArea& area);
|
||||
|
||||
// this is in fact always called with a constexpr size argument
|
||||
[[nodiscard]] inline void* alloc(size_t size, size_t alignment, size_t extra) noexcept {
|
||||
[[nodiscard]] inline void* alloc(size_t size, size_t, size_t extra) noexcept {
|
||||
void* p = nullptr;
|
||||
if (size <= mPool0.getSize()) p = mPool0.alloc(size, 16, extra);
|
||||
else if (size <= mPool1.getSize()) p = mPool1.alloc(size, 16, extra);
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_PLATFORM_FACTORY_H
|
||||
#define TNT_FILAMENT_BACKEND_PLATFORM_FACTORY_H
|
||||
|
||||
#include "backend/DriverEnums.h"
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include "utils/compiler.h"
|
||||
#include <utils/compiler.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
|
||||
@@ -17,15 +17,13 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_SAMPLERGROUP_H
|
||||
#define TNT_FILAMENT_BACKEND_PRIVATE_SAMPLERGROUP_H
|
||||
|
||||
#include "backend/DriverApiForward.h"
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <backend/DriverApiForward.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/SamplerDescriptor.h>
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <backend/PixelBufferDescriptor.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
@@ -21,7 +21,12 @@
|
||||
|
||||
#include <backend/AcquiredImage.h>
|
||||
#include <backend/BufferDescriptor.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <math/half.h>
|
||||
@@ -29,6 +34,13 @@
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
using namespace utils;
|
||||
using namespace filament::math;
|
||||
|
||||
@@ -119,7 +131,8 @@ void DriverBase::purge() noexcept {
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void DriverBase::scheduleDestroySlow(BufferDescriptor&& buffer) noexcept {
|
||||
scheduleCallback(buffer.getHandler(), [buffer = std::move(buffer)]() {
|
||||
auto const handler = buffer.getHandler();
|
||||
scheduleCallback(handler, [buffer = std::move(buffer)]() {
|
||||
// user callback is called when BufferDescriptor gets destroyed
|
||||
});
|
||||
}
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
#include <tsl/robin_map.h>
|
||||
#include <utils/Hash.h>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
namespace filament::backend {
|
||||
|
||||
struct MetalContext;
|
||||
|
||||
@@ -37,8 +36,7 @@ public:
|
||||
|
||||
struct BlitArgs {
|
||||
struct Attachment {
|
||||
id<MTLTexture> color = nil;
|
||||
id<MTLTexture> depth = nil;
|
||||
id<MTLTexture> texture = nil;
|
||||
MTLRegion region = {};
|
||||
uint8_t level = 0;
|
||||
uint32_t slice = 0; // must be 0 on source attachment
|
||||
@@ -46,25 +44,13 @@ public:
|
||||
|
||||
// Valid source formats: 2D, 2DArray, 2DMultisample, 3D
|
||||
// Valid destination formats: 2D, 2DArray, 3D, Cube
|
||||
Attachment source, destination;
|
||||
Attachment source;
|
||||
Attachment destination;
|
||||
SamplerMagFilter filter;
|
||||
|
||||
bool blitColor() const {
|
||||
return source.color != nil && destination.color != nil;
|
||||
}
|
||||
|
||||
bool blitDepth() const {
|
||||
return source.depth != nil && destination.depth != nil;
|
||||
}
|
||||
|
||||
bool colorDestinationIsFullAttachment() const {
|
||||
return destination.color.width == destination.region.size.width &&
|
||||
destination.color.height == destination.region.size.height;
|
||||
}
|
||||
|
||||
bool depthDestinationIsFullAttachment() const {
|
||||
return destination.depth.width == destination.region.size.width &&
|
||||
destination.depth.height == destination.region.size.height;
|
||||
bool destinationIsFullAttachment() const {
|
||||
return destination.texture.width == destination.region.size.width &&
|
||||
destination.texture.height == destination.region.size.height;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -78,48 +64,36 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
static void setupColorAttachment(const BlitArgs& args, MTLRenderPassDescriptor* descriptor,
|
||||
uint32_t depthPlane);
|
||||
static void setupDepthAttachment(const BlitArgs& args, MTLRenderPassDescriptor* descriptor,
|
||||
uint32_t depthPlane);
|
||||
static void setupAttachment(MTLRenderPassAttachmentDescriptor* descriptor,
|
||||
const BlitArgs& args, uint32_t depthPlane);
|
||||
|
||||
struct BlitFunctionKey {
|
||||
bool blitColor;
|
||||
bool blitDepth;
|
||||
bool msaaColorSource;
|
||||
bool msaaDepthSource;
|
||||
bool sources3D;
|
||||
|
||||
char padding[3];
|
||||
bool msaaColorSource{};
|
||||
bool sources3D{};
|
||||
char padding[2]{};
|
||||
|
||||
bool isValid() const noexcept {
|
||||
// MSAA 3D textures do not exist.
|
||||
bool hasMsaa = msaaColorSource || msaaDepthSource;
|
||||
bool const hasMsaa = msaaColorSource;
|
||||
return !(hasMsaa && sources3D);
|
||||
}
|
||||
|
||||
bool operator==(const BlitFunctionKey& rhs) const noexcept {
|
||||
return blitColor == rhs.blitColor &&
|
||||
blitDepth == rhs.blitDepth &&
|
||||
msaaColorSource == rhs.msaaColorSource &&
|
||||
msaaDepthSource == rhs.msaaDepthSource &&
|
||||
return msaaColorSource == rhs.msaaColorSource &&
|
||||
sources3D == rhs.sources3D;
|
||||
}
|
||||
|
||||
BlitFunctionKey() {
|
||||
std::memset(this, 0, sizeof(BlitFunctionKey));
|
||||
}
|
||||
};
|
||||
|
||||
void blitFastPath(id<MTLCommandBuffer> cmdBuffer, bool& blitColor, bool& blitDepth,
|
||||
static bool blitFastPath(id<MTLCommandBuffer> cmdBuffer,
|
||||
const BlitArgs& args, const char* label);
|
||||
void blitSlowPath(id<MTLCommandBuffer> cmdBuffer, bool& blitColor, bool& blitDepth,
|
||||
|
||||
void blitSlowPath(id<MTLCommandBuffer> cmdBuffer,
|
||||
const BlitArgs& args, const char* label);
|
||||
void blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor, bool blitDepth,
|
||||
const BlitArgs& args, uint32_t depthPlaneSource, uint32_t depthPlaneDest,
|
||||
const char* label);
|
||||
id<MTLTexture> createIntermediateTexture(id<MTLTexture> t, MTLSize size);
|
||||
id<MTLFunction> compileFragmentFunction(BlitFunctionKey key);
|
||||
|
||||
void blitDepthPlane(id <MTLCommandBuffer> cmdBuffer, const BlitArgs& args,
|
||||
uint32_t depthPlaneSource, uint32_t depthPlaneDest, const char* label);
|
||||
|
||||
id<MTLFunction> compileFragmentFunction(BlitFunctionKey key) const;
|
||||
id<MTLFunction> getBlitVertexFunction();
|
||||
id<MTLFunction> getBlitFragmentFunction(BlitFunctionKey key);
|
||||
|
||||
@@ -130,10 +104,9 @@ private:
|
||||
tsl::robin_map<BlitFunctionKey, Function, HashFn> mBlitFunctions;
|
||||
|
||||
id<MTLFunction> mVertexFunction = nil;
|
||||
|
||||
};
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
} // namespace filament::backend
|
||||
|
||||
|
||||
#endif //TNT_METALBLITTER_H
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
#include "MetalUtils.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
namespace filament::backend {
|
||||
|
||||
static const char* functionLibrary = R"(
|
||||
#include <metal_stdlib>
|
||||
@@ -37,13 +37,7 @@ struct VertexOut
|
||||
|
||||
struct FragmentOut
|
||||
{
|
||||
#ifdef BLIT_COLOR
|
||||
float4 color [[color(0)]];
|
||||
#endif
|
||||
|
||||
#ifdef BLIT_DEPTH
|
||||
float depth [[depth(any)]];
|
||||
#endif
|
||||
};
|
||||
|
||||
vertex VertexOut
|
||||
@@ -67,7 +61,6 @@ fragment FragmentOut
|
||||
blitterFrag(VertexOut in [[stage_in]],
|
||||
sampler sourceSampler [[sampler(0)]],
|
||||
|
||||
#ifdef BLIT_COLOR
|
||||
#ifdef MSAA_COLOR_SOURCE
|
||||
texture2d_ms<float, access::read> sourceColor [[texture(0)]],
|
||||
#elif SOURCES_3D
|
||||
@@ -75,32 +68,18 @@ blitterFrag(VertexOut in [[stage_in]],
|
||||
#else
|
||||
texture2d<float, access::sample> sourceColor [[texture(0)]],
|
||||
#endif // MSAA_COLOR_SOURCE
|
||||
#endif // BLIT_COLOR
|
||||
|
||||
#ifdef BLIT_DEPTH
|
||||
#ifdef MSAA_DEPTH_SOURCE
|
||||
texture2d_ms<float, access::read> sourceDepth [[texture(1)]],
|
||||
#elif SOURCES_3D
|
||||
texture3d<float, access::sample> sourceDepth [[texture(1)]],
|
||||
#else
|
||||
texture2d<float, access::sample> sourceDepth [[texture(1)]],
|
||||
#endif // MSAA_DEPTH_SOURCE
|
||||
#endif // BLIT_DEPTH
|
||||
|
||||
constant FragmentArgs* args [[buffer(0)]])
|
||||
{
|
||||
FragmentOut out = {};
|
||||
|
||||
#if defined(BLIT_COLOR) || defined(BLIT_DEPTH)
|
||||
// These coordinates match the Vulkan vkCmdBlitImage spec:
|
||||
// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdBlitImage.html
|
||||
float2 uvbase = in.position.xy; // unnormalized coordinates at center of texel: (1.5, 2.5, etc)
|
||||
float2 uvoffset = uvbase - args->dstOffset;
|
||||
float2 uvscaled = uvoffset * args->scale;
|
||||
float2 uv = uvscaled + args->srcOffset;
|
||||
#endif
|
||||
|
||||
#ifdef BLIT_COLOR
|
||||
#ifdef MSAA_COLOR_SOURCE
|
||||
out.color = float4(0.0);
|
||||
for (uint s = 0; s < sourceColor.get_num_samples(); s++) {
|
||||
@@ -116,201 +95,106 @@ blitterFrag(VertexOut in [[stage_in]],
|
||||
float2 uvnorm = uv / float2(sourceColor.get_width(args->lod), sourceColor.get_height(args->lod));
|
||||
out.color += sourceColor.sample(sourceSampler, uvnorm, level(args->lod));
|
||||
#endif // MSAA_COLOR_SOURCE
|
||||
#endif // BLIT_COLOR
|
||||
|
||||
#ifdef BLIT_DEPTH
|
||||
#ifdef MSAA_DEPTH_SOURCE
|
||||
out.depth = 0.0;
|
||||
for (uint s = 0; s < sourceDepth.get_num_samples(); s++) {
|
||||
out.depth += sourceDepth.read(static_cast<uint2>(uv), s).r;
|
||||
}
|
||||
out.depth /= sourceDepth.get_num_samples();
|
||||
#elif SOURCES_3D
|
||||
float2 uvnormd = uv / float2(sourceDepth.get_width(args->lod), sourceDepth.get_height(args->lod));
|
||||
float3 coords = float3(uvnormd, (static_cast<float>(args->depthPlane) + 0.5) /
|
||||
sourceDepth.get_depth(args->lod));
|
||||
out.depth = sourceDepth.sample(sourceSampler, coords, level(args->lod)).r;
|
||||
#else
|
||||
float2 uvnormd = uv / float2(sourceDepth.get_width(args->lod), sourceDepth.get_height(args->lod));
|
||||
out.depth = sourceDepth.sample(sourceSampler, uvnormd, level(args->lod)).r;
|
||||
#endif // MSAA_DEPTH_SOURCE
|
||||
#endif // BLIT_DEPTH
|
||||
return out;
|
||||
}
|
||||
)";
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline bool MTLSizeEqual(T a, T b) noexcept {
|
||||
return (a.width == b.width && a.height == b.height && a.depth == b.depth);
|
||||
}
|
||||
|
||||
MetalBlitter::MetalBlitter(MetalContext& context) noexcept : mContext(context) { }
|
||||
|
||||
#define MTLSizeEqual(a, b) (a.width == b.width && a.height == b.height && a.depth == b.depth)
|
||||
|
||||
void MetalBlitter::blit(id<MTLCommandBuffer> cmdBuffer, const BlitArgs& args, const char* label) {
|
||||
bool blitColor = args.blitColor();
|
||||
bool blitDepth = args.blitDepth();
|
||||
|
||||
ASSERT_PRECONDITION(args.source.region.size.depth == args.destination.region.size.depth,
|
||||
"Blitting requires the source and destination regions to have the same depth.");
|
||||
|
||||
if (args.source.color && args.source.depth) {
|
||||
MTLTextureType colorType = args.source.color.textureType;
|
||||
MTLTextureType depthType = args.source.depth.textureType;
|
||||
|
||||
if (colorType == MTLTextureType2DMultisample) colorType = MTLTextureType2D;
|
||||
if (depthType == MTLTextureType2DMultisample) depthType = MTLTextureType2D;
|
||||
|
||||
ASSERT_PRECONDITION(colorType == depthType,
|
||||
"Blitting requires color and depth sources to be the same texture type.");
|
||||
}
|
||||
|
||||
// Determine if the blit for color or depth are eligible to use a MTLBlitCommandEncoder.
|
||||
// blitColor and / or blitDepth are set to false upon success, to indicate that no more work is
|
||||
// necessary for that attachment.
|
||||
blitFastPath(cmdBuffer, blitColor, blitDepth, args, label);
|
||||
|
||||
if (!blitColor && !blitDepth) {
|
||||
// blitFastPath returns true upon success.
|
||||
// blitFastPath fails if either the format or sampleCount don't match (i.e. resolve)
|
||||
if (blitFastPath(cmdBuffer, args, label)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the destination is MSAA and we weren't able to use the fast path, report an error, as
|
||||
// blitting to a MSAA texture isn't supported through the "slow path" yet.
|
||||
const bool colorDestinationIsMultisample =
|
||||
blitColor && args.destination.color.textureType == MTLTextureType2DMultisample;
|
||||
const bool depthDestinationIsMultisample =
|
||||
blitDepth && args.destination.depth.textureType == MTLTextureType2DMultisample;
|
||||
ASSERT_PRECONDITION(!colorDestinationIsMultisample && !depthDestinationIsMultisample,
|
||||
"Blitting between MSAA render targets with differing pixel formats and/or regions is not supported.");
|
||||
// If we end-up here, it means that either:
|
||||
// - we're resolving a color texture
|
||||
// - src/dest formats didn't match, or we're scaling -- this can only happen with the legacy
|
||||
// blit() path and implies that the format is not depth.
|
||||
// note: in the future we will support a fast-path resolve
|
||||
|
||||
// If the destination texture doesn't have the MTLTextureUsageRenderTarget flag, we have to blit
|
||||
// to an intermediate texture first to perform the format conversion. Then, we can perform a
|
||||
// "fast blit" to the final destination texture.
|
||||
UTILS_UNUSED_IN_RELEASE
|
||||
const bool destinationIsMultisample =
|
||||
args.destination.texture.textureType == MTLTextureType2DMultisample;
|
||||
|
||||
id<MTLTexture> intermediateColor = nil;
|
||||
id<MTLTexture> intermediateDepth = nil;
|
||||
BlitArgs slowBlit = args;
|
||||
BlitArgs finalBlit = args;
|
||||
|
||||
MTLRegion sourceRegionNoOffset = MTLRegionMake3D(0, 0, 0,
|
||||
args.source.region.size.width, args.source.region.size.height,
|
||||
args.source.region.size.depth);
|
||||
|
||||
if (blitColor && !(args.destination.color.usage & MTLTextureUsageRenderTarget)) {
|
||||
intermediateColor = createIntermediateTexture(args.destination.color, args.source.region.size);
|
||||
slowBlit.destination.color = finalBlit.source.color = intermediateColor;
|
||||
slowBlit.destination.level = finalBlit.source.level = 0;
|
||||
slowBlit.destination.slice = finalBlit.source.slice = 0;
|
||||
slowBlit.destination.region = finalBlit.source.region = sourceRegionNoOffset;
|
||||
}
|
||||
if (blitDepth && !(args.destination.depth.usage & MTLTextureUsageRenderTarget)) {
|
||||
intermediateDepth = createIntermediateTexture(args.destination.depth, args.source.region.size);
|
||||
slowBlit.destination.depth = finalBlit.source.depth = intermediateDepth;
|
||||
slowBlit.destination.level = finalBlit.source.level = 0;
|
||||
slowBlit.destination.slice = finalBlit.source.slice = 0;
|
||||
slowBlit.destination.region = finalBlit.source.region = sourceRegionNoOffset;
|
||||
}
|
||||
|
||||
blitSlowPath(cmdBuffer, blitColor, blitDepth, slowBlit, label);
|
||||
|
||||
bool finalBlitColor = intermediateColor != nil;
|
||||
bool finalBlitDepth = intermediateDepth != nil;
|
||||
blitFastPath(cmdBuffer, finalBlitColor, finalBlitDepth, finalBlit, label);
|
||||
assert_invariant(!destinationIsMultisample);
|
||||
assert_invariant((args.destination.texture.usage & MTLTextureUsageRenderTarget));
|
||||
blitSlowPath(cmdBuffer, args, label);
|
||||
}
|
||||
|
||||
void MetalBlitter::blitFastPath(id<MTLCommandBuffer> cmdBuffer, bool& blitColor, bool& blitDepth,
|
||||
bool MetalBlitter::blitFastPath(id<MTLCommandBuffer> cmdBuffer,
|
||||
const BlitArgs& args, const char* label) {
|
||||
if (blitColor) {
|
||||
if (args.source.color.sampleCount == args.destination.color.sampleCount &&
|
||||
args.source.color.pixelFormat == args.destination.color.pixelFormat &&
|
||||
MTLSizeEqual(args.source.region.size, args.destination.region.size)) {
|
||||
|
||||
id<MTLBlitCommandEncoder> blitEncoder = [cmdBuffer blitCommandEncoder];
|
||||
blitEncoder.label = @(label);
|
||||
[blitEncoder copyFromTexture:args.source.color
|
||||
sourceSlice:args.source.slice
|
||||
sourceLevel:args.source.level
|
||||
sourceOrigin:args.source.region.origin
|
||||
sourceSize:args.source.region.size
|
||||
toTexture:args.destination.color
|
||||
destinationSlice:args.destination.slice
|
||||
destinationLevel:args.destination.level
|
||||
destinationOrigin:args.destination.region.origin];
|
||||
[blitEncoder endEncoding];
|
||||
if (args.source.texture.sampleCount == args.destination.texture.sampleCount &&
|
||||
args.source.texture.pixelFormat == args.destination.texture.pixelFormat &&
|
||||
MTLSizeEqual(args.source.region.size, args.destination.region.size)) {
|
||||
|
||||
blitColor = false;
|
||||
}
|
||||
}
|
||||
id<MTLBlitCommandEncoder> const blitEncoder = [cmdBuffer blitCommandEncoder];
|
||||
blitEncoder.label = @(label);
|
||||
[blitEncoder copyFromTexture:args.source.texture
|
||||
sourceSlice:args.source.slice
|
||||
sourceLevel:args.source.level
|
||||
sourceOrigin:args.source.region.origin
|
||||
sourceSize:args.source.region.size
|
||||
toTexture:args.destination.texture
|
||||
destinationSlice:args.destination.slice
|
||||
destinationLevel:args.destination.level
|
||||
destinationOrigin:args.destination.region.origin];
|
||||
[blitEncoder endEncoding];
|
||||
|
||||
if (blitDepth) {
|
||||
if (args.source.depth.sampleCount == args.destination.depth.sampleCount &&
|
||||
args.source.depth.pixelFormat == args.destination.depth.pixelFormat &&
|
||||
MTLSizeEqual(args.source.region.size, args.destination.region.size)) {
|
||||
|
||||
id<MTLBlitCommandEncoder> blitEncoder = [cmdBuffer blitCommandEncoder];
|
||||
blitEncoder.label = @(label);
|
||||
[blitEncoder copyFromTexture:args.source.depth
|
||||
sourceSlice:args.source.slice
|
||||
sourceLevel:args.source.level
|
||||
sourceOrigin:args.source.region.origin
|
||||
sourceSize:args.source.region.size
|
||||
toTexture:args.destination.depth
|
||||
destinationSlice:args.destination.slice
|
||||
destinationLevel:args.destination.level
|
||||
destinationOrigin:args.destination.region.origin];
|
||||
[blitEncoder endEncoding];
|
||||
|
||||
blitDepth = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MetalBlitter::blitSlowPath(id<MTLCommandBuffer> cmdBuffer, bool& blitColor, bool& blitDepth,
|
||||
void MetalBlitter::blitSlowPath(id<MTLCommandBuffer> cmdBuffer,
|
||||
const BlitArgs& args, const char* label) {
|
||||
|
||||
// scaling in any dimension is not allowed
|
||||
assert_invariant(args.source.region.size.depth == args.destination.region.size.depth);
|
||||
// we're always blitting a single plane
|
||||
uint32_t depthPlaneSource = args.source.region.origin.z;
|
||||
uint32_t depthPlaneDest = args.destination.region.origin.z;
|
||||
|
||||
assert_invariant(args.source.region.size.depth == args.destination.region.size.depth);
|
||||
uint32_t depthPlaneCount = args.source.region.size.depth;
|
||||
for (NSUInteger d = 0; d < depthPlaneCount; d++) {
|
||||
blitDepthPlane(cmdBuffer, blitColor, blitDepth, args, depthPlaneSource++, depthPlaneDest++,
|
||||
label);
|
||||
for (NSUInteger d = 0; d < args.source.region.size.depth; d++) {
|
||||
blitDepthPlane(cmdBuffer, args,
|
||||
depthPlaneSource++, depthPlaneDest++, label);
|
||||
}
|
||||
|
||||
blitColor = false;
|
||||
blitDepth = false;
|
||||
}
|
||||
|
||||
void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor, bool blitDepth,
|
||||
const BlitArgs& args, uint32_t depthPlaneSource, uint32_t depthPlaneDest,
|
||||
const char* label) {
|
||||
MTLRenderPassDescriptor* descriptor = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||
void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, const BlitArgs& args,
|
||||
uint32_t depthPlaneSource, uint32_t depthPlaneDest, const char* label) {
|
||||
|
||||
if (blitColor) {
|
||||
setupColorAttachment(args, descriptor, depthPlaneDest);
|
||||
}
|
||||
MTLRenderPassDescriptor* const descriptor = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||
|
||||
if (blitDepth) {
|
||||
setupDepthAttachment(args, descriptor, depthPlaneDest);
|
||||
}
|
||||
setupAttachment(descriptor.colorAttachments[0], args, depthPlaneDest);
|
||||
|
||||
id<MTLRenderCommandEncoder> encoder = [cmdBuffer renderCommandEncoderWithDescriptor:descriptor];
|
||||
id<MTLRenderCommandEncoder> const encoder =
|
||||
[cmdBuffer renderCommandEncoderWithDescriptor:descriptor];
|
||||
encoder.label = @(label);
|
||||
|
||||
BlitFunctionKey key;
|
||||
key.blitColor = blitColor;
|
||||
key.blitDepth = blitDepth;
|
||||
key.msaaColorSource = blitColor && args.source.color.textureType == MTLTextureType2DMultisample;
|
||||
key.msaaDepthSource = blitDepth && args.source.depth.textureType == MTLTextureType2DMultisample;
|
||||
key.sources3D = blitColor && args.source.color.textureType == MTLTextureType3D;
|
||||
if (key.sources3D && blitDepth) {
|
||||
assert_invariant(args.source.depth.textureType == MTLTextureType3D);
|
||||
}
|
||||
id<MTLFunction> fragmentFunction = getBlitFragmentFunction(key);
|
||||
key.msaaColorSource = args.source.texture.textureType == MTLTextureType2DMultisample;
|
||||
key.sources3D = args.source.texture.textureType == MTLTextureType3D;
|
||||
id<MTLFunction> const fragmentFunction = getBlitFragmentFunction(key);
|
||||
|
||||
MetalPipelineState pipelineState {
|
||||
MetalPipelineState const pipelineState {
|
||||
.vertexFunction = getBlitVertexFunction(),
|
||||
.fragmentFunction = fragmentFunction,
|
||||
.vertexDescription = {},
|
||||
.colorAttachmentPixelFormat = {
|
||||
blitColor ? args.destination.color.pixelFormat : MTLPixelFormatInvalid,
|
||||
args.destination.texture.pixelFormat,
|
||||
MTLPixelFormatInvalid,
|
||||
MTLPixelFormatInvalid,
|
||||
MTLPixelFormatInvalid,
|
||||
@@ -319,31 +203,21 @@ void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor
|
||||
MTLPixelFormatInvalid,
|
||||
MTLPixelFormatInvalid
|
||||
},
|
||||
.depthAttachmentPixelFormat =
|
||||
blitDepth ? args.destination.depth.pixelFormat : MTLPixelFormatInvalid,
|
||||
.depthAttachmentPixelFormat = MTLPixelFormatInvalid,
|
||||
.sampleCount = 1,
|
||||
.blendState = {}
|
||||
};
|
||||
id<MTLRenderPipelineState> pipeline = mContext.pipelineStateCache.getOrCreateState(pipelineState);
|
||||
id<MTLRenderPipelineState> const pipeline =
|
||||
mContext.pipelineStateCache.getOrCreateState(pipelineState);
|
||||
[encoder setRenderPipelineState:pipeline];
|
||||
|
||||
// For texture arrays, create a view of the texture at the given slice (layer).
|
||||
id<MTLTexture> srcTextureColor = args.source.color;
|
||||
|
||||
id<MTLTexture> srcTextureColor = args.source.texture;
|
||||
if (srcTextureColor && srcTextureColor.textureType == MTLTextureType2DArray) {
|
||||
srcTextureColor = createTextureViewWithSingleSlice(srcTextureColor, args.source.slice);
|
||||
}
|
||||
id<MTLTexture> srcTextureDepth = args.source.depth;
|
||||
if (srcTextureDepth && srcTextureDepth.textureType == MTLTextureType2DArray) {
|
||||
srcTextureDepth = createTextureViewWithSingleSlice(srcTextureDepth, args.source.slice);
|
||||
}
|
||||
|
||||
if (blitColor) {
|
||||
[encoder setFragmentTexture:srcTextureColor atIndex:0];
|
||||
}
|
||||
|
||||
if (blitDepth) {
|
||||
[encoder setFragmentTexture:srcTextureDepth atIndex:1];
|
||||
}
|
||||
[encoder setFragmentTexture:srcTextureColor atIndex:0];
|
||||
|
||||
SamplerMinFilter filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST;
|
||||
if (args.filter == SamplerMagFilter::NEAREST) {
|
||||
@@ -352,13 +226,13 @@ void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor
|
||||
filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST;
|
||||
}
|
||||
|
||||
SamplerState s {
|
||||
SamplerState const s {
|
||||
.samplerParams = {
|
||||
.filterMag = args.filter,
|
||||
.filterMin = filterMin
|
||||
}
|
||||
};
|
||||
id<MTLSamplerState> sampler = mContext.samplerStateCache.getOrCreateState(s);
|
||||
id<MTLSamplerState> const sampler = mContext.samplerStateCache.getOrCreateState(s);
|
||||
[encoder setFragmentSamplerState:sampler atIndex:0];
|
||||
|
||||
MTLViewport viewport;
|
||||
@@ -370,11 +244,11 @@ void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor
|
||||
viewport.zfar = 1.0;
|
||||
[encoder setViewport:viewport];
|
||||
|
||||
DepthStencilState depthStencilState {
|
||||
DepthStencilState const depthStencilState {
|
||||
.depthCompare = MTLCompareFunctionAlways,
|
||||
.depthWriteEnabled = blitDepth
|
||||
.depthWriteEnabled = false
|
||||
};
|
||||
id<MTLDepthStencilState> depthStencil =
|
||||
id<MTLDepthStencilState> const depthStencil =
|
||||
mContext.depthStencilStateCache.getOrCreateState(depthStencilState);
|
||||
[encoder setDepthStencilState:depthStencil];
|
||||
|
||||
@@ -410,80 +284,42 @@ void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor
|
||||
[encoder endEncoding];
|
||||
}
|
||||
|
||||
id<MTLTexture> MetalBlitter::createIntermediateTexture(id<MTLTexture> t, MTLSize size) {
|
||||
MTLTextureDescriptor* descriptor = [MTLTextureDescriptor new];
|
||||
descriptor.textureType = size.depth == 1 ? MTLTextureType2D : MTLTextureType3D;
|
||||
descriptor.pixelFormat = t.pixelFormat;
|
||||
descriptor.width = size.width;
|
||||
descriptor.height = size.height;
|
||||
descriptor.depth = size.depth;
|
||||
descriptor.usage = t.usage & MTLTextureUsageRenderTarget;
|
||||
return [mContext.device newTextureWithDescriptor:descriptor];
|
||||
}
|
||||
|
||||
void MetalBlitter::shutdown() noexcept {
|
||||
mBlitFunctions.clear();
|
||||
mVertexFunction = nil;
|
||||
}
|
||||
|
||||
void MetalBlitter::setupColorAttachment(const BlitArgs& args,
|
||||
MTLRenderPassDescriptor* descriptor, uint32_t depthPlane) {
|
||||
descriptor.colorAttachments[0].texture = args.destination.color;
|
||||
descriptor.colorAttachments[0].level = args.destination.level;
|
||||
descriptor.colorAttachments[0].slice = args.destination.slice;
|
||||
descriptor.colorAttachments[0].depthPlane = depthPlane;
|
||||
|
||||
descriptor.colorAttachments[0].loadAction = MTLLoadActionLoad;
|
||||
void MetalBlitter::setupAttachment(MTLRenderPassAttachmentDescriptor* descriptor,
|
||||
const BlitArgs& args, uint32_t depthPlane) {
|
||||
descriptor.texture = args.destination.texture;
|
||||
descriptor.level = args.destination.level;
|
||||
descriptor.slice = args.destination.slice;
|
||||
descriptor.depthPlane = depthPlane;
|
||||
descriptor.loadAction = MTLLoadActionLoad;
|
||||
// We don't need to load the contents of the attachment if we're blitting over all of it.
|
||||
if (args.colorDestinationIsFullAttachment()) {
|
||||
descriptor.colorAttachments[0].loadAction = MTLLoadActionDontCare;
|
||||
if (args.destinationIsFullAttachment()) {
|
||||
descriptor.loadAction = MTLLoadActionDontCare;
|
||||
}
|
||||
|
||||
descriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
|
||||
descriptor.storeAction = MTLStoreActionStore;
|
||||
}
|
||||
|
||||
void MetalBlitter::setupDepthAttachment(const BlitArgs& args, MTLRenderPassDescriptor* descriptor,
|
||||
uint32_t depthPlane) {
|
||||
descriptor.depthAttachment.texture = args.destination.depth;
|
||||
descriptor.depthAttachment.level = args.destination.level;
|
||||
descriptor.depthAttachment.slice = args.destination.slice;
|
||||
descriptor.depthAttachment.depthPlane = depthPlane;
|
||||
|
||||
descriptor.depthAttachment.loadAction = MTLLoadActionLoad;
|
||||
// We don't need to load the contents of the attachment if we're blitting over all of it.
|
||||
if (args.depthDestinationIsFullAttachment()) {
|
||||
descriptor.depthAttachment.loadAction = MTLLoadActionDontCare;
|
||||
}
|
||||
|
||||
descriptor.depthAttachment.storeAction = MTLStoreActionStore;
|
||||
}
|
||||
|
||||
id<MTLFunction> MetalBlitter::compileFragmentFunction(BlitFunctionKey key) {
|
||||
MTLCompileOptions* options = [MTLCompileOptions new];
|
||||
NSMutableDictionary* macros = [NSMutableDictionary dictionary];
|
||||
if (key.blitColor) {
|
||||
macros[@"BLIT_COLOR"] = @"1";
|
||||
}
|
||||
if (key.blitDepth) {
|
||||
macros[@"BLIT_DEPTH"] = @"1";
|
||||
}
|
||||
id<MTLFunction> MetalBlitter::compileFragmentFunction(BlitFunctionKey key) const {
|
||||
MTLCompileOptions* const options = [MTLCompileOptions new];
|
||||
NSMutableDictionary* const macros = [NSMutableDictionary dictionary];
|
||||
if (key.msaaColorSource) {
|
||||
macros[@"MSAA_COLOR_SOURCE"] = @"1";
|
||||
}
|
||||
if (key.msaaDepthSource) {
|
||||
macros[@"MSAA_DEPTH_SOURCE"] = @"1";
|
||||
}
|
||||
if (key.sources3D) {
|
||||
macros[@"SOURCES_3D"] = @"1";
|
||||
}
|
||||
options.preprocessorMacros = macros;
|
||||
NSString* objcSource = [NSString stringWithCString:functionLibrary
|
||||
encoding:NSUTF8StringEncoding];
|
||||
NSString* const objcSource = [NSString stringWithCString:functionLibrary
|
||||
encoding:NSUTF8StringEncoding];
|
||||
NSError* error = nil;
|
||||
id<MTLLibrary> library = [mContext.device newLibraryWithSource:objcSource
|
||||
options:options
|
||||
error:&error];
|
||||
id<MTLFunction> function = [library newFunctionWithName:@"blitterFrag"];
|
||||
id <MTLLibrary> const library = [mContext.device newLibraryWithSource:objcSource
|
||||
options:options
|
||||
error:&error];
|
||||
id <MTLFunction> const function = [library newFunctionWithName:@"blitterFrag"];
|
||||
|
||||
if (!library || !function) {
|
||||
if (error) {
|
||||
@@ -501,13 +337,14 @@ id<MTLFunction> MetalBlitter::getBlitVertexFunction() {
|
||||
return mVertexFunction;
|
||||
}
|
||||
|
||||
NSString* objcSource = [NSString stringWithCString:functionLibrary
|
||||
encoding:NSUTF8StringEncoding];
|
||||
NSString* const objcSource = [NSString stringWithCString:functionLibrary
|
||||
encoding:NSUTF8StringEncoding];
|
||||
NSError* error = nil;
|
||||
id<MTLLibrary> library = [mContext.device newLibraryWithSource:objcSource
|
||||
options:nil
|
||||
error:&error];
|
||||
id<MTLFunction> function = [library newFunctionWithName:@"blitterVertex"];
|
||||
id <MTLLibrary> const library = [mContext.device newLibraryWithSource:objcSource
|
||||
options:nil
|
||||
error:&error];
|
||||
|
||||
id<MTLFunction> const function = [library newFunctionWithName:@"blitterVertex"];
|
||||
|
||||
if (!library || !function) {
|
||||
if (error) {
|
||||
@@ -535,5 +372,5 @@ id<MTLFunction> MetalBlitter::getBlitFragmentFunction(BlitFunctionKey key) {
|
||||
return function;
|
||||
}
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
} // namespace filament::backend
|
||||
|
||||
|
||||
@@ -73,7 +73,10 @@ private:
|
||||
std::unordered_set<MetalBufferPoolEntry const*> mUsedStages;
|
||||
|
||||
// Store the current "time" (really just a frame count) and LRU eviction parameters.
|
||||
uint64_t mCurrentFrame = 0;
|
||||
// An atomic is necessary as mCurrentFrame is incremented in gc() (called on
|
||||
// the driver thread) and read from acquireBuffer() and releaseBuffer(),
|
||||
// which may be called on non-driver threads.
|
||||
std::atomic<uint64_t> mCurrentFrame = 0;
|
||||
static constexpr uint32_t TIME_BEFORE_EVICTION = 10;
|
||||
};
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "MetalContext.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/trap.h>
|
||||
|
||||
#include <thread>
|
||||
|
||||
@@ -142,7 +142,10 @@ struct MetalContext {
|
||||
// Fences, only supported on macOS 10.14 and iOS 12 and above.
|
||||
API_AVAILABLE(macos(10.14), ios(12.0))
|
||||
MTLSharedEventListener* eventListener = nil;
|
||||
uint64_t signalId = 1;
|
||||
// signalId is incremented in the MetalFence constructor, which is called on
|
||||
// both the driver (MetalTimerQueryFence::beginTimeElapsedQuery) and main
|
||||
// threads (in createFenceS), so an atomic is necessary.
|
||||
std::atomic<uint64_t> signalId = 1;
|
||||
|
||||
MetalTimerQueryInterface* timerQueryImpl;
|
||||
|
||||
|
||||
@@ -28,6 +28,10 @@
|
||||
#include <utils/Log.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
|
||||
@@ -52,13 +56,13 @@ class MetalDriver final : public DriverBase {
|
||||
|
||||
public:
|
||||
static Driver* create(MetalPlatform* platform, const Platform::DriverConfig& driverConfig);
|
||||
void runAtNextTick(const std::function<void()>& fn) noexcept;
|
||||
|
||||
private:
|
||||
|
||||
friend class MetalSwapChain;
|
||||
|
||||
MetalPlatform& mPlatform;
|
||||
|
||||
MetalContext* mContext;
|
||||
|
||||
ShaderModel getShaderModel() const noexcept final;
|
||||
@@ -66,6 +70,13 @@ private:
|
||||
// Overrides the default implementation by wrapping the call to fn in an @autoreleasepool block.
|
||||
void execute(std::function<void(void)> const& fn) noexcept final;
|
||||
|
||||
/*
|
||||
* Tasks run regularly on the driver thread.
|
||||
*/
|
||||
void executeTickOps() noexcept;
|
||||
std::vector<std::function<void()>> mTickOps;
|
||||
std::mutex mTickOpsLock;
|
||||
|
||||
/*
|
||||
* Driver interface
|
||||
*/
|
||||
|
||||
@@ -166,6 +166,7 @@ MetalDriver::~MetalDriver() noexcept {
|
||||
}
|
||||
|
||||
void MetalDriver::tick(int) {
|
||||
executeTickOps();
|
||||
}
|
||||
|
||||
void MetalDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
|
||||
@@ -583,6 +584,8 @@ void MetalDriver::terminate() {
|
||||
// This must be done before calling bufferPool->reset() to ensure no buffers are in flight.
|
||||
finish();
|
||||
|
||||
executeTickOps();
|
||||
|
||||
mContext->bufferPool->reset();
|
||||
mContext->commandQueue = nil;
|
||||
|
||||
@@ -727,6 +730,10 @@ bool MetalDriver::isParallelShaderCompileSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetalDriver::isDepthStencilResolveSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
switch (workaround) {
|
||||
case Workaround::SPLIT_EASU:
|
||||
@@ -865,10 +872,6 @@ void MetalDriver::generateMipmaps(Handle<HwTexture> th) {
|
||||
tex->generateMipmaps();
|
||||
}
|
||||
|
||||
bool MetalDriver::canGenerateMipmaps() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescriptor&& data) {
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"updateSamplerGroup must be called outside of a render pass.");
|
||||
@@ -1256,14 +1259,14 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
|
||||
textureDescriptor.usage = MTLTextureUsageShaderWrite | MTLTextureUsageRenderTarget;
|
||||
id<MTLTexture> readPixelsTexture = [mContext->device newTextureWithDescriptor:textureDescriptor];
|
||||
|
||||
MetalBlitter::BlitArgs args;
|
||||
MetalBlitter::BlitArgs args{};
|
||||
args.filter = SamplerMagFilter::NEAREST;
|
||||
args.source.level = miplevel;
|
||||
args.source.region = MTLRegionMake2D(0, 0, srcTexture.width >> miplevel, srcTexture.height >> miplevel);
|
||||
args.source.texture = srcTexture;
|
||||
args.destination.level = 0;
|
||||
args.destination.region = MTLRegionMake2D(0, 0, readPixelsTexture.width, readPixelsTexture.height);
|
||||
args.source.color = srcTexture;
|
||||
args.destination.color = readPixelsTexture;
|
||||
args.destination.texture = readPixelsTexture;
|
||||
|
||||
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "readPixels blit");
|
||||
|
||||
@@ -1299,24 +1302,123 @@ void MetalDriver::readBufferSubData(backend::BufferObjectHandle boh,
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void MetalDriver::blit(TargetBufferFlags buffers,
|
||||
void MetalDriver::resolve(
|
||||
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer,
|
||||
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer) {
|
||||
auto* const srcTexture = handle_cast<MetalTexture>(src);
|
||||
auto* const dstTexture = handle_cast<MetalTexture>(dst);
|
||||
assert_invariant(srcTexture);
|
||||
assert_invariant(dstTexture);
|
||||
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder == nil,
|
||||
"resolve() cannot be invoked inside a render pass.");
|
||||
|
||||
ASSERT_PRECONDITION(
|
||||
dstTexture->width == srcTexture->width && dstTexture->height == srcTexture->height,
|
||||
"invalid resolve: src and dst sizes don't match");
|
||||
|
||||
ASSERT_PRECONDITION(srcTexture->samples > 1 && dstTexture->samples == 1,
|
||||
"invalid resolve: src.samples=%u, dst.samples=%u",
|
||||
+srcTexture->samples, +dstTexture->samples);
|
||||
|
||||
ASSERT_PRECONDITION(srcTexture->format == dstTexture->format,
|
||||
"src and dst texture format don't match");
|
||||
|
||||
ASSERT_PRECONDITION(!isDepthFormat(srcTexture->format),
|
||||
"can't resolve depth formats");
|
||||
|
||||
ASSERT_PRECONDITION(!isStencilFormat(srcTexture->format),
|
||||
"can't resolve stencil formats");
|
||||
|
||||
ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
|
||||
"texture doesn't have BLIT_DST");
|
||||
|
||||
ASSERT_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC),
|
||||
"texture doesn't have BLIT_SRC");
|
||||
|
||||
// FIXME: on metal the blit() call below always take the slow path (using a shader)
|
||||
|
||||
blit( dst, dstLevel, dstLayer, {},
|
||||
src, srcLevel, srcLayer, {},
|
||||
{ dstTexture->width, dstTexture->height });
|
||||
}
|
||||
|
||||
void MetalDriver::blit(
|
||||
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer, math::uint2 dstOrigin,
|
||||
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer, math::uint2 srcOrigin,
|
||||
math::uint2 size) {
|
||||
|
||||
|
||||
auto isBlitableTextureType = [](MTLTextureType t) -> bool {
|
||||
return t == MTLTextureType2D || t == MTLTextureType2DMultisample ||
|
||||
t == MTLTextureType2DArray;
|
||||
};
|
||||
|
||||
auto* const srcTexture = handle_cast<MetalTexture>(src);
|
||||
auto* const dstTexture = handle_cast<MetalTexture>(dst);
|
||||
assert_invariant(srcTexture);
|
||||
assert_invariant(dstTexture);
|
||||
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder == nil,
|
||||
"blit() cannot be invoked inside a render pass.");
|
||||
|
||||
ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
|
||||
"texture doesn't have BLIT_DST");
|
||||
|
||||
ASSERT_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC),
|
||||
"texture doesn't have BLIT_SRC");
|
||||
|
||||
ASSERT_PRECONDITION(srcTexture->format == dstTexture->format,
|
||||
"src and dst texture format don't match");
|
||||
|
||||
ASSERT_PRECONDITION(isBlitableTextureType(srcTexture->getMtlTextureForRead().textureType) &&
|
||||
isBlitableTextureType(dstTexture->getMtlTextureForWrite().textureType),
|
||||
"Metal does not support blitting to/from non-2D textures.");
|
||||
|
||||
MetalBlitter::BlitArgs args{};
|
||||
args.filter = SamplerMagFilter::NEAREST;
|
||||
args.source.region = MTLRegionMake2D(
|
||||
(NSUInteger)srcOrigin.x,
|
||||
std::max(srcTexture->height - (int64_t)srcOrigin.y - size.y, (int64_t)0),
|
||||
size.x, size.y);
|
||||
args.destination.region = MTLRegionMake2D(
|
||||
(NSUInteger)dstOrigin.x,
|
||||
std::max(dstTexture->height - (int64_t)dstOrigin.y - size.y, (int64_t)0),
|
||||
size.x, size.y);
|
||||
|
||||
// FIXME: we shouldn't need to know the type here. This is an artifact of using the old API.
|
||||
|
||||
args.source.texture = srcTexture->getMtlTextureForRead();
|
||||
args.destination.texture = dstTexture->getMtlTextureForWrite();
|
||||
args.source.level = srcLevel;
|
||||
args.source.slice = srcLayer;
|
||||
args.destination.level = dstLevel;
|
||||
args.destination.slice = dstLayer;
|
||||
|
||||
// TODO: The blit() call below always take the fast path.
|
||||
|
||||
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "blit/resolve");
|
||||
|
||||
dstTexture->extendLodRangeTo(dstLevel);
|
||||
}
|
||||
|
||||
void MetalDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
Handle<HwRenderTarget> dst, Viewport dstRect,
|
||||
Handle<HwRenderTarget> src, Viewport srcRect,
|
||||
SamplerMagFilter filter) {
|
||||
// If we're the in middle of a render pass, finish it.
|
||||
// This condition should only occur during copyFrame. It's okay to end the render pass because
|
||||
// we don't issue any other rendering commands.
|
||||
if (mContext->currentRenderPassEncoder) {
|
||||
[mContext->currentRenderPassEncoder endEncoding];
|
||||
mContext->currentRenderPassEncoder = nil;
|
||||
}
|
||||
|
||||
// Note: blitDEPRECATED is only used by Renderer::copyFrame
|
||||
// It is called between beginFrame and endFrame, but should never be called in the middle of
|
||||
// a render pass.
|
||||
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder == nil,
|
||||
"blitDEPRECATED() cannot be invoked inside a render pass.");
|
||||
|
||||
auto srcTarget = handle_cast<MetalRenderTarget>(src);
|
||||
auto dstTarget = handle_cast<MetalRenderTarget>(dst);
|
||||
|
||||
ASSERT_PRECONDITION(
|
||||
!(buffers & (TargetBufferFlags::COLOR_ALL & ~TargetBufferFlags::COLOR0)),
|
||||
"Blitting only supports COLOR0");
|
||||
ASSERT_PRECONDITION(buffers == TargetBufferFlags::COLOR0,
|
||||
"blitDEPRECATED only supports COLOR0");
|
||||
|
||||
ASSERT_PRECONDITION(srcRect.left >= 0 && srcRect.bottom >= 0 &&
|
||||
dstRect.left >= 0 && dstRect.bottom >= 0,
|
||||
@@ -1327,54 +1429,28 @@ void MetalDriver::blit(TargetBufferFlags buffers,
|
||||
t == MTLTextureType2DArray;
|
||||
};
|
||||
|
||||
// MetalBlitter supports blitting color and depth simultaneously, but for simplicitly we'll blit
|
||||
// them separately. In practice, Filament only ever blits a single buffer at a time anyway.
|
||||
// We always blit from/to the COLOR0 attachment.
|
||||
MetalRenderTarget::Attachment const srcColorAttachment = srcTarget->getReadColorAttachment(0);
|
||||
MetalRenderTarget::Attachment const dstColorAttachment = dstTarget->getDrawColorAttachment(0);
|
||||
|
||||
if (any(buffers & TargetBufferFlags::COLOR_ALL)) {
|
||||
// We always blit from/to the COLOR0 attachment.
|
||||
MetalRenderTarget::Attachment srcColorAttachment = srcTarget->getReadColorAttachment(0);
|
||||
MetalRenderTarget::Attachment dstColorAttachment = dstTarget->getDrawColorAttachment(0);
|
||||
if (srcColorAttachment && dstColorAttachment) {
|
||||
ASSERT_PRECONDITION(isBlitableTextureType(srcColorAttachment.getTexture().textureType) &&
|
||||
isBlitableTextureType(dstColorAttachment.getTexture().textureType),
|
||||
"Metal does not support blitting to/from non-2D textures.");
|
||||
|
||||
if (srcColorAttachment && dstColorAttachment) {
|
||||
ASSERT_PRECONDITION(isBlitableTextureType(srcColorAttachment.getTexture().textureType) &&
|
||||
isBlitableTextureType(dstColorAttachment.getTexture().textureType),
|
||||
"Metal does not support blitting to/from non-2D textures.");
|
||||
MetalBlitter::BlitArgs args{};
|
||||
args.filter = filter;
|
||||
args.source.region = srcTarget->getRegionFromClientRect(srcRect);
|
||||
args.source.texture = srcColorAttachment.getTexture();
|
||||
args.source.level = srcColorAttachment.level;
|
||||
args.source.slice = srcColorAttachment.layer;
|
||||
|
||||
MetalBlitter::BlitArgs args;
|
||||
args.filter = filter;
|
||||
args.source.region = srcTarget->getRegionFromClientRect(srcRect);
|
||||
args.destination.region = dstTarget->getRegionFromClientRect(dstRect);
|
||||
args.source.color = srcColorAttachment.getTexture();
|
||||
args.destination.color = dstColorAttachment.getTexture();
|
||||
args.source.level = srcColorAttachment.level;
|
||||
args.destination.level = dstColorAttachment.level;
|
||||
args.source.slice = srcColorAttachment.layer;
|
||||
args.destination.slice = dstColorAttachment.layer;
|
||||
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "Color blit");
|
||||
}
|
||||
}
|
||||
args.destination.region = dstTarget->getRegionFromClientRect(dstRect);
|
||||
args.destination.texture = dstColorAttachment.getTexture();
|
||||
args.destination.level = dstColorAttachment.level;
|
||||
args.destination.slice = dstColorAttachment.layer;
|
||||
|
||||
if (any(buffers & TargetBufferFlags::DEPTH)) {
|
||||
MetalRenderTarget::Attachment srcDepthAttachment = srcTarget->getDepthAttachment();
|
||||
MetalRenderTarget::Attachment dstDepthAttachment = dstTarget->getDepthAttachment();
|
||||
|
||||
if (srcDepthAttachment && dstDepthAttachment) {
|
||||
ASSERT_PRECONDITION(isBlitableTextureType(srcDepthAttachment.getTexture().textureType) &&
|
||||
isBlitableTextureType(dstDepthAttachment.getTexture().textureType),
|
||||
"Metal does not support blitting to/from non-2D textures.");
|
||||
|
||||
MetalBlitter::BlitArgs args;
|
||||
args.filter = filter;
|
||||
args.source.region = srcTarget->getRegionFromClientRect(srcRect);
|
||||
args.destination.region = dstTarget->getRegionFromClientRect(dstRect);
|
||||
args.source.depth = srcDepthAttachment.getTexture();
|
||||
args.destination.depth = dstDepthAttachment.getTexture();
|
||||
args.source.level = srcDepthAttachment.level;
|
||||
args.destination.level = dstDepthAttachment.level;
|
||||
args.source.slice = srcDepthAttachment.layer;
|
||||
args.destination.slice = dstDepthAttachment.layer;
|
||||
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "Depth blit");
|
||||
}
|
||||
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "blitDEPRECATED");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1512,7 +1588,7 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
stencilPixelFormat = stencilAttachment.getPixelFormat();
|
||||
assert_invariant(isMetalFormatStencil(stencilPixelFormat));
|
||||
}
|
||||
MetalPipelineState pipelineState {
|
||||
MetalPipelineState const pipelineState {
|
||||
.vertexFunction = vertex,
|
||||
.fragmentFunction = fragment,
|
||||
.vertexDescription = primitive->vertexDescription,
|
||||
@@ -1530,13 +1606,13 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
.stencilAttachmentPixelFormat = stencilPixelFormat,
|
||||
.sampleCount = mContext->currentRenderTarget->getSamples(),
|
||||
.blendState = BlendState {
|
||||
.blendingEnabled = rs.hasBlending(),
|
||||
.rgbBlendOperation = getMetalBlendOperation(rs.blendEquationRGB),
|
||||
.alphaBlendOperation = getMetalBlendOperation(rs.blendEquationAlpha),
|
||||
.sourceRGBBlendFactor = getMetalBlendFactor(rs.blendFunctionSrcRGB),
|
||||
.sourceAlphaBlendFactor = getMetalBlendFactor(rs.blendFunctionSrcAlpha),
|
||||
.rgbBlendOperation = getMetalBlendOperation(rs.blendEquationRGB),
|
||||
.destinationAlphaBlendFactor = getMetalBlendFactor(rs.blendFunctionDstAlpha),
|
||||
.destinationRGBBlendFactor = getMetalBlendFactor(rs.blendFunctionDstRGB),
|
||||
.destinationAlphaBlendFactor = getMetalBlendFactor(rs.blendFunctionDstAlpha)
|
||||
.sourceAlphaBlendFactor = getMetalBlendFactor(rs.blendFunctionSrcAlpha),
|
||||
.sourceRGBBlendFactor = getMetalBlendFactor(rs.blendFunctionSrcRGB),
|
||||
.blendingEnabled = rs.hasBlending(),
|
||||
},
|
||||
.colorWrite = rs.colorWrite
|
||||
};
|
||||
@@ -1835,6 +1911,21 @@ void MetalDriver::enumerateBoundBuffers(BufferObjectBinding bindingType,
|
||||
void MetalDriver::resetState(int) {
|
||||
}
|
||||
|
||||
void MetalDriver::runAtNextTick(const std::function<void()>& fn) noexcept {
|
||||
std::lock_guard<std::mutex> const lock(mTickOpsLock);
|
||||
mTickOps.push_back(fn);
|
||||
}
|
||||
|
||||
void MetalDriver::executeTickOps() noexcept {
|
||||
std::vector<std::function<void()>> ops;
|
||||
mTickOpsLock.lock();
|
||||
std::swap(ops, mTickOps);
|
||||
mTickOpsLock.unlock();
|
||||
for (const auto& f : ops) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
// explicit instantiation of the Dispatcher
|
||||
template class ConcreteDispatcher<MetalDriver>;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "MetalUtils.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/trap.h>
|
||||
|
||||
#define NSERROR_CHECK(message) \
|
||||
|
||||
@@ -39,7 +39,15 @@ namespace filament {
|
||||
namespace backend {
|
||||
|
||||
static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) {
|
||||
NSUInteger u = 0;
|
||||
NSUInteger u = MTLTextureUsageUnknown;
|
||||
|
||||
if (any(usage & TextureUsage::SAMPLEABLE)) {
|
||||
u |= MTLTextureUsageShaderRead;
|
||||
}
|
||||
if (any(usage & TextureUsage::UPLOADABLE)) {
|
||||
// This is only needed because of the slowpath is MetalBlitter
|
||||
u |= MTLTextureUsageRenderTarget;
|
||||
}
|
||||
if (any(usage & TextureUsage::COLOR_ATTACHMENT)) {
|
||||
u |= MTLTextureUsageRenderTarget;
|
||||
}
|
||||
@@ -49,9 +57,13 @@ static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) {
|
||||
if (any(usage & TextureUsage::STENCIL_ATTACHMENT)) {
|
||||
u |= MTLTextureUsageRenderTarget;
|
||||
}
|
||||
|
||||
// All textures can be blitted from, so they must have the UsageShaderRead flag.
|
||||
u |= MTLTextureUsageShaderRead;
|
||||
if (any(usage & TextureUsage::BLIT_DST)) {
|
||||
// This is only needed because of the slowpath is MetalBlitter
|
||||
u |= MTLTextureUsageRenderTarget;
|
||||
}
|
||||
if (any(usage & TextureUsage::BLIT_SRC)) {
|
||||
u |= MTLTextureUsageShaderRead;
|
||||
}
|
||||
|
||||
return MTLTextureUsage(u);
|
||||
}
|
||||
@@ -233,13 +245,30 @@ void MetalSwapChain::present() {
|
||||
}
|
||||
}
|
||||
|
||||
struct PresentDrawableData {
|
||||
void* drawable = nullptr;
|
||||
MetalDriver* driver = nullptr;
|
||||
};
|
||||
|
||||
void presentDrawable(bool presentFrame, void* user) {
|
||||
// CFBridgingRelease here is used to balance the CFBridgingRetain inside of acquireDrawable.
|
||||
id<CAMetalDrawable> drawable = (id<CAMetalDrawable>) CFBridgingRelease(user);
|
||||
auto* presentDrawableData = static_cast<PresentDrawableData*>(user);
|
||||
|
||||
// CFBridgingRelease here is used to balance the CFBridgingRetain inside acquireDrawable.
|
||||
id<CAMetalDrawable> drawable =
|
||||
(id<CAMetalDrawable>)CFBridgingRelease(presentDrawableData->drawable);
|
||||
if (presentFrame) {
|
||||
[drawable present];
|
||||
}
|
||||
// The drawable will be released here when the "drawable" variable goes out of scope.
|
||||
|
||||
// Schedule the drawable destruction on the driver thread.
|
||||
void* voidDrawable = (void*) CFBridgingRetain(drawable);
|
||||
MetalDriver* driver = presentDrawableData->driver;
|
||||
driver->runAtNextTick([voidDrawable]() {
|
||||
// The drawable is released here.
|
||||
CFBridgingRelease(voidDrawable);
|
||||
});
|
||||
|
||||
delete presentDrawableData;
|
||||
}
|
||||
|
||||
void MetalSwapChain::scheduleFrameScheduledCallback() {
|
||||
@@ -254,13 +283,15 @@ void MetalSwapChain::scheduleFrameScheduledCallback() {
|
||||
// capture the _this_ pointer (MetalSwapChain*) instead of the drawable.
|
||||
id<CAMetalDrawable> d = drawable;
|
||||
void* userData = frameScheduledUserData;
|
||||
MetalDriver* driver = context.driver;
|
||||
[getPendingCommandBuffer(&context) addScheduledHandler:^(id<MTLCommandBuffer> cb) {
|
||||
// CFBridgingRetain is used here to give the drawable a +1 retain count before
|
||||
// casting it to a void*.
|
||||
PresentCallable callable(presentDrawable, (void*) CFBridgingRetain(d));
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
callback(callable, userData);
|
||||
});
|
||||
auto* presentDrawableData = new PresentDrawableData;
|
||||
presentDrawableData->drawable = (void*) CFBridgingRetain(d);
|
||||
presentDrawableData->driver = driver;
|
||||
PresentCallable callable(presentDrawable, (void*) presentDrawableData);
|
||||
callback(callable, userData);
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -756,6 +787,7 @@ void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region
|
||||
#endif
|
||||
|
||||
id<MTLTexture> stagingTexture = [context.device newTextureWithDescriptor:descriptor];
|
||||
// FIXME? Why is this not just `MTLRegion sourceRegion = region;` ?
|
||||
MTLRegion sourceRegion = MTLRegionMake3D(0, 0, 0,
|
||||
region.size.width, region.size.height, region.size.depth);
|
||||
[stagingTexture replaceRegion:sourceRegion
|
||||
@@ -782,16 +814,16 @@ void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region
|
||||
slices:NSMakeRange(0, slices)];
|
||||
}
|
||||
|
||||
MetalBlitter::BlitArgs args;
|
||||
MetalBlitter::BlitArgs args{};
|
||||
args.filter = SamplerMagFilter::NEAREST;
|
||||
args.source.level = 0;
|
||||
args.source.slice = 0;
|
||||
args.source.region = sourceRegion;
|
||||
args.source.texture = stagingTexture;
|
||||
args.destination.level = level;
|
||||
args.destination.slice = slice;
|
||||
args.destination.region = region;
|
||||
args.source.color = stagingTexture;
|
||||
args.destination.color = destinationTexture;
|
||||
args.destination.texture = destinationTexture;
|
||||
context.blitter->blit(getPendingCommandBuffer(&context), args, "Texture upload blit");
|
||||
}
|
||||
|
||||
|
||||
@@ -103,10 +103,19 @@ void MetalShaderCompiler::terminate() noexcept {
|
||||
NSString* objcSource = [[NSString alloc] initWithBytes:source.data()
|
||||
length:source.size() - 1
|
||||
encoding:NSUTF8StringEncoding];
|
||||
|
||||
// By default, Metal uses the most recent language version.
|
||||
MTLCompileOptions* options = [MTLCompileOptions new];
|
||||
|
||||
// Disable Fast Math optimizations.
|
||||
// This ensures that operations adhere to IEEE standards for floating-point arithmetic,
|
||||
// which is crucial for half precision floats in scenarios where fast math optimizations
|
||||
// lead to inaccuracies, such as in handling special values like NaN or Infinity.
|
||||
options.fastMathEnabled = NO;
|
||||
|
||||
NSError* error = nil;
|
||||
// When options is nil, Metal uses the most recent language version available.
|
||||
id<MTLLibrary> library = [device newLibraryWithSource:objcSource
|
||||
options:nil
|
||||
options:options
|
||||
error:&error];
|
||||
if (library == nil) {
|
||||
if (error) {
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#include "MetalEnums.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
|
||||
|
||||
@@ -182,6 +182,10 @@ bool NoopDriver::isParallelShaderCompileSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NoopDriver::isDepthStencilResolveSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NoopDriver::isWorkaroundNeeded(Workaround) {
|
||||
return false;
|
||||
}
|
||||
@@ -252,10 +256,6 @@ void NoopDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh) {
|
||||
|
||||
void NoopDriver::generateMipmaps(Handle<HwTexture> th) { }
|
||||
|
||||
bool NoopDriver::canGenerateMipmaps() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void NoopDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
|
||||
BufferDescriptor&& data) {
|
||||
scheduleDestroy(std::move(data));
|
||||
@@ -322,12 +322,23 @@ void NoopDriver::readBufferSubData(backend::BufferObjectHandle boh,
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void NoopDriver::blit(TargetBufferFlags buffers,
|
||||
void NoopDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
Handle<HwRenderTarget> dst, Viewport dstRect,
|
||||
Handle<HwRenderTarget> src, Viewport srcRect,
|
||||
SamplerMagFilter filter) {
|
||||
}
|
||||
|
||||
void NoopDriver::resolve(
|
||||
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer,
|
||||
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer) {
|
||||
}
|
||||
|
||||
void NoopDriver::blit(
|
||||
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer, math::uint2 dstOrigin,
|
||||
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer, math::uint2 srcOrigin,
|
||||
math::uint2 size) {
|
||||
}
|
||||
|
||||
void NoopDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
|
||||
uint32_t instanceCount) {
|
||||
}
|
||||
|
||||
@@ -274,6 +274,7 @@ void OpenGLContext::setDefaultState() noexcept {
|
||||
glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_NICEST);
|
||||
#endif
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
if (ext.EXT_clip_control) {
|
||||
#if defined(BACKEND_OPENGL_VERSION_GL)
|
||||
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
|
||||
@@ -281,6 +282,7 @@ void OpenGLContext::setDefaultState() noexcept {
|
||||
glClipControlEXT(GL_LOWER_LEFT_EXT, GL_ZERO_TO_ONE_EXT);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ext.EXT_clip_cull_distance) {
|
||||
glEnable(GL_CLIP_DISTANCE0);
|
||||
@@ -309,7 +311,9 @@ void OpenGLContext::initProcs(Procs* procs,
|
||||
# ifdef BACKEND_OPENGL_VERSION_GL
|
||||
procs->getQueryObjectui64v = glGetQueryObjectui64v; // only core in GL
|
||||
# elif defined(GL_EXT_disjoint_timer_query)
|
||||
procs->getQueryObjectui64v = glGetQueryObjectui64vEXT;
|
||||
# ifndef __EMSCRIPTEN__
|
||||
procs->getQueryObjectui64v = glGetQueryObjectui64vEXT;
|
||||
# endif
|
||||
# endif // BACKEND_OPENGL_VERSION_GL
|
||||
|
||||
// core in ES 3.0 and GL 4.3
|
||||
@@ -321,6 +325,7 @@ void OpenGLContext::initProcs(Procs* procs,
|
||||
|
||||
#ifdef BACKEND_OPENGL_VERSION_GLES
|
||||
# ifndef IOS // IOS is guaranteed to have ES3.x
|
||||
# ifndef __EMSCRIPTEN__
|
||||
if (UTILS_UNLIKELY(major == 2)) {
|
||||
// Runtime OpenGL version is ES 2.x
|
||||
if (UTILS_LIKELY(ext.OES_vertex_array_object)) {
|
||||
@@ -348,6 +353,7 @@ void OpenGLContext::initProcs(Procs* procs,
|
||||
|
||||
procs->maxShaderCompilerThreadsKHR = glMaxShaderCompilerThreadsKHR;
|
||||
}
|
||||
# endif // __EMSCRIPTEN__
|
||||
# endif // IOS
|
||||
#else
|
||||
procs->maxShaderCompilerThreadsKHR = glMaxShaderCompilerThreadsARB;
|
||||
@@ -421,7 +427,6 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
|
||||
if (strstr(renderer, "Mali-T")) {
|
||||
bugs->disable_glFlush = true;
|
||||
bugs->disable_shared_context_draws = true;
|
||||
bugs->texture_external_needs_rebind = true;
|
||||
// We have not verified that timer queries work on Mali-T, so we disable to be safe.
|
||||
bugs->dont_use_timer_query = true;
|
||||
}
|
||||
@@ -476,7 +481,6 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
|
||||
// (that should be regardless of ANGLE, but we should double-check)
|
||||
bugs->split_easu = true;
|
||||
}
|
||||
// TODO: see if we could use `bugs.allow_read_only_ancillary_feedback_loop = true`
|
||||
}
|
||||
|
||||
#ifdef BACKEND_OPENGL_VERSION_GLES
|
||||
@@ -581,17 +585,23 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
|
||||
// figure out and initialize the extensions we need
|
||||
using namespace std::literals;
|
||||
ext->APPLE_color_buffer_packed_float = exts.has("GL_APPLE_color_buffer_packed_float"sv);
|
||||
#ifndef __EMSCRIPTEN__
|
||||
ext->EXT_clip_control = exts.has("GL_EXT_clip_control"sv);
|
||||
#endif
|
||||
ext->EXT_clip_cull_distance = exts.has("GL_EXT_clip_cull_distance"sv);
|
||||
ext->EXT_color_buffer_float = exts.has("GL_EXT_color_buffer_float"sv);
|
||||
ext->EXT_color_buffer_half_float = exts.has("GL_EXT_color_buffer_half_float"sv);
|
||||
#ifndef __EMSCRIPTEN__
|
||||
ext->EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv);
|
||||
#endif
|
||||
ext->EXT_discard_framebuffer = exts.has("GL_EXT_discard_framebuffer"sv);
|
||||
#ifndef __EMSCRIPTEN__
|
||||
ext->EXT_disjoint_timer_query = exts.has("GL_EXT_disjoint_timer_query"sv);
|
||||
ext->EXT_multisampled_render_to_texture = exts.has("GL_EXT_multisampled_render_to_texture"sv);
|
||||
ext->EXT_multisampled_render_to_texture2 = exts.has("GL_EXT_multisampled_render_to_texture2"sv);
|
||||
#endif
|
||||
ext->EXT_shader_framebuffer_fetch = exts.has("GL_EXT_shader_framebuffer_fetch"sv);
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
#ifndef __EMSCRIPTEN__
|
||||
ext->EXT_texture_compression_etc2 = true;
|
||||
#endif
|
||||
ext->EXT_texture_compression_s3tc = exts.has("GL_EXT_texture_compression_s3tc"sv);
|
||||
|
||||
@@ -222,10 +222,6 @@ public:
|
||||
// Some drivers have gl state issues when drawing from shared contexts
|
||||
bool disable_shared_context_draws;
|
||||
|
||||
// Some drivers require the GL_TEXTURE_EXTERNAL_OES target to be bound when
|
||||
// the texture image changes, even if it's already bound to that texture
|
||||
bool texture_external_needs_rebind;
|
||||
|
||||
// Some web browsers seem to immediately clear the default framebuffer when calling
|
||||
// glInvalidateFramebuffer with WebGL 2.0
|
||||
bool disable_invalidate_framebuffer;
|
||||
@@ -433,9 +429,6 @@ private:
|
||||
{ bugs.disable_shared_context_draws,
|
||||
"disable_shared_context_draws",
|
||||
""},
|
||||
{ bugs.texture_external_needs_rebind,
|
||||
"texture_external_needs_rebind",
|
||||
""},
|
||||
{ bugs.disable_invalidate_framebuffer,
|
||||
"disable_invalidate_framebuffer",
|
||||
""},
|
||||
@@ -711,7 +704,7 @@ void OpenGLContext::bindTexture(GLuint unit, GLuint target, GLuint texId, size_t
|
||||
update_state(state.textures.units[unit].targets[targetIndex].texture_id, texId, [&]() {
|
||||
activeTexture(unit);
|
||||
glBindTexture(target, texId);
|
||||
}, (target == GL_TEXTURE_EXTERNAL_OES) && bugs.texture_external_needs_rebind);
|
||||
}, target == GL_TEXTURE_EXTERNAL_OES);
|
||||
}
|
||||
|
||||
void OpenGLContext::bindTexture(GLuint unit, GLuint target, GLuint texId) noexcept {
|
||||
|
||||
@@ -147,9 +147,9 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
|
||||
#endif
|
||||
|
||||
size_t const defaultSize = FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
|
||||
Platform::DriverConfig validConfig {driverConfig};
|
||||
Platform::DriverConfig validConfig{ driverConfig };
|
||||
validConfig.handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize);
|
||||
OpenGLDriver* const driver = new OpenGLDriver(ec, validConfig);
|
||||
OpenGLDriver* const driver = new(std::nothrow) OpenGLDriver(ec, validConfig);
|
||||
return driver;
|
||||
}
|
||||
|
||||
@@ -171,7 +171,8 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
|
||||
mContext(),
|
||||
mShaderCompilerService(*this),
|
||||
mHandleAllocator("Handles", driverConfig.handleArenaSize),
|
||||
mSamplerMap(32) {
|
||||
mSamplerMap(32),
|
||||
mDriverConfig(driverConfig) {
|
||||
|
||||
std::fill(mSamplerBindings.begin(), mSamplerBindings.end(), nullptr);
|
||||
|
||||
@@ -537,7 +538,7 @@ void OpenGLDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
|
||||
|
||||
|
||||
if (UTILS_UNLIKELY(mContext.isES2())) {
|
||||
// Here we patch the specification constants to enable or not the rec709 output
|
||||
// Here we patch the specialization constants to enable or not the rec709 output
|
||||
// color space emulation in this program. Obviously, the backend shouldn't know about
|
||||
// specific spec-constants, so we need to handle failures gracefully. This cannot be
|
||||
// done at Material creation time because only the backend has access to
|
||||
@@ -1064,6 +1065,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
}
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
} else
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_multisampled_render_to_texture
|
||||
// EXT_multisampled_render_to_texture only support GL_COLOR_ATTACHMENT0
|
||||
if (!attachmentTypeNotSupportedByMSRTT && (t->depth <= 1)
|
||||
@@ -1076,7 +1078,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
// This extension only exists on OpenGL ES.
|
||||
gl.bindFramebuffer(GL_FRAMEBUFFER, rt->gl.fbo);
|
||||
if (any(t->usage & TextureUsage::SAMPLEABLE)) {
|
||||
glext::glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER,
|
||||
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER,
|
||||
attachment, target, t->gl.id, binfo.level, rt->gl.samples);
|
||||
} else {
|
||||
glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment,
|
||||
@@ -1084,7 +1086,8 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
}
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
} else
|
||||
#endif
|
||||
#endif // GL_EXT_multisampled_render_to_texture
|
||||
#endif // __EMSCRIPTEN__
|
||||
if (!any(t->usage & TextureUsage::SAMPLEABLE) && t->samples > 1) {
|
||||
assert_invariant(rt->gl.samples > 1);
|
||||
assert_invariant(glIsRenderbuffer(t->gl.id));
|
||||
@@ -1183,6 +1186,7 @@ void OpenGLDriver::renderBufferStorage(GLuint rbo, GLenum internalformat, uint32
|
||||
uint32_t height, uint8_t samples) const noexcept {
|
||||
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
|
||||
if (samples > 1) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_multisampled_render_to_texture
|
||||
auto& gl = mContext;
|
||||
if (gl.ext.EXT_multisampled_render_to_texture ||
|
||||
@@ -1190,7 +1194,8 @@ void OpenGLDriver::renderBufferStorage(GLuint rbo, GLenum internalformat, uint32
|
||||
glext::glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER,
|
||||
samples, internalformat, width, height);
|
||||
} else
|
||||
#endif
|
||||
#endif // GL_EXT_multisampled_render_to_texture
|
||||
#endif // __EMSCRIPTEN__
|
||||
{
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
glRenderbufferStorageMultisample(GL_RENDERBUFFER,
|
||||
@@ -1906,6 +1911,10 @@ bool OpenGLDriver::isParallelShaderCompileSupported() {
|
||||
return mShaderCompilerService.isParallelShaderCompileSupported();
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isDepthStencilResolveSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
switch (workaround) {
|
||||
case Workaround::SPLIT_EASU:
|
||||
@@ -2227,8 +2236,7 @@ void OpenGLDriver::setMinMaxLevels(Handle<HwTexture> th, uint32_t minLevel, uint
|
||||
t->gl.baseLevel = (int8_t)minLevel;
|
||||
glTexParameteri(t->gl.target, GL_TEXTURE_BASE_LEVEL, t->gl.baseLevel);
|
||||
|
||||
t->gl
|
||||
.maxLevel = (int8_t)maxLevel; // NOTE: according to the GL spec, the default value of this 1000
|
||||
t->gl.maxLevel = (int8_t)maxLevel; // NOTE: according to the GL spec, the default value of this 1000
|
||||
glTexParameteri(t->gl.target, GL_TEXTURE_MAX_LEVEL, t->gl.maxLevel);
|
||||
}
|
||||
#endif
|
||||
@@ -2278,16 +2286,13 @@ void OpenGLDriver::generateMipmaps(Handle<HwTexture> th) {
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
}
|
||||
|
||||
bool OpenGLDriver::canGenerateMipmaps() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
|
||||
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
|
||||
uint32_t width, uint32_t height, uint32_t depth,
|
||||
PixelBufferDescriptor&& p) {
|
||||
auto& gl = mContext;
|
||||
|
||||
assert_invariant(t != nullptr);
|
||||
assert_invariant(xoffset + width <= std::max(1u, t->width >> level));
|
||||
assert_invariant(yoffset + height <= std::max(1u, t->height >> level));
|
||||
assert_invariant(t->samples <= 1);
|
||||
@@ -2986,16 +2991,18 @@ GLuint OpenGLDriver::getSamplerSlow(SamplerParams params) const noexcept {
|
||||
#endif
|
||||
|
||||
void OpenGLDriver::insertEventMarker(char const* string, uint32_t len) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_debug_marker
|
||||
auto& gl = mContext;
|
||||
if (gl.ext.EXT_debug_marker) {
|
||||
glInsertEventMarkerEXT(GLsizei(len ? len : strlen(string)), string);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLDriver::pushGroupMarker(char const* string, uint32_t len) {
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_debug_marker
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
|
||||
if (UTILS_LIKELY(mContext.ext.EXT_debug_marker)) {
|
||||
@@ -3008,9 +3015,11 @@ void OpenGLDriver::pushGroupMarker(char const* string, uint32_t len) {
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_BEGIN(string);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLDriver::popGroupMarker(int) {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_EXT_debug_marker
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_OPENGL
|
||||
if (UTILS_LIKELY(mContext.ext.EXT_debug_marker)) {
|
||||
@@ -3023,6 +3032,7 @@ void OpenGLDriver::popGroupMarker(int) {
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_END();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLDriver::startCapture(int) {
|
||||
@@ -3438,75 +3448,230 @@ void OpenGLDriver::clearWithRasterPipe(TargetBufferFlags clearFlags,
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
}
|
||||
|
||||
void OpenGLDriver::blit(TargetBufferFlags buffers,
|
||||
Handle<HwRenderTarget> dst, Viewport dstRect,
|
||||
Handle<HwRenderTarget> src, Viewport srcRect,
|
||||
SamplerMagFilter filter) {
|
||||
void OpenGLDriver::resolve(
|
||||
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer,
|
||||
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer) {
|
||||
DEBUG_MARKER()
|
||||
GLTexture const* const s = handle_cast<GLTexture*>(src);
|
||||
GLTexture const* const d = handle_cast<GLTexture*>(dst);
|
||||
assert_invariant(s);
|
||||
assert_invariant(d);
|
||||
|
||||
ASSERT_PRECONDITION(
|
||||
d->width == s->width && d->height == s->height,
|
||||
"invalid resolve: src and dst sizes don't match");
|
||||
|
||||
ASSERT_PRECONDITION(s->samples > 1 && d->samples == 1,
|
||||
"invalid resolve: src.samples=%u, dst.samples=%u",
|
||||
+s->samples, +d->samples);
|
||||
|
||||
blit( dst, dstLevel, dstLayer, {},
|
||||
src, srcLevel, srcLayer, {},
|
||||
{ d->width, d->height });
|
||||
}
|
||||
|
||||
void OpenGLDriver::blit(
|
||||
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer, uint2 dstOrigin,
|
||||
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer, uint2 srcOrigin,
|
||||
uint2 size) {
|
||||
DEBUG_MARKER()
|
||||
UTILS_UNUSED_IN_RELEASE auto& gl = mContext;
|
||||
assert_invariant(!gl.isES2());
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
GLbitfield const mask = getAttachmentBitfield(buffers);
|
||||
if (mask) {
|
||||
GLenum glFilterMode = (filter == SamplerMagFilter::NEAREST) ? GL_NEAREST : GL_LINEAR;
|
||||
if (mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) {
|
||||
// GL_INVALID_OPERATION is generated if mask contains any of the GL_DEPTH_BUFFER_BIT or
|
||||
// GL_STENCIL_BUFFER_BIT and filter is not GL_NEAREST.
|
||||
glFilterMode = GL_NEAREST;
|
||||
}
|
||||
|
||||
// note: for msaa RenderTargets with non-msaa attachments, we copy from the msaa sidecar
|
||||
// buffer -- this should produce the same output that if we copied from the resolved
|
||||
// texture. EXT_multisampled_render_to_texture seems to allow both behaviours, and this
|
||||
// is an emulation of that. We cannot use the resolved texture easily because it's not
|
||||
// actually attached to the RenderTarget. Another implementation would be to do a
|
||||
// reverse-resolve, but that wouldn't buy us anything.
|
||||
GLRenderTarget const* s = handle_cast<GLRenderTarget const*>(src);
|
||||
GLRenderTarget const* d = handle_cast<GLRenderTarget const*>(dst);
|
||||
GLTexture* d = handle_cast<GLTexture*>(dst);
|
||||
GLTexture* s = handle_cast<GLTexture*>(src);
|
||||
assert_invariant(d);
|
||||
assert_invariant(s);
|
||||
|
||||
// blit operations are only supported from RenderTargets that have only COLOR0 (or
|
||||
// no color buffer at all)
|
||||
assert_invariant(
|
||||
!(s->targets & (TargetBufferFlags::COLOR_ALL & ~TargetBufferFlags::COLOR0)));
|
||||
ASSERT_PRECONDITION_NON_FATAL(any(d->usage & TextureUsage::BLIT_DST),
|
||||
"texture doesn't have BLIT_DST");
|
||||
|
||||
assert_invariant(
|
||||
!(d->targets & (TargetBufferFlags::COLOR_ALL & ~TargetBufferFlags::COLOR0)));
|
||||
ASSERT_PRECONDITION_NON_FATAL(any(s->usage & TextureUsage::BLIT_SRC),
|
||||
"texture doesn't have BLIT_SRC");
|
||||
|
||||
// With GLES 3.x, GL_INVALID_OPERATION is generated if the value of GL_SAMPLE_BUFFERS
|
||||
// for the draw buffer is greater than zero. This works with OpenGL, so we want to
|
||||
// make sure to catch this scenario.
|
||||
assert_invariant(d->gl.samples <= 1);
|
||||
ASSERT_PRECONDITION_NON_FATAL(s->format == d->format,
|
||||
"src and dst texture format don't match");
|
||||
|
||||
// GL_INVALID_OPERATION is generated if GL_SAMPLE_BUFFERS for the read buffer is greater
|
||||
// than zero and the formats of draw and read buffers are not identical.
|
||||
// However, it's not well-defined in the spec what "format" means. So it's difficult
|
||||
// to have an assert here -- especially when dealing with the default framebuffer
|
||||
enum class AttachmentType : GLenum {
|
||||
COLOR = GL_COLOR_ATTACHMENT0,
|
||||
DEPTH = GL_DEPTH_ATTACHMENT,
|
||||
STENCIL = GL_STENCIL_ATTACHMENT,
|
||||
DEPTH_STENCIL = GL_DEPTH_STENCIL_ATTACHMENT,
|
||||
};
|
||||
|
||||
// GL_INVALID_OPERATION is generated if GL_SAMPLE_BUFFERS for the read buffer is greater
|
||||
// than zero and (...) the source and destination rectangles are not defined with the
|
||||
// same (X0, Y0) and (X1, Y1) bounds.
|
||||
auto getFormatType = [](TextureFormat format) -> AttachmentType {
|
||||
bool const depth = isDepthFormat(format);
|
||||
bool const stencil = isStencilFormat(format);
|
||||
if (depth && stencil) return AttachmentType::DEPTH_STENCIL;
|
||||
if (depth) return AttachmentType::DEPTH;
|
||||
if (stencil) return AttachmentType::STENCIL;
|
||||
return AttachmentType::COLOR;
|
||||
};
|
||||
|
||||
// Additionally, the EXT_multisampled_render_to_texture extension doesn't specify what
|
||||
// happens when blitting from an "implicit" resolve render target (does it work?), so
|
||||
// to ere on the safe side, we don't allow it.
|
||||
if (s->gl.samples > 1) {
|
||||
assert_invariant(!memcmp(&dstRect, &srcRect, sizeof(srcRect)));
|
||||
}
|
||||
AttachmentType const type = getFormatType(d->format);
|
||||
assert_invariant(type == getFormatType(s->format));
|
||||
|
||||
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, s->gl.fbo);
|
||||
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, d->gl.fbo);
|
||||
// GL_INVALID_OPERATION is generated if mask contains any of the GL_DEPTH_BUFFER_BIT or
|
||||
// GL_STENCIL_BUFFER_BIT and filter is not GL_NEAREST.
|
||||
GLbitfield mask = {};
|
||||
switch (type) {
|
||||
case AttachmentType::COLOR:
|
||||
mask = GL_COLOR_BUFFER_BIT;
|
||||
break;
|
||||
case AttachmentType::DEPTH:
|
||||
mask = GL_DEPTH_BUFFER_BIT;
|
||||
break;
|
||||
case AttachmentType::STENCIL:
|
||||
mask = GL_STENCIL_BUFFER_BIT;
|
||||
break;
|
||||
case AttachmentType::DEPTH_STENCIL:
|
||||
mask = GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT;
|
||||
break;
|
||||
};
|
||||
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_READ_FRAMEBUFFER)
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_DRAW_FRAMEBUFFER)
|
||||
GLuint fbo[2] = {};
|
||||
glGenFramebuffers(2, fbo);
|
||||
|
||||
gl.disable(GL_SCISSOR_TEST);
|
||||
glBlitFramebuffer(
|
||||
srcRect.left, srcRect.bottom, srcRect.right(), srcRect.top(),
|
||||
dstRect.left, dstRect.bottom, dstRect.right(), dstRect.top(),
|
||||
mask, glFilterMode);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo[0]);
|
||||
switch (d->target) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
if (any(d->usage & TextureUsage::SAMPLEABLE)) {
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GLenum(type),
|
||||
GL_TEXTURE_2D, d->gl.id, dstLevel);
|
||||
} else {
|
||||
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GLenum(type),
|
||||
GL_RENDERBUFFER, d->gl.id);
|
||||
}
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GLenum(type),
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + dstLayer, d->gl.id, dstLevel);
|
||||
break;
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
case SamplerType::SAMPLER_3D:
|
||||
glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GLenum(type),
|
||||
d->gl.id, dstLevel, dstLayer);
|
||||
break;
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
break;
|
||||
}
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_READ_FRAMEBUFFER)
|
||||
|
||||
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, fbo[1]);
|
||||
switch (s->target) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
if (any(s->usage & TextureUsage::SAMPLEABLE)) {
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GLenum(type),
|
||||
GL_TEXTURE_2D, s->gl.id, srcLevel);
|
||||
} else {
|
||||
glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, GLenum(type),
|
||||
GL_RENDERBUFFER, s->gl.id);
|
||||
}
|
||||
break;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GLenum(type),
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + srcLayer, s->gl.id, srcLevel);
|
||||
break;
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
case SamplerType::SAMPLER_3D:
|
||||
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GLenum(type),
|
||||
s->gl.id, srcLevel, srcLayer);
|
||||
break;
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
break;
|
||||
}
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_DRAW_FRAMEBUFFER)
|
||||
|
||||
gl.disable(GL_SCISSOR_TEST);
|
||||
glBlitFramebuffer(
|
||||
srcOrigin.x, srcOrigin.y, srcOrigin.x + size.x, srcOrigin.y + size.y,
|
||||
dstOrigin.x, dstOrigin.y, dstOrigin.x + size.x, dstOrigin.y + size.y,
|
||||
mask, GL_NEAREST);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
|
||||
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
|
||||
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
glDeleteFramebuffers(2, fbo);
|
||||
|
||||
if (any(d->usage & TextureUsage::SAMPLEABLE)) {
|
||||
// In a sense, blitting to a texture level is similar to calling setTextureData on it; in
|
||||
// both cases, we update the base/max LOD to give shaders access to levels as they become
|
||||
// available. Note that this can only expand the LOD range (never shrink it), and that
|
||||
// users can override this range by calling setMinMaxLevels().
|
||||
updateTextureLodRange(d, int8_t(dstLevel));
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
Handle<HwRenderTarget> dst, Viewport dstRect,
|
||||
Handle<HwRenderTarget> src, Viewport srcRect,
|
||||
SamplerMagFilter filter) {
|
||||
|
||||
// Note: blitDEPRECATED is only used by Renderer::copyFrame
|
||||
|
||||
DEBUG_MARKER()
|
||||
UTILS_UNUSED_IN_RELEASE auto& gl = mContext;
|
||||
assert_invariant(!gl.isES2());
|
||||
|
||||
ASSERT_PRECONDITION(buffers == TargetBufferFlags::COLOR0,
|
||||
"blitDEPRECATED only supports COLOR0");
|
||||
|
||||
ASSERT_PRECONDITION(srcRect.left >= 0 && srcRect.bottom >= 0 &&
|
||||
dstRect.left >= 0 && dstRect.bottom >= 0,
|
||||
"Source and destination rects must be positive.");
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
|
||||
GLenum const glFilterMode = (filter == SamplerMagFilter::NEAREST) ? GL_NEAREST : GL_LINEAR;
|
||||
|
||||
// note: for msaa RenderTargets with non-msaa attachments, we copy from the msaa sidecar
|
||||
// buffer -- this should produce the same output that if we copied from the resolved
|
||||
// texture. EXT_multisampled_render_to_texture seems to allow both behaviours, and this
|
||||
// is an emulation of that. We cannot use the resolved texture easily because it's not
|
||||
// actually attached to the RenderTarget. Another implementation would be to do a
|
||||
// reverse-resolve, but that wouldn't buy us anything.
|
||||
GLRenderTarget const* s = handle_cast<GLRenderTarget const*>(src);
|
||||
GLRenderTarget const* d = handle_cast<GLRenderTarget const*>(dst);
|
||||
|
||||
// With GLES 3.x, GL_INVALID_OPERATION is generated if the value of GL_SAMPLE_BUFFERS
|
||||
// for the draw buffer is greater than zero. This works with OpenGL, so we want to
|
||||
// make sure to catch this scenario.
|
||||
assert_invariant(d->gl.samples <= 1);
|
||||
|
||||
// GL_INVALID_OPERATION is generated if GL_SAMPLE_BUFFERS for the read buffer is greater
|
||||
// than zero and the formats of draw and read buffers are not identical.
|
||||
// However, it's not well-defined in the spec what "format" means. So it's difficult
|
||||
// to have an assert here -- especially when dealing with the default framebuffer
|
||||
|
||||
// GL_INVALID_OPERATION is generated if GL_SAMPLE_BUFFERS for the read buffer is greater
|
||||
// than zero and (...) the source and destination rectangles are not defined with the
|
||||
// same (X0, Y0) and (X1, Y1) bounds.
|
||||
|
||||
// Additionally, the EXT_multisampled_render_to_texture extension doesn't specify what
|
||||
// happens when blitting from an "implicit" resolve render target (does it work?), so
|
||||
// to ere on the safe side, we don't allow it.
|
||||
if (s->gl.samples > 1) {
|
||||
assert_invariant(!memcmp(&dstRect, &srcRect, sizeof(srcRect)));
|
||||
}
|
||||
|
||||
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, s->gl.fbo);
|
||||
gl.bindFramebuffer(GL_DRAW_FRAMEBUFFER, d->gl.fbo);
|
||||
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_READ_FRAMEBUFFER)
|
||||
CHECK_GL_FRAMEBUFFER_STATUS(utils::slog.e, GL_DRAW_FRAMEBUFFER)
|
||||
|
||||
gl.disable(GL_SCISSOR_TEST);
|
||||
glBlitFramebuffer(
|
||||
srcRect.left, srcRect.bottom, srcRect.right(), srcRect.top(),
|
||||
dstRect.left, dstRect.bottom, dstRect.right(), dstRect.top(),
|
||||
GL_COLOR_BUFFER_BIT, glFilterMode);
|
||||
CHECK_GL_ERROR(utils::slog.e)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -400,6 +400,9 @@ private:
|
||||
// timer query implementation
|
||||
OpenGLTimerQueryInterface* mTimerQueryImpl = nullptr;
|
||||
|
||||
const Platform::DriverConfig mDriverConfig;
|
||||
Platform::DriverConfig const& getDriverConfig() const noexcept { return mDriverConfig; }
|
||||
|
||||
// for ES2 sRGB support
|
||||
GLSwapChain* mCurrentDrawSwapChain = nullptr;
|
||||
bool mRec709OutputColorspace = false;
|
||||
|
||||
@@ -140,20 +140,43 @@ void* ShaderCompilerService::getUserData(const program_token_t& token) noexcept
|
||||
ShaderCompilerService::ShaderCompilerService(OpenGLDriver& driver)
|
||||
: mDriver(driver),
|
||||
mBlobCache(driver.getContext()),
|
||||
mCallbackManager(driver),
|
||||
KHR_parallel_shader_compile(driver.getContext().ext.KHR_parallel_shader_compile) {
|
||||
mCallbackManager(driver) {
|
||||
}
|
||||
|
||||
ShaderCompilerService::~ShaderCompilerService() noexcept = default;
|
||||
|
||||
bool ShaderCompilerService::isParallelShaderCompileSupported() const noexcept {
|
||||
return KHR_parallel_shader_compile || mShaderCompilerThreadCount;
|
||||
assert_invariant(mMode != Mode::UNDEFINED);
|
||||
return mMode != Mode::SYNCHRONOUS;
|
||||
}
|
||||
|
||||
void ShaderCompilerService::init() noexcept {
|
||||
// If we have KHR_parallel_shader_compile, we always use it, it should be more resource
|
||||
// friendly.
|
||||
if (!KHR_parallel_shader_compile) {
|
||||
if (UTILS_UNLIKELY(mDriver.getDriverConfig().disableParallelShaderCompile)) {
|
||||
// user disabled parallel shader compile
|
||||
mMode = Mode::SYNCHRONOUS;
|
||||
return;
|
||||
}
|
||||
|
||||
// Here we decide which mode we'll be using. We always prefer our own thread-pool if
|
||||
// that mode is available because, we have no control on how the compilation queues are
|
||||
// handled if done by the driver (so at the very least we'd need to decode this per-driver).
|
||||
// In theory, using Mode::ASYNCHRONOUS (a.k.a. KHR_parallel_shader_compile) could be more
|
||||
// efficient, since it doesn't require shared contexts.
|
||||
// In practice, we already know that with ANGLE, Mode::ASYNCHRONOUS can cause very long
|
||||
// pauses at glDraw() time in the situation where glLinkProgram() has been emitted, but has
|
||||
// other programs ahead of it in ANGLE's queue.
|
||||
if (mDriver.mPlatform.isExtraContextSupported()) {
|
||||
// our thread-pool if possible
|
||||
mMode = Mode::THREAD_POOL;
|
||||
} else if (mDriver.getContext().ext.KHR_parallel_shader_compile) {
|
||||
// if not, async shader compilation and link if the driver supports it
|
||||
mMode = Mode::ASYNCHRONOUS;
|
||||
} else {
|
||||
// fallback to synchronous shader compilation
|
||||
mMode = Mode::SYNCHRONOUS;
|
||||
}
|
||||
|
||||
if (mMode == Mode::THREAD_POOL) {
|
||||
// - on Adreno there is a single compiler object. We can't use a pool > 1
|
||||
// also glProgramBinary blocks if other threads are compiling.
|
||||
// - on Mali shader compilation can be multi-threaded, but program linking happens on
|
||||
@@ -163,37 +186,42 @@ void ShaderCompilerService::init() noexcept {
|
||||
// - on macOS (M1 MacBook Pro/Ventura) there is global lock around all GL APIs when using
|
||||
// a shared context, so parallel shader compilation yields no benefit.
|
||||
// - on windows/linux we could use more threads, tbd.
|
||||
if (mDriver.mPlatform.isExtraContextSupported()) {
|
||||
// By default, we use one thread at the same priority as the gl thread. This is the
|
||||
// safest choice that avoids priority inversions.
|
||||
uint32_t poolSize = 1;
|
||||
JobSystem::Priority priority = JobSystem::Priority::DISPLAY;
|
||||
|
||||
auto const& renderer = mDriver.getContext().state.renderer;
|
||||
if (UTILS_UNLIKELY(strstr(renderer, "PowerVR"))) {
|
||||
// The PowerVR driver support parallel shader compilation well, so we use 2
|
||||
// threads, we can use lower priority threads here because urgent compilations
|
||||
// will most likely happen on the main gl thread. Using too many thread can
|
||||
// increase memory pressure significantly.
|
||||
poolSize = 2;
|
||||
priority = JobSystem::Priority::BACKGROUND;
|
||||
}
|
||||
// By default, we use one thread at the same priority as the gl thread. This is the
|
||||
// safest choice that avoids priority inversions.
|
||||
uint32_t poolSize = 1;
|
||||
JobSystem::Priority priority = JobSystem::Priority::DISPLAY;
|
||||
|
||||
mShaderCompilerThreadCount = poolSize;
|
||||
mCompilerThreadPool.init(mShaderCompilerThreadCount,
|
||||
[&platform = mDriver.mPlatform, priority]() {
|
||||
// give the thread a name
|
||||
JobSystem::setThreadName("CompilerThreadPool");
|
||||
// run at a slightly lower priority than other filament threads
|
||||
JobSystem::setThreadPriority(priority);
|
||||
// create a gl context current to this thread
|
||||
platform.createContext(true);
|
||||
},
|
||||
[&platform = mDriver.mPlatform]() {
|
||||
// release context and thread state
|
||||
platform.releaseContext();
|
||||
});
|
||||
auto const& renderer = mDriver.getContext().state.renderer;
|
||||
// Some drivers support parallel shader compilation well, so we use N
|
||||
// threads, we can use lower priority threads here because urgent compilations
|
||||
// will most likely happen on the main gl thread. Using too many thread can
|
||||
// increase memory pressure significantly.
|
||||
if (UTILS_UNLIKELY(strstr(renderer, "PowerVR"))) {
|
||||
// For PowerVR, it's unclear what the cost of extra shared contexts is, so we only
|
||||
// use 2 to be safe.
|
||||
poolSize = 2;
|
||||
priority = JobSystem::Priority::BACKGROUND;
|
||||
} else if (UTILS_UNLIKELY(strstr(renderer, "ANGLE"))) {
|
||||
// Angle shared contexts are not expensive once we have two.
|
||||
poolSize = (std::thread::hardware_concurrency() + 1) / 2;
|
||||
priority = JobSystem::Priority::BACKGROUND;
|
||||
}
|
||||
|
||||
mShaderCompilerThreadCount = poolSize;
|
||||
mCompilerThreadPool.init(mShaderCompilerThreadCount,
|
||||
[&platform = mDriver.mPlatform, priority]() {
|
||||
// give the thread a name
|
||||
JobSystem::setThreadName("CompilerThreadPool");
|
||||
// run at a slightly lower priority than other filament threads
|
||||
JobSystem::setThreadPriority(priority);
|
||||
// create a gl context current to this thread
|
||||
platform.createContext(true);
|
||||
},
|
||||
[&platform = mDriver.mPlatform]() {
|
||||
// release context and thread state
|
||||
platform.releaseContext();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +255,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
|
||||
token->handle = mCallbackManager.get();
|
||||
|
||||
CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
|
||||
if (mShaderCompilerThreadCount) {
|
||||
if (mMode == Mode::THREAD_POOL) {
|
||||
// queue a compile job
|
||||
mCompilerThreadPool.queue(priorityQueue, token,
|
||||
[this, &gl, program = std::move(program), token]() mutable {
|
||||
@@ -276,7 +304,8 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
|
||||
token->shaderSourceCode);
|
||||
|
||||
runAtNextTick(priorityQueue, token, [this, token](Job const&) {
|
||||
if (KHR_parallel_shader_compile) {
|
||||
assert_invariant(mMode != Mode::THREAD_POOL);
|
||||
if (mMode == Mode::ASYNCHRONOUS) {
|
||||
// don't attempt to link this program if all shaders are not done compiling
|
||||
GLint status;
|
||||
if (token->gl.program) {
|
||||
@@ -300,7 +329,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
|
||||
// link the program, this also cannot fail because status is checked later.
|
||||
token->gl.program = linkProgram(mDriver.getContext(),
|
||||
token->gl.shaders, token->attributes);
|
||||
if (KHR_parallel_shader_compile) {
|
||||
if (mMode == Mode::ASYNCHRONOUS) {
|
||||
// wait until the link finishes...
|
||||
return false;
|
||||
}
|
||||
@@ -341,7 +370,7 @@ void ShaderCompilerService::terminate(program_token_t& token) {
|
||||
|
||||
bool const canceled = token->compiler.cancelTickOp(token);
|
||||
|
||||
if (token->compiler.mShaderCompilerThreadCount) {
|
||||
if (token->compiler.mMode == Mode::THREAD_POOL) {
|
||||
auto job = token->compiler.mCompilerThreadPool.dequeue(token);
|
||||
if (!job) {
|
||||
// The job is being executed right now. We need to wait for it to finish to avoid a
|
||||
@@ -375,7 +404,7 @@ void ShaderCompilerService::terminate(program_token_t& token) {
|
||||
|
||||
void ShaderCompilerService::tick() {
|
||||
// we don't need to run executeTickOps() if we're using the thread-pool
|
||||
if (UTILS_UNLIKELY(!mShaderCompilerThreadCount)) {
|
||||
if (UTILS_UNLIKELY(mMode != Mode::THREAD_POOL)) {
|
||||
executeTickOps();
|
||||
}
|
||||
}
|
||||
@@ -400,7 +429,7 @@ void ShaderCompilerService::getProgramFromCompilerPool(program_token_t& token) n
|
||||
GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
|
||||
SYSTRACE_CALL();
|
||||
if (!token->gl.program) {
|
||||
if (mShaderCompilerThreadCount) {
|
||||
if (mMode == Mode::THREAD_POOL) {
|
||||
// we need this program right now, remove it from the queue
|
||||
auto job = mCompilerThreadPool.dequeue(token);
|
||||
if (job) {
|
||||
@@ -416,7 +445,7 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept {
|
||||
// Block until we get the program from the pool. Generally this wouldn't block
|
||||
// because we just compiled the program above, when executing job.
|
||||
ShaderCompilerService::getProgramFromCompilerPool(token);
|
||||
} else if (KHR_parallel_shader_compile) {
|
||||
} else if (mMode == Mode::ASYNCHRONOUS) {
|
||||
// we force the program link -- which might stall, either here or below in
|
||||
// checkProgramStatus(), but we don't have a choice, we need to use the program now.
|
||||
token->compiler.cancelTickOp(token);
|
||||
@@ -524,7 +553,8 @@ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
// add support for ARB_shading_language_packing if needed
|
||||
auto const packingFunctions = process_ARB_shading_language_packing(context);
|
||||
|
||||
// split shader source, so we can insert the specification constants and the packing functions
|
||||
// split shader source, so we can insert the specialization constants and the packing
|
||||
// functions
|
||||
auto const [prolog, body] = splitShaderSource(source);
|
||||
|
||||
const std::array<const char*, 4> sources = {
|
||||
|
||||
@@ -95,19 +95,42 @@ public:
|
||||
CallbackHandler* handler, CallbackHandler::Callback callback, void* user);
|
||||
|
||||
private:
|
||||
struct Job {
|
||||
template<typename FUNC>
|
||||
Job(FUNC&& fn) : fn(std::forward<FUNC>(fn)) {}
|
||||
Job(std::function<bool(Job const& job)> fn,
|
||||
CallbackHandler* handler, void* user, CallbackHandler::Callback callback)
|
||||
: fn(std::move(fn)), handler(handler), user(user), callback(callback) {
|
||||
}
|
||||
std::function<bool(Job const& job)> fn;
|
||||
CallbackHandler* handler = nullptr;
|
||||
void* user = nullptr;
|
||||
CallbackHandler::Callback callback{};
|
||||
};
|
||||
|
||||
enum class Mode {
|
||||
UNDEFINED, // init() has not been called yet.
|
||||
SYNCHRONOUS, // synchronous shader compilation
|
||||
THREAD_POOL, // asynchronous shader compilation using a thread-pool (most common)
|
||||
ASYNCHRONOUS // asynchronous shader compilation using KHR_parallel_shader_compile
|
||||
};
|
||||
|
||||
OpenGLDriver& mDriver;
|
||||
OpenGLBlobCache mBlobCache;
|
||||
CallbackManager mCallbackManager;
|
||||
CompilerThreadPool mCompilerThreadPool;
|
||||
|
||||
const bool KHR_parallel_shader_compile;
|
||||
uint32_t mShaderCompilerThreadCount = 0u;
|
||||
Mode mMode = Mode::UNDEFINED; // valid after init() is called
|
||||
|
||||
using ContainerType = std::tuple<CompilerPriorityQueue, program_token_t, Job>;
|
||||
std::vector<ContainerType> mRunAtNextTickOps;
|
||||
|
||||
GLuint initialize(ShaderCompilerService::program_token_t& token) noexcept;
|
||||
|
||||
static void getProgramFromCompilerPool(program_token_t& token) noexcept;
|
||||
|
||||
static void compileShaders(
|
||||
static void compileShaders(
|
||||
OpenGLContext& context,
|
||||
Program::ShaderSource shadersSource,
|
||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
|
||||
@@ -127,27 +150,11 @@ private:
|
||||
|
||||
static bool checkProgramStatus(program_token_t const& token) noexcept;
|
||||
|
||||
struct Job {
|
||||
template<typename FUNC>
|
||||
Job(FUNC&& fn) : fn(std::forward<FUNC>(fn)) {}
|
||||
Job(std::function<bool(Job const& job)> fn,
|
||||
CallbackHandler* handler, void* user, CallbackHandler::Callback callback)
|
||||
: fn(std::move(fn)), handler(handler), user(user), callback(callback) {
|
||||
}
|
||||
std::function<bool(Job const& job)> fn;
|
||||
CallbackHandler* handler = nullptr;
|
||||
void* user = nullptr;
|
||||
CallbackHandler::Callback callback{};
|
||||
};
|
||||
|
||||
void runAtNextTick(CompilerPriorityQueue priority,
|
||||
const program_token_t& token, Job job) noexcept;
|
||||
void executeTickOps() noexcept;
|
||||
bool cancelTickOp(program_token_t token) noexcept;
|
||||
// order of insertion is important
|
||||
|
||||
using ContainerType = std::tuple<CompilerPriorityQueue, program_token_t, Job>;
|
||||
std::vector<ContainerType> mRunAtNextTickOps;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -28,6 +28,7 @@ static void getProcAddress(T& pfn, const char* name) noexcept {
|
||||
}
|
||||
|
||||
namespace glext {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_OES_EGL_image
|
||||
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
|
||||
#endif
|
||||
@@ -72,10 +73,11 @@ PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
|
||||
// use getProcAddress for ES3.1 and above entry points.
|
||||
PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
|
||||
#endif
|
||||
|
||||
static std::once_flag sGlExtInitialized;
|
||||
#endif // __EMSCRIPTEN__
|
||||
|
||||
void importGLESExtensionsEntryPoints() {
|
||||
#ifndef __EMSCRIPTEN__
|
||||
std::call_once(sGlExtInitialized, +[]() {
|
||||
#ifdef GL_OES_EGL_image
|
||||
getProcAddress(glEGLImageTargetTexture2DOES, "glEGLImageTargetTexture2DOES");
|
||||
@@ -119,6 +121,7 @@ void importGLESExtensionsEntryPoints() {
|
||||
getProcAddress(glDispatchCompute, "glDispatchCompute");
|
||||
#endif
|
||||
});
|
||||
#endif // __EMSCRIPTEN__
|
||||
}
|
||||
|
||||
} // namespace glext
|
||||
|
||||
@@ -112,6 +112,7 @@ namespace glext {
|
||||
// it is currently called from PlatformEGL.
|
||||
void importGLESExtensionsEntryPoints();
|
||||
|
||||
#ifndef __EMSCRIPTEN__
|
||||
#ifdef GL_OES_EGL_image
|
||||
extern PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES;
|
||||
#endif
|
||||
@@ -153,6 +154,7 @@ extern PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
|
||||
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
|
||||
extern PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
|
||||
#endif
|
||||
#endif // __EMSCRIPTEN__
|
||||
} // namespace glext
|
||||
|
||||
using namespace glext;
|
||||
|
||||
@@ -293,20 +293,6 @@ io::ostream& operator<<(io::ostream& out, TextureFormat format) {
|
||||
return out;
|
||||
}
|
||||
|
||||
io::ostream& operator<<(io::ostream& out, TextureUsage usage) {
|
||||
switch (usage) {
|
||||
CASE(TextureUsage, NONE)
|
||||
CASE(TextureUsage, DEFAULT)
|
||||
CASE(TextureUsage, COLOR_ATTACHMENT)
|
||||
CASE(TextureUsage, DEPTH_ATTACHMENT)
|
||||
CASE(TextureUsage, STENCIL_ATTACHMENT)
|
||||
CASE(TextureUsage, UPLOADABLE)
|
||||
CASE(TextureUsage, SAMPLEABLE)
|
||||
CASE(TextureUsage, SUBPASS_INPUT)
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
io::ostream& operator<<(io::ostream& out, TextureCubemapFace face) {
|
||||
switch (face) {
|
||||
CASE(TextureCubemapFace, NEGATIVE_X)
|
||||
@@ -469,7 +455,7 @@ io::ostream& operator<<(io::ostream& out, filament::backend::Viewport const& vie
|
||||
|
||||
io::ostream& operator<<(io::ostream& out, TargetBufferFlags flags) {
|
||||
// TODO: implement decoding of enum
|
||||
out << uint8_t(flags);
|
||||
out << uint32_t(flags);
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,22 +38,15 @@ using ImgUtil = VulkanImageUtility;
|
||||
namespace {
|
||||
|
||||
inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect, VkFilter filter,
|
||||
const VkExtent2D srcExtent, VulkanAttachment src, VulkanAttachment dst,
|
||||
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},
|
||||
}};
|
||||
if constexpr (FVK_ENABLED(FVK_DEBUG_BLITTER)) {
|
||||
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;
|
||||
}
|
||||
|
||||
const VkImageSubresourceRange srcRange = {
|
||||
.aspectMask = aspect,
|
||||
@@ -71,6 +64,36 @@ inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect,
|
||||
.layerCount = 1,
|
||||
};
|
||||
|
||||
VulkanLayout oldSrcLayout = src.getLayout();
|
||||
VulkanLayout oldDstLayout = dst.getLayout();
|
||||
|
||||
src.texture->transitionLayout(cmdbuffer, srcRange, VulkanLayout::TRANSFER_SRC);
|
||||
dst.texture->transitionLayout(cmdbuffer, dstRange, VulkanLayout::TRANSFER_DST);
|
||||
|
||||
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] },
|
||||
}};
|
||||
vkCmdBlitImage(cmdbuffer,
|
||||
src.getImage(), ImgUtil::getVkLayout(VulkanLayout::TRANSFER_SRC),
|
||||
dst.getImage(), ImgUtil::getVkLayout(VulkanLayout::TRANSFER_DST),
|
||||
1, blitRegions, filter);
|
||||
|
||||
if (oldSrcLayout == VulkanLayout::UNDEFINED) {
|
||||
oldSrcLayout = ImgUtil::getDefaultLayout(src.texture->usage);
|
||||
}
|
||||
if (oldDstLayout == VulkanLayout::UNDEFINED) {
|
||||
oldDstLayout = ImgUtil::getDefaultLayout(dst.texture->usage);
|
||||
}
|
||||
src.texture->transitionLayout(cmdbuffer, srcRange, oldSrcLayout);
|
||||
dst.texture->transitionLayout(cmdbuffer, dstRange, oldDstLayout);
|
||||
}
|
||||
|
||||
inline void resolveFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect,
|
||||
VulkanAttachment src, VulkanAttachment dst) {
|
||||
|
||||
if constexpr (FVK_ENABLED(FVK_DEBUG_BLITTER)) {
|
||||
utils::slog.d << "Fast blit from=" << src.texture->getVkImage() << ",level=" << (int) src.level
|
||||
<< " layout=" << src.getLayout()
|
||||
@@ -78,25 +101,41 @@ inline void blitFast(const VkCommandBuffer cmdbuffer, VkImageAspectFlags aspect,
|
||||
<< " layout=" << dst.getLayout() << utils::io::endl;
|
||||
}
|
||||
|
||||
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,
|
||||
};
|
||||
|
||||
VulkanLayout oldSrcLayout = src.getLayout();
|
||||
VulkanLayout oldDstLayout = dst.getLayout();
|
||||
|
||||
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);
|
||||
}
|
||||
assert_invariant(
|
||||
aspect != VK_IMAGE_ASPECT_DEPTH_BIT && "Resolve with depth is not yet supported.");
|
||||
const VkImageResolve resolveRegions[1] = {{
|
||||
.srcSubresource = { aspect, src.level, src.layer, 1 },
|
||||
.srcOffset = { 0, 0 },
|
||||
.dstSubresource = { aspect, dst.level, dst.layer, 1 },
|
||||
.dstOffset = { 0, 0 },
|
||||
.extent = { src.getExtent2D().width, src.getExtent2D().height, 1 },
|
||||
}};
|
||||
vkCmdResolveImage(cmdbuffer,
|
||||
src.getImage(), ImgUtil::getVkLayout(VulkanLayout::TRANSFER_SRC),
|
||||
dst.getImage(), ImgUtil::getVkLayout(VulkanLayout::TRANSFER_DST),
|
||||
1, resolveRegions);
|
||||
|
||||
if (oldSrcLayout == VulkanLayout::UNDEFINED) {
|
||||
oldSrcLayout = ImgUtil::getDefaultLayout(src.texture->usage);
|
||||
@@ -115,52 +154,22 @@ struct BlitterUniforms {
|
||||
|
||||
}// anonymous namespace
|
||||
|
||||
VulkanBlitter::VulkanBlitter(VulkanStagePool& stagePool, VulkanPipelineCache& pipelineCache,
|
||||
VulkanFboCache& fboCache, VulkanSamplerCache& samplerCache) noexcept
|
||||
: mStagePool(stagePool), mPipelineCache(pipelineCache), mFramebufferCache(fboCache),
|
||||
mSamplerCache(samplerCache) {}
|
||||
VulkanBlitter::VulkanBlitter() noexcept = default;
|
||||
|
||||
void VulkanBlitter::initialize(VkPhysicalDevice physicalDevice, VkDevice device,
|
||||
VmaAllocator allocator, VulkanCommands* commands, VulkanTexture* emptyTexture) noexcept {
|
||||
VmaAllocator allocator, VulkanCommands* commands) noexcept {
|
||||
mPhysicalDevice = physicalDevice;
|
||||
mDevice = device;
|
||||
mAllocator = allocator;
|
||||
mCommands = commands;
|
||||
mEmptyTexture = emptyTexture;
|
||||
}
|
||||
|
||||
void VulkanBlitter::blitColor(BlitArgs args) {
|
||||
const VulkanAttachment src = args.srcTarget->getColor(args.targetIndex);
|
||||
const VulkanAttachment dst = args.dstTarget->getColor(0);
|
||||
const VkImageAspectFlags aspect = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) {
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_BLIT_FORMAT)
|
||||
VkPhysicalDevice const gpu = mPhysicalDevice;
|
||||
VkFormatProperties info;
|
||||
vkGetPhysicalDeviceFormatProperties(gpu, src.getFormat(), &info);
|
||||
if (!ASSERT_POSTCONDITION_NON_FATAL(info.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT,
|
||||
"Source format is not blittable %d", src.getFormat())) {
|
||||
return;
|
||||
}
|
||||
vkGetPhysicalDeviceFormatProperties(gpu, dst.getFormat(), &info);
|
||||
if (!ASSERT_POSTCONDITION_NON_FATAL(info.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT,
|
||||
"Destination format is not blittable %d", dst.getFormat())) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VkCommandBuffer const cmdbuffer = commands.buffer();
|
||||
commands.acquire(src.texture);
|
||||
commands.acquire(dst.texture);
|
||||
// src and dst should have the same aspect here
|
||||
VkImageAspectFlags const aspect = src.texture->getImageAspect();
|
||||
|
||||
blitFast(cmdbuffer, aspect, args.filter, args.srcTarget->getExtent(), src, dst,
|
||||
args.srcRectPair, args.dstRectPair);
|
||||
}
|
||||
|
||||
void VulkanBlitter::blitDepth(BlitArgs args) {
|
||||
const VulkanAttachment src = args.srcTarget->getDepth();
|
||||
const VulkanAttachment dst = args.dstTarget->getDepth();
|
||||
const VkImageAspectFlags aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
assert_invariant(!(aspect & VK_IMAGE_ASPECT_DEPTH_BIT));
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_BLIT_FORMAT)
|
||||
VkPhysicalDevice const gpu = mPhysicalDevice;
|
||||
@@ -177,286 +186,40 @@ void VulkanBlitter::blitDepth(BlitArgs args) {
|
||||
}
|
||||
#endif
|
||||
|
||||
assert_invariant(src.texture && dst.texture);
|
||||
|
||||
if (src.texture->samples > 1 && dst.texture->samples == 1) {
|
||||
blitSlowDepth(args.filter, args.srcTarget->getExtent(), src, dst, args.srcRectPair,
|
||||
args.dstRectPair);
|
||||
return;
|
||||
}
|
||||
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VkCommandBuffer const cmdbuffer = commands.buffer();
|
||||
commands.acquire(src.texture);
|
||||
commands.acquire(dst.texture);
|
||||
blitFast(cmdbuffer, aspect, args.filter, args.srcTarget->getExtent(), src, dst, args.srcRectPair,
|
||||
args.dstRectPair);
|
||||
resolveFast(cmdbuffer, aspect, src, dst);
|
||||
}
|
||||
|
||||
void VulkanBlitter::blit(VkFilter filter,
|
||||
VulkanAttachment dst, const VkOffset3D* dstRectPair,
|
||||
VulkanAttachment src, const VkOffset3D* srcRectPair) {
|
||||
#if FVK_ENABLED(FVK_DEBUG_BLIT_FORMAT)
|
||||
VkPhysicalDevice const gpu = mPhysicalDevice;
|
||||
VkFormatProperties info;
|
||||
vkGetPhysicalDeviceFormatProperties(gpu, src.getFormat(), &info);
|
||||
if (!ASSERT_POSTCONDITION_NON_FATAL(info.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT,
|
||||
"Depth src format is not blittable %d", src.getFormat())) {
|
||||
return;
|
||||
}
|
||||
vkGetPhysicalDeviceFormatProperties(gpu, dst.getFormat(), &info);
|
||||
if (!ASSERT_POSTCONDITION_NON_FATAL(info.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT,
|
||||
"Depth dst format is not blittable %d", dst.getFormat())) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
// src and dst should have the same aspect here
|
||||
VkImageAspectFlags const aspect = src.texture->getImageAspect();
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VkCommandBuffer const cmdbuffer = commands.buffer();
|
||||
commands.acquire(src.texture);
|
||||
commands.acquire(dst.texture);
|
||||
blitFast(cmdbuffer, aspect, filter, src, dst, srcRectPair, dstRectPair);
|
||||
}
|
||||
|
||||
void VulkanBlitter::terminate() noexcept {
|
||||
if (mDepthResolveProgram) {
|
||||
delete mDepthResolveProgram;
|
||||
mDepthResolveProgram = nullptr;
|
||||
}
|
||||
|
||||
if (mTriangleBuffer) {
|
||||
delete mTriangleBuffer;
|
||||
mTriangleBuffer = nullptr;
|
||||
}
|
||||
|
||||
if (mParamsBuffer) {
|
||||
delete mParamsBuffer;
|
||||
mParamsBuffer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// If we created these shader modules in the constructor, the device might not be ready yet.
|
||||
// It is easier to do lazy initialization, which can also improve load time.
|
||||
void VulkanBlitter::lazyInit() noexcept {
|
||||
if (mDepthResolveProgram != nullptr) {
|
||||
return;
|
||||
}
|
||||
assert_invariant(mDevice);
|
||||
|
||||
auto decode = [device = mDevice](const uint8_t* compressed, int compressedSize) {
|
||||
const size_t spirvSize = smolv::GetDecodedBufferSize(compressed, compressedSize);
|
||||
FixedCapacityVector<uint8_t> spirv(spirvSize);
|
||||
smolv::Decode(compressed, compressedSize, spirv.data(), spirvSize);
|
||||
|
||||
VkShaderModuleCreateInfo moduleInfo = {};
|
||||
moduleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
moduleInfo.codeSize = spirvSize;
|
||||
moduleInfo.pCode = (uint32_t*) spirv.data();
|
||||
|
||||
VkShaderModule result = VK_NULL_HANDLE;
|
||||
vkCreateShaderModule(device, &moduleInfo, VKALLOC, &result);
|
||||
assert_invariant(result);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
VkShaderModule vertexShader = decode(VKSHADERS_BLITDEPTHVS_DATA, VKSHADERS_BLITDEPTHVS_SIZE);
|
||||
VkShaderModule fragmentShader = decode(VKSHADERS_BLITDEPTHFS_DATA, VKSHADERS_BLITDEPTHFS_SIZE);
|
||||
|
||||
// Allocate one anonymous sampler at slot 0.
|
||||
VulkanProgram::CustomSamplerInfoList samplers = {
|
||||
{0, 0, ShaderStageFlags::FRAGMENT},
|
||||
};
|
||||
mDepthResolveProgram = new VulkanProgram(mDevice, vertexShader, fragmentShader, samplers);
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_BLITTER)
|
||||
utils::slog.d << "Created Shader Module for VulkanBlitter "
|
||||
<< "shaders = (" << vertexShader << ", " << fragmentShader << ")"
|
||||
<< utils::io::endl;
|
||||
#endif
|
||||
|
||||
static const float kTriangleVertices[] = {
|
||||
-1.0f, -1.0f,
|
||||
+1.0f, -1.0f,
|
||||
-1.0f, +1.0f,
|
||||
+1.0f, +1.0f,
|
||||
};
|
||||
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VkCommandBuffer const cmdbuffer = commands.buffer();
|
||||
|
||||
mTriangleBuffer = new VulkanBuffer(mAllocator, mStagePool, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
||||
sizeof(kTriangleVertices));
|
||||
|
||||
mTriangleBuffer->loadFromCpu(cmdbuffer, kTriangleVertices, 0, sizeof(kTriangleVertices));
|
||||
|
||||
mParamsBuffer = new VulkanBuffer(mAllocator, mStagePool, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
||||
sizeof(BlitterUniforms));
|
||||
}
|
||||
|
||||
// At a high level, the procedure for resolving depth looks like this:
|
||||
// 1. Load uniforms
|
||||
// 2. Begin render pass
|
||||
// 3. Draw a big triangle
|
||||
// 4. End render pass.
|
||||
void VulkanBlitter::blitSlowDepth(VkFilter filter, const VkExtent2D srcExtent, VulkanAttachment src,
|
||||
VulkanAttachment dst, const VkOffset3D srcRect[2], const VkOffset3D dstRect[2]) {
|
||||
lazyInit();
|
||||
|
||||
VulkanCommandBuffer* commands = &mCommands->get();
|
||||
VkCommandBuffer const cmdbuffer = commands->buffer();
|
||||
commands->acquire(src.texture);
|
||||
commands->acquire(dst.texture);
|
||||
|
||||
BlitterUniforms const uniforms = {
|
||||
.sampleCount = src.texture->samples,
|
||||
.inverseSampleCount = 1.0f / float(src.texture->samples),
|
||||
};
|
||||
mParamsBuffer->loadFromCpu(cmdbuffer, &uniforms, 0, sizeof(uniforms));
|
||||
|
||||
VkImageAspectFlags const aspect = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
|
||||
// We will transition the src into sampler layout and also keep it in sampler layout for
|
||||
// consistency.
|
||||
VulkanLayout const samplerLayout = VulkanLayout::DEPTH_SAMPLER;
|
||||
|
||||
// BEGIN RENDER PASS
|
||||
// -----------------
|
||||
|
||||
const VulkanFboCache::RenderPassKey rpkey = {
|
||||
.initialColorLayoutMask = 0,
|
||||
.initialDepthLayout = VulkanLayout::UNDEFINED,
|
||||
.renderPassDepthLayout = samplerLayout,
|
||||
.finalDepthLayout = samplerLayout,
|
||||
.depthFormat = dst.getFormat(),
|
||||
.clear = {},
|
||||
.discardStart = TargetBufferFlags::DEPTH,
|
||||
.samples = 1,
|
||||
};
|
||||
|
||||
const VkRenderPass renderPass = mFramebufferCache.getRenderPass(rpkey);
|
||||
mPipelineCache.bindRenderPass(renderPass, 0);
|
||||
|
||||
const VulkanFboCache::FboKey fbkey {
|
||||
.renderPass = renderPass,
|
||||
.width = (uint16_t) (dst.texture->width >> dst.level),
|
||||
.height = (uint16_t) (dst.texture->height >> dst.level),
|
||||
.layers = 1,
|
||||
.samples = rpkey.samples,
|
||||
.color = {},
|
||||
.resolve = {},
|
||||
.depth = dst.getImageView(aspect),
|
||||
};
|
||||
const VkFramebuffer vkfb = mFramebufferCache.getFramebuffer(fbkey);
|
||||
|
||||
VkRenderPassBeginInfo renderPassInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
|
||||
.renderPass = renderPass,
|
||||
.framebuffer = vkfb,
|
||||
};
|
||||
renderPassInfo.renderArea.offset.x = dstRect[0].x;
|
||||
renderPassInfo.renderArea.offset.y = dstRect[0].y;
|
||||
renderPassInfo.renderArea.extent.width = dstRect[1].x - dstRect[0].x;
|
||||
renderPassInfo.renderArea.extent.height = dstRect[1].y - dstRect[0].y;
|
||||
|
||||
// We need to transition the source into a sampler since it'll be sampled in the shader.
|
||||
const VkImageSubresourceRange srcRange = {
|
||||
.aspectMask = aspect,
|
||||
.baseMipLevel = src.level,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = src.layer,
|
||||
.layerCount = 1,
|
||||
};
|
||||
src.texture->transitionLayout(cmdbuffer, srcRange, samplerLayout);
|
||||
|
||||
vkCmdBeginRenderPass(cmdbuffer, &renderPassInfo, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
VkViewport viewport = {
|
||||
.x = (float) dstRect[0].x,
|
||||
.y = (float) dstRect[0].y,
|
||||
.width = (float) renderPassInfo.renderArea.extent.width,
|
||||
.height = (float) renderPassInfo.renderArea.extent.height,
|
||||
.minDepth = 0.0f,
|
||||
.maxDepth = 1.0f
|
||||
};
|
||||
|
||||
vkCmdSetViewport(cmdbuffer, 0, 1, &viewport);
|
||||
|
||||
// DRAW THE TRIANGLE
|
||||
// -----------------
|
||||
|
||||
mPipelineCache.bindProgram(mDepthResolveProgram);
|
||||
mPipelineCache.bindPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP);
|
||||
|
||||
auto vkraster = mPipelineCache.getCurrentRasterState();
|
||||
vkraster.depthWriteEnable = true;
|
||||
vkraster.depthCompareOp = SamplerCompareFunc::A;
|
||||
vkraster.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
|
||||
vkraster.alphaToCoverageEnable = false,
|
||||
vkraster.blendEnable = VK_FALSE,
|
||||
vkraster.srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||
vkraster.dstColorBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
vkraster.colorBlendOp = BlendEquation::ADD,
|
||||
vkraster.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||
vkraster.dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO,
|
||||
vkraster.alphaBlendOp = BlendEquation::ADD,
|
||||
vkraster.colorWriteMask = (VkColorComponentFlags) 0,
|
||||
vkraster.cullMode = VK_CULL_MODE_NONE;
|
||||
vkraster.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
||||
vkraster.depthBiasEnable = VK_FALSE;
|
||||
vkraster.colorTargetCount = 0;
|
||||
mPipelineCache.bindRasterState(vkraster);
|
||||
|
||||
VkBuffer buffers[1] = {};
|
||||
VkDeviceSize offsets[1] = {};
|
||||
buffers[0] = mTriangleBuffer->getGpuBuffer();
|
||||
VkVertexInputAttributeDescription attribDesc = {
|
||||
.location = 0,
|
||||
.binding = 0,
|
||||
.format = VK_FORMAT_R32G32_SFLOAT,
|
||||
};
|
||||
VkVertexInputBindingDescription bufferDesc = {
|
||||
.binding = 0,
|
||||
.stride = sizeof(float) * 2,
|
||||
};
|
||||
mPipelineCache.bindVertexArray(&attribDesc, &bufferDesc, 1);
|
||||
|
||||
// Select nearest filtering and clamp_to_edge.
|
||||
VkSampler vksampler = mSamplerCache.getSampler({});
|
||||
|
||||
VkDescriptorImageInfo samplers[VulkanPipelineCache::SAMPLER_BINDING_COUNT];
|
||||
VulkanTexture* textures[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {nullptr};
|
||||
for (auto& sampler : samplers) {
|
||||
sampler = {
|
||||
.sampler = vksampler,
|
||||
.imageView = mEmptyTexture->getPrimaryImageView(),
|
||||
.imageLayout = ImgUtil::getVkLayout(VulkanLayout::READ_WRITE),
|
||||
};
|
||||
}
|
||||
|
||||
samplers[0] = {
|
||||
.sampler = vksampler,
|
||||
.imageView = src.getImageView(VK_IMAGE_ASPECT_DEPTH_BIT),
|
||||
.imageLayout = ImgUtil::getVkLayout(samplerLayout),
|
||||
};
|
||||
textures[0] = src.texture;
|
||||
|
||||
mPipelineCache.bindSamplers(samplers, textures,
|
||||
VulkanPipelineCache::getUsageFlags(0, ShaderStageFlags::FRAGMENT));
|
||||
|
||||
auto previousUbo = mPipelineCache.getUniformBufferBinding(0);
|
||||
mPipelineCache.bindUniformBuffer(0, mParamsBuffer->getGpuBuffer());
|
||||
|
||||
if (!mPipelineCache.bindDescriptors(cmdbuffer)) {
|
||||
assert_invariant(false);
|
||||
}
|
||||
|
||||
const VkRect2D scissor = {
|
||||
.offset = renderPassInfo.renderArea.offset,
|
||||
.extent = renderPassInfo.renderArea.extent,
|
||||
};
|
||||
|
||||
mPipelineCache.bindScissor(cmdbuffer, scissor);
|
||||
|
||||
if (!mPipelineCache.bindPipeline(commands)) {
|
||||
assert_invariant(false);
|
||||
}
|
||||
|
||||
vkCmdBindVertexBuffers(cmdbuffer, 0, 1, buffers, offsets);
|
||||
vkCmdDraw(cmdbuffer, 4, 1, 0, 0);
|
||||
|
||||
// END RENDER PASS
|
||||
// ---------------
|
||||
|
||||
vkCmdEndRenderPass(cmdbuffer);
|
||||
|
||||
VkMemoryBarrier barrier {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
};
|
||||
VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||
vkCmdPipelineBarrier(cmdbuffer, srcStageMask,
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT | // <== For Mali
|
||||
VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
0, 1, &barrier, 0, nullptr, 0, nullptr);
|
||||
|
||||
mPipelineCache.bindUniformBuffer(0, previousUbo.buffer, previousUbo.offset, previousUbo.size);
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -32,46 +32,24 @@ struct VulkanProgram;
|
||||
|
||||
class VulkanBlitter {
|
||||
public:
|
||||
VulkanBlitter(VulkanStagePool& stagePool, VulkanPipelineCache& pipelineCache,
|
||||
VulkanFboCache& fboCache, VulkanSamplerCache& samplerCache) noexcept;
|
||||
VulkanBlitter() noexcept;
|
||||
|
||||
void initialize(VkPhysicalDevice physicalDevice, VkDevice device, VmaAllocator allocator,
|
||||
VulkanCommands* commands, VulkanTexture* emptyTexture) noexcept;
|
||||
void initialize(VkPhysicalDevice physicalDevice, VkDevice device,
|
||||
VmaAllocator allocator, VulkanCommands* commands) noexcept;
|
||||
|
||||
struct BlitArgs {
|
||||
const VulkanRenderTarget* dstTarget;
|
||||
const VkOffset3D* dstRectPair;
|
||||
const VulkanRenderTarget* srcTarget;
|
||||
const VkOffset3D* srcRectPair;
|
||||
VkFilter filter = VK_FILTER_NEAREST;
|
||||
int targetIndex = 0;
|
||||
};
|
||||
void blit(VkFilter filter,
|
||||
VulkanAttachment dst, const VkOffset3D* dstRectPair,
|
||||
VulkanAttachment src, const VkOffset3D* srcRectPair);
|
||||
|
||||
void blitColor(BlitArgs args);
|
||||
void blitDepth(BlitArgs args);
|
||||
void resolve(VulkanAttachment dst, VulkanAttachment src);
|
||||
|
||||
void terminate() noexcept;
|
||||
|
||||
private:
|
||||
void lazyInit() noexcept;
|
||||
|
||||
void blitSlowDepth(VkFilter filter, const VkExtent2D srcExtent, VulkanAttachment src,
|
||||
VulkanAttachment dst, const VkOffset3D srcRect[2], const VkOffset3D dstRect[2]);
|
||||
|
||||
VulkanBuffer* mTriangleBuffer = nullptr;
|
||||
VulkanBuffer* mParamsBuffer = nullptr;
|
||||
VulkanProgram* mDepthResolveProgram = nullptr;
|
||||
|
||||
UTILS_UNUSED VkPhysicalDevice mPhysicalDevice;
|
||||
VkDevice mDevice;
|
||||
VmaAllocator mAllocator;
|
||||
VulkanCommands* mCommands;
|
||||
VulkanTexture* mEmptyTexture;
|
||||
|
||||
VulkanStagePool& mStagePool;
|
||||
VulkanPipelineCache& mPipelineCache;
|
||||
VulkanFboCache& mFramebufferCache;
|
||||
VulkanSamplerCache& mSamplerCache;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -54,7 +54,8 @@ VmaAllocator createAllocator(VkInstance instance, VkPhysicalDevice physicalDevic
|
||||
VmaAllocator allocator;
|
||||
VmaVulkanFunctions const funcs {
|
||||
#if VMA_DYNAMIC_VULKAN_FUNCTIONS
|
||||
.vkGetInstanceProcAddr = vkGetInstanceProcAddr, .vkGetDeviceProcAddr = vkGetDeviceProcAddr,
|
||||
.vkGetInstanceProcAddr = vkGetInstanceProcAddr,
|
||||
.vkGetDeviceProcAddr = vkGetDeviceProcAddr,
|
||||
#else
|
||||
.vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties,
|
||||
.vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties,
|
||||
@@ -155,7 +156,6 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
|
||||
mResourceManager(&mResourceAllocator),
|
||||
mThreadSafeResourceManager(&mResourceAllocator),
|
||||
mPipelineCache(&mResourceAllocator),
|
||||
mBlitter(mStagePool, mPipelineCache, mFramebufferCache, mSamplerCache),
|
||||
mReadPixels(mPlatform->getDevice()),
|
||||
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported) {
|
||||
|
||||
@@ -205,7 +205,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
|
||||
|
||||
mPipelineCache.setDummyTexture(mEmptyTexture->getPrimaryImageView());
|
||||
mBlitter.initialize(mPlatform->getPhysicalDevice(), mPlatform->getDevice(), mAllocator,
|
||||
mCommands.get(), mEmptyTexture.get());
|
||||
mCommands.get());
|
||||
}
|
||||
|
||||
VulkanDriver::~VulkanDriver() noexcept = default;
|
||||
@@ -426,7 +426,8 @@ void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
|
||||
}
|
||||
|
||||
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
|
||||
auto vkprogram = mResourceAllocator.construct<VulkanProgram>(ph, mPlatform->getDevice(), program);
|
||||
auto vkprogram
|
||||
= mResourceAllocator.construct<VulkanProgram>(ph, mPlatform->getDevice(), program);
|
||||
mResourceManager.acquire(vkprogram);
|
||||
}
|
||||
|
||||
@@ -755,6 +756,7 @@ bool VulkanDriver::isFrameTimeSupported() {
|
||||
}
|
||||
|
||||
bool VulkanDriver::isAutoDepthResolveSupported() {
|
||||
// TODO: this could be supported with vk 1.2 or VK_KHR_depth_stencil_resolve
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -770,6 +772,11 @@ bool VulkanDriver::isParallelShaderCompileSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VulkanDriver::isDepthStencilResolveSupported() {
|
||||
// TODO: apparently it could be supported in core 1.2 and/or with VK_KHR_depth_stencil_resolve
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
switch (workaround) {
|
||||
case Workaround::SPLIT_EASU: {
|
||||
@@ -953,10 +960,43 @@ void VulkanDriver::setExternalImagePlane(Handle<HwTexture> th, void* image, uint
|
||||
void VulkanDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh) {
|
||||
}
|
||||
|
||||
void VulkanDriver::generateMipmaps(Handle<HwTexture> th) { }
|
||||
void VulkanDriver::generateMipmaps(Handle<HwTexture> th) {
|
||||
auto* const t = mResourceAllocator.handle_cast<VulkanTexture*>(th);
|
||||
assert_invariant(t);
|
||||
|
||||
bool VulkanDriver::canGenerateMipmaps() {
|
||||
return false;
|
||||
int32_t layerCount = int32_t(t->depth);
|
||||
if (t->target == SamplerType::SAMPLER_CUBEMAP_ARRAY ||
|
||||
t->target == SamplerType::SAMPLER_CUBEMAP) {
|
||||
layerCount *= 6;
|
||||
}
|
||||
|
||||
// FIXME: the loop below can perform many layout transitions and back. We should be
|
||||
// able to optimize that.
|
||||
|
||||
uint8_t level = 0;
|
||||
int32_t srcw = int32_t(t->width);
|
||||
int32_t srch = int32_t(t->height);
|
||||
do {
|
||||
int32_t const dstw = std::max(srcw >> 1, 1);
|
||||
int32_t const dsth = std::max(srch >> 1, 1);
|
||||
const VkOffset3D srcOffsets[2] = {{ 0, 0, 0 }, { srcw, srch, 1 }};
|
||||
const VkOffset3D dstOffsets[2] = {{ 0, 0, 0 }, { dstw, dsth, 1 }};
|
||||
|
||||
// TODO: there should be a way to do this using layerCount in vkBlitImage
|
||||
// TODO: vkBlitImage should be able to handle 3D textures too
|
||||
for (int32_t layer = 0; layer < layerCount; layer++) {
|
||||
mBlitter.blit(VK_FILTER_LINEAR,
|
||||
{ .texture = t, .level = uint8_t(level + 1), .layer = (uint16_t)layer },
|
||||
dstOffsets,
|
||||
{ .texture = t, .level = uint8_t(level ), .layer = (uint16_t)layer },
|
||||
srcOffsets);
|
||||
}
|
||||
|
||||
level++;
|
||||
srcw = dstw;
|
||||
srch = dsth;
|
||||
} while ((srcw > 1 || srch > 1) && level < t->levels);
|
||||
t->setPrimaryRange(0, t->levels - 1);
|
||||
}
|
||||
|
||||
void VulkanDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
|
||||
@@ -1437,50 +1477,138 @@ void VulkanDriver::readBufferSubData(backend::BufferObjectHandle boh,
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void VulkanDriver::blit(TargetBufferFlags buffers, Handle<HwRenderTarget> dst, Viewport dstRect,
|
||||
Handle<HwRenderTarget> src, Viewport srcRect, SamplerMagFilter filter) {
|
||||
void VulkanDriver::resolve(
|
||||
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer,
|
||||
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("resolve");
|
||||
|
||||
ASSERT_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE,
|
||||
"resolve() cannot be invoked inside a render pass.");
|
||||
|
||||
auto* const srcTexture = mResourceAllocator.handle_cast<VulkanTexture*>(src);
|
||||
auto* const dstTexture = mResourceAllocator.handle_cast<VulkanTexture*>(dst);
|
||||
assert_invariant(srcTexture);
|
||||
assert_invariant(dstTexture);
|
||||
|
||||
ASSERT_PRECONDITION(
|
||||
dstTexture->width == srcTexture->width && dstTexture->height == srcTexture->height,
|
||||
"invalid resolve: src and dst sizes don't match");
|
||||
|
||||
ASSERT_PRECONDITION(srcTexture->samples > 1 && dstTexture->samples == 1,
|
||||
"invalid resolve: src.samples=%u, dst.samples=%u",
|
||||
+srcTexture->samples, +dstTexture->samples);
|
||||
|
||||
ASSERT_PRECONDITION(srcTexture->format == dstTexture->format,
|
||||
"src and dst texture format don't match");
|
||||
|
||||
ASSERT_PRECONDITION(!isDepthFormat(srcTexture->format),
|
||||
"can't resolve depth formats");
|
||||
|
||||
ASSERT_PRECONDITION(!isStencilFormat(srcTexture->format),
|
||||
"can't resolve stencil formats");
|
||||
|
||||
ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
|
||||
"texture doesn't have BLIT_DST");
|
||||
|
||||
ASSERT_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC),
|
||||
"texture doesn't have BLIT_SRC");
|
||||
|
||||
mBlitter.resolve(
|
||||
{ .texture = dstTexture, .level = dstLevel, .layer = dstLayer },
|
||||
{ .texture = srcTexture, .level = srcLevel, .layer = srcLayer });
|
||||
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::blit(
|
||||
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer, math::uint2 dstOrigin,
|
||||
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer, math::uint2 srcOrigin,
|
||||
math::uint2 size) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("blit");
|
||||
|
||||
assert_invariant(mCurrentRenderPass.renderPass == VK_NULL_HANDLE);
|
||||
ASSERT_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE,
|
||||
"blit() cannot be invoked inside a render pass.");
|
||||
|
||||
// blit operation only support COLOR0 color buffer
|
||||
assert_invariant(
|
||||
!(buffers & (TargetBufferFlags::COLOR_ALL & ~TargetBufferFlags::COLOR0)));
|
||||
auto* const srcTexture = mResourceAllocator.handle_cast<VulkanTexture*>(src);
|
||||
auto* const dstTexture = mResourceAllocator.handle_cast<VulkanTexture*>(dst);
|
||||
|
||||
if (UTILS_UNLIKELY(mCurrentRenderPass.renderPass)) {
|
||||
utils::slog.e << "Blits cannot be invoked inside a render pass." << utils::io::endl;
|
||||
return;
|
||||
}
|
||||
ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
|
||||
"texture doesn't have BLIT_DST");
|
||||
|
||||
ASSERT_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC),
|
||||
"texture doesn't have BLIT_SRC");
|
||||
|
||||
ASSERT_PRECONDITION(srcTexture->format == dstTexture->format,
|
||||
"src and dst texture format don't match");
|
||||
|
||||
// The Y inversion below makes it so that Vk matches GL and Metal.
|
||||
|
||||
auto const srcLeft = int32_t(srcOrigin.x);
|
||||
auto const dstLeft = int32_t(dstOrigin.x);
|
||||
auto const srcTop = int32_t(srcTexture->height - (srcOrigin.y + size.y));
|
||||
auto const dstTop = int32_t(dstTexture->height - (dstOrigin.y + size.y));
|
||||
auto const srcRight = int32_t(srcOrigin.x + size.x);
|
||||
auto const dstRight = int32_t(dstOrigin.x + size.x);
|
||||
auto const srcBottom = int32_t(srcTop + size.y);
|
||||
auto const dstBottom = int32_t(dstTop + size.y);
|
||||
VkOffset3D const srcOffsets[2] = { { srcLeft, srcTop, 0 }, { srcRight, srcBottom, 1 }};
|
||||
VkOffset3D const dstOffsets[2] = { { dstLeft, dstTop, 0 }, { dstRight, dstBottom, 1 }};
|
||||
|
||||
// no scaling guaranteed
|
||||
mBlitter.blit(VK_FILTER_NEAREST,
|
||||
{ .texture = dstTexture, .level = dstLevel, .layer = dstLayer }, dstOffsets,
|
||||
{ .texture = srcTexture, .level = srcLevel, .layer = srcLayer }, srcOffsets);
|
||||
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
Handle<HwRenderTarget> dst, Viewport dstRect,
|
||||
Handle<HwRenderTarget> src, Viewport srcRect,
|
||||
SamplerMagFilter filter) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("blitDEPRECATED");
|
||||
|
||||
// Note: blitDEPRECATED is only used for Renderer::copyFrame()
|
||||
|
||||
ASSERT_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE,
|
||||
"blitDEPRECATED() cannot be invoked inside a render pass.");
|
||||
|
||||
ASSERT_PRECONDITION(buffers == TargetBufferFlags::COLOR0,
|
||||
"blitDEPRECATED only supports COLOR0");
|
||||
|
||||
ASSERT_PRECONDITION(srcRect.left >= 0 && srcRect.bottom >= 0 &&
|
||||
dstRect.left >= 0 && dstRect.bottom >= 0,
|
||||
"Source and destination rects must be positive.");
|
||||
|
||||
VulkanRenderTarget* dstTarget = mResourceAllocator.handle_cast<VulkanRenderTarget*>(dst);
|
||||
VulkanRenderTarget* srcTarget = mResourceAllocator.handle_cast<VulkanRenderTarget*>(src);
|
||||
|
||||
VkFilter vkfilter = filter == SamplerMagFilter::NEAREST ? VK_FILTER_NEAREST : VK_FILTER_LINEAR;
|
||||
VkFilter const vkfilter = (filter == SamplerMagFilter::NEAREST) ?
|
||||
VK_FILTER_NEAREST : VK_FILTER_LINEAR;
|
||||
|
||||
// The Y inversion below makes it so that Vk matches GL and Metal.
|
||||
|
||||
const VkExtent2D srcExtent = srcTarget->getExtent();
|
||||
const int32_t srcLeft = srcRect.left;
|
||||
const int32_t srcTop = srcExtent.height - srcRect.bottom - srcRect.height;
|
||||
const int32_t srcRight = srcRect.left + srcRect.width;
|
||||
const int32_t srcBottom = srcTop + srcRect.height;
|
||||
const VkOffset3D srcOffsets[2] = { { srcLeft, srcTop, 0 }, { srcRight, srcBottom, 1 }};
|
||||
VkExtent2D const srcExtent = srcTarget->getExtent();
|
||||
VkExtent2D const dstExtent = dstTarget->getExtent();
|
||||
|
||||
const VkExtent2D dstExtent = dstTarget->getExtent();
|
||||
const int32_t dstLeft = dstRect.left;
|
||||
const int32_t dstTop = dstExtent.height - dstRect.bottom - dstRect.height;
|
||||
const int32_t dstRight = dstRect.left + dstRect.width;
|
||||
const int32_t dstBottom = dstTop + dstRect.height;
|
||||
const VkOffset3D dstOffsets[2] = { { dstLeft, dstTop, 0 }, { dstRight, dstBottom, 1 }};
|
||||
auto const dstLeft = int32_t(dstRect.left);
|
||||
auto const srcLeft = int32_t(srcRect.left);
|
||||
auto const dstTop = int32_t(dstExtent.height - (dstRect.bottom + dstRect.height));
|
||||
auto const srcTop = int32_t(srcExtent.height - (srcRect.bottom + srcRect.height));
|
||||
auto const dstRight = int32_t(dstRect.left + dstRect.width);
|
||||
auto const srcRight = int32_t(srcRect.left + srcRect.width);
|
||||
auto const dstBottom = int32_t(dstTop + dstRect.height);
|
||||
auto const srcBottom = int32_t(srcTop + srcRect.height);
|
||||
VkOffset3D const srcOffsets[2] = { { srcLeft, srcTop, 0 }, { srcRight, srcBottom, 1 }};
|
||||
VkOffset3D const dstOffsets[2] = { { dstLeft, dstTop, 0 }, { dstRight, dstBottom, 1 }};
|
||||
|
||||
if (any(buffers & TargetBufferFlags::DEPTH) && srcTarget->hasDepth() && dstTarget->hasDepth()) {
|
||||
mBlitter.blitDepth({dstTarget, dstOffsets, srcTarget, srcOffsets});
|
||||
}
|
||||
mBlitter.blit(vkfilter,
|
||||
dstTarget->getColor(0), dstOffsets,
|
||||
srcTarget->getColor(0), srcOffsets);
|
||||
|
||||
if (any(buffers & TargetBufferFlags::COLOR0)) {
|
||||
mBlitter.blitColor({ dstTarget, dstOffsets, srcTarget, srcOffsets, vkfilter, int(0) });
|
||||
}
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
|
||||
@@ -18,10 +18,11 @@
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "spirv/VulkanSpirvUtils.h"
|
||||
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Panic.h> // ASSERT_POSTCONDITION
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
@@ -46,61 +47,39 @@ static void clampToFramebuffer(VkRect2D* rect, uint32_t fbWidth, uint32_t fbHeig
|
||||
rect->extent.height = std::max(top - y, 0);
|
||||
}
|
||||
|
||||
VulkanProgram::VulkanProgram(VkDevice device, const Program& builder) noexcept
|
||||
VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
|
||||
: HwProgram(builder.getName()),
|
||||
VulkanResource(VulkanResourceType::PROGRAM),
|
||||
mInfo(new PipelineInfo(builder.getSpecializationConstants().size())),
|
||||
mInfo(new PipelineInfo()),
|
||||
mDevice(device) {
|
||||
auto& blobs = builder.getShadersSource();
|
||||
Program::ShaderSource const& blobs = builder.getShadersSource();
|
||||
auto& modules = mInfo->shaders;
|
||||
|
||||
auto const& specializationConstants = builder.getSpecializationConstants();
|
||||
|
||||
std::vector<uint32_t> shader;
|
||||
for (size_t i = 0; i < MAX_SHADER_MODULES; i++) {
|
||||
const auto& blob = blobs[i];
|
||||
uint32_t* data = (uint32_t*)blob.data();
|
||||
Program::ShaderBlob const& blob = blobs[i];
|
||||
|
||||
uint32_t* data = (uint32_t*) blob.data();
|
||||
size_t dataSize = blob.size();
|
||||
|
||||
if (!specializationConstants.empty()) {
|
||||
workaroundSpecConstant(blob, specializationConstants, shader);
|
||||
data = (uint32_t*) shader.data();
|
||||
dataSize = shader.size() * 4;
|
||||
}
|
||||
|
||||
VkShaderModule& module = modules[i];
|
||||
VkShaderModuleCreateInfo moduleInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
.codeSize = blob.size(),
|
||||
.codeSize = dataSize,
|
||||
.pCode = data,
|
||||
};
|
||||
VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, &module);
|
||||
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create shader module.");
|
||||
}
|
||||
|
||||
// Note that bools are 4-bytes in Vulkan
|
||||
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBool32.html
|
||||
constexpr uint32_t const CONSTANT_SIZE = 4;
|
||||
|
||||
// populate the specialization constants requirements right now
|
||||
auto const& specializationConstants = builder.getSpecializationConstants();
|
||||
uint32_t const specConstCount = static_cast<uint32_t>(specializationConstants.size());
|
||||
char* specData = mInfo->specConstData.get();
|
||||
if (specConstCount > 0) {
|
||||
mInfo->specializationInfo = {
|
||||
.mapEntryCount = specConstCount,
|
||||
.pMapEntries = mInfo->specConsts.data(),
|
||||
.dataSize = specConstCount * CONSTANT_SIZE,
|
||||
.pData = specData,
|
||||
};
|
||||
}
|
||||
for (uint32_t i = 0; i < specConstCount; ++i) {
|
||||
uint32_t const offset = i * CONSTANT_SIZE;
|
||||
mInfo->specConsts[i] = {
|
||||
.constantID = specializationConstants[i].id,
|
||||
.offset = offset,
|
||||
.size = CONSTANT_SIZE,
|
||||
};
|
||||
using SpecConstant = Program::SpecializationConstant::Type;
|
||||
char const* addr = (char*)specData + offset;
|
||||
SpecConstant const& arg = specializationConstants[i].value;
|
||||
if (std::holds_alternative<bool>(arg)) {
|
||||
*((VkBool32*)addr) = std::get<bool>(arg) ? VK_TRUE : VK_FALSE;
|
||||
} else if (std::holds_alternative<float>(arg)) {
|
||||
*((float*)addr) = std::get<float>(arg);
|
||||
} else {
|
||||
*((int32_t*)addr) = std::get<int32_t>(arg);
|
||||
}
|
||||
}
|
||||
|
||||
auto& groupInfo = builder.getSamplerGroupInfo();
|
||||
auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex;
|
||||
auto& usage = mInfo->usage;
|
||||
@@ -123,7 +102,7 @@ VulkanProgram::VulkanProgram(VkDevice device, const Program& builder) noexcept
|
||||
VulkanProgram::VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs,
|
||||
CustomSamplerInfoList const& samplerInfo) noexcept
|
||||
: VulkanResource(VulkanResourceType::PROGRAM),
|
||||
mInfo(new PipelineInfo(0)),
|
||||
mInfo(new PipelineInfo()),
|
||||
mDevice(device) {
|
||||
mInfo->shaders[0] = vs;
|
||||
mInfo->shaders[1] = fs;
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include "VulkanResources.h"
|
||||
#include "VulkanSwapChain.h"
|
||||
#include "VulkanTexture.h"
|
||||
#include "VulkanUtility.h"
|
||||
|
||||
#include "private/backend/SamplerGroup.h"
|
||||
|
||||
@@ -38,7 +37,7 @@ class VulkanTimestamps;
|
||||
|
||||
struct VulkanProgram : public HwProgram, VulkanResource {
|
||||
|
||||
VulkanProgram(VkDevice device, const Program& builder) noexcept;
|
||||
VulkanProgram(VkDevice device, Program const& builder) noexcept;
|
||||
|
||||
struct CustomSamplerInfo {
|
||||
uint8_t groupIndex;
|
||||
@@ -65,21 +64,14 @@ struct VulkanProgram : public HwProgram, VulkanResource {
|
||||
return mInfo->bindingToSamplerIndex;
|
||||
}
|
||||
|
||||
inline VkSpecializationInfo const& getSpecConstInfo() const {
|
||||
return mInfo->specializationInfo;
|
||||
}
|
||||
|
||||
private:
|
||||
// TODO: handle compute shaders.
|
||||
// The expected order of shaders - from frontend to backend - is vertex, fragment, compute.
|
||||
static constexpr uint8_t MAX_SHADER_MODULES = 2;
|
||||
static constexpr uint8_t const MAX_SHADER_MODULES = 2;
|
||||
|
||||
struct PipelineInfo {
|
||||
PipelineInfo(size_t specConstsCount) :
|
||||
bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff),
|
||||
specConsts(specConstsCount, VkSpecializationMapEntry{}),
|
||||
specConstData(new char[specConstsCount * 4])
|
||||
{}
|
||||
PipelineInfo()
|
||||
: bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff) {}
|
||||
|
||||
// This bitset maps to each of the sampler in the sampler groups associated with this
|
||||
// program, and whether each sampler is used in which shader (i.e. vert, frag, compute).
|
||||
@@ -87,10 +79,7 @@ private:
|
||||
|
||||
// We store the samplerGroupIndex as the top 8-bit and the index within each group as the lower 8-bit.
|
||||
utils::FixedCapacityVector<uint16_t> bindingToSamplerIndex;
|
||||
VkShaderModule shaders[MAX_SHADER_MODULES] = {VK_NULL_HANDLE};
|
||||
VkSpecializationInfo specializationInfo = {};
|
||||
utils::FixedCapacityVector<VkSpecializationMapEntry> specConsts;
|
||||
std::unique_ptr<char[]> specConstData;
|
||||
VkShaderModule shaders[MAX_SHADER_MODULES] = { VK_NULL_HANDLE };
|
||||
};
|
||||
|
||||
PipelineInfo* mInfo;
|
||||
|
||||
@@ -360,9 +360,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
|
||||
// If we reach this point, we need to create and stash a brand new pipeline object.
|
||||
shaderStages[0].module = mPipelineRequirements.shaders[0];
|
||||
shaderStages[0].pSpecializationInfo = mSpecializationRequirements;
|
||||
shaderStages[1].module = mPipelineRequirements.shaders[1];
|
||||
shaderStages[1].pSpecializationInfo = mSpecializationRequirements;
|
||||
|
||||
// Expand our size-optimized structs into the proper Vk structs.
|
||||
uint32_t numVertexAttribs = 0;
|
||||
@@ -568,7 +566,6 @@ VulkanPipelineCache::PipelineLayoutCacheEntry* VulkanPipelineCache::getOrCreateP
|
||||
void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
|
||||
mPipelineRequirements.shaders[0] = program->getVertexShader();
|
||||
mPipelineRequirements.shaders[1] = program->getFragmentShader();
|
||||
mSpecializationRequirements = &program->getSpecConstInfo();
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexcept {
|
||||
|
||||
@@ -416,7 +416,6 @@ private:
|
||||
RasterState mCurrentRasterState;
|
||||
PipelineKey mPipelineRequirements = {};
|
||||
DescriptorKey mDescriptorRequirements = {};
|
||||
VkSpecializationInfo const* mSpecializationRequirements = nullptr;
|
||||
|
||||
// Current bindings for the pipeline and descriptor sets.
|
||||
PipelineKey mBoundPipeline = {};
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include "vulkan/VulkanConstants.h"
|
||||
#include "vulkan/VulkanDriverFactory.h"
|
||||
@@ -23,6 +24,11 @@
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#define LINUX_OR_FREEBSD 1
|
||||
#endif
|
||||
@@ -73,7 +79,7 @@
|
||||
#elif defined(WIN32)
|
||||
// No platform specific includes
|
||||
#else
|
||||
#error Not a supported Vulkan platform
|
||||
// Not a supported Vulkan platform
|
||||
#endif
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* 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 "VulkanSpirvUtils.h"
|
||||
|
||||
#include <utils/compiler.h> // UTILS_UNUSED_IN_RELEASE, UTILS_FALLTHROUGH
|
||||
#include <utils/debug.h> // assert_invariant
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <spirv/unified1/spirv.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
// This function transforms an OpSpecConstant instruction into just a OpConstant instruction.
|
||||
// Additionally, it will adjust the value of the constant as given by the program.
|
||||
void getTransformedConstantInst(SpecConstantValue* value, uint32_t* inst) {
|
||||
|
||||
// The first word is the size of the instruction and the instruction type.
|
||||
// The second word is the type of the instruction.
|
||||
// The third word is the "result id" (i.e. the variable to store into).
|
||||
// The fourth word (only for floats and ints) is the literal representing the value.
|
||||
// We only want to change the first and fourth words.
|
||||
constexpr size_t const OP = 0;
|
||||
constexpr size_t const VALUE = 3;
|
||||
|
||||
if (!value) {
|
||||
// If a specialization wasn't provided, just switch the opcode.
|
||||
uint32_t const op = inst[OP] & 0x0000FFFF;
|
||||
if (op == spv::Op::OpSpecConstantFalse) {
|
||||
inst[OP] = (3 << 16) | spv::Op::OpConstantFalse;
|
||||
} else if (op == spv::Op::OpSpecConstantTrue) {
|
||||
inst[OP] = (3 << 16) | spv::Op::OpConstantTrue;
|
||||
} else if (op == spv::Op::OpSpecConstant) {
|
||||
inst[OP] = (4 << 16) | spv::Op::OpConstant;
|
||||
}
|
||||
} else if (std::holds_alternative<bool>(*value)) {
|
||||
inst[OP] = (3 << 16)
|
||||
| (std::get<bool>(*value) ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse);
|
||||
} else if (std::holds_alternative<float>(*value)) {
|
||||
float const fval = std::get<float>(*value);
|
||||
inst[OP] = (4 << 16) | spv::Op::OpConstant;
|
||||
inst[VALUE] = *reinterpret_cast<uint32_t const*>(&fval);
|
||||
} else {
|
||||
int const ival = std::get<int>(*value);
|
||||
inst[OP] = (4 << 16) | spv::Op::OpConstant;
|
||||
inst[VALUE] = *reinterpret_cast<uint32_t const*>(&ival);
|
||||
}
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void workaroundSpecConstant(Program::ShaderBlob const& blob,
|
||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specConstants,
|
||||
std::vector<uint32_t>& output) {
|
||||
using WordMap = std::unordered_map<uint32_t, uint32_t>;
|
||||
using SpecValueMap = std::unordered_map<uint32_t, SpecConstantValue>;
|
||||
constexpr size_t const HEADER_SIZE = 5;
|
||||
|
||||
WordMap varToIdMap;
|
||||
SpecValueMap idToValue;
|
||||
|
||||
for (auto spec: specConstants) {
|
||||
idToValue[spec.id] = spec.value;
|
||||
}
|
||||
|
||||
// The follow loop will iterate through all the instructions and look for instructions of the
|
||||
// form:
|
||||
//
|
||||
// OpDecorate %1 SpecId 0
|
||||
// %1 = OpSpecConstantFalse %bool
|
||||
//
|
||||
// We want to track the mapping between the variable id %1 and the specId 0. And when seeing a
|
||||
// %1 as the result, we want to change that instruction to just an OpConsant where the constant
|
||||
// value is adjusted by the spec values provided in the program.
|
||||
// Additionally, we will turn the SpecId decorations to no-ops.
|
||||
size_t const dataSize = blob.size() / 4;
|
||||
|
||||
output.resize(dataSize);
|
||||
uint32_t const* data = (uint32_t*) blob.data();
|
||||
uint32_t* outputData = output.data();
|
||||
|
||||
std::memcpy(&outputData[0], &data[0], HEADER_SIZE * 4);
|
||||
size_t outputCursor = HEADER_SIZE;
|
||||
|
||||
for (uint32_t cursor = HEADER_SIZE, cursorEnd = dataSize; cursor < cursorEnd;) {
|
||||
uint32_t const firstWord = data[cursor];
|
||||
uint32_t const wordCount = firstWord >> 16;
|
||||
uint32_t const op = firstWord & 0x0000FFFF;
|
||||
|
||||
switch(op) {
|
||||
case spv::Op::OpSpecConstant:
|
||||
case spv::Op::OpSpecConstantTrue:
|
||||
case spv::Op::OpSpecConstantFalse: {
|
||||
uint32_t const targetVar = data[cursor + 2];
|
||||
|
||||
UTILS_UNUSED_IN_RELEASE WordMap::const_iterator idItr = varToIdMap.find(targetVar);
|
||||
assert_invariant(idItr != varToIdMap.end() &&
|
||||
"Cannot find variable previously decorated with SpecId");
|
||||
|
||||
auto valItr = idToValue.find(idItr->second);
|
||||
|
||||
SpecConstantValue* val = (valItr != idToValue.end()) ? &valItr->second : nullptr;
|
||||
std::memcpy(&outputData[outputCursor], &data[cursor], wordCount * 4);
|
||||
getTransformedConstantInst(val, &outputData[outputCursor]);
|
||||
outputCursor += wordCount;
|
||||
break;
|
||||
}
|
||||
case spv::Op::OpDecorate: {
|
||||
if (data[cursor + 2] == spv::Decoration::DecorationSpecId) {
|
||||
uint32_t const targetVar = data[cursor + 1];
|
||||
uint32_t const specId = data[cursor + 3];
|
||||
varToIdMap[targetVar] = specId;
|
||||
|
||||
// Note these decorations do not need to be written to the output.
|
||||
break;
|
||||
}
|
||||
// else fallthrough and copy like all other instructions
|
||||
UTILS_FALLTHROUGH;
|
||||
}
|
||||
default:
|
||||
std::memcpy(&outputData[outputCursor], &data[cursor], wordCount * 4);
|
||||
outputCursor += wordCount;
|
||||
break;
|
||||
}
|
||||
cursor += wordCount;
|
||||
}
|
||||
output.resize(outputCursor);
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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_VULKANSPIRVUTILS_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANSPIRVUTILS_H
|
||||
|
||||
#include "backend/Program.h"
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using SpecConstantValue = Program::SpecializationConstant::Type;
|
||||
|
||||
// For certain drivers, using spec constant can lead to compile errors [1] or undesirable behaviors
|
||||
// [2]. In those instances, we simply change the spirv and set them to constants.
|
||||
//
|
||||
// (Implemenation note: we cannot write to the blob because spirv-validator does not properly handle
|
||||
// the Nop (no-op) instruction, and swiftshader validates the shader before compilation. So we need
|
||||
// to skip those instructions instead).
|
||||
//
|
||||
// [1]: QC driver cannot use spec constant to size fields
|
||||
// (https://github.com/google/filament/issues/6444).
|
||||
// [2]: An internal driver does not DCE a block guarded by a spec-const boolean set to false
|
||||
// (b/310603393).
|
||||
void workaroundSpecConstant(Program::ShaderBlob const& blob,
|
||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specConstants,
|
||||
std::vector<uint32_t>& output);
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKANSPIRVUTILS_H
|
||||
@@ -233,7 +233,7 @@ TEST_F(BackendTest, ColorMagnify) {
|
||||
|
||||
// Do a "magnify" blit from level 1 of the source RT to the level 0 of the destination RT.
|
||||
const int srcLevel = 1;
|
||||
api.blit(TargetBufferFlags::COLOR0, dstRenderTargets[0],
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTargets[0],
|
||||
{0, 0, kDstTexWidth, kDstTexHeight}, srcRenderTargets[srcLevel],
|
||||
{0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel}, SamplerMagFilter::LINEAR);
|
||||
|
||||
@@ -308,7 +308,8 @@ TEST_F(BackendTest, ColorMinify) {
|
||||
|
||||
// Do a "minify" blit from level 1 of the source RT to the level 0 of the destination RT.
|
||||
const int srcLevel = 1;
|
||||
api.blit(TargetBufferFlags::COLOR0,
|
||||
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0,
|
||||
dstRenderTargets[0], {0, 0, kDstTexWidth, kDstTexHeight},
|
||||
srcRenderTargets[srcLevel], {0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel},
|
||||
SamplerMagFilter::LINEAR);
|
||||
@@ -334,147 +335,6 @@ TEST_F(BackendTest, ColorMinify) {
|
||||
executeCommands();
|
||||
}
|
||||
|
||||
TEST_F(BackendTest, DepthMinify) {
|
||||
auto& api = getDriverApi();
|
||||
|
||||
constexpr int kSrcTexWidth = 1024;
|
||||
constexpr int kSrcTexHeight = 1024;
|
||||
constexpr int kDstTexWidth = 256;
|
||||
constexpr int kDstTexHeight = 256;
|
||||
constexpr auto kColorTexFormat = TextureFormat::RGBA8;
|
||||
constexpr auto kDepthTexFormat = TextureFormat::DEPTH24;
|
||||
constexpr int kNumLevels = 3;
|
||||
|
||||
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
|
||||
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a program.
|
||||
ProgramHandle program;
|
||||
{
|
||||
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
prog.uniformBlockBindings({{"params", 1}});
|
||||
program = api.createProgram(std::move(prog));
|
||||
}
|
||||
|
||||
// Create a VertexBuffer, IndexBuffer, and RenderPrimitive.
|
||||
TrianglePrimitive* triangle = new TrianglePrimitive(api);
|
||||
|
||||
// Create textures for the first rendering pass.
|
||||
Handle<HwTexture> srcColorTexture = api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kColorTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
|
||||
|
||||
Handle<HwTexture> srcDepthTexture = api.createTexture(
|
||||
SamplerType::SAMPLER_2D, 1, kDepthTexFormat, 1, kSrcTexWidth, kSrcTexHeight,
|
||||
1, TextureUsage::DEPTH_ATTACHMENT);
|
||||
|
||||
// Create textures for the second rendering pass.
|
||||
Handle<HwTexture> dstColorTexture = api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kColorTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
|
||||
|
||||
Handle<HwTexture> dstDepthTexture = api.createTexture(
|
||||
SamplerType::SAMPLER_2D, 1, kDepthTexFormat, 1, kDstTexWidth, kDstTexHeight,
|
||||
1, TextureUsage::DEPTH_ATTACHMENT);
|
||||
|
||||
// Create render targets.
|
||||
const int level = 0;
|
||||
|
||||
Handle<HwRenderTarget> srcRenderTarget = api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1,
|
||||
{{ srcColorTexture, level }},
|
||||
{ srcDepthTexture, level }, {});
|
||||
|
||||
Handle<HwRenderTarget> dstRenderTarget = api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH,
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1,
|
||||
{{ dstColorTexture, level }},
|
||||
{ dstDepthTexture, level }, {});
|
||||
|
||||
// Prep for rendering.
|
||||
RenderPassParams params = {};
|
||||
params.flags.clear = TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH;
|
||||
params.clearColor = float4(1, 1, 0, 1);
|
||||
params.clearDepth = 1.0;
|
||||
params.viewport.width = kSrcTexWidth;
|
||||
params.viewport.height = kSrcTexHeight;
|
||||
|
||||
PipelineState state = {};
|
||||
state.rasterState.colorWrite = true;
|
||||
state.rasterState.depthWrite = true;
|
||||
state.rasterState.depthFunc = RasterState::DepthFunc::L;
|
||||
state.rasterState.culling = RasterState::CullingMode::NONE;
|
||||
state.program = program;
|
||||
auto ubuffer = api.createBufferObject(sizeof(MaterialParams),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0);
|
||||
api.bindUniformBuffer(0, ubuffer);
|
||||
|
||||
// Draw red triangle into srcRenderTarget.
|
||||
uploadUniforms(api, ubuffer, {
|
||||
.color = float4(1, 0, 0, 1),
|
||||
.scale = float4(1, 1, 0.5, 0),
|
||||
});
|
||||
api.beginRenderPass(srcRenderTarget, params);
|
||||
api.draw(state, triangle->getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
api.endFrame(0);
|
||||
|
||||
// Copy over the color buffer and the depth buffer.
|
||||
api.blit(TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH, dstRenderTarget,
|
||||
{0, 0, kDstTexWidth, kDstTexHeight}, srcRenderTarget,
|
||||
{0, 0, kSrcTexWidth, kSrcTexHeight}, SamplerMagFilter::LINEAR);
|
||||
|
||||
// Draw a larger blue triangle into dstRenderTarget. We carefully place it below the red
|
||||
// triangle in order to exercise depth testing.
|
||||
api.beginFrame(0, 0);
|
||||
params.viewport.width = kDstTexWidth;
|
||||
params.viewport.height = kDstTexHeight;
|
||||
params.flags.clear = TargetBufferFlags::NONE;
|
||||
uploadUniforms(api, ubuffer, {
|
||||
.color = float4(0, 0, 1, 1),
|
||||
.scale = float4(1.2, 1.2, 0.75, 0),
|
||||
});
|
||||
api.beginRenderPass(dstRenderTarget, params);
|
||||
api.draw(state, triangle->getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
api.endFrame(0);
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams sparams { kDstTexWidth, kDstTexHeight, "DepthMinify.png" };
|
||||
api.beginFrame(0, 0);
|
||||
dumpScreenshot(api, dstRenderTarget, &sparams);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
const uint32_t expected = 0x92b6f5c1;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", sparams.pixelHashResult, expected);
|
||||
EXPECT_TRUE(sparams.pixelHashResult == expected);
|
||||
|
||||
// Cleanup.
|
||||
api.destroyBufferObject(ubuffer);
|
||||
api.destroyProgram(program);
|
||||
api.destroyTexture(srcColorTexture);
|
||||
api.destroyTexture(dstColorTexture);
|
||||
api.destroyTexture(srcDepthTexture);
|
||||
api.destroyTexture(dstDepthTexture);
|
||||
api.destroySwapChain(swapChain);
|
||||
api.destroyRenderTarget(srcRenderTarget);
|
||||
api.destroyRenderTarget(dstRenderTarget);
|
||||
delete triangle;
|
||||
executeCommands();
|
||||
}
|
||||
|
||||
TEST_F(BackendTest, ColorResolve) {
|
||||
auto& api = getDriverApi();
|
||||
|
||||
@@ -550,7 +410,7 @@ TEST_F(BackendTest, ColorResolve) {
|
||||
api.endFrame(0);
|
||||
|
||||
// Resolve the MSAA render target into the single-sample render target.
|
||||
api.blit(TargetBufferFlags::COLOR,
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR,
|
||||
dstRenderTarget, {0, 0, kDstTexWidth, kDstTexHeight},
|
||||
srcRenderTarget, {0, 0, kSrcTexWidth, kSrcTexHeight},
|
||||
SamplerMagFilter::NEAREST);
|
||||
@@ -577,142 +437,6 @@ TEST_F(BackendTest, ColorResolve) {
|
||||
flushAndWait();
|
||||
}
|
||||
|
||||
TEST_F(BackendTest, DepthResolve) {
|
||||
auto& api = getDriverApi();
|
||||
|
||||
constexpr int kSrcTexWidth = 256;
|
||||
constexpr int kSrcTexHeight = 256;
|
||||
constexpr int kDstTexWidth = 256;
|
||||
constexpr int kDstTexHeight = 256;
|
||||
constexpr auto kColorTexFormat = TextureFormat::RGBA8;
|
||||
constexpr auto kDepthTexFormat = TextureFormat::DEPTH24;
|
||||
constexpr int kSampleCount = 4;
|
||||
|
||||
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
|
||||
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a program.
|
||||
ProgramHandle program;
|
||||
{
|
||||
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
prog.uniformBlockBindings({{"params", 1}});
|
||||
program = api.createProgram(std::move(prog));
|
||||
}
|
||||
|
||||
// Create a VertexBuffer, IndexBuffer, and RenderPrimitive.
|
||||
TrianglePrimitive* triangle = new TrianglePrimitive(api);
|
||||
|
||||
// Create 4-sample textures.
|
||||
Handle<HwTexture> srcColorTexture = api.createTexture(
|
||||
SamplerType::SAMPLER_2D, 1, kColorTexFormat, kSampleCount, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::COLOR_ATTACHMENT);
|
||||
|
||||
Handle<HwTexture> srcDepthTexture = api.createTexture(
|
||||
SamplerType::SAMPLER_2D, 1, kDepthTexFormat, kSampleCount, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::DEPTH_ATTACHMENT);
|
||||
|
||||
// Create 1-sample textures.
|
||||
Handle<HwTexture> dstColorTexture = api.createTexture(
|
||||
SamplerType::SAMPLER_2D, 1, kColorTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
|
||||
|
||||
Handle<HwTexture> dstDepthTexture = api.createTexture(
|
||||
SamplerType::SAMPLER_2D, 1, kDepthTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::DEPTH_ATTACHMENT);
|
||||
|
||||
// Create a 4-sample render target with 4-sample textures.
|
||||
Handle<HwRenderTarget> srcRenderTarget = api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH, kSrcTexWidth, kSrcTexHeight,
|
||||
kSampleCount, {{ srcColorTexture }}, { srcDepthTexture }, {});
|
||||
|
||||
// Create a 1-sample render target with 1-sample textures.
|
||||
Handle<HwRenderTarget> dstRenderTarget = api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH, kDstTexWidth, kDstTexHeight,
|
||||
1, {{ dstColorTexture }}, { dstDepthTexture }, {});
|
||||
|
||||
// Prep for rendering.
|
||||
RenderPassParams params = {};
|
||||
params.flags.clear = TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH;
|
||||
params.clearColor = float4(1, 1, 0, 1);
|
||||
params.clearDepth = 1.0;
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
params.viewport.width = kSrcTexWidth;
|
||||
params.viewport.height = kSrcTexHeight;
|
||||
|
||||
PipelineState state = {};
|
||||
state.rasterState.colorWrite = true;
|
||||
state.rasterState.depthWrite = true;
|
||||
state.rasterState.depthFunc = RasterState::DepthFunc::L;
|
||||
state.rasterState.culling = RasterState::CullingMode::NONE;
|
||||
state.program = program;
|
||||
auto ubuffer = api.createBufferObject(sizeof(MaterialParams),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
api.beginFrame(0, 0);
|
||||
api.bindUniformBuffer(0, ubuffer);
|
||||
|
||||
// Draw red triangle into srcRenderTarget.
|
||||
uploadUniforms(api, ubuffer, {
|
||||
.color = float4(1, 0, 0, 1),
|
||||
.scale = float4(1, 1, 0.5, 0),
|
||||
});
|
||||
api.beginRenderPass(srcRenderTarget, params);
|
||||
api.draw(state, triangle->getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
api.endFrame(0);
|
||||
|
||||
// Resolve the MSAA color and depth buffers into the single-sample render target.
|
||||
api.blit(TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH, dstRenderTarget,
|
||||
{0, 0, kDstTexWidth, kDstTexHeight}, srcRenderTarget,
|
||||
{0, 0, kSrcTexWidth, kSrcTexHeight}, SamplerMagFilter::LINEAR);
|
||||
|
||||
// Draw a larger blue triangle into dstRenderTarget. We carefully place it below the red
|
||||
// triangle in order to exercise depth testing.
|
||||
api.beginFrame(0, 0);
|
||||
params.viewport.width = kDstTexWidth;
|
||||
params.viewport.height = kDstTexHeight;
|
||||
params.flags.clear = TargetBufferFlags::NONE;
|
||||
uploadUniforms(api, ubuffer, {
|
||||
.color = float4(0, 0, 1, 1),
|
||||
.scale = float4(1.2, 1.2, 0.75, 0),
|
||||
});
|
||||
api.beginRenderPass(dstRenderTarget, params);
|
||||
api.draw(state, triangle->getRenderPrimitive(), 1);
|
||||
api.endRenderPass();
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams sparams { kDstTexWidth, kDstTexHeight, "DepthResolve.png" };
|
||||
api.beginFrame(0, 0);
|
||||
dumpScreenshot(api, dstRenderTarget, &sparams);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
const uint32_t expected = 0x5a38e6ba;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", sparams.pixelHashResult, expected);
|
||||
EXPECT_TRUE(sparams.pixelHashResult == expected);
|
||||
|
||||
// Cleanup.
|
||||
api.destroyBufferObject(ubuffer);
|
||||
api.destroyProgram(program);
|
||||
api.destroyTexture(srcColorTexture);
|
||||
api.destroyTexture(dstColorTexture);
|
||||
api.destroyTexture(srcDepthTexture);
|
||||
api.destroyTexture(dstDepthTexture);
|
||||
api.destroySwapChain(swapChain);
|
||||
api.destroyRenderTarget(srcRenderTarget);
|
||||
api.destroyRenderTarget(dstRenderTarget);
|
||||
delete triangle;
|
||||
executeCommands();
|
||||
}
|
||||
|
||||
TEST_F(BackendTest, Blit2DTextureArray) {
|
||||
auto& api = getDriverApi();
|
||||
|
||||
@@ -755,7 +479,7 @@ TEST_F(BackendTest, Blit2DTextureArray) {
|
||||
|
||||
// Do a blit from kSrcTexLayer of the source RT to kDstTexLayer of the destination RT.
|
||||
const int srcLevel = 0;
|
||||
api.blit(TargetBufferFlags::COLOR0, dstRenderTarget,
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
|
||||
{0, 0, kDstTexWidth, kDstTexHeight}, srcRenderTarget,
|
||||
{0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel}, SamplerMagFilter::LINEAR);
|
||||
|
||||
@@ -846,7 +570,7 @@ TEST_F(BackendTest, BlitRegion) {
|
||||
api.createRenderTarget(TargetBufferFlags::COLOR, kDstTexWidth >> kDstLevel,
|
||||
kDstTexHeight >> kDstLevel, 1, {dstTexture, kDstLevel, 0}, {}, {});
|
||||
|
||||
api.blit(TargetBufferFlags::COLOR0, dstRenderTarget, dstRect, srcRenderTarget, srcRect,
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget, dstRect, srcRenderTarget, srcRect,
|
||||
SamplerMagFilter::LINEAR);
|
||||
|
||||
// Push through an empty frame to allow the texture to upload and the blit to execute.
|
||||
@@ -933,7 +657,7 @@ TEST_F(BackendTest, BlitRegionToSwapChain) {
|
||||
|
||||
api.beginFrame(0, 0);
|
||||
|
||||
api.blit(TargetBufferFlags::COLOR0, dstRenderTarget,
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
|
||||
dstRect, srcRenderTargets[srcLevel],
|
||||
srcRect, SamplerMagFilter::LINEAR);
|
||||
|
||||
|
||||
@@ -21,10 +21,14 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <math/mat3.h>
|
||||
#include <math/mat4.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <float.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -70,7 +74,7 @@ public:
|
||||
* @return This bounding box
|
||||
*/
|
||||
Box& set(const math::float3& min, const math::float3& max) noexcept {
|
||||
// float3 ctor needed for visual studio
|
||||
// float3 ctor needed for Visual Studio
|
||||
center = (max + min) * math::float3(0.5f);
|
||||
halfExtent = (max - min) * math::float3(0.5f);
|
||||
return *this;
|
||||
@@ -130,17 +134,17 @@ public:
|
||||
struct UTILS_PUBLIC Aabb {
|
||||
|
||||
/** min coordinates */
|
||||
math::float3 min = std::numeric_limits<float>::max();
|
||||
math::float3 min = FLT_MAX;
|
||||
|
||||
/** max coordinates */
|
||||
math::float3 max = std::numeric_limits<float>::lowest();
|
||||
math::float3 max = -FLT_MAX;
|
||||
|
||||
/**
|
||||
* Computes the center of the box.
|
||||
* @return (max + min)/2
|
||||
*/
|
||||
math::float3 center() const noexcept {
|
||||
// float3 ctor needed for visual studio
|
||||
// float3 ctor needed for Visual Studio
|
||||
return (max + min) * math::float3(0.5f);
|
||||
}
|
||||
|
||||
@@ -149,7 +153,7 @@ struct UTILS_PUBLIC Aabb {
|
||||
* @return (max - min)/2
|
||||
*/
|
||||
math::float3 extent() const noexcept {
|
||||
// float3 ctor needed for visual studio
|
||||
// float3 ctor needed for Visual Studio
|
||||
return (max - min) * math::float3(0.5f);
|
||||
}
|
||||
|
||||
@@ -198,12 +202,14 @@ struct UTILS_PUBLIC Aabb {
|
||||
* @return the maximum signed distance to the box. Negative if p is in the box
|
||||
*/
|
||||
float contains(math::float3 p) const noexcept {
|
||||
// we don't use std::max to avoid a dependency on <algorithm>
|
||||
auto const maximum = [](auto a, auto b) { return a > b ? a : b; };
|
||||
float d = min.x - p.x;
|
||||
d = std::max(d, min.y - p.y);
|
||||
d = std::max(d, min.z - p.z);
|
||||
d = std::max(d, p.x - max.x);
|
||||
d = std::max(d, p.y - max.y);
|
||||
d = std::max(d, p.z - max.z);
|
||||
d = maximum(d, min.y - p.y);
|
||||
d = maximum(d, min.z - p.z);
|
||||
d = maximum(d, p.x - max.x);
|
||||
d = maximum(d, p.y - max.y);
|
||||
d = maximum(d, p.z - max.z);
|
||||
return d;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,11 +22,13 @@
|
||||
#include <filament/FilamentAPI.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <backend/BufferDescriptor.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
class FBufferObject;
|
||||
|
||||
@@ -28,6 +28,11 @@
|
||||
#include <math/vec4.h>
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace utils {
|
||||
class Entity;
|
||||
} // namespace utils
|
||||
@@ -184,7 +189,7 @@ public:
|
||||
* @see Fov.
|
||||
*/
|
||||
static math::mat4 projection(Fov direction, double fovInDegrees,
|
||||
double aspect, double near, double far = std::numeric_limits<double>::infinity());
|
||||
double aspect, double near, double far = INFINITY);
|
||||
|
||||
/** Returns the projection matrix from the focal length.
|
||||
*
|
||||
@@ -194,7 +199,7 @@ public:
|
||||
* @param far distance in world units from the camera to the far plane. \p far > \p near.
|
||||
*/
|
||||
static math::mat4 projection(double focalLengthInMillimeters,
|
||||
double aspect, double near, double far = std::numeric_limits<double>::infinity());
|
||||
double aspect, double near, double far = INFINITY);
|
||||
|
||||
|
||||
/** Sets the projection matrix from a frustum defined by six planes.
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
//! RGB color in linear space
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
class Engine;
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#define TNT_FILAMENT_COLOR_SPACE_H
|
||||
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec3.h>
|
||||
|
||||
namespace filament::color {
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
|
||||
@@ -19,9 +19,14 @@
|
||||
|
||||
#include <filament/FilamentAPI.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Invocable.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace utils {
|
||||
class Entity;
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
/**
|
||||
|
||||
@@ -23,9 +23,12 @@
|
||||
|
||||
#include <math/mat4.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <utils/unwindows.h> // Because we define NEAR and FAR in the Plane enum.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
class Box;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
class Engine;
|
||||
|
||||
@@ -18,11 +18,14 @@
|
||||
#define TNT_FILAMENT_INSTANCEBUFFER_H
|
||||
|
||||
#include <filament/FilamentAPI.h>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
/**
|
||||
@@ -45,7 +48,7 @@ public:
|
||||
* >= 1 and <= \c Engine::getMaxAutomaticInstances()
|
||||
* @see Engine::getMaxAutomaticInstances
|
||||
*/
|
||||
Builder(size_t instanceCount) noexcept;
|
||||
explicit Builder(size_t instanceCount) noexcept;
|
||||
|
||||
Builder(Builder const& rhs) noexcept;
|
||||
Builder(Builder&& rhs) noexcept;
|
||||
|
||||
@@ -27,6 +27,9 @@
|
||||
#include <math/mathfwd.h>
|
||||
#include <math/quat.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace utils {
|
||||
class Entity;
|
||||
} // namespace utils
|
||||
|
||||
@@ -30,7 +30,12 @@
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace utils {
|
||||
class CString;
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
class Material;
|
||||
|
||||
@@ -21,8 +21,11 @@
|
||||
|
||||
#include <filament/Engine.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user