Compare commits
57
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f08c87424 | ||
|
|
408b2371f0 | ||
|
|
6ea08b40a8 | ||
|
|
5eb0f7d3ec | ||
|
|
d465d59c3a | ||
|
|
af9a60d175 | ||
|
|
8e0f0c92ce | ||
|
|
5a14fb4b5d | ||
|
|
22a1aacb21 | ||
|
|
e5b6c11399 | ||
|
|
85ebd67a28 | ||
|
|
f33a779668 | ||
|
|
1b5916b655 | ||
|
|
d8720cd858 | ||
|
|
44636bddaa | ||
|
|
1741723687 | ||
|
|
ad2c291d4a | ||
|
|
52ffd80041 | ||
|
|
44dde744ea | ||
|
|
c18ddf929e | ||
|
|
5661365248 | ||
|
|
bcb9d258c2 | ||
|
|
53a94c4547 | ||
|
|
928ffe94a7 | ||
|
|
dcb78c7144 | ||
|
|
43dd8fa1cc | ||
|
|
91a5f00d30 | ||
|
|
0eff54b807 | ||
|
|
68eb0ce3f1 | ||
|
|
023318c08d | ||
|
|
c0e3aa2b0a | ||
|
|
a6d1b9188f | ||
|
|
43193c5ac5 | ||
|
|
35c58047ee | ||
|
|
2190f3544f | ||
|
|
fd8ea78e04 | ||
|
|
7fc336a6d9 | ||
|
|
3cc6fae5c0 | ||
|
|
f4f75da016 | ||
|
|
bdeba1aa86 | ||
|
|
8991be4eea | ||
|
|
940b93ceb8 | ||
|
|
00eb3d0d4f | ||
|
|
7f61eb7a0a | ||
|
|
53f506670c | ||
|
|
9fe7ab31ed | ||
|
|
008534b8a2 | ||
|
|
2c5842f908 | ||
|
|
a8ee9d473e | ||
|
|
1e3ab83e6a | ||
|
|
9720da2c68 | ||
|
|
ee74e2d50c | ||
|
|
6171009add | ||
|
|
d299a444fa | ||
|
|
107276e9cc | ||
|
|
fe00cbf106 | ||
|
|
02d0ddbcfe |
@@ -19,7 +19,7 @@ jobs:
|
||||
- uses: ./.github/actions/mac-prereq
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/mac && printf "y" | ./build.sh presubmit
|
||||
cd build/mac && printf "y" | ./build.sh presubmit-with-test
|
||||
- name: Test material parser
|
||||
run: |
|
||||
out/cmake-release/filament/test/test_material_parser
|
||||
|
||||
+6
-2
@@ -473,7 +473,7 @@ endif()
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${EXTRA_SANITIZE_OPTIONS}")
|
||||
|
||||
if (FILAMENT_ENABLE_COVERAGE)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fprofile-instr-generate -fcoverage-mapping")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-instr-generate -fcoverage-mapping -Wno-pass-failed")
|
||||
endif()
|
||||
|
||||
# Disable the stack check for macOS to workaround a known issue in clang 11.0.0.
|
||||
@@ -563,6 +563,10 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
add_definitions(-DFILAMENT_SUPPORTS_WEBGPU)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBP_TEXTURES)
|
||||
add_definitions(-DFILAMENT_SUPPORTS_WEBP_TEXTURES)
|
||||
endif()
|
||||
|
||||
# Build with Metal support on non-WebGL Apple platforms.
|
||||
if (APPLE AND NOT WEBGL)
|
||||
option(FILAMENT_SUPPORTS_METAL "Include the Metal backend" ON)
|
||||
@@ -915,7 +919,6 @@ endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBP_TEXTURES)
|
||||
add_subdirectory(${EXTERNAL}/libwebp/tnt)
|
||||
add_definitions(-DFILAMENT_SUPPORTS_WEBP_TEXTURES)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_VULKAN)
|
||||
@@ -973,6 +976,7 @@ if (IS_HOST_PLATFORM)
|
||||
add_subdirectory(${TOOLS}/roughness-prefilter)
|
||||
add_subdirectory(${TOOLS}/specular-color)
|
||||
add_subdirectory(${TOOLS}/uberz)
|
||||
add_subdirectory(${TOOLS}/specgen)
|
||||
endif()
|
||||
|
||||
# Generate exported executables for cross-compiled builds (Android, WebGL, and iOS)
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.68.2'
|
||||
implementation 'com.google.android.filament:filament-android:1.68.3'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.68.2'
|
||||
pod 'Filament', '~> 1.68.3'
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -7,6 +7,10 @@ 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.68.4
|
||||
|
||||
- gltfio: Add optional support for webp textures (EXT_texture_webp), controlled via FILAMENT_SUPPORTS_WEBP_TEXTURES cmake option
|
||||
|
||||
## v1.68.3
|
||||
|
||||
- materials: added support for the glTF `KHR_materials_dispersion` extension, which adds dispersion for refractive objects
|
||||
|
||||
@@ -335,6 +335,13 @@ Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderRefract
|
||||
builder->refractionMode((MaterialBuilder::RefractionMode) mode);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderReflectionMode(JNIEnv* env,
|
||||
jclass, jlong nativeBuilder, jint mode) {
|
||||
auto builder = (MaterialBuilder*) nativeBuilder;
|
||||
builder->reflectionMode((MaterialBuilder::ReflectionMode) mode);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderRefractionType(JNIEnv* env,
|
||||
jclass, jlong nativeBuilder, jint type) {
|
||||
|
||||
+12
@@ -177,6 +177,11 @@ public class MaterialBuilder {
|
||||
SCREEN_SPACE
|
||||
}
|
||||
|
||||
public enum ReflectionMode {
|
||||
DEFAULT,
|
||||
SCREEN_SPACE
|
||||
}
|
||||
|
||||
public enum RefractionType {
|
||||
SOLID,
|
||||
THIN
|
||||
@@ -403,6 +408,12 @@ public class MaterialBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public MaterialBuilder reflectionMode(ReflectionMode mode) {
|
||||
nMaterialBuilderReflectionMode(mNativeObject, mode.ordinal());
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public MaterialBuilder refractionType(RefractionType type) {
|
||||
nMaterialBuilderRefractionType(mNativeObject, type.ordinal());
|
||||
@@ -604,6 +615,7 @@ public class MaterialBuilder {
|
||||
private static native void nMaterialBuilderSpecularAntiAliasingThreshold(long mNativeObject,
|
||||
float threshold);
|
||||
private static native void nMaterialBuilderRefractionMode(long nativeBuilder, int mode);
|
||||
private static native void nMaterialBuilderReflectionMode(long nativeBuilder, int mode);
|
||||
private static native void nMaterialBuilderRefractionType(long nativeBuilder, int type);
|
||||
private static native void nMaterialBuilderClearCoatIorChange(long mNativeObject,
|
||||
boolean clearCoatIorChange);
|
||||
|
||||
@@ -2041,10 +2041,6 @@ public class View {
|
||||
* use an AABB neighborhood
|
||||
*/
|
||||
AABB,
|
||||
/**
|
||||
* use the variance of the neighborhood (not recommended)
|
||||
*/
|
||||
VARIANCE,
|
||||
/**
|
||||
* use both AABB and variance
|
||||
*/
|
||||
@@ -2075,7 +2071,7 @@ public class View {
|
||||
}
|
||||
|
||||
/**
|
||||
* reconstruction filter width typically between 1 (sharper) and 2 (smoother)
|
||||
* @deprecated has no effect.
|
||||
*/
|
||||
public float filterWidth = 1.0f;
|
||||
/**
|
||||
@@ -2095,9 +2091,9 @@ public class View {
|
||||
*/
|
||||
public boolean enabled = false;
|
||||
/**
|
||||
* 4x TAA upscaling. Disables Dynamic Resolution. [BETA]
|
||||
* Upscaling factor. Disables Dynamic Resolution. [BETA]
|
||||
*/
|
||||
public boolean upscaling = false;
|
||||
public float upscaling = 1.0f;
|
||||
/**
|
||||
* whether to filter the history buffer
|
||||
*/
|
||||
@@ -2110,6 +2106,10 @@ public class View {
|
||||
* whether to use the YcoCg color-space for history rejection
|
||||
*/
|
||||
public boolean useYCoCg = false;
|
||||
/**
|
||||
* set to true for HDR content
|
||||
*/
|
||||
public boolean hdr = true;
|
||||
/**
|
||||
* type of color gamut box
|
||||
*/
|
||||
|
||||
@@ -6,6 +6,7 @@ option(FILAMENT_ENABLE_FGVIEWER "Enables Frame Graph Viewer" OFF)
|
||||
option(FILAMENT_ENABLE_MATDBG "Enables Material debugger" OFF)
|
||||
option(FILAMENT_DISABLE_MATOPT "Disables material optimizations" OFF)
|
||||
option(FILAMENT_SUPPORTS_WEBGPU "Enables WebGPU on Android" OFF)
|
||||
option(FILAMENT_SUPPORTS_WEBP_TEXTURES "Enable webp texture support on Android" OFF)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
|
||||
@@ -30,6 +31,12 @@ add_library(stb STATIC IMPORTED)
|
||||
set_target_properties(stb PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libstb.a)
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBP_TEXTURES)
|
||||
add_library(webpdecoder STATIC IMPORTED)
|
||||
set_target_properties(webpdecoder PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libwebpdecoder.a)
|
||||
endif()
|
||||
|
||||
add_library(basis_transcoder STATIC IMPORTED)
|
||||
set_target_properties(basis_transcoder PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libbasis_transcoder.a)
|
||||
@@ -81,6 +88,10 @@ set(GLTFIO_INCLUDE_DIRS
|
||||
../../libs/ktxreader/include
|
||||
)
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBP_TEXTURES)
|
||||
list(APPEND GLTFIO_INCLUDE_DIRS ../../third_party/libwebp/src)
|
||||
endif()
|
||||
|
||||
add_library(gltfio-jni SHARED ${GLTFIO_SRCS})
|
||||
|
||||
target_compile_definitions(gltfio-jni PUBLIC GLTFIO_DRACO_SUPPORTED=1)
|
||||
@@ -107,3 +118,6 @@ target_link_libraries(gltfio-jni
|
||||
PRIVATE perfetto # needed only when FILAMENT_ENABLE_PERFETTO is defined
|
||||
PRIVATE log # needed only when perfetto above is used
|
||||
)
|
||||
if (FILAMENT_SUPPORTS_WEBP_TEXTURES)
|
||||
target_link_libraries(gltfio-jni PRIVATE webpdecoder)
|
||||
endif()
|
||||
|
||||
@@ -128,6 +128,18 @@ Java_com_google_android_filament_gltfio_ResourceLoader_nCreateKtx2Provider(JNIEn
|
||||
return (jlong) createKtx2Provider(engine);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_gltfio_ResourceLoader_nIsWebpSupported(JNIEnv*, jclass) {
|
||||
return (jboolean) isWebpSupported();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_gltfio_ResourceLoader_nCreateWebpProvider(JNIEnv*, jclass,
|
||||
jlong nativeEngine) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
return (jlong) createWebpProvider(engine);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_gltfio_ResourceLoader_nDestroyTextureProvider(JNIEnv*, jclass,
|
||||
jlong nativeProvider) {
|
||||
|
||||
+23
@@ -37,6 +37,7 @@ public class ResourceLoader {
|
||||
private final long mNativeObject;
|
||||
private final long mNativeStbProvider;
|
||||
private final long mNativeKtx2Provider;
|
||||
private final long mNativeWebpProvider;
|
||||
|
||||
/**
|
||||
* Constructs a resource loader tied to the given Filament engine.
|
||||
@@ -50,9 +51,17 @@ public class ResourceLoader {
|
||||
mNativeObject = nCreateResourceLoader(nativeEngine, false);
|
||||
mNativeStbProvider = nCreateStbProvider(nativeEngine);
|
||||
mNativeKtx2Provider = nCreateKtx2Provider(nativeEngine);
|
||||
|
||||
nAddTextureProvider(mNativeObject, "image/jpeg", mNativeStbProvider);
|
||||
nAddTextureProvider(mNativeObject, "image/png", mNativeStbProvider);
|
||||
nAddTextureProvider(mNativeObject, "image/ktx2", mNativeKtx2Provider);
|
||||
if (nIsWebpSupported()) {
|
||||
mNativeWebpProvider = nCreateWebpProvider(nativeEngine);
|
||||
nAddTextureProvider(mNativeObject, "image/webp", mNativeWebpProvider);
|
||||
}
|
||||
else {
|
||||
mNativeWebpProvider = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,9 +77,17 @@ public class ResourceLoader {
|
||||
mNativeObject = nCreateResourceLoader(nativeEngine, normalizeSkinningWeights);
|
||||
mNativeStbProvider = nCreateStbProvider(nativeEngine);
|
||||
mNativeKtx2Provider = nCreateKtx2Provider(nativeEngine);
|
||||
|
||||
nAddTextureProvider(mNativeObject, "image/jpeg", mNativeStbProvider);
|
||||
nAddTextureProvider(mNativeObject, "image/png", mNativeStbProvider);
|
||||
nAddTextureProvider(mNativeObject, "image/ktx2", mNativeKtx2Provider);
|
||||
if (nIsWebpSupported()) {
|
||||
mNativeWebpProvider = nCreateWebpProvider(nativeEngine);
|
||||
nAddTextureProvider(mNativeObject, "image/webp", mNativeWebpProvider);
|
||||
}
|
||||
else {
|
||||
mNativeWebpProvider = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,6 +97,9 @@ public class ResourceLoader {
|
||||
nDestroyResourceLoader(mNativeObject);
|
||||
nDestroyTextureProvider(mNativeStbProvider);
|
||||
nDestroyTextureProvider(mNativeKtx2Provider);
|
||||
if (nIsWebpSupported()) {
|
||||
nDestroyTextureProvider(mNativeWebpProvider);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -191,6 +211,9 @@ public class ResourceLoader {
|
||||
|
||||
private static native long nCreateStbProvider(long nativeEngine);
|
||||
private static native long nCreateKtx2Provider(long nativeEngine);
|
||||
private static native boolean nIsWebpSupported();
|
||||
private static native long nCreateWebpProvider(long nativeEngine);
|
||||
|
||||
private static native void nAddTextureProvider(long nativeLoader, String url, long nativeProvider);
|
||||
private static native void nDestroyTextureProvider(long nativeProvider);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.68.2
|
||||
VERSION_NAME=1.68.3
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
+38
-18
@@ -76,11 +76,16 @@ class MainActivity : Activity() {
|
||||
private var texture: Texture? = null
|
||||
private var renderTarget: RenderTarget? = null
|
||||
|
||||
private var useExternalTexture = true
|
||||
|
||||
private lateinit var offscreenView: View
|
||||
private lateinit var offscreenCamera: Camera
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
// To use set this flag with adb, run
|
||||
// adb shell am start -n com.google.android.filament.texturetarget/.MainActivity --ez useExternalTexture false
|
||||
useExternalTexture = intent.getBooleanExtra("useExternalTexture", true)
|
||||
surfaceView = SurfaceView(this)
|
||||
setContentView(surfaceView)
|
||||
choreographer = Choreographer.getInstance()
|
||||
@@ -173,8 +178,15 @@ class MainActivity : Activity() {
|
||||
readUncompressedAsset("materials/baked_color.filamat").let {
|
||||
triangleMaterial = Material.Builder().payload(it, it.remaining()).build(engine)
|
||||
}
|
||||
readUncompressedAsset("materials/textured.filamat").let {
|
||||
texturedMaterial = Material.Builder().payload(it, it.remaining()).build(engine)
|
||||
|
||||
if (useExternalTexture) {
|
||||
readUncompressedAsset("materials/texturedExternal.filamat").let {
|
||||
texturedMaterial = Material.Builder().payload(it, it.remaining()).build(engine)
|
||||
}
|
||||
} else {
|
||||
readUncompressedAsset("materials/textured.filamat").let {
|
||||
texturedMaterial = Material.Builder().payload(it, it.remaining()).build(engine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,33 +397,41 @@ class MainActivity : Activity() {
|
||||
texture?.let { engine.destroyTexture(it) }
|
||||
hardwareBuffer?.close()
|
||||
|
||||
// Create a new render target.
|
||||
hardwareBuffer = HardwareBuffer.create(width, height,
|
||||
HardwareBuffer.RGBA_8888, 1,
|
||||
HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT)
|
||||
if (useExternalTexture) {
|
||||
// Create a new render target.
|
||||
hardwareBuffer = HardwareBuffer.create(width, height,
|
||||
HardwareBuffer.RGBA_8888, 1,
|
||||
HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE or HardwareBuffer.USAGE_GPU_COLOR_OUTPUT)
|
||||
|
||||
texture = Texture.Builder()
|
||||
.width(width)
|
||||
.height(height)
|
||||
.usage(Texture.Usage.COLOR_ATTACHMENT or Texture.Usage.SAMPLEABLE)
|
||||
.sampler(Texture.Sampler.SAMPLER_EXTERNAL)
|
||||
.format(Texture.InternalFormat.RGBA8)
|
||||
.external()
|
||||
.build(engine)
|
||||
texture = Texture.Builder()
|
||||
.width(width)
|
||||
.height(height)
|
||||
.usage(Texture.Usage.COLOR_ATTACHMENT or Texture.Usage.SAMPLEABLE)
|
||||
.sampler(Texture.Sampler.SAMPLER_EXTERNAL)
|
||||
.format(Texture.InternalFormat.RGBA8)
|
||||
.external()
|
||||
.build(engine)
|
||||
|
||||
texture!!.setExternalImage(engine, hardwareBuffer!!)
|
||||
texture!!.setExternalImage(engine, hardwareBuffer!!)
|
||||
} else {
|
||||
texture = Texture.Builder()
|
||||
.width(width)
|
||||
.height(height)
|
||||
.levels(1)
|
||||
.usage(Texture.Usage.COLOR_ATTACHMENT or Texture.Usage.SAMPLEABLE)
|
||||
.format(Texture.InternalFormat.RGBA8)
|
||||
.build(engine)
|
||||
}
|
||||
|
||||
renderTarget = RenderTarget.Builder()
|
||||
.texture(RenderTarget.AttachmentPoint.COLOR, texture!!)
|
||||
.build(engine)
|
||||
|
||||
offscreenView.renderTarget = renderTarget
|
||||
|
||||
// Set the texture on the quad material.
|
||||
texturedMaterial.defaultInstance.setParameter("texture", texture!!,
|
||||
TextureSampler(TextureSampler.MinFilter.LINEAR, TextureSampler.MagFilter.LINEAR,
|
||||
TextureSampler.WrapMode.CLAMP_TO_EDGE))
|
||||
|
||||
TextureSampler.WrapMode.CLAMP_TO_EDGE))
|
||||
FilamentHelper.synchronizePendingFrames(engine)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ material {
|
||||
shadingModel : unlit,
|
||||
parameters : [
|
||||
{
|
||||
type : samplerExternal,
|
||||
type : sampler2d,
|
||||
name : texture
|
||||
}
|
||||
],
|
||||
@@ -15,6 +15,6 @@ material {
|
||||
fragment {
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
material.baseColor = texture(materialParams_texture, getUV0());
|
||||
material.baseColor = texture(materialParams_texture, uvToRenderTargetUV(getUV0()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
material {
|
||||
name : textured,
|
||||
shadingModel : unlit,
|
||||
parameters : [
|
||||
{
|
||||
type : samplerExternal,
|
||||
name : texture
|
||||
}
|
||||
],
|
||||
requires: [
|
||||
uv0
|
||||
]
|
||||
}
|
||||
|
||||
fragment {
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
material.baseColor = texture(materialParams_texture, uvToRenderTargetUV(getUV0()));
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,11 @@ if [[ "$TARGET" == "presubmit" ]]; then
|
||||
BUILD_RELEASE=release
|
||||
fi
|
||||
|
||||
if [[ "$TARGET" == "presubmit-with-test" ]]; then
|
||||
BUILD_RELEASE=release
|
||||
RUN_TESTS=-u
|
||||
fi
|
||||
|
||||
if [[ "$TARGET" == "debug" ]]; then
|
||||
BUILD_DEBUG=debug
|
||||
GENERATE_ARCHIVES=-a
|
||||
|
||||
+30
-6
@@ -97,6 +97,7 @@ in table [standardProperties].
|
||||
**transmission** | Defines how much of the diffuse light of a dielectric is transmitted through the object, in other words this defines how transparent an object is
|
||||
**ior** | Index of refraction, either for refractive objects or as an alternative to reflectance
|
||||
**microThickness** | Thickness of the thin layer of refractive objects
|
||||
**dispersion** | Strength of the dispersion effect for refractive objects, specified as 20/Abbe number
|
||||
**bentNormal** | A normal pointing in the average unoccluded direction. Can be used to improve indirect lighting quality
|
||||
**shadowStrength** | Strength factor between 0 and 1 for all shadows received by this material
|
||||
[Table [standardProperties]: Properties of the standard model]
|
||||
@@ -126,6 +127,7 @@ The type and range of each property is described in table [standardPropertiesTyp
|
||||
**absorption** | float3 | [0..n] |
|
||||
**microThickness** | float | [0..n] |
|
||||
**thickness** | float | [0..n] |
|
||||
**dispersion** | float | [0..n] | Realistic values are between [0, 1], with the exception of Rutile, which has a value of 2.04
|
||||
[Table [standardPropertiesTypes]: Range and type of the standard model's properties]
|
||||
|
||||
|
||||
@@ -153,13 +155,14 @@ The type and range of each property is described in table [standardPropertiesTyp
|
||||
as-is, which can lead to physically impossible materials, however, this might be desirable
|
||||
for artistic reasons.
|
||||
|
||||
!!! Note: About `thickness` and `microThickness` for refraction
|
||||
!!! Note: About `thickness`, `microThickness` and `dispersion` for refraction
|
||||
`thickness` represents the thickness of solid objects in the direction of the normal, for
|
||||
satisfactory results, this should be provided per fragment (e.g.: as a texture) or at least per
|
||||
vertex. `microThickness` represent the thickness of the thin layer of an object, and can
|
||||
generally be provided as a constant value. For example, a 1mm thin hollow sphere of radius 1m,
|
||||
would have a `thickness` of 1 and a `microThickness` of 0.001. Currently `thickness` is not
|
||||
used when `refractionType` is set to `thin`.
|
||||
would have a `thickness` of 1 and a `microThickness` of 0.001. Dispersion controls the angular
|
||||
separation of colors transmitting through a volume, and can be set by a constant value.
|
||||
Currently `thickness` and `dispersion` are not used when `refractionType` is set to `thin`.
|
||||
|
||||
### Base color
|
||||
|
||||
@@ -651,6 +654,26 @@ the `refractionType` is set to `solid` and `absorption` coefficients are set.
|
||||
![Figure [varyingThickness]: `thickness` varying from 0.0 at the top of the prism to 3.0 at the
|
||||
bottom of the prism](images/material_thickness.png)
|
||||
|
||||
### Dispersion
|
||||
|
||||
The dispersion property controls the angular separation of colors transmitting through a relatively
|
||||
clear volume. It can only be used when `refractionType` is set to `volume`.
|
||||
Its value is specified as 20/Abbe number. When the value is zero, no dispersion is used.
|
||||
|
||||
Table [commonMatDispersion] describes acceptable dispersion values for various types of materials.
|
||||
|
||||
Material | Abbe Number (V) | Dispersion (20/V)
|
||||
--------------------------:|:------------------:|:-----------------
|
||||
Rutile | 9.8 | 2.04
|
||||
Polycarbonate | 32 | 0.625
|
||||
Diamond | 55 | 0.36
|
||||
Water | 55 | 0.36
|
||||
Crown Glass | 59 | 0.33
|
||||
[Table [commonMatDispersion]: Dispersion of common materials]
|
||||
|
||||
![Figure [dispersionProperty]: `dispersion` varying from 0.0
|
||||
(left) to 5.0 (right)](images/materials/dispersion.png)
|
||||
|
||||
## Subsurface model
|
||||
|
||||
### Thickness
|
||||
@@ -2276,6 +2299,7 @@ struct MaterialInputs {
|
||||
float3 absorption; // default float3(0.0, 0.0, 0.0)
|
||||
float ior; // default: 1.5
|
||||
float microThickness; // default: 0.0, not available with refractionType "solid"
|
||||
float dispersion; // default: 0.0, not available with refractionType "thin"
|
||||
}
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -2456,9 +2480,9 @@ type aliases:
|
||||
**uint2** | uvec2 | A vector of 2 unsigned integers
|
||||
**uint3** | uvec3 | A vector of 3 unsigned integers
|
||||
**uint4** | uvec4 | A vector of 4 unsigned integers
|
||||
**float2** | float2 | A vector of 2 floats
|
||||
**float3** | float3 | A vector of 3 floats
|
||||
**float4** | float4 | A vector of 4 floats
|
||||
**float2** | vec2 | A vector of 2 floats
|
||||
**float3** | vec3 | A vector of 3 floats
|
||||
**float4** | vec4 | A vector of 4 floats
|
||||
**float4x4** | mat4 | A 4x4 float matrix
|
||||
**float3x3** | mat3 | A 3x3 float matrix
|
||||
|
||||
|
||||
@@ -327,12 +327,41 @@ host tools are required to successfully build for Android.</p>
|
||||
<p>Note that the main target is the ARM 64-bit target. Our implementation is optimized first and
|
||||
foremost for <code>arm64-v8a</code>.</p>
|
||||
<p>To build Android on Windows machines, see <a href="android/Windows.html">android/Windows.md</a>.</p>
|
||||
<h4 id="important-sdk-location"><a class="header" href="#important-sdk-location">Important: SDK location</a></h4>
|
||||
<p>Either ensure your <code>ANDROID_HOME</code> environment variable is set or make sure the root project
|
||||
contains a <code>local.properties</code> file with the <code>sdk.dir</code> property pointing to your installation of
|
||||
the Android SDK.</p>
|
||||
<h4 id="easy-android-build"><a class="header" href="#easy-android-build">Easy Android build</a></h4>
|
||||
<p>The easiest way to build Filament for Android is to use <code>build.sh</code> and the
|
||||
<code>-p android</code> flag. For instance to build the release target:</p>
|
||||
<pre><code class="language-shell">./build.sh -p android release
|
||||
</code></pre>
|
||||
<p>To build a sample (such as <code>android/samples/sample-hello-triangle</code>) for an ARM 64-bit phone, you would run</p>
|
||||
<pre><code class="language-shell">./build.sh -p android -q arm64-v8a -k sample-hello-triangle release
|
||||
</code></pre>
|
||||
<p>The output APK can be found in <code>android/samples/sample-hello-triangle/build/outputs/apk/release/sample-hello-triangle-release-unsigned.apk</code></p>
|
||||
<p>Run <code>build.sh -h</code> for more information.</p>
|
||||
<h4 id="android-studio"><a class="header" href="#android-studio">Android Studio</a></h4>
|
||||
<p>You must use the latest stable release of Android Studio.</p>
|
||||
<p>The Android build of filament is separated into java/kotlin client APIs, a layer of jni bindings
|
||||
that bridges java/kotlin with native code, and Filament and other component code that have been compiled
|
||||
into architecture-specific libraries. Our default Android Studio gradle setup can compile java/kotlin and
|
||||
the jni bindings for you, but it will treat the filament libraries as already compiled and present on
|
||||
the system.</p>
|
||||
<p>Therefore, before compiling the sample app or any other targets, you must
|
||||
make sure that the native filament libraries have been compiled and are located at a prescribed location
|
||||
so that the jni bindings can link against them. You can do so by using the easy build script</p>
|
||||
<pre><code class="language-shell">./build.sh -p android release -q arm64-v8a
|
||||
</code></pre>
|
||||
<p>Note that the above step will also install host machine tools into prescribed locations. These tools are
|
||||
required for compiling Filament assets such as materials and environment maps.</p>
|
||||
<p>Now we are ready to compile the apps. To open the project, point Studio to the <code>android</code> folder.
|
||||
After opening the project and syncing with Gradle, select the sample of your choice
|
||||
using the drop-down widget in the toolbar. Additionally, you will need to select a deployment target.
|
||||
By doing so, Android Studio will automatically try to compile the app only for that specific
|
||||
device's architecture. So if you are targeting a new Pixel phone, make sure that the step above
|
||||
(compiling the library) is targeting ARM 64-bit (<code>-q arm64-v8a</code> ), and if you are running the app on
|
||||
an emulator on a Linux machine with an x86 64-bit chipset, you would indicate (<code>-q x86_64</code>) in the above step.</p>
|
||||
<h4 id="manual-builds"><a class="header" href="#manual-builds">Manual builds</a></h4>
|
||||
<p>Invoke CMake in a build directory of your choice, inside of filament's directory. The commands
|
||||
below show how to build Filament for ARM 64-bit (<code>aarch64</code>).</p>
|
||||
|
||||
+1
-1
@@ -203,7 +203,7 @@ tree into <code>docs_src/src_mdbook/src/dup</code>. Moreover, to restore valid l
|
||||
to perform a number of URL replacements in addition to the copy. These replacements are
|
||||
described in <a href="https://github.com/google/filament/blob/main/docs_src/build/duplicates.json"><code>docs_src/build/duplicates.json</code></a>.</p>
|
||||
<h3 id="core-concept-docs"><a class="header" href="#core-concept-docs">Core concept docs</a></h3>
|
||||
<p>The primary design of Filament as a phyiscally-based renderer and details of its materials
|
||||
<p>The primary design of Filament as a physically-based renderer and details of its materials
|
||||
system are described in <code>Filament.md.html</code> and <code>Materials.md.html</code>, respectively. These two
|
||||
documents are written in <a href="https://casual-effects.com/markdeep/"><code>markdeep</code></a>. To embed them into our book, we</p>
|
||||
<ol>
|
||||
|
||||
+2
-2
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.68.0'
|
||||
implementation 'com.google.android.filament:filament-android:1.68.3'
|
||||
}
|
||||
</code></pre>
|
||||
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
|
||||
@@ -196,7 +196,7 @@ dependencies {
|
||||
</div>
|
||||
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
|
||||
<p>iOS projects can use CocoaPods to install the latest release:</p>
|
||||
<pre><code class="language-shell">pod 'Filament', '~> 1.68.0'
|
||||
<pre><code class="language-shell">pod 'Filament', '~> 1.68.3'
|
||||
</code></pre>
|
||||
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
|
||||
<ul>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.6 MiB |
+2732
-2773
File diff suppressed because one or more lines are too long
+1903
-1902
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -307,7 +307,7 @@ class FilamentViewer extends LitElement {
|
||||
asyncInterval: 30
|
||||
};
|
||||
|
||||
const doneAddingResources = (resourceLoader, stbProvider, ktx2Provider) => {
|
||||
const doneAddingResources = (resourceLoader, stbProvider, ktx2Provider, webpProvider) => {
|
||||
this.srcBlobResources = {};
|
||||
resourceLoader.asyncBeginLoad(this.asset);
|
||||
const timer = setInterval(() => {
|
||||
@@ -318,6 +318,9 @@ class FilamentViewer extends LitElement {
|
||||
resourceLoader.delete();
|
||||
stbProvider.delete();
|
||||
ktx2Provider.delete();
|
||||
if (webpProvider) {
|
||||
webpProvider.delete();
|
||||
}
|
||||
this.animator = this.asset.getInstance().getAnimator();
|
||||
this.animationStartTime = Date.now();
|
||||
}
|
||||
@@ -335,18 +338,25 @@ class FilamentViewer extends LitElement {
|
||||
|
||||
const stbProvider = new Filament.gltfio$StbProvider(this.engine);
|
||||
const ktx2Provider = new Filament.gltfio$Ktx2Provider(this.engine);
|
||||
let webpProvider = null;
|
||||
|
||||
resourceLoader.addStbProvider("image/jpeg", stbProvider);
|
||||
resourceLoader.addStbProvider("image/png", stbProvider);
|
||||
resourceLoader.addKtx2Provider("image/ktx2", ktx2Provider);
|
||||
|
||||
|
||||
if (Filament.gltfio$WebpProvider.isWebpSupported()) {
|
||||
webpProvider = new Filament.gltfio$WebpProvider(this.engine);
|
||||
resourceLoader.addWebpProvider("image/webp", webpProvider);
|
||||
}
|
||||
|
||||
let remaining = Object.keys(this.srcBlobResources).length;
|
||||
for (const name in this.srcBlobResources) {
|
||||
this.srcBlobResources[name].arrayBuffer().then(buffer => {
|
||||
const desc = getBufferDescriptor(new Uint8Array(buffer));
|
||||
resourceLoader.addResourceData(name, getBufferDescriptor(desc));
|
||||
if (--remaining === 0) {
|
||||
doneAddingResources(resourceLoader, stbProvider, ktx2Provider);
|
||||
doneAddingResources(resourceLoader, stbProvider, ktx2Provider, webpProvider);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ to perform a number of URL replacements in addition to the copy. These replaceme
|
||||
described in [`docs_src/build/duplicates.json`].
|
||||
|
||||
### Core concept docs
|
||||
The primary design of Filament as a phyiscally-based renderer and details of its materials
|
||||
The primary design of Filament as a physically-based renderer and details of its materials
|
||||
system are described in `Filament.md.html` and `Materials.md.html`, respectively. These two
|
||||
documents are written in [`markdeep`]. To embed them into our book, we
|
||||
1. Convert the markdeep into html
|
||||
|
||||
@@ -10,6 +10,6 @@ sortedcontainers==2.4.0
|
||||
trio==0.31.0
|
||||
trio-websocket==0.12.2
|
||||
typing_extensions==4.15.0
|
||||
urllib3==2.6.0
|
||||
urllib3==2.6.3
|
||||
websocket-client==1.9.0
|
||||
wsproto==1.2.0
|
||||
|
||||
@@ -161,7 +161,7 @@ The type and range of each property is described in table [standardPropertiesTyp
|
||||
vertex. `microThickness` represent the thickness of the thin layer of an object, and can
|
||||
generally be provided as a constant value. For example, a 1mm thin hollow sphere of radius 1m,
|
||||
would have a `thickness` of 1 and a `microThickness` of 0.001. Dispersion controls the angular
|
||||
separation of colors transmitting through a volume, and can be set by a contant value.
|
||||
separation of colors transmitting through a volume, and can be set by a constant value.
|
||||
Currently `thickness` and `dispersion` are not used when `refractionType` is set to `thin`.
|
||||
|
||||
### Base color
|
||||
@@ -671,6 +671,9 @@ Water | 55 | 0.36
|
||||
Crown Glass | 59 | 0.33
|
||||
[Table [commonMatDispersion]: Dispersion of common materials]
|
||||
|
||||
![Figure [dispersionProperty]: `dispersion` varying from 0.0
|
||||
(left) to 5.0 (right)](images/materials/dispersion.png)
|
||||
|
||||
## Subsurface model
|
||||
|
||||
### Thickness
|
||||
@@ -2477,9 +2480,9 @@ type aliases:
|
||||
**uint2** | uvec2 | A vector of 2 unsigned integers
|
||||
**uint3** | uvec3 | A vector of 3 unsigned integers
|
||||
**uint4** | uvec4 | A vector of 4 unsigned integers
|
||||
**float2** | float2 | A vector of 2 floats
|
||||
**float3** | float3 | A vector of 3 floats
|
||||
**float4** | float4 | A vector of 4 floats
|
||||
**float2** | vec2 | A vector of 2 floats
|
||||
**float3** | vec3 | A vector of 3 floats
|
||||
**float4** | vec4 | A vector of 4 floats
|
||||
**float4x4** | mat4 | A 4x4 float matrix
|
||||
**float3x3** | mat3 | A 3x3 float matrix
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.6 MiB |
@@ -307,7 +307,7 @@ class FilamentViewer extends LitElement {
|
||||
asyncInterval: 30
|
||||
};
|
||||
|
||||
const doneAddingResources = (resourceLoader, stbProvider, ktx2Provider) => {
|
||||
const doneAddingResources = (resourceLoader, stbProvider, ktx2Provider, webpProvider) => {
|
||||
this.srcBlobResources = {};
|
||||
resourceLoader.asyncBeginLoad(this.asset);
|
||||
const timer = setInterval(() => {
|
||||
@@ -318,6 +318,9 @@ class FilamentViewer extends LitElement {
|
||||
resourceLoader.delete();
|
||||
stbProvider.delete();
|
||||
ktx2Provider.delete();
|
||||
if (webpProvider) {
|
||||
webpProvider.delete();
|
||||
}
|
||||
this.animator = this.asset.getInstance().getAnimator();
|
||||
this.animationStartTime = Date.now();
|
||||
}
|
||||
@@ -335,18 +338,25 @@ class FilamentViewer extends LitElement {
|
||||
|
||||
const stbProvider = new Filament.gltfio$StbProvider(this.engine);
|
||||
const ktx2Provider = new Filament.gltfio$Ktx2Provider(this.engine);
|
||||
let webpProvider = null;
|
||||
|
||||
resourceLoader.addStbProvider("image/jpeg", stbProvider);
|
||||
resourceLoader.addStbProvider("image/png", stbProvider);
|
||||
resourceLoader.addKtx2Provider("image/ktx2", ktx2Provider);
|
||||
|
||||
|
||||
if (Filament.gltfio$WebpProvider.isWebpSupported()) {
|
||||
webpProvider = new Filament.gltfio$WebpProvider(this.engine);
|
||||
resourceLoader.addWebpProvider("image/webp", webpProvider);
|
||||
}
|
||||
|
||||
let remaining = Object.keys(this.srcBlobResources).length;
|
||||
for (const name in this.srcBlobResources) {
|
||||
this.srcBlobResources[name].arrayBuffer().then(buffer => {
|
||||
const desc = getBufferDescriptor(new Uint8Array(buffer));
|
||||
resourceLoader.addResourceData(name, getBufferDescriptor(desc));
|
||||
if (--remaining === 0) {
|
||||
doneAddingResources(resourceLoader, stbProvider, ktx2Provider);
|
||||
doneAddingResources(resourceLoader, stbProvider, ktx2Provider, webpProvider);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -97,7 +97,6 @@ set(SRCS
|
||||
src/RenderableManager.cpp
|
||||
src/Renderer.cpp
|
||||
src/RendererUtils.cpp
|
||||
src/ResourceAllocator.cpp
|
||||
src/ResourceList.cpp
|
||||
src/Scene.cpp
|
||||
src/ShadowMap.cpp
|
||||
@@ -108,6 +107,7 @@ set(SRCS
|
||||
src/SwapChain.cpp
|
||||
src/Sync.cpp
|
||||
src/Texture.cpp
|
||||
src/TextureCache.cpp
|
||||
src/ToneMapper.cpp
|
||||
src/TransformManager.cpp
|
||||
src/UniformBuffer.cpp
|
||||
@@ -158,6 +158,7 @@ set(SRCS
|
||||
src/fg/FrameGraphTexture.cpp
|
||||
src/fg/PassNode.cpp
|
||||
src/fg/ResourceNode.cpp
|
||||
src/fg/ResourceCreationContext.cpp
|
||||
src/fsr.cpp
|
||||
src/ZstdHelper.cpp
|
||||
)
|
||||
@@ -168,6 +169,7 @@ set(PRIVATE_HDRS
|
||||
src/Bimap.h
|
||||
src/BufferPoolAllocator.h
|
||||
src/ColorSpaceUtils.h
|
||||
src/compact.h
|
||||
src/Culler.h
|
||||
src/DFG.h
|
||||
src/FilamentAPI-impl.h
|
||||
@@ -188,16 +190,17 @@ set(PRIVATE_HDRS
|
||||
src/RenderPass.h
|
||||
src/RenderPrimitive.h
|
||||
src/RendererUtils.h
|
||||
src/ResourceAllocator.h
|
||||
src/ResourceList.h
|
||||
src/ShadowMap.h
|
||||
src/ShadowMapManager.h
|
||||
src/SharedHandle.h
|
||||
src/TextureCache.h
|
||||
src/UniformBuffer.h
|
||||
src/components/CameraManager.h
|
||||
src/components/LightManager.h
|
||||
src/components/RenderableManager.h
|
||||
src/components/TransformManager.h
|
||||
src/details/AsyncHelpers.h
|
||||
src/details/BufferAllocator.h
|
||||
src/details/BufferObject.h
|
||||
src/details/Camera.h
|
||||
@@ -232,6 +235,7 @@ set(PRIVATE_HDRS
|
||||
src/ds/TypedUniformBuffer.h
|
||||
src/fg/Blackboard.h
|
||||
src/fg/FrameGraph.h
|
||||
src/fg/FrameGraphDummyLink.h
|
||||
src/fg/FrameGraphId.h
|
||||
src/fg/FrameGraphPass.h
|
||||
src/fg/FrameGraphRenderPass.h
|
||||
@@ -241,6 +245,7 @@ set(PRIVATE_HDRS
|
||||
src/fg/details/DependencyGraph.h
|
||||
src/fg/details/PassNode.h
|
||||
src/fg/details/Resource.h
|
||||
src/fg/details/ResourceAllocator.h
|
||||
src/fg/details/ResourceNode.h
|
||||
src/fg/details/Utilities.h
|
||||
src/fsr.h
|
||||
|
||||
@@ -28,6 +28,7 @@ set(SRCS
|
||||
src/BackendUtils.cpp
|
||||
src/BlobCacheKey.cpp
|
||||
src/Callable.cpp
|
||||
src/CallbackHandler.cpp
|
||||
src/CallbackManager.cpp
|
||||
src/CircularBuffer.cpp
|
||||
src/CommandBufferQueue.cpp
|
||||
|
||||
@@ -64,7 +64,7 @@ public:
|
||||
virtual void post(void* user, Callback callback) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~CallbackHandler() = default;
|
||||
virtual ~CallbackHandler();
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -499,7 +499,7 @@ using descriptor_set_t = uint8_t;
|
||||
|
||||
using descriptor_binding_t = uint8_t;
|
||||
|
||||
struct DescriptorSetLayoutBinding {
|
||||
struct DescriptorSetLayoutDescriptor {
|
||||
static bool isSampler(DescriptorType type) noexcept {
|
||||
return int(type) <= int(DescriptorType::SAMPLER_EXTERNAL);
|
||||
}
|
||||
@@ -513,8 +513,8 @@ struct DescriptorSetLayoutBinding {
|
||||
DescriptorFlags flags = DescriptorFlags::NONE;
|
||||
uint16_t count = 0;
|
||||
|
||||
friend bool operator==(DescriptorSetLayoutBinding const& lhs,
|
||||
DescriptorSetLayoutBinding const& rhs) noexcept {
|
||||
friend bool operator==(DescriptorSetLayoutDescriptor const& lhs,
|
||||
DescriptorSetLayoutDescriptor const& rhs) noexcept {
|
||||
return lhs.type == rhs.type &&
|
||||
lhs.flags == rhs.flags &&
|
||||
lhs.count == rhs.count &&
|
||||
@@ -1385,7 +1385,7 @@ static_assert(sizeof(SamplerParams) <= sizeof(uint64_t),
|
||||
|
||||
struct DescriptorSetLayout {
|
||||
std::variant<utils::StaticString, utils::CString, std::monostate> label;
|
||||
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
|
||||
utils::FixedCapacityVector<DescriptorSetLayoutDescriptor> descriptors;
|
||||
};
|
||||
|
||||
//! blending equation function
|
||||
@@ -1715,6 +1715,12 @@ using CompositorTiming = Platform::CompositorTiming;
|
||||
|
||||
using AsynchronousMode = Platform::AsynchronousMode;
|
||||
|
||||
using AsyncCallId = uint32_t;
|
||||
|
||||
static constexpr AsyncCallId InvalidAsyncCallId = std::numeric_limits<AsyncCallId>::max();
|
||||
|
||||
using AsynchronousMode = Platform::AsynchronousMode;
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
template<> struct utils::EnableBitMaskOperators<filament::backend::ShaderStageFlags>
|
||||
|
||||
@@ -288,7 +288,8 @@ public:
|
||||
|
||||
/**
|
||||
* Set to `true` to forcibly disable parallel shader compilation in the backend.
|
||||
* Currently only honored by the GL and Metal backends.
|
||||
* Currently only honored by the GL and Metal backends, and the Vulkan backend
|
||||
* when some experimental features are enabled.
|
||||
*/
|
||||
bool disableParallelShaderCompile = false;
|
||||
|
||||
@@ -319,6 +320,12 @@ public:
|
||||
*/
|
||||
StereoscopicType stereoscopicType = StereoscopicType::NONE;
|
||||
|
||||
/*
|
||||
* The number of eyes to render when stereoscopic rendering is enabled. Supported values are
|
||||
* between 1 and Engine::getMaxStereoscopicEyes() (inclusive).
|
||||
*/
|
||||
uint8_t stereoscopicEyeCount = 2;
|
||||
|
||||
/**
|
||||
* Assert the native window associated to a SwapChain is valid when calling makeCurrent().
|
||||
* This is only supported for:
|
||||
@@ -340,6 +347,17 @@ public:
|
||||
*/
|
||||
GpuContextPriority gpuContextPriority = GpuContextPriority::DEFAULT;
|
||||
|
||||
/**
|
||||
* Enables asynchronous pipeline cache preloading, if supported on this device.
|
||||
* This is only supported for:
|
||||
* - VulkanPlatform
|
||||
* When the following device extensions are available:
|
||||
* - VK_KHR_dynamic_rendering
|
||||
* - VK_EXT_vertex_input_dynamic_state
|
||||
* Should be enabled only for devices where it has been shown this is effective.
|
||||
*/
|
||||
bool vulkanEnableAsyncPipelineCachePrewarming = false;
|
||||
|
||||
/**
|
||||
* Bypass the staging buffer because the device is of Unified Memory Architecture.
|
||||
* This is only supported for:
|
||||
|
||||
@@ -50,7 +50,13 @@ public:
|
||||
descriptor_binding_t binding;
|
||||
};
|
||||
|
||||
struct DescriptorSetLayoutBinding {
|
||||
descriptor_set_t set;
|
||||
DescriptorSetLayout layout;
|
||||
};
|
||||
|
||||
using SpecializationConstant = std::variant<int32_t, float, bool>;
|
||||
using DescriptorSetLayoutArray = utils::FixedCapacityVector<DescriptorSetLayoutBinding>;
|
||||
|
||||
struct Uniform { // For ES2 support
|
||||
utils::CString name; // full qualified name of the uniform field
|
||||
@@ -147,6 +153,19 @@ public:
|
||||
return mDescriptorBindings;
|
||||
}
|
||||
|
||||
inline Program& descriptorLayout(backend::descriptor_set_t set,
|
||||
DescriptorSetLayout descriptorLayout) noexcept {
|
||||
mDescriptorLayouts.push_back({
|
||||
.set = set,
|
||||
.layout = std::move(descriptorLayout),
|
||||
});
|
||||
return *this;
|
||||
}
|
||||
|
||||
const DescriptorSetLayoutArray& getDescriptorSetLayouts() const noexcept {
|
||||
return mDescriptorLayouts;
|
||||
}
|
||||
|
||||
utils::FixedCapacityVector<PushConstant> const& getPushConstants(
|
||||
ShaderStage stage) const noexcept {
|
||||
return mPushConstants[static_cast<uint8_t>(stage)];
|
||||
@@ -176,6 +195,11 @@ private:
|
||||
std::array<utils::FixedCapacityVector<PushConstant>, SHADER_TYPE_COUNT> mPushConstants;
|
||||
DescriptorSetInfo mDescriptorBindings;
|
||||
|
||||
// Descriptions for descriptor set layouts that may be used for this Program, which
|
||||
// can be useful for attempting to compile the pipeline ahead of time.
|
||||
DescriptorSetLayoutArray mDescriptorLayouts =
|
||||
DescriptorSetLayoutArray::with_capacity(MAX_DESCRIPTOR_SET_COUNT);
|
||||
|
||||
// For ES2 support only
|
||||
AttributesInfo mAttributes;
|
||||
BindingUniformsInfo mBindingUniformsInfo;
|
||||
|
||||
@@ -64,7 +64,7 @@ protected:
|
||||
std::vector<int> mAttribs;
|
||||
|
||||
// For shared contexts
|
||||
static constexpr int SHARED_CONTEXT_NUM = 1;
|
||||
static constexpr int SHARED_CONTEXT_NUM = 2;
|
||||
std::vector<HGLRC> mAdditionalContexts;
|
||||
std::atomic<int> mNextFreeSharedContextIndex{0};
|
||||
};
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <utils/Hash.h>
|
||||
#include <utils/PrivateImplementation.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
@@ -61,8 +62,15 @@ public:
|
||||
return std::hash<std::string>{}(s.data());
|
||||
}
|
||||
};
|
||||
// Note: utils::CString::operator== has an edge case that breaks for the extension set.
|
||||
// Instead, we'll provide our own comparator.
|
||||
struct ExtensionEqualFn {
|
||||
bool operator()(utils::CString const& a, utils::CString const& b) const noexcept {
|
||||
return strcmp(a.c_str(), b.c_str()) == 0;
|
||||
}
|
||||
};
|
||||
// Utility for managing device or instance extensions during initialization.
|
||||
using ExtensionSet = std::unordered_set<utils::CString, ExtensionHashFn>;
|
||||
using ExtensionSet = std::unordered_set<utils::CString, ExtensionHashFn, ExtensionEqualFn>;
|
||||
|
||||
/**
|
||||
* A collection of handles to objects and metadata that comprises a Vulkan context. The client
|
||||
@@ -437,6 +445,50 @@ protected:
|
||||
std::shared_ptr<VulkanCmdFence> fenceStatus;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates the VkInstance used by Filament's Vulkan backend.
|
||||
*
|
||||
* This method can be overridden in subclasses to customize VkInstance creation, such as
|
||||
* adding application-specific layers or extensions.
|
||||
*
|
||||
* The provided `createInfo` contains layers and extensions required by Filament.
|
||||
* If you override this method and need to modify the `createInfo` struct, you must first
|
||||
* make a copy of it and modify the copy.
|
||||
*
|
||||
* @param createInfo The VkInstanceCreateInfo prepared by Filament.
|
||||
* @return The created VkInstance, or VK_NULL_HANDLE on failure.
|
||||
*/
|
||||
virtual VkInstance createVkInstance(VkInstanceCreateInfo const& createInfo) noexcept;
|
||||
|
||||
/**
|
||||
* Selects a VkPhysicalDevice (GPU) for Filament's Vulkan backend to use.
|
||||
*
|
||||
* This method can be overridden in subclasses to implement custom GPU selection logic.
|
||||
* For example, an application might override this to prefer a discrete GPU over an
|
||||
* integrated one based on device properties.
|
||||
*
|
||||
* The default implementation selects the first device that meets Filament's requirements.
|
||||
*
|
||||
* @param instance The VkInstance to enumerate devices from.
|
||||
* @return The selected VkPhysicalDevice, or VK_NULL_HANDLE if no suitable device is found.
|
||||
*/
|
||||
virtual VkPhysicalDevice selectVkPhysicalDevice(VkInstance instance) noexcept;
|
||||
|
||||
/**
|
||||
* Creates the VkDevice used by Filament's Vulkan backend.
|
||||
*
|
||||
* This method can be overridden in subclasses to customize VkDevice creation, such as
|
||||
* adding application-specific extensions or enabling features.
|
||||
*
|
||||
* The provided `createInfo` contains extensions and features required by Filament.
|
||||
* If you override this method and need to modify the `createInfo` struct, you must first
|
||||
* make a copy of it and modify the copy.
|
||||
*
|
||||
* @param createInfo The VkDeviceCreateInfo prepared by Filament.
|
||||
* @return The created VkDevice, or VK_NULL_HANDLE on failure.
|
||||
*/
|
||||
virtual VkDevice createVkDevice(VkDeviceCreateInfo const& createInfo) noexcept;
|
||||
|
||||
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
|
||||
virtual ExtensionSet getSwapchainInstanceExtensions() const = 0;
|
||||
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
|
||||
@@ -450,6 +502,36 @@ protected:
|
||||
bool isTransientAttachmentSupported() const noexcept;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Contains information about features that should be requested
|
||||
* when calling vkCreateDevice, based on feature support from
|
||||
* vkGetPhysicalDeviceFeatures2.
|
||||
*/
|
||||
struct MiscDeviceFeatures {
|
||||
/**
|
||||
* This allows creation of a VkGraphicsPipeline without a
|
||||
* render pass specified.
|
||||
*/
|
||||
bool dynamicRendering;
|
||||
|
||||
/**
|
||||
* Allows creation of a 2d image view, or 2d image view array,
|
||||
* to be created from a 3d VkImage.
|
||||
*/
|
||||
bool imageView2Don3DImage;
|
||||
};
|
||||
|
||||
void createInstance(ExtensionSet const& requiredExts) noexcept;
|
||||
|
||||
void queryAndSetDeviceFeatures(Platform::DriverConfig const& driverConfig,
|
||||
ExtensionSet const& instExts, ExtensionSet const& deviceExts,
|
||||
void* sharedContext) noexcept;
|
||||
|
||||
void createLogicalDeviceAndQueues(ExtensionSet const& deviceExtensions,
|
||||
VkPhysicalDeviceFeatures const& features,
|
||||
VkPhysicalDeviceVulkan11Features const& vk11Features, bool createProtectedQueue,
|
||||
MiscDeviceFeatures const& requestedFeatures) noexcept;
|
||||
|
||||
friend struct VulkanPlatformPrivate;
|
||||
};
|
||||
|
||||
|
||||
@@ -70,6 +70,11 @@ public:
|
||||
// is where the driver can execute user callbacks.
|
||||
virtual void purge() noexcept = 0;
|
||||
|
||||
// Called from the engine thread (render-thread) to execute the `callback` via the `handler` if
|
||||
// it is available. Otherwise, if `handler` is null, the `callback` is executed on the main
|
||||
// thread via `purge()`.
|
||||
virtual void scheduleCallback(CallbackHandler* handler, void* user, CallbackHandler::Callback callback) = 0;
|
||||
|
||||
virtual ShaderModel getShaderModel() const noexcept = 0;
|
||||
|
||||
// The shader languages used for shaders for this driver in order of preference, used to inform
|
||||
|
||||
@@ -74,8 +74,9 @@
|
||||
#define APPLY10(M, A, ...) EXPAND(M(A)), EXPAND(APPLY9(M, __VA_ARGS__))
|
||||
#define APPLY11(M, A, ...) EXPAND(M(A)), EXPAND(APPLY10(M, __VA_ARGS__))
|
||||
#define APPLY12(M, A, ...) EXPAND(M(A)), EXPAND(APPLY11(M, __VA_ARGS__))
|
||||
#define APPLY_N__(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, X, ...) APPLY##X
|
||||
#define APPLY(M, ...) EXPAND(EXPAND(APPLY_N__(M, __VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))(M, __VA_ARGS__))
|
||||
#define APPLY13(M, A, ...) EXPAND(M(A)), EXPAND(APPLY12(M, __VA_ARGS__))
|
||||
#define APPLY_N__(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, X, ...) APPLY##X
|
||||
#define APPLY(M, ...) EXPAND(EXPAND(APPLY_N__(M, __VA_ARGS__, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))(M, __VA_ARGS__))
|
||||
|
||||
|
||||
#define PAIR_ARGS_0(M, ...)
|
||||
@@ -91,11 +92,12 @@
|
||||
#define PAIR_ARGS_10(M, X, Y, ...) M(X, Y), EXPAND(PAIR_ARGS_9(M, __VA_ARGS__))
|
||||
#define PAIR_ARGS_11(M, X, Y, ...) M(X, Y), EXPAND(PAIR_ARGS_10(M, __VA_ARGS__))
|
||||
#define PAIR_ARGS_12(M, X, Y, ...) M(X, Y), EXPAND(PAIR_ARGS_11(M, __VA_ARGS__))
|
||||
#define PAIR_ARGS_13(M, X, Y, ...) M(X, Y), EXPAND(PAIR_ARGS_12(M, __VA_ARGS__))
|
||||
|
||||
#define PAIR_ARGS_N__(_0, E1, _1, E2, _2, E3, _3, E4, _4, E5, _5, E6, _6, E7, _7, E8, _8, E9, _9, E10, _10, E11, _11, E12, _12, X, ...) PAIR_ARGS_##X
|
||||
#define PAIR_ARGS_N__(_0, E1, _1, E2, _2, E3, _3, E4, _4, E5, _5, E6, _6, E7, _7, E8, _8, E9, _9, E10, _10, E11, _11, E12, _12, E13, _13, X, ...) PAIR_ARGS_##X
|
||||
|
||||
#define PAIR_ARGS_N(M, ...) \
|
||||
EXPAND(EXPAND(PAIR_ARGS_N__(0, ##__VA_ARGS__, 12, E, 11, E, 10, E, 9, E, 8, E, 7, E, 6, E, 5, E, 4, E, 3, E, 2, E, 1, E, 0))(M, __VA_ARGS__))
|
||||
EXPAND(EXPAND(PAIR_ARGS_N__(0, ##__VA_ARGS__, 13, E, 12, E, 11, E, 10, E, 9, E, 8, E, 7, E, 6, E, 5, E, 4, E, 3, E, 2, E, 1, E, 0))(M, __VA_ARGS__))
|
||||
|
||||
#define ARG(T, P) T P
|
||||
|
||||
@@ -192,11 +194,27 @@ DECL_DRIVER_API_TAGGED_R_N(backend::IndexBufferHandle, createIndexBuffer,
|
||||
uint32_t, indexCount,
|
||||
backend::BufferUsage, usage)
|
||||
|
||||
DECL_DRIVER_API_TAGGED_R_N(backend::IndexBufferHandle, createIndexBufferAsync,
|
||||
backend::ElementType, elementType,
|
||||
uint32_t, indexCount,
|
||||
backend::BufferUsage, usage,
|
||||
backend::CallbackHandler*, handler,
|
||||
backend::CallbackHandler::Callback, callback,
|
||||
void*, user)
|
||||
|
||||
DECL_DRIVER_API_TAGGED_R_N(backend::BufferObjectHandle, createBufferObject,
|
||||
uint32_t, byteCount,
|
||||
backend::BufferObjectBinding, bindingType,
|
||||
backend::BufferUsage, usage)
|
||||
|
||||
DECL_DRIVER_API_TAGGED_R_N(backend::BufferObjectHandle, createBufferObjectAsync,
|
||||
uint32_t, byteCount,
|
||||
backend::BufferObjectBinding, bindingType,
|
||||
backend::BufferUsage, usage,
|
||||
backend::CallbackHandler*, handler,
|
||||
backend::CallbackHandler::Callback, callback,
|
||||
void*, user)
|
||||
|
||||
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTexture,
|
||||
backend::SamplerType, target,
|
||||
uint8_t, levels,
|
||||
@@ -207,6 +225,19 @@ DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTexture,
|
||||
uint32_t, depth,
|
||||
backend::TextureUsage, usage)
|
||||
|
||||
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTextureAsync,
|
||||
backend::SamplerType, target,
|
||||
uint8_t, levels,
|
||||
backend::TextureFormat, format,
|
||||
uint8_t, samples,
|
||||
uint32_t, width,
|
||||
uint32_t, height,
|
||||
uint32_t, depth,
|
||||
backend::TextureUsage, usage,
|
||||
backend::CallbackHandler*, handler,
|
||||
backend::CallbackHandler::Callback, callback,
|
||||
void*, user)
|
||||
|
||||
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTextureView,
|
||||
backend::TextureHandle, texture,
|
||||
uint8_t, baseLevel,
|
||||
@@ -219,6 +250,16 @@ DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTextureViewSwizzle,
|
||||
backend::TextureSwizzle, b,
|
||||
backend::TextureSwizzle, a)
|
||||
|
||||
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTextureViewSwizzleAsync,
|
||||
backend::TextureHandle, texture,
|
||||
backend::TextureSwizzle, r,
|
||||
backend::TextureSwizzle, g,
|
||||
backend::TextureSwizzle, b,
|
||||
backend::TextureSwizzle, a,
|
||||
backend::CallbackHandler*, handler,
|
||||
backend::CallbackHandler::Callback, callback,
|
||||
void*, user)
|
||||
|
||||
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTextureExternalImage2,
|
||||
backend::SamplerType, target,
|
||||
backend::TextureFormat, format,
|
||||
@@ -254,6 +295,20 @@ DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, importTexture,
|
||||
uint32_t, depth,
|
||||
backend::TextureUsage, usage)
|
||||
|
||||
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, importTextureAsync,
|
||||
intptr_t, id,
|
||||
backend::SamplerType, target,
|
||||
uint8_t, levels,
|
||||
backend::TextureFormat, format,
|
||||
uint8_t, samples,
|
||||
uint32_t, width,
|
||||
uint32_t, height,
|
||||
uint32_t, depth,
|
||||
backend::TextureUsage, usage,
|
||||
backend::CallbackHandler*, handler,
|
||||
backend::CallbackHandler::Callback, callback,
|
||||
void*, user)
|
||||
|
||||
DECL_DRIVER_API_TAGGED_R_N(backend::RenderPrimitiveHandle, createRenderPrimitive,
|
||||
backend::VertexBufferHandle, vbh,
|
||||
backend::IndexBufferHandle, ibh,
|
||||
@@ -357,6 +412,7 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthStencilResolveSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isDepthStencilBlitSupported, backend::TextureFormat, format)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedTexturesSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthClampSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isAsynchronousModeEnabled)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(size_t, getMaxTextureSize, backend::SamplerType, target)
|
||||
@@ -382,16 +438,40 @@ DECL_DRIVER_API_N(setVertexBufferObject,
|
||||
uint32_t, index,
|
||||
backend::BufferObjectHandle, bufferObject)
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::AsyncCallId, setVertexBufferObjectAsync,
|
||||
backend::VertexBufferHandle, vbh,
|
||||
uint32_t, index,
|
||||
backend::BufferObjectHandle, bufferObject,
|
||||
backend::CallbackHandler*, handler,
|
||||
backend::CallbackHandler::Callback, callback,
|
||||
void*, user)
|
||||
|
||||
DECL_DRIVER_API_N(updateIndexBuffer,
|
||||
backend::IndexBufferHandle, ibh,
|
||||
backend::BufferDescriptor&&, data,
|
||||
uint32_t, byteOffset)
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::AsyncCallId, updateIndexBufferAsync,
|
||||
backend::IndexBufferHandle, ibh,
|
||||
backend::BufferDescriptor&&, data,
|
||||
uint32_t, byteOffset,
|
||||
backend::CallbackHandler*, handler,
|
||||
backend::CallbackHandler::Callback, callback,
|
||||
void*, user)
|
||||
|
||||
DECL_DRIVER_API_N(updateBufferObject,
|
||||
backend::BufferObjectHandle, ibh,
|
||||
backend::BufferDescriptor&&, data,
|
||||
uint32_t, byteOffset)
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::AsyncCallId, updateBufferObjectAsync,
|
||||
backend::BufferObjectHandle, ibh,
|
||||
backend::BufferDescriptor&&, data,
|
||||
uint32_t, byteOffset,
|
||||
backend::CallbackHandler*, handler,
|
||||
backend::CallbackHandler::Callback, callback,
|
||||
void*, user)
|
||||
|
||||
DECL_DRIVER_API_N(updateBufferObjectUnsynchronized,
|
||||
backend::BufferObjectHandle, boh,
|
||||
backend::BufferDescriptor&&, data,
|
||||
@@ -411,6 +491,20 @@ DECL_DRIVER_API_N(update3DImage,
|
||||
uint32_t, depth,
|
||||
backend::PixelBufferDescriptor&&, data)
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::AsyncCallId, update3DImageAsync,
|
||||
backend::TextureHandle, th,
|
||||
uint32_t, level,
|
||||
uint32_t, xoffset,
|
||||
uint32_t, yoffset,
|
||||
uint32_t, zoffset,
|
||||
uint32_t, width,
|
||||
uint32_t, height,
|
||||
uint32_t, depth,
|
||||
backend::PixelBufferDescriptor&&, data,
|
||||
backend::CallbackHandler*, handler,
|
||||
backend::CallbackHandler::Callback, callback,
|
||||
void*, user)
|
||||
|
||||
DECL_DRIVER_API_N(generateMipmaps,
|
||||
backend::TextureHandle, th)
|
||||
|
||||
@@ -577,6 +671,19 @@ DECL_DRIVER_API_N(dispatchCompute,
|
||||
DECL_DRIVER_API_N(scissor,
|
||||
Viewport, scissor)
|
||||
|
||||
/*
|
||||
* Asyncronous operation helpers
|
||||
* --------------------
|
||||
*/
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::AsyncCallId, queueCommandAsync,
|
||||
utils::Invocable<void(void)>&&, command,
|
||||
backend::CallbackHandler*, handler,
|
||||
backend::CallbackHandler::Callback, callback,
|
||||
void*, user)
|
||||
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, cancelAsyncJob, backend::AsyncCallId, jobId)
|
||||
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (C) 2025 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 <backend/CallbackHandler.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
// Define the virtual destructor here to avoid a compiler warning that we treat as an error.
|
||||
// "'CallbackHandler' has no out-of-line virtual method definitions; its vtable will be emitted in every translation unit"
|
||||
// The warning occurs when a class inherits from CallbackHandler.
|
||||
CallbackHandler::~CallbackHandler() = default;
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -19,13 +19,17 @@
|
||||
|
||||
#include <backend/PixelBufferDescriptor.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <math/scalar.h>
|
||||
#include <math/half.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
@@ -76,7 +80,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Converts a n-channel image of UBYTE, INT, UINT, or FLOAT to a different type.
|
||||
// Converts a n-channel image of UBYTE, INT, UINT, HALF, or FLOAT to a different type.
|
||||
template<typename dstComponentType, typename srcComponentType>
|
||||
static void reshapeImage(uint8_t* UTILS_RESTRICT dest, const uint8_t* UTILS_RESTRICT src,
|
||||
size_t srcBytesPerRow,
|
||||
@@ -91,6 +95,8 @@ public:
|
||||
UTILS_ASSUME(minChannelCount <= 4);
|
||||
dest += (dstRowOffset * dstBytesPerRow);
|
||||
const int inds[4] = { swizzle ? 2 : 0, 1, swizzle ? 0 : 2, 3 };
|
||||
|
||||
utils::slog.e <<"dstMaxValue=" << +dstMaxValue << utils::io::endl;
|
||||
for (size_t row = 0; row < height; ++row) {
|
||||
const srcComponentType* in = (const srcComponentType*) src;
|
||||
dstComponentType* out = (dstComponentType*)dest + (dstColumnOffset * dstChannelCount);
|
||||
@@ -119,6 +125,7 @@ public:
|
||||
static bool reshapeImage(PixelBufferDescriptor* UTILS_RESTRICT dst, PixelDataType srcType,
|
||||
uint32_t srcChannelCount, const uint8_t* UTILS_RESTRICT srcBytes, int srcBytesPerRow,
|
||||
int width, int height, bool swizzle) {
|
||||
using namespace utils;
|
||||
size_t dstChannelCount;
|
||||
switch (dst->format) {
|
||||
case PixelDataFormat::R_INTEGER: dstChannelCount = 1; break;
|
||||
@@ -129,7 +136,9 @@ public:
|
||||
case PixelDataFormat::RG: dstChannelCount = 2; break;
|
||||
case PixelDataFormat::RGB: dstChannelCount = 3; break;
|
||||
case PixelDataFormat::RGBA: dstChannelCount = 4; break;
|
||||
default: return false;
|
||||
default:
|
||||
slog.e << "DataReshaper: unsupported dst->format: " << (int)dst->format << io::endl;
|
||||
return false;
|
||||
}
|
||||
void (*reshaper)(uint8_t* dest, const uint8_t* src, size_t srcBytesPerRow,
|
||||
size_t srcChannelCount,
|
||||
@@ -155,7 +164,10 @@ public:
|
||||
case FLOAT: reshaper = reshapeImage<uint8_t, float>; break;
|
||||
case INT: reshaper = reshapeImage<uint8_t, int32_t>; break;
|
||||
case UINT: reshaper = reshapeImage<uint8_t, uint32_t>; break;
|
||||
default: return false;
|
||||
case HALF: reshaper = reshapeImage<uint8_t, math::half>; break;
|
||||
default:
|
||||
slog.e << "DataReshaper: UBYTE dst, unsupported srcType: " << (int)srcType << io::endl;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case FLOAT:
|
||||
@@ -164,7 +176,9 @@ public:
|
||||
case FLOAT: reshaper = reshapeImage<float, float>; break;
|
||||
case INT: reshaper = reshapeImage<float, int32_t>; break;
|
||||
case UINT: reshaper = reshapeImage<float, uint32_t>; break;
|
||||
default: return false;
|
||||
default:
|
||||
slog.e << "DataReshaper: FLOAT dst, unsupported srcType: " << (int)srcType << io::endl;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case INT:
|
||||
@@ -173,7 +187,9 @@ public:
|
||||
case FLOAT: reshaper = reshapeImage<int32_t, float>; break;
|
||||
case INT: reshaper = reshapeImage<int32_t, int32_t>; break;
|
||||
case UINT: reshaper = reshapeImage<int32_t, uint32_t>; break;
|
||||
default: return false;
|
||||
default:
|
||||
slog.e << "DataReshaper: INT dst, unsupported srcType: " << (int)srcType << io::endl;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case UINT:
|
||||
@@ -182,16 +198,21 @@ public:
|
||||
case FLOAT: reshaper = reshapeImage<uint32_t, float>; break;
|
||||
case INT: reshaper = reshapeImage<uint32_t, int32_t>; break;
|
||||
case UINT: reshaper = reshapeImage<uint32_t, uint32_t>; break;
|
||||
default: return false;
|
||||
default:
|
||||
slog.e << "DataReshaper: UINT dst, unsupported srcType: " << (int)srcType << io::endl;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case HALF:
|
||||
switch (srcType) {
|
||||
case HALF: reshaper = copyImage; break;
|
||||
default: return false;
|
||||
default:
|
||||
slog.e << "DataReshaper: HALF dst, unsupported srcType: " << (int)srcType << io::endl;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
slog.e << "DataReshaper: unsupported dst->type: " << (int)dst->type << io::endl;
|
||||
return false;
|
||||
}
|
||||
uint8_t* dstBytes = (uint8_t*) dst->buffer;
|
||||
@@ -200,6 +221,7 @@ public:
|
||||
reshaper(dstBytes, srcBytes, srcBytesPerRow, srcChannelCount,
|
||||
dst->top, dst->left, dstBytesPerRow,
|
||||
dstChannelCount, width, height, swizzle);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -209,6 +231,7 @@ template<> inline int32_t getMaxValue() { return 0x7fffffff; }
|
||||
template<> inline uint32_t getMaxValue() { return 0xffffffff; }
|
||||
template<> inline uint16_t getMaxValue() { return 0x3c00; } // 0x3c00 is 1.0 in half-float.
|
||||
template<> inline uint8_t getMaxValue() { return 0xff; }
|
||||
template<> inline math::half getMaxValue() { return math::half(1.0f); }
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
|
||||
@@ -28,7 +28,9 @@
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/JobSystem.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <math/half.h>
|
||||
#include <math/vec2.h>
|
||||
@@ -47,10 +49,12 @@ using namespace filament::math;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
DriverBase::DriverBase() noexcept {
|
||||
DriverBase::DriverBase(const Platform::DriverConfig& driverConfig) noexcept
|
||||
: mDriverConfig(driverConfig) {
|
||||
if constexpr (UTILS_HAS_THREADING) {
|
||||
// This thread services user callbacks
|
||||
mServiceThread = std::thread([this]() {
|
||||
JobSystem::setThreadName("ServiceThread");
|
||||
do {
|
||||
auto& serviceThreadCondition = mServiceThreadCondition;
|
||||
auto& serviceThreadCallbackQueue = mServiceThreadCallbackQueue;
|
||||
|
||||
@@ -72,22 +72,24 @@ struct HwVertexBuffer : public HwBase {
|
||||
};
|
||||
|
||||
struct HwBufferObject : public HwBase {
|
||||
uint32_t byteCount{};
|
||||
uint32_t byteCount : 31;
|
||||
uint32_t asynchronous : 1;
|
||||
|
||||
HwBufferObject() noexcept = default;
|
||||
explicit HwBufferObject(uint32_t byteCount) noexcept : byteCount(byteCount) {}
|
||||
explicit HwBufferObject(uint32_t byteCount, bool async) noexcept : byteCount(byteCount), asynchronous(async) {}
|
||||
};
|
||||
|
||||
struct HwMemoryMappedBuffer : public HwBase {
|
||||
};
|
||||
|
||||
struct HwIndexBuffer : public HwBase {
|
||||
uint32_t count : 27;
|
||||
uint32_t count : 26; // 67M indices
|
||||
uint32_t elementSize : 5;
|
||||
uint32_t asynchronous : 1;
|
||||
|
||||
HwIndexBuffer() noexcept : count{}, elementSize{} { }
|
||||
HwIndexBuffer(uint8_t elementSize, uint32_t indexCount) noexcept :
|
||||
count(indexCount), elementSize(elementSize) {
|
||||
HwIndexBuffer(uint8_t elementSize, uint32_t indexCount, bool async) noexcept :
|
||||
count(indexCount), elementSize(elementSize), asynchronous(async) {
|
||||
// we could almost store elementSize on 4 bits because it's never > 16 and never 0
|
||||
assert_invariant(elementSize > 0 && elementSize <= 16);
|
||||
assert_invariant(indexCount < (1u << 27));
|
||||
@@ -120,16 +122,19 @@ struct HwTexture : public HwBase {
|
||||
uint8_t levels : 4; // This allows up to 15 levels (max texture size of 32768 x 32768)
|
||||
uint8_t samples : 4; // Sample count per pixel (should always be a power of 2)
|
||||
TextureFormat format{};
|
||||
uint8_t reserved0 = 0;
|
||||
struct {
|
||||
uint8_t asynchronous : 1;
|
||||
uint8_t reserved : 7;
|
||||
};
|
||||
TextureUsage usage{};
|
||||
uint16_t reserved1 = 0;
|
||||
HwStream* hwStream = nullptr;
|
||||
|
||||
HwTexture() noexcept : levels{}, samples{} {}
|
||||
HwTexture(backend::SamplerType target, uint8_t levels, uint8_t samples,
|
||||
uint32_t width, uint32_t height, uint32_t depth, TextureFormat fmt, TextureUsage usage) noexcept
|
||||
: width(width), height(height), depth(depth),
|
||||
target(target), levels(levels), samples(samples), format(fmt), usage(usage) { }
|
||||
HwTexture(backend::SamplerType target, uint8_t levels, uint8_t samples, uint32_t width,
|
||||
uint32_t height, uint32_t depth, TextureFormat fmt, TextureUsage usage, bool async) noexcept
|
||||
: width(width), height(height), depth(depth), target(target), levels(levels),
|
||||
samples(samples), format(fmt), asynchronous(async), usage(usage) {}
|
||||
};
|
||||
|
||||
struct HwRenderTarget : public HwBase {
|
||||
@@ -172,7 +177,7 @@ struct HwTimerQuery : public HwBase {
|
||||
|
||||
class DriverBase : public Driver {
|
||||
public:
|
||||
DriverBase() noexcept;
|
||||
explicit DriverBase(const Platform::DriverConfig& driverConfig) noexcept;
|
||||
~DriverBase() noexcept override;
|
||||
|
||||
void purge() noexcept final;
|
||||
@@ -205,7 +210,7 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
void scheduleCallback(CallbackHandler* handler, void* user, CallbackHandler::Callback callback);
|
||||
void scheduleCallback(CallbackHandler* handler, void* user, CallbackHandler::Callback callback) final;
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Privates
|
||||
@@ -214,6 +219,10 @@ public:
|
||||
protected:
|
||||
class CallbackDataDetails;
|
||||
|
||||
Platform::DriverConfig const& getDriverConfig() const noexcept {
|
||||
return mDriverConfig;
|
||||
}
|
||||
|
||||
void scheduleDestroy(BufferDescriptor&& buffer) {
|
||||
if (buffer.hasCallback()) {
|
||||
scheduleDestroySlow(std::move(buffer));
|
||||
@@ -228,6 +237,8 @@ protected:
|
||||
void debugCommandEnd(CommandStream* cmds, bool synchronous, const char* methodName) noexcept override;
|
||||
|
||||
private:
|
||||
const Platform::DriverConfig mDriverConfig;
|
||||
|
||||
std::mutex mPurgeLock;
|
||||
std::vector<std::pair<void*, CallbackHandler::Callback>> mCallbacks;
|
||||
|
||||
|
||||
@@ -194,7 +194,7 @@ void Platform::debugUpdateStat(const char* key, uint64_t intValue) {
|
||||
callback = mDebugUpdateStat;
|
||||
}
|
||||
if (callback) {
|
||||
(*callback)(key, intValue, "");
|
||||
(*callback)(key, intValue, utils::CString(""));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -161,6 +161,7 @@ private:
|
||||
const std::function<void(const BufferState&, MetalBuffer*, uint32_t)>& f);
|
||||
|
||||
backend::StereoscopicType const mStereoscopicType;
|
||||
backend::AsynchronousMode const mAsynchronousMode;
|
||||
};
|
||||
|
||||
} // namespace backend
|
||||
|
||||
@@ -111,14 +111,16 @@ Dispatcher MetalDriver::getDispatcher() const noexcept {
|
||||
|
||||
MetalDriver::MetalDriver(
|
||||
PlatformMetal* platform, const Platform::DriverConfig& driverConfig) noexcept
|
||||
: mPlatform(*platform),
|
||||
: DriverBase(driverConfig),
|
||||
mPlatform(*platform),
|
||||
mContext(new MetalContext),
|
||||
mHandleAllocator(
|
||||
"Handles",
|
||||
driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck,
|
||||
driverConfig.disableHeapHandleTags),
|
||||
mStereoscopicType(driverConfig.stereoscopicType) {
|
||||
mStereoscopicType(driverConfig.stereoscopicType),
|
||||
mAsynchronousMode(driverConfig.asynchronousMode) {
|
||||
mContext->driver = this;
|
||||
|
||||
TrackedMetalBuffer::setPlatform(platform);
|
||||
@@ -461,6 +463,12 @@ void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elem
|
||||
mHandleAllocator.associateTagToHandle(ibh.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void MetalDriver::createIndexBufferAsyncR(Handle<HwIndexBuffer> ibh, ElementType elementType,
|
||||
uint32_t indexCount, BufferUsage usage, CallbackHandler* handler,
|
||||
CallbackHandler::Callback callback, void* user, utils::ImmutableCString&& tag) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage, utils::ImmutableCString&& tag) {
|
||||
auto* bufferObject =
|
||||
@@ -472,6 +480,12 @@ void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteC
|
||||
mHandleAllocator.associateTagToHandle(boh.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void MetalDriver::createBufferObjectAsyncR(Handle<HwBufferObject> boh, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage, CallbackHandler* handler,
|
||||
CallbackHandler::Callback callback, void* user, utils::ImmutableCString&& tag) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
// fixme: TextureUsage is a bitfield
|
||||
inline const char* stringify(TextureUsage usage) {
|
||||
switch (usage) {
|
||||
@@ -523,6 +537,13 @@ void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8
|
||||
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void MetalDriver::createTextureAsyncR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height,
|
||||
uint32_t depth, TextureUsage usage, CallbackHandler* handler,
|
||||
CallbackHandler::Callback callback, void* user, utils::ImmutableCString&& tag) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void MetalDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
uint8_t baseLevel, uint8_t levelCount, utils::ImmutableCString&& tag) {
|
||||
MetalTexture const* src = handle_cast<MetalTexture>(srch);
|
||||
@@ -543,6 +564,13 @@ void MetalDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTextu
|
||||
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void MetalDriver::createTextureViewSwizzleAsyncR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
|
||||
backend::TextureSwizzle a, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user, utils::ImmutableCString&& tag) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void MetalDriver::createTextureExternalImage2R(Handle<HwTexture> th,
|
||||
backend::SamplerType target,
|
||||
backend::TextureFormat format,
|
||||
@@ -602,6 +630,14 @@ void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
|
||||
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void MetalDriver::importTextureAsyncR(Handle<HwTexture> th, intptr_t i,
|
||||
SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height,
|
||||
uint32_t depth, TextureUsage usage, CallbackHandler* handler,
|
||||
CallbackHandler::Callback callback, void* user, utils::ImmutableCString&& tag) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void MetalDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
|
||||
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
|
||||
PrimitiveType pt, utils::ImmutableCString&& tag) {
|
||||
@@ -794,14 +830,26 @@ Handle<HwIndexBuffer> MetalDriver::createIndexBufferS() noexcept {
|
||||
return alloc_handle<MetalIndexBuffer>();
|
||||
}
|
||||
|
||||
Handle<HwIndexBuffer> MetalDriver::createIndexBufferAsyncS() noexcept {
|
||||
return alloc_handle<MetalIndexBuffer>();
|
||||
}
|
||||
|
||||
Handle<HwBufferObject> MetalDriver::createBufferObjectS() noexcept {
|
||||
return alloc_handle<MetalBufferObject>();
|
||||
}
|
||||
|
||||
Handle<HwBufferObject> MetalDriver::createBufferObjectAsyncS() noexcept {
|
||||
return alloc_handle<MetalBufferObject>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> MetalDriver::createTextureS() noexcept {
|
||||
return alloc_handle<MetalTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> MetalDriver::createTextureAsyncS() noexcept {
|
||||
return alloc_handle<MetalTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> MetalDriver::createTextureViewS() noexcept {
|
||||
return alloc_handle<MetalTexture>();
|
||||
}
|
||||
@@ -810,6 +858,10 @@ Handle<HwTexture> MetalDriver::createTextureViewSwizzleS() noexcept {
|
||||
return alloc_handle<MetalTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> MetalDriver::createTextureViewSwizzleAsyncS() noexcept {
|
||||
return alloc_handle<MetalTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> MetalDriver::createTextureExternalImage2S() noexcept {
|
||||
return alloc_handle<MetalTexture>();
|
||||
}
|
||||
@@ -826,6 +878,30 @@ Handle<HwTexture> MetalDriver::importTextureS() noexcept {
|
||||
return alloc_handle<MetalTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> MetalDriver::importTextureAsyncS() noexcept {
|
||||
return alloc_handle<MetalTexture>();
|
||||
}
|
||||
|
||||
AsyncCallId MetalDriver::update3DImageAsyncS() noexcept {
|
||||
// TODO: implement this.
|
||||
return InvalidAsyncCallId;
|
||||
}
|
||||
|
||||
AsyncCallId MetalDriver::setVertexBufferObjectAsyncS() noexcept {
|
||||
// TODO: implement this.
|
||||
return InvalidAsyncCallId;
|
||||
}
|
||||
|
||||
AsyncCallId MetalDriver::updateBufferObjectAsyncS() noexcept {
|
||||
// TODO: implement this.
|
||||
return InvalidAsyncCallId;
|
||||
}
|
||||
|
||||
AsyncCallId MetalDriver::updateIndexBufferAsyncS() noexcept {
|
||||
// TODO: implement this.
|
||||
return InvalidAsyncCallId;
|
||||
}
|
||||
|
||||
Handle<HwRenderPrimitive> MetalDriver::createRenderPrimitiveS() noexcept {
|
||||
return alloc_handle<MetalRenderPrimitive>();
|
||||
}
|
||||
@@ -874,6 +950,11 @@ Handle<HwDescriptorSet> MetalDriver::createDescriptorSetS() noexcept {
|
||||
return alloc_handle<MetalDescriptorSet>();
|
||||
}
|
||||
|
||||
AsyncCallId MetalDriver::queueCommandAsyncS() noexcept {
|
||||
// TODO: implement this.
|
||||
return InvalidAsyncCallId;
|
||||
}
|
||||
|
||||
void MetalDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
|
||||
if (vbih) {
|
||||
destruct_handle<MetalVertexBufferInfo>(vbih);
|
||||
@@ -1216,6 +1297,10 @@ bool MetalDriver::isDepthClampSupported() {
|
||||
return mContext->supportsDepthClamp;
|
||||
}
|
||||
|
||||
bool MetalDriver::isAsynchronousModeEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
switch (workaround) {
|
||||
case Workaround::SPLIT_EASU:
|
||||
@@ -1291,6 +1376,12 @@ void MetalDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&
|
||||
[&]() { return mHandleAllocator.getHandleTag(ibh.getId()).c_str_safe(); });
|
||||
scheduleDestroy(std::move(data));
|
||||
}
|
||||
void MetalDriver::updateIndexBufferAsyncR(AsyncCallId jobId, Handle<HwIndexBuffer> ibh,
|
||||
BufferDescriptor&& data, uint32_t byteOffset, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
|
||||
void MetalDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& data,
|
||||
uint32_t byteOffset) {
|
||||
@@ -1307,6 +1398,12 @@ void MetalDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescripto
|
||||
scheduleDestroy(std::move(data));
|
||||
}
|
||||
|
||||
void MetalDriver::updateBufferObjectAsyncR(AsyncCallId jobId, Handle<HwBufferObject> boh,
|
||||
BufferDescriptor&& data, uint32_t byteOffset, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void MetalDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> boh,
|
||||
BufferDescriptor&& data, uint32_t byteOffset) {
|
||||
auto* bo = handle_cast<MetalBufferObject>(boh);
|
||||
@@ -1332,6 +1429,12 @@ void MetalDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t ind
|
||||
vertexBuffer->buffers[index] = bufferObject->getBuffer();
|
||||
}
|
||||
|
||||
void MetalDriver::setVertexBufferObjectAsyncR(AsyncCallId jobId, Handle<HwVertexBuffer> vbh,
|
||||
uint32_t index, Handle<HwBufferObject> boh, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void MetalDriver::update3DImage(Handle<HwTexture> th, uint32_t level,
|
||||
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
|
||||
uint32_t width, uint32_t height, uint32_t depth,
|
||||
@@ -1348,6 +1451,14 @@ void MetalDriver::update3DImage(Handle<HwTexture> th, uint32_t level,
|
||||
th.getId(), level, xoffset, yoffset, zoffset, width, height, depth);
|
||||
}
|
||||
|
||||
void MetalDriver::update3DImageAsyncR(AsyncCallId jobId, Handle<HwTexture> th,
|
||||
uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
|
||||
uint32_t width, uint32_t height, uint32_t depth,
|
||||
PixelBufferDescriptor&& data, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void MetalDriver::setupExternalImage2(Platform::ExternalImageHandleRef image) {
|
||||
// FIXME: implement setupExternalImage2
|
||||
}
|
||||
@@ -2300,6 +2411,16 @@ void MetalDriver::copyToMemoryMappedBuffer(MemoryMappedBufferHandle mmbh, size_t
|
||||
mmb->copy(*this, offset, std::move(data));
|
||||
}
|
||||
|
||||
void MetalDriver::queueCommandAsyncR(AsyncCallId jobId, utils::Invocable<void()>&& command,
|
||||
CallbackHandler* handler, CallbackHandler::Callback const callback, void* user) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
bool MetalDriver::cancelAsyncJob(AsyncCallId jobId) {
|
||||
// TODO: implement this.
|
||||
return false;
|
||||
}
|
||||
|
||||
// explicit instantiation of the Dispatcher
|
||||
template class ConcreteDispatcher<MetalDriver>;
|
||||
|
||||
|
||||
@@ -510,7 +510,7 @@ class MetalDescriptorSetLayout : public HwDescriptorSetLayout {
|
||||
public:
|
||||
MetalDescriptorSetLayout(DescriptorSetLayout&& layout) noexcept;
|
||||
|
||||
const auto& getBindings() const noexcept { return mLayout.bindings; }
|
||||
const auto& getBindings() const noexcept { return mLayout.descriptors; }
|
||||
|
||||
size_t getDynamicOffsetCount() const noexcept { return mDynamicOffsetCount; }
|
||||
|
||||
|
||||
@@ -544,7 +544,7 @@ MetalAttachment MetalSwapChain::acquireBaseDrawable() {
|
||||
|
||||
MetalBufferObject::MetalBufferObject(MetalContext& context, BufferObjectBinding bindingType,
|
||||
BufferUsage usage, uint32_t byteCount)
|
||||
: HwBufferObject(byteCount), buffer(context, bindingType, usage, byteCount) {}
|
||||
: HwBufferObject(byteCount, false), buffer(context, bindingType, usage, byteCount) {}
|
||||
|
||||
void MetalBufferObject::updateBuffer(
|
||||
void* data, size_t size, uint32_t byteOffset, TagResolver&& getHandleTag) {
|
||||
@@ -635,7 +635,7 @@ MetalVertexBuffer::MetalVertexBuffer(MetalContext& context,
|
||||
}
|
||||
|
||||
MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
|
||||
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
|
||||
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount, false),
|
||||
buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { }
|
||||
|
||||
MetalProgram::MetalProgram(MetalContext& context, Program&& program) noexcept
|
||||
@@ -664,7 +664,7 @@ void MetalProgram::initialize() {
|
||||
MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth,
|
||||
TextureUsage usage) noexcept
|
||||
: HwTexture(target, levels, samples, width, height, depth, format, usage), context(context) {
|
||||
: HwTexture(target, levels, samples, width, height, depth, format, usage, false), context(context) {
|
||||
assert_invariant(target != SamplerType::SAMPLER_EXTERNAL);
|
||||
|
||||
devicePixelFormat = decidePixelFormat(&context, format);
|
||||
@@ -756,7 +756,7 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
|
||||
MetalTexture::MetalTexture(MetalContext& context, MetalTexture const* src, uint8_t baseLevel,
|
||||
uint8_t levelCount) noexcept
|
||||
: HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth,
|
||||
src->format, src->usage),
|
||||
src->format, src->usage, false),
|
||||
context(context),
|
||||
devicePixelFormat(src->devicePixelFormat),
|
||||
externalImage(src->externalImage) {
|
||||
@@ -767,7 +767,7 @@ MetalTexture::MetalTexture(MetalContext& context, MetalTexture const* src, uint8
|
||||
MetalTexture::MetalTexture(MetalContext& context, MetalTexture const* src, TextureSwizzle r,
|
||||
TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) noexcept
|
||||
: HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth,
|
||||
src->format, src->usage),
|
||||
src->format, src->usage, false),
|
||||
context(context),
|
||||
devicePixelFormat(src->devicePixelFormat),
|
||||
externalImage(src->externalImage) {
|
||||
@@ -786,13 +786,13 @@ MetalTexture::MetalTexture(MetalContext& context, MetalTexture const* src, Textu
|
||||
MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t levels, TextureFormat format,
|
||||
uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
|
||||
id<MTLTexture> metalTexture) noexcept
|
||||
: HwTexture(target, levels, samples, width, height, depth, format, usage), context(context) {
|
||||
: HwTexture(target, levels, samples, width, height, depth, format, usage, false), context(context) {
|
||||
texture = metalTexture;
|
||||
}
|
||||
|
||||
MetalTexture::MetalTexture(MetalContext& context, TextureFormat format, uint32_t width,
|
||||
uint32_t height, TextureUsage usage, CVPixelBufferRef image) noexcept
|
||||
: HwTexture(SamplerType::SAMPLER_EXTERNAL, 1, 1, width, height, 1, format, usage),
|
||||
: HwTexture(SamplerType::SAMPLER_EXTERNAL, 1, 1, width, height, 1, format, usage, false),
|
||||
context(context),
|
||||
externalImage(std::make_shared<MetalExternalImage>(
|
||||
MetalExternalImage::createFromImage(context, image))) {
|
||||
@@ -801,7 +801,7 @@ MetalTexture::MetalTexture(MetalContext& context, TextureFormat format, uint32_t
|
||||
|
||||
MetalTexture::MetalTexture(MetalContext& context, TextureFormat format, uint32_t width,
|
||||
uint32_t height, TextureUsage usage, CVPixelBufferRef image, uint32_t plane) noexcept
|
||||
: HwTexture(SamplerType::SAMPLER_EXTERNAL, 1, 1, width, height, 1, format, usage),
|
||||
: HwTexture(SamplerType::SAMPLER_EXTERNAL, 1, 1, width, height, 1, format, usage, false),
|
||||
context(context),
|
||||
externalImage(std::make_shared<MetalExternalImage>(
|
||||
MetalExternalImage::createFromImagePlane(context, image, plane))) {
|
||||
@@ -1441,7 +1441,7 @@ void MetalFence::cancel() {
|
||||
MetalDescriptorSetLayout::MetalDescriptorSetLayout(DescriptorSetLayout&& l) noexcept
|
||||
: mLayout(std::move(l)) {
|
||||
size_t dynamicBindings = 0;
|
||||
for (const auto& binding : mLayout.bindings) {
|
||||
for (const auto& binding : mLayout.descriptors) {
|
||||
if (any(binding.flags & DescriptorFlags::DYNAMIC_OFFSET)) {
|
||||
dynamicBindings++;
|
||||
}
|
||||
|
||||
@@ -28,10 +28,13 @@
|
||||
namespace filament::backend {
|
||||
|
||||
Driver* NoopDriver::create() {
|
||||
return new NoopDriver();
|
||||
Platform::DriverConfig driverConfig;
|
||||
return new NoopDriver(driverConfig);
|
||||
}
|
||||
|
||||
NoopDriver::NoopDriver() noexcept = default;
|
||||
NoopDriver::NoopDriver(const Platform::DriverConfig& driverConfig) noexcept
|
||||
: DriverBase(driverConfig) {
|
||||
}
|
||||
|
||||
NoopDriver::~NoopDriver() noexcept = default;
|
||||
|
||||
@@ -247,6 +250,10 @@ bool NoopDriver::isDepthClampSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NoopDriver::isAsynchronousModeEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NoopDriver::isWorkaroundNeeded(Workaround) {
|
||||
return false;
|
||||
}
|
||||
@@ -465,4 +472,8 @@ bool NoopDriver::queryFrameTimestamps(SwapChainHandle swapChain, uint64_t frameI
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NoopDriver::cancelAsyncJob(AsyncCallId jobId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
namespace filament::backend {
|
||||
|
||||
class NoopDriver final : public DriverBase {
|
||||
NoopDriver() noexcept;
|
||||
NoopDriver(const Platform::DriverConfig& driverConfig) noexcept;
|
||||
~NoopDriver() noexcept override;
|
||||
Dispatcher getDispatcher() const noexcept final;
|
||||
|
||||
@@ -55,7 +55,7 @@ private:
|
||||
|
||||
#define DECL_DRIVER_API_RETURN(RetType, methodName, paramsDecl, params) \
|
||||
RetType methodName##S() noexcept override { \
|
||||
return RetType((RetType::HandleId)nextFakeHandle++); } \
|
||||
return RetType(nextFakeHandle++); } \
|
||||
UTILS_ALWAYS_INLINE inline void methodName##R(RetType, paramsDecl) { }
|
||||
|
||||
#include "private/backend/DriverAPI.inc"
|
||||
|
||||
@@ -70,7 +70,7 @@ public:
|
||||
assert_invariant(binding < MAX_DESCRIPTOR_COUNT);
|
||||
assert_invariant(entry.binding < 128); // we reserve 1 bit for the type right now
|
||||
mStorage[set][binding] = { uint8_t(entry.binding),
|
||||
DescriptorSetLayoutBinding::isSampler(entry.type) };
|
||||
DescriptorSetLayoutDescriptor::isSampler(entry.type) };
|
||||
mActiveDescriptors[set].set(binding);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,8 +30,8 @@ namespace filament::backend {
|
||||
struct GLBufferObject : public HwBufferObject {
|
||||
using HwBufferObject::HwBufferObject;
|
||||
GLBufferObject(uint32_t size,
|
||||
BufferObjectBinding bindingType, BufferUsage usage) noexcept
|
||||
: HwBufferObject(size), usage(usage), bindingType(bindingType) {
|
||||
BufferObjectBinding bindingType, BufferUsage usage, bool async) noexcept
|
||||
: HwBufferObject(size, async), usage(usage), bindingType(bindingType) {
|
||||
}
|
||||
|
||||
struct {
|
||||
|
||||
@@ -56,7 +56,7 @@ GLDescriptorSet::GLDescriptorSet(OpenGLContext& gl, DescriptorSetLayoutHandle ds
|
||||
|
||||
// We have allocated enough storage for all descriptors. Now allocate the empty descriptor
|
||||
// themselves.
|
||||
for (auto const& entry : layout->bindings) {
|
||||
for (auto const& entry : layout->descriptors) {
|
||||
size_t const index = entry.binding;
|
||||
|
||||
// now we'll initialize the alternative for each way we can handle this descriptor.
|
||||
@@ -372,10 +372,10 @@ void GLDescriptorSet::validate(HandleAllocatorGL& allocator,
|
||||
|
||||
UTILS_UNUSED_IN_RELEASE
|
||||
bool const pipelineLayoutMatchesDescriptorSetLayout = std::equal(
|
||||
dsl->bindings.begin(), dsl->bindings.end(),
|
||||
cur->bindings.begin(),
|
||||
[](DescriptorSetLayoutBinding const& lhs,
|
||||
DescriptorSetLayoutBinding const& rhs) {
|
||||
dsl->descriptors.begin(), dsl->descriptors.end(),
|
||||
cur->descriptors.begin(),
|
||||
[](DescriptorSetLayoutDescriptor const& lhs,
|
||||
DescriptorSetLayoutDescriptor const& rhs) {
|
||||
return lhs.type == rhs.type &&
|
||||
lhs.stageFlags == rhs.stageFlags &&
|
||||
lhs.binding == rhs.binding &&
|
||||
|
||||
@@ -33,12 +33,12 @@ struct GLDescriptorSetLayout : public HwDescriptorSetLayout, public DescriptorSe
|
||||
explicit GLDescriptorSetLayout(DescriptorSetLayout&& layout) noexcept
|
||||
: DescriptorSetLayout(std::move(layout)) {
|
||||
|
||||
std::sort(bindings.begin(), bindings.end(),
|
||||
std::sort(descriptors.begin(), descriptors.end(),
|
||||
[](auto&& lhs, auto&& rhs){
|
||||
return lhs.binding < rhs.binding;
|
||||
});
|
||||
|
||||
auto p = std::max_element(bindings.cbegin(), bindings.cend(),
|
||||
auto p = std::max_element(descriptors.cbegin(), descriptors.cend(),
|
||||
[](auto const& lhs, auto const& rhs) {
|
||||
return lhs.binding < rhs.binding;
|
||||
});
|
||||
|
||||
@@ -373,8 +373,8 @@ public:
|
||||
|
||||
FeatureLevel getFeatureLevel() const noexcept { return mFeatureLevel; }
|
||||
|
||||
// This is the index of the context in use. Must be 0 or 1. This is used to manange the
|
||||
// OpenGL name of ContainerObjects within each context.
|
||||
// This is the index of the context in use. Must be either 0 (Unprotected) or 1 (Protected).
|
||||
// This is used to manage the OpenGL name of ContainerObjects within each context.
|
||||
uint32_t contextIndex = 0;
|
||||
|
||||
// Try to keep the State structure sorted by data-access patterns
|
||||
@@ -536,7 +536,7 @@ private:
|
||||
TimerQueryFactoryInterface* mTimerQueryFactory = nullptr;
|
||||
std::vector<std::function<void(OpenGLContext&)>> mDestroyWithNormalContext;
|
||||
RenderPrimitive mDefaultVAO;
|
||||
std::optional<GLuint> mDefaultFbo[2];
|
||||
std::optional<GLuint> mDefaultFbo[2]; // 0:Unprotected, 1:Protected
|
||||
mutable tsl::robin_map<SamplerParams, GLuint,
|
||||
SamplerParams::Hasher, SamplerParams::EqualTo> mSamplerMap;
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
#include "OpenGLDriver.h"
|
||||
|
||||
#include "CommandStreamDispatcher.h"
|
||||
#include "GLTexture.h"
|
||||
#include "GLMemoryMappedBuffer.h"
|
||||
#include "GLTexture.h"
|
||||
#include "GLUtils.h"
|
||||
#include "OpenGLContext.h"
|
||||
#include "OpenGLDriverFactory.h"
|
||||
@@ -126,6 +126,7 @@
|
||||
|
||||
#if DEBUG_MARKER_LEVEL == DEBUG_MARKER_PROFILE
|
||||
# define DEBUG_MARKER()
|
||||
# define DEBUG_MARKER_NAME(name)
|
||||
# define PROFILE_MARKER(marker) PROFILE_SCOPE(marker);
|
||||
# if DEBUG_GROUP_MARKER_LEVEL != DEBUG_GROUP_MARKER_NONE
|
||||
# error PROFILING is exclusive; group markers must be disabled.
|
||||
@@ -135,12 +136,14 @@
|
||||
# endif
|
||||
#elif DEBUG_MARKER_LEVEL > DEBUG_MARKER_NONE
|
||||
# define DEBUG_MARKER() DebugMarker _debug_marker(*this, __func__);
|
||||
# define DEBUG_MARKER_NAME(name) DebugMarker _debug_marker(*this, name);
|
||||
# define PROFILE_MARKER(marker) DEBUG_MARKER()
|
||||
# if DEBUG_MARKER_LEVEL & DEBUG_MARKER_PROFILE
|
||||
# error PROFILING is exclusive; all other debug features must be disabled.
|
||||
# endif
|
||||
#else
|
||||
# define DEBUG_MARKER()
|
||||
# define DEBUG_MARKER_NAME(name)
|
||||
# define PROFILE_MARKER(marker)
|
||||
#endif
|
||||
|
||||
@@ -286,14 +289,14 @@ OpenGLDriver::DebugMarker::~DebugMarker() noexcept {
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfig& driverConfig) noexcept
|
||||
: mPlatform(*platform),
|
||||
: OpenGLDriverBase(driverConfig),
|
||||
mPlatform(*platform),
|
||||
mContext(mPlatform, driverConfig),
|
||||
mShaderCompilerService(*this),
|
||||
mHandleAllocator("Handles",
|
||||
driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck,
|
||||
driverConfig.disableHeapHandleTags),
|
||||
mDriverConfig(driverConfig),
|
||||
mCurrentPushConstants(new(std::nothrow) PushConstantBundle{}) {
|
||||
// set a reasonable default value for our stream array
|
||||
mTexturesWithStreamsAttached.reserve(8);
|
||||
@@ -312,6 +315,26 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
|
||||
#endif
|
||||
|
||||
mShaderCompilerService.init();
|
||||
|
||||
if (driverConfig.asynchronousMode != AsynchronousMode::NONE) {
|
||||
mJobQueue = JobQueue::create();
|
||||
|
||||
bool const useThreadWorker =
|
||||
(driverConfig.asynchronousMode == AsynchronousMode::THREAD_PREFERRED) &&
|
||||
UTILS_HAS_THREADING;
|
||||
|
||||
if (useThreadWorker) {
|
||||
ThreadWorker::Config threadWorkerConfig{
|
||||
"JobQueueThreadWorker",
|
||||
JobSystem::Priority::NORMAL,
|
||||
[this]() { mPlatform.createContext(true); },
|
||||
[this]() { mPlatform.releaseContext(); },
|
||||
};
|
||||
mJobWorker = ThreadWorker::create(mJobQueue, std::move(threadWorkerConfig));
|
||||
} else {
|
||||
mJobWorker = AmortizationWorker::create(mJobQueue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OpenGLDriver::~OpenGLDriver() noexcept { // NOLINT(modernize-use-equals-default)
|
||||
@@ -359,8 +382,10 @@ void OpenGLDriver::terminate() {
|
||||
delete mCurrentPushConstants;
|
||||
mCurrentPushConstants = nullptr;
|
||||
|
||||
if (getJobWorker()) {
|
||||
getJobWorker()->terminate();
|
||||
}
|
||||
mContext.terminate();
|
||||
|
||||
mPlatform.terminate();
|
||||
}
|
||||
|
||||
@@ -412,7 +437,8 @@ void OpenGLDriver::setPushConstant(ShaderStage const stage, uint8_t const index,
|
||||
if (std::holds_alternative<bool>(value)) {
|
||||
assert_invariant(type == ConstantType::BOOL);
|
||||
bool const bval = std::get<bool>(value);
|
||||
glUniform1i(location, bval ? 1 : 0);
|
||||
// This must be the 'ui' version of glUniform1 due to a crash on M-series macbooks.
|
||||
glUniform1ui(location, bval ? 1 : 0);
|
||||
} else if (std::holds_alternative<float>(value)) {
|
||||
assert_invariant(type == ConstantType::FLOAT);
|
||||
float const fval = std::get<float>(value);
|
||||
@@ -576,10 +602,18 @@ Handle<HwIndexBuffer> OpenGLDriver::createIndexBufferS() noexcept {
|
||||
return initHandle<GLIndexBuffer>();
|
||||
}
|
||||
|
||||
Handle<HwIndexBuffer> OpenGLDriver::createIndexBufferAsyncS() noexcept {
|
||||
return initHandle<GLIndexBuffer>();
|
||||
}
|
||||
|
||||
Handle<HwBufferObject> OpenGLDriver::createBufferObjectS() noexcept {
|
||||
return initHandle<GLBufferObject>();
|
||||
}
|
||||
|
||||
Handle<HwBufferObject> OpenGLDriver::createBufferObjectAsyncS() noexcept {
|
||||
return initHandle<GLBufferObject>();
|
||||
}
|
||||
|
||||
Handle<HwRenderPrimitive> OpenGLDriver::createRenderPrimitiveS() noexcept {
|
||||
return initHandle<GLRenderPrimitive>();
|
||||
}
|
||||
@@ -592,6 +626,10 @@ Handle<HwTexture> OpenGLDriver::createTextureS() noexcept {
|
||||
return initHandle<GLTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> OpenGLDriver::createTextureAsyncS() noexcept {
|
||||
return initHandle<GLTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> OpenGLDriver::createTextureViewS() noexcept {
|
||||
return initHandle<GLTexture>();
|
||||
}
|
||||
@@ -600,6 +638,10 @@ Handle<HwTexture> OpenGLDriver::createTextureViewSwizzleS() noexcept {
|
||||
return initHandle<GLTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> OpenGLDriver::createTextureViewSwizzleAsyncS() noexcept {
|
||||
return initHandle<GLTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> OpenGLDriver::createTextureExternalImage2S() noexcept {
|
||||
return initHandle<GLTexture>();
|
||||
}
|
||||
@@ -616,6 +658,30 @@ Handle<HwTexture> OpenGLDriver::importTextureS() noexcept {
|
||||
return initHandle<GLTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> OpenGLDriver::importTextureAsyncS() noexcept {
|
||||
return initHandle<GLTexture>();
|
||||
}
|
||||
|
||||
AsyncCallId OpenGLDriver::update3DImageAsyncS() noexcept {
|
||||
assert_invariant(getJobQueue());
|
||||
return getJobQueue()->issueJobId();
|
||||
}
|
||||
|
||||
AsyncCallId OpenGLDriver::setVertexBufferObjectAsyncS() noexcept {
|
||||
assert_invariant(getJobQueue());
|
||||
return getJobQueue()->issueJobId();
|
||||
}
|
||||
|
||||
AsyncCallId OpenGLDriver::updateBufferObjectAsyncS() noexcept {
|
||||
assert_invariant(getJobQueue());
|
||||
return getJobQueue()->issueJobId();
|
||||
}
|
||||
|
||||
AsyncCallId OpenGLDriver::updateIndexBufferAsyncS() noexcept {
|
||||
assert_invariant(getJobQueue());
|
||||
return getJobQueue()->issueJobId();
|
||||
}
|
||||
|
||||
Handle<HwRenderTarget> OpenGLDriver::createDefaultRenderTargetS() noexcept {
|
||||
return initHandle<GLRenderTarget>();
|
||||
}
|
||||
@@ -656,6 +722,11 @@ MemoryMappedBufferHandle OpenGLDriver::mapBufferS() noexcept {
|
||||
return initHandle<GLMemoryMappedBuffer>();
|
||||
}
|
||||
|
||||
AsyncCallId OpenGLDriver::queueCommandAsyncS() noexcept {
|
||||
assert_invariant(getJobQueue());
|
||||
return getJobQueue()->issueJobId();
|
||||
}
|
||||
|
||||
void OpenGLDriver::createVertexBufferInfoR(
|
||||
Handle<HwVertexBufferInfo> vbih,
|
||||
uint8_t bufferCount,
|
||||
@@ -677,17 +748,11 @@ void OpenGLDriver::createVertexBufferR(
|
||||
mHandleAllocator.associateTagToHandle(vbh.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void OpenGLDriver::createIndexBufferR(
|
||||
Handle<HwIndexBuffer> ibh,
|
||||
ElementType const elementType,
|
||||
uint32_t indexCount,
|
||||
BufferUsage const usage,
|
||||
ImmutableCString&& tag) {
|
||||
DEBUG_MARKER()
|
||||
|
||||
void OpenGLDriver::createIndexBufferCommon(Handle<HwIndexBuffer> ibh, ElementType const elementType,
|
||||
uint32_t indexCount, BufferUsage const usage, utils::ImmutableCString&& tag) {
|
||||
auto& gl = mContext;
|
||||
uint8_t const elementSize = static_cast<uint8_t>(getElementTypeSize(elementType));
|
||||
GLIndexBuffer* ib = construct<GLIndexBuffer>(ibh, elementSize, indexCount);
|
||||
GLIndexBuffer* ib = handle_cast<GLIndexBuffer*>(ibh);
|
||||
glGenBuffers(1, &ib->gl.buffer);
|
||||
GLsizeiptr const size = elementSize * GLsizeiptr(indexCount);
|
||||
gl.bindVertexArray(nullptr);
|
||||
@@ -697,9 +762,52 @@ void OpenGLDriver::createIndexBufferR(
|
||||
mHandleAllocator.associateTagToHandle(ibh.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage, ImmutableCString&& tag) {
|
||||
void OpenGLDriver::createIndexBufferR(
|
||||
Handle<HwIndexBuffer> ibh,
|
||||
ElementType const elementType,
|
||||
uint32_t indexCount,
|
||||
BufferUsage const usage,
|
||||
ImmutableCString&& tag) {
|
||||
DEBUG_MARKER()
|
||||
|
||||
uint8_t const elementSize = static_cast<uint8_t>(getElementTypeSize(elementType));
|
||||
// For object creation, the object should be constructed first to determine the initial settings
|
||||
// early. For example, the `asynchronous` field needs to be decided at this stage so that
|
||||
// subsequent backend APIs can handle operations based on this setting.
|
||||
construct<GLIndexBuffer>(ibh, elementSize, indexCount, false);
|
||||
|
||||
createIndexBufferCommon(ibh, elementType, indexCount, usage, std::move(tag));
|
||||
}
|
||||
|
||||
void OpenGLDriver::createIndexBufferAsyncR(
|
||||
Handle<HwIndexBuffer> ibh,
|
||||
ElementType const elementType,
|
||||
uint32_t indexCount,
|
||||
BufferUsage const usage,
|
||||
CallbackHandler* handler, CallbackHandler::Callback const callback, void* user,
|
||||
ImmutableCString&& tag) {
|
||||
uint8_t const elementSize = static_cast<uint8_t>(getElementTypeSize(elementType));
|
||||
// For object creation, the object should be constructed first to determine the initial settings
|
||||
// early. For example, the `asynchronous` field needs to be decided at this stage so that
|
||||
// subsequent backend APIs can handle operations based on this setting.
|
||||
construct<GLIndexBuffer>(ibh, elementSize, indexCount, true);
|
||||
|
||||
assert_invariant(getJobQueue());
|
||||
|
||||
getJobQueue()->push([this, ibh, elementType, indexCount, usage, handler, callback, user,
|
||||
tag=std::move(tag)]() mutable {
|
||||
DEBUG_MARKER_NAME("createIndexBufferAsyncR")
|
||||
createIndexBufferCommon(ibh, elementType, indexCount, usage, std::move(tag));
|
||||
// glFlush() should be called when using a shared context for this operation. Without it,
|
||||
// the driver may delay submitting commands to the GPU, preventing other contexts from
|
||||
// seeing the changes immediately. This ensures submitting the current commands right away.
|
||||
glFlush();
|
||||
scheduleCallback(handler, user, callback);
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLDriver::createBufferObjectCommon(Handle<HwBufferObject> boh, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage, utils::ImmutableCString&& tag) {
|
||||
assert_invariant(byteCount > 0);
|
||||
|
||||
auto& gl = mContext;
|
||||
@@ -707,7 +815,7 @@ void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byte
|
||||
gl.bindVertexArray(nullptr);
|
||||
}
|
||||
|
||||
GLBufferObject* bo = construct<GLBufferObject>(boh, byteCount, bindingType, usage);
|
||||
GLBufferObject* bo = handle_cast<GLBufferObject*>(boh);
|
||||
if (UTILS_UNLIKELY(bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) {
|
||||
bo->gl.id = ++mLastAssignedEmulatedUboId;
|
||||
bo->gl.buffer = malloc(byteCount);
|
||||
@@ -723,6 +831,40 @@ void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byte
|
||||
mHandleAllocator.associateTagToHandle(boh.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage, ImmutableCString&& tag) {
|
||||
DEBUG_MARKER()
|
||||
|
||||
// For object creation, the object should be constructed first to determine the initial settings
|
||||
// early. For example, the `asynchronous` field needs to be decided at this stage so that
|
||||
// subsequent backend APIs can handle operations based on this setting.
|
||||
construct<GLBufferObject>(boh, byteCount, bindingType, usage, false);
|
||||
|
||||
createBufferObjectCommon(boh, byteCount, bindingType, usage, std::move(tag));
|
||||
}
|
||||
|
||||
void OpenGLDriver::createBufferObjectAsyncR(Handle<HwBufferObject> boh, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user, ImmutableCString&& tag) {
|
||||
// For object creation, the object should be constructed first to determine the initial settings
|
||||
// early. For example, the `asynchronous` field needs to be decided at this stage so that
|
||||
// subsequent backend APIs can handle operations based on this setting.
|
||||
construct<GLBufferObject>(boh, byteCount, bindingType, usage, true);
|
||||
|
||||
assert_invariant(getJobQueue());
|
||||
|
||||
getJobQueue()->push([this, boh, byteCount, bindingType, usage, handler, callback, user,
|
||||
tag=std::move(tag)]() mutable {
|
||||
DEBUG_MARKER_NAME("createBufferObjectAsyncR")
|
||||
createBufferObjectCommon(boh, byteCount, bindingType, usage, std::move(tag));
|
||||
// glFlush() should be called when using a shared context for this operation. Without it,
|
||||
// the driver may delay submitting commands to the GPU, preventing other contexts from
|
||||
// seeing the changes immediately. This ensures submitting the current commands right away.
|
||||
glFlush();
|
||||
scheduleCallback(handler, user, callback);
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
|
||||
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
|
||||
PrimitiveType const pt, ImmutableCString&& tag) {
|
||||
@@ -870,11 +1012,9 @@ void OpenGLDriver::textureStorage(GLTexture* t,
|
||||
t->depth = depth;
|
||||
}
|
||||
|
||||
void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
void OpenGLDriver::createTextureCommon(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth,
|
||||
TextureUsage usage, ImmutableCString&& tag) {
|
||||
DEBUG_MARKER()
|
||||
|
||||
GLenum internalFormat = getInternalFormat(format);
|
||||
assert_invariant(internalFormat);
|
||||
|
||||
@@ -895,7 +1035,11 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
|
||||
auto const& gl = mContext;
|
||||
samples = std::clamp(samples, uint8_t(1u), uint8_t(gl.gets.max_samples));
|
||||
GLTexture* t = construct<GLTexture>(th, target, levels, samples, width, height, depth, format, usage);
|
||||
GLTexture* t = handle_cast<GLTexture*>(th);
|
||||
|
||||
// Overwrite the updated `usage`.
|
||||
t->usage = usage;
|
||||
|
||||
if (UTILS_LIKELY(usage & TextureUsage::SAMPLEABLE)) {
|
||||
|
||||
if (UTILS_UNLIKELY(gl.isES2())) {
|
||||
@@ -964,6 +1108,44 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth,
|
||||
TextureUsage usage, ImmutableCString&& tag) {
|
||||
DEBUG_MARKER()
|
||||
|
||||
// For object creation, the object should be constructed first to determine the initial settings
|
||||
// early. For example, the `asynchronous` field needs to be decided at this stage so that
|
||||
// subsequent backend APIs can handle operations based on this setting.
|
||||
construct<GLTexture>(th, target, levels, samples, width, height, depth, format, usage, false);
|
||||
|
||||
createTextureCommon(th, target, levels, format, samples, width, height, depth, usage,
|
||||
std::move(tag));
|
||||
}
|
||||
|
||||
void OpenGLDriver::createTextureAsyncR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth,
|
||||
TextureUsage usage, CallbackHandler* handler, CallbackHandler::Callback const callback,
|
||||
void* user, ImmutableCString&& tag) {
|
||||
// For object creation, the object should be constructed first to determine the initial settings
|
||||
// early. For example, the `asynchronous` field needs to be decided at this stage so that
|
||||
// subsequent backend APIs can handle operations based on this setting.
|
||||
construct<GLTexture>(th, target, levels, samples, width, height, depth, format, usage, true);
|
||||
|
||||
assert_invariant(getJobQueue());
|
||||
|
||||
getJobQueue()->push([this, th, target, levels, format, samples, width, height, depth, usage,
|
||||
handler, callback, user, tag=std::move(tag)]() mutable {
|
||||
DEBUG_MARKER_NAME("createTextureAsyncR")
|
||||
createTextureCommon(th, target, levels, format, samples, width, height, depth, usage,
|
||||
std::move(tag));
|
||||
// glFlush() should be called when using a shared context for this operation. Without it,
|
||||
// the driver may delay submitting commands to the GPU, preventing other contexts from
|
||||
// seeing the changes immediately. This ensures submitting the current commands right away.
|
||||
glFlush();
|
||||
scheduleCallback(handler, user, callback);
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLDriver::createTextureViewR(Handle<HwTexture> th,
|
||||
Handle<HwTexture> srch, uint8_t const baseLevel, uint8_t const levelCount, ImmutableCString&& tag) {
|
||||
DEBUG_MARKER()
|
||||
@@ -986,7 +1168,8 @@ void OpenGLDriver::createTextureViewR(Handle<HwTexture> th,
|
||||
src->samples,
|
||||
src->width, src->height, src->depth,
|
||||
src->format,
|
||||
src->usage);
|
||||
src->usage,
|
||||
src->asynchronous);
|
||||
|
||||
t->gl = src->gl;
|
||||
t->gl.sidecarRenderBufferMS = 0;
|
||||
@@ -1011,11 +1194,9 @@ void OpenGLDriver::createTextureViewR(Handle<HwTexture> th,
|
||||
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void OpenGLDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
TextureSwizzle const r, TextureSwizzle const g, TextureSwizzle const b, TextureSwizzle const a,
|
||||
ImmutableCString&& tag) {
|
||||
|
||||
DEBUG_MARKER()
|
||||
void OpenGLDriver::createTextureViewSwizzleCommon(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
TextureSwizzle const r, TextureSwizzle const g, TextureSwizzle const b,
|
||||
TextureSwizzle const a, ImmutableCString&& tag) {
|
||||
GLTexture const* const src = handle_cast<GLTexture const*>(srch);
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(any(src->usage & TextureUsage::SAMPLEABLE))
|
||||
@@ -1029,14 +1210,7 @@ void OpenGLDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwText
|
||||
src->ref = initHandle<GLTextureRef>();
|
||||
}
|
||||
|
||||
GLTexture* t = construct<GLTexture>(th,
|
||||
src->target,
|
||||
src->levels,
|
||||
src->samples,
|
||||
src->width, src->height, src->depth,
|
||||
src->format,
|
||||
src->usage);
|
||||
|
||||
GLTexture* t = handle_cast<GLTexture*>(th);
|
||||
t->gl = src->gl;
|
||||
t->gl.baseLevel = src->gl.baseLevel;
|
||||
t->gl.maxLevel = src->gl.maxLevel;
|
||||
@@ -1077,6 +1251,46 @@ void OpenGLDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwText
|
||||
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void OpenGLDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
TextureSwizzle const r, TextureSwizzle const g, TextureSwizzle const b, TextureSwizzle const a,
|
||||
ImmutableCString&& tag) {
|
||||
DEBUG_MARKER()
|
||||
|
||||
// For object creation, the object should be constructed first to determine the initial settings
|
||||
// early. For example, the `asynchronous` field needs to be decided at this stage so that
|
||||
// subsequent backend APIs can handle operations based on this setting.
|
||||
GLTexture const* const src = handle_cast<GLTexture const*>(srch);
|
||||
construct<GLTexture>(th, src->target, src->levels, src->samples, src->width, src->height,
|
||||
src->depth, src->format, src->usage, src->asynchronous);
|
||||
|
||||
createTextureViewSwizzleCommon(th, srch, r, g, b, a, std::move(tag));
|
||||
}
|
||||
|
||||
void OpenGLDriver::createTextureViewSwizzleAsyncR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
TextureSwizzle const r, TextureSwizzle const g, TextureSwizzle const b, TextureSwizzle const a,
|
||||
CallbackHandler* handler, CallbackHandler::Callback const callback, void* user,
|
||||
ImmutableCString&& tag) {
|
||||
// For object creation, the object should be constructed first to determine the initial settings
|
||||
// early. For example, the `asynchronous` field needs to be decided at this stage so that
|
||||
// subsequent backend APIs can handle operations based on this setting.
|
||||
GLTexture const* const src = handle_cast<GLTexture const*>(srch);
|
||||
construct<GLTexture>(th, src->target, src->levels, src->samples, src->width, src->height,
|
||||
src->depth, src->format, src->usage, src->asynchronous);
|
||||
|
||||
assert_invariant(getJobQueue());
|
||||
|
||||
getJobQueue()->push([this, th, srch, r, g, b, a, handler, callback, user,
|
||||
tag=std::move(tag)]() mutable {
|
||||
DEBUG_MARKER_NAME("createTextureViewSwizzleAsyncR")
|
||||
createTextureViewSwizzleCommon(th, srch, r, g, b, a, std::move(tag));
|
||||
// glFlush() should be called when using a shared context for this operation. Without it,
|
||||
// the driver may delay submitting commands to the GPU, preventing other contexts from
|
||||
// seeing the changes immediately. This ensures submitting the current commands right away.
|
||||
glFlush();
|
||||
scheduleCallback(handler, user, callback);
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLDriver::createTextureExternalImage2R(Handle<HwTexture> th, SamplerType target,
|
||||
TextureFormat format, uint32_t width, uint32_t height, TextureUsage usage,
|
||||
Platform::ExternalImageHandleRef image, ImmutableCString&& tag) {
|
||||
@@ -1094,7 +1308,7 @@ void OpenGLDriver::createTextureExternalImage2R(Handle<HwTexture> th, SamplerTyp
|
||||
}
|
||||
assert_invariant(internalFormat);
|
||||
|
||||
GLTexture* const t = construct<GLTexture>(th, target, 1, 1, width, height, 1, format, usage);
|
||||
GLTexture* const t = construct<GLTexture>(th, target, 1, 1, width, height, 1, format, usage, false);
|
||||
assert_invariant(t);
|
||||
|
||||
t->externalTexture = mPlatform.createExternalImageTexture();
|
||||
@@ -1146,7 +1360,7 @@ void OpenGLDriver::createTextureExternalImageR(Handle<HwTexture> th, SamplerType
|
||||
}
|
||||
assert_invariant(internalFormat);
|
||||
|
||||
GLTexture* const t = construct<GLTexture>(th, target, 1, 1, width, height, 1, format, usage);
|
||||
GLTexture* const t = construct<GLTexture>(th, target, 1, 1, width, height, 1, format, usage, false);
|
||||
assert_invariant(t);
|
||||
|
||||
t->externalTexture = mPlatform.createExternalImageTexture();
|
||||
@@ -1186,14 +1400,12 @@ void OpenGLDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
|
||||
// not relevant for the OpenGL backend
|
||||
}
|
||||
|
||||
void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t const id,
|
||||
void OpenGLDriver::importTextureCommon(Handle<HwTexture> th, intptr_t const id,
|
||||
SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
|
||||
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage, ImmutableCString&& tag) {
|
||||
DEBUG_MARKER()
|
||||
|
||||
auto const& gl = mContext;
|
||||
samples = std::clamp(samples, uint8_t(1u), uint8_t(gl.gets.max_samples));
|
||||
GLTexture* t = construct<GLTexture>(th, target, levels, samples, width, height, depth, format, usage);
|
||||
GLTexture* t = handle_cast<GLTexture*>(th);
|
||||
|
||||
t->gl.id = GLuint(id);
|
||||
t->gl.imported = true;
|
||||
@@ -1244,6 +1456,46 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t const id,
|
||||
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t const id,
|
||||
SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
|
||||
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage, ImmutableCString&& tag) {
|
||||
DEBUG_MARKER()
|
||||
|
||||
// For object creation, the object should be constructed first to determine the initial settings
|
||||
// early. For example, the `asynchronous` field needs to be decided at this stage so that
|
||||
// subsequent backend APIs can handle operations based on this setting.
|
||||
construct<GLTexture>(th, target, levels, samples, width, height, depth, format, usage, false);
|
||||
|
||||
importTextureCommon(th, id, target, levels, format, samples, width, height, depth, usage,
|
||||
std::move(tag));
|
||||
}
|
||||
|
||||
void OpenGLDriver::importTextureAsyncR(Handle<HwTexture> th, intptr_t const id,
|
||||
SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
|
||||
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
|
||||
CallbackHandler* handler, CallbackHandler::Callback const callback, void* user,
|
||||
ImmutableCString&& tag) {
|
||||
|
||||
// For object creation, the object should be constructed first to determine the initial settings
|
||||
// early. For example, the `asynchronous` field needs to be decided at this stage so that
|
||||
// subsequent backend APIs can handle operations based on this setting.
|
||||
construct<GLTexture>(th, target, levels, samples, width, height, depth, format, usage, true);
|
||||
|
||||
assert_invariant(getJobQueue());
|
||||
|
||||
getJobQueue()->push([this, th, id, target, levels, format, samples, width, height, depth, usage,
|
||||
handler, callback, user, tag=std::move(tag)]() mutable {
|
||||
DEBUG_MARKER_NAME("importTextureAsyncR")
|
||||
importTextureCommon(th, id, target, levels, format, samples, width, height, depth, usage,
|
||||
std::move(tag));
|
||||
// glFlush() should be called when using a shared context for this operation. Without it,
|
||||
// the driver may delay submitting commands to the GPU, preventing other contexts from
|
||||
// seeing the changes immediately. This ensures submitting the current commands right away.
|
||||
glFlush();
|
||||
scheduleCallback(handler, user, callback);
|
||||
});
|
||||
}
|
||||
|
||||
void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer const* vb) {
|
||||
// NOTE: this is called often and must be as efficient as possible.
|
||||
|
||||
@@ -1931,30 +2183,53 @@ void OpenGLDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDriver::destroyIndexBufferCommon(Handle<HwIndexBuffer> ibh) {
|
||||
GLIndexBuffer const* ib = handle_cast<const GLIndexBuffer*>(ibh);
|
||||
auto& gl = mContext;
|
||||
gl.deleteBuffer(ib->gl.buffer, GL_ELEMENT_ARRAY_BUFFER);
|
||||
destruct(ibh, ib);
|
||||
}
|
||||
|
||||
void OpenGLDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
|
||||
DEBUG_MARKER()
|
||||
|
||||
if (ibh) {
|
||||
auto& gl = mContext;
|
||||
GLIndexBuffer const* ib = handle_cast<const GLIndexBuffer*>(ibh);
|
||||
gl.deleteBuffer(ib->gl.buffer, GL_ELEMENT_ARRAY_BUFFER);
|
||||
destruct(ibh, ib);
|
||||
if (ib->asynchronous) {
|
||||
getJobQueue()->push([this, ibh]() {
|
||||
destroyIndexBufferCommon(ibh);
|
||||
});
|
||||
} else {
|
||||
destroyIndexBufferCommon(ibh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDriver::destroyBufferObjectCommon(Handle<HwBufferObject> boh) {
|
||||
GLBufferObject const* bo = handle_cast<const GLBufferObject*>(boh);
|
||||
auto& gl = mContext;
|
||||
// check we're not destroying a buffer that has active mappings
|
||||
assert_invariant(bo->mappingCount == 0);
|
||||
if (UTILS_UNLIKELY(bo->bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) {
|
||||
free(bo->gl.buffer);
|
||||
} else {
|
||||
gl.deleteBuffer(bo->gl.id, bo->gl.binding);
|
||||
}
|
||||
destruct(boh, bo);
|
||||
}
|
||||
|
||||
void OpenGLDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
|
||||
DEBUG_MARKER()
|
||||
|
||||
if (boh) {
|
||||
auto& gl = mContext;
|
||||
GLBufferObject const* bo = handle_cast<const GLBufferObject*>(boh);
|
||||
// check we're not destroying a buffer that has active mappings
|
||||
assert_invariant(bo->mappingCount == 0);
|
||||
if (UTILS_UNLIKELY(bo->bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) {
|
||||
free(bo->gl.buffer);
|
||||
if (bo->asynchronous) {
|
||||
getJobQueue()->push([this, boh]() {
|
||||
destroyBufferObjectCommon(boh);
|
||||
});
|
||||
} else {
|
||||
gl.deleteBuffer(bo->gl.id, bo->gl.binding);
|
||||
destroyBufferObjectCommon(boh);
|
||||
}
|
||||
destruct(boh, bo);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1990,53 +2265,63 @@ void OpenGLDriver::destroyProgram(Handle<HwProgram> ph) {
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDriver::destroyTextureCommon(Handle<HwTexture> th) {
|
||||
GLTexture* t = handle_cast<GLTexture*>(th);
|
||||
auto& gl = mContext;
|
||||
if (UTILS_LIKELY(!t->gl.imported)) {
|
||||
if (UTILS_LIKELY(t->usage & TextureUsage::SAMPLEABLE)) {
|
||||
// drop a reference
|
||||
uint16_t count = 0;
|
||||
if (UTILS_UNLIKELY(t->ref)) {
|
||||
// the common case is that we don't have a ref handle
|
||||
GLTextureRef* const ref = handle_cast<GLTextureRef*>(t->ref);
|
||||
count = --(ref->count);
|
||||
if (count == 0) {
|
||||
destruct(t->ref, ref);
|
||||
}
|
||||
}
|
||||
if (count == 0) {
|
||||
// if this was the last reference, we destroy the refcount as well as
|
||||
// the GL texture name itself.
|
||||
gl.unbindTexture(t->gl.target, t->gl.id);
|
||||
if (UTILS_UNLIKELY(t->hwStream)) {
|
||||
detachStream(t);
|
||||
}
|
||||
if (UTILS_UNLIKELY(t->externalTexture)) {
|
||||
mPlatform.destroyExternalImageTexture(t->externalTexture);
|
||||
} else {
|
||||
glDeleteTextures(1, &t->gl.id);
|
||||
}
|
||||
} else {
|
||||
// The Handle<HwTexture> is always destroyed. For extra precaution we also
|
||||
// check that the GLTexture has a trivial destructor.
|
||||
static_assert(std::is_trivially_destructible_v<GLTexture>);
|
||||
}
|
||||
} else {
|
||||
assert_invariant(t->gl.target == GL_RENDERBUFFER);
|
||||
glDeleteRenderbuffers(1, &t->gl.id);
|
||||
}
|
||||
if (t->gl.sidecarRenderBufferMS) {
|
||||
glDeleteRenderbuffers(1, &t->gl.sidecarRenderBufferMS);
|
||||
}
|
||||
} else {
|
||||
gl.unbindTexture(t->gl.target, t->gl.id);
|
||||
}
|
||||
destruct(th, t);
|
||||
}
|
||||
|
||||
void OpenGLDriver::destroyTexture(Handle<HwTexture> th) {
|
||||
DEBUG_MARKER()
|
||||
|
||||
if (th) {
|
||||
auto& gl = mContext;
|
||||
GLTexture* t = handle_cast<GLTexture*>(th);
|
||||
|
||||
if (UTILS_LIKELY(!t->gl.imported)) {
|
||||
if (UTILS_LIKELY(t->usage & TextureUsage::SAMPLEABLE)) {
|
||||
// drop a reference
|
||||
uint16_t count = 0;
|
||||
if (UTILS_UNLIKELY(t->ref)) {
|
||||
// the common case is that we don't have a ref handle
|
||||
GLTextureRef* const ref = handle_cast<GLTextureRef*>(t->ref);
|
||||
count = --(ref->count);
|
||||
if (count == 0) {
|
||||
destruct(t->ref, ref);
|
||||
}
|
||||
}
|
||||
if (count == 0) {
|
||||
// if this was the last reference, we destroy the refcount as well as
|
||||
// the GL texture name itself.
|
||||
gl.unbindTexture(t->gl.target, t->gl.id);
|
||||
if (UTILS_UNLIKELY(t->hwStream)) {
|
||||
detachStream(t);
|
||||
}
|
||||
if (UTILS_UNLIKELY(t->externalTexture)) {
|
||||
mPlatform.destroyExternalImageTexture(t->externalTexture);
|
||||
} else {
|
||||
glDeleteTextures(1, &t->gl.id);
|
||||
}
|
||||
} else {
|
||||
// The Handle<HwTexture> is always destroyed. For extra precaution we also
|
||||
// check that the GLTexture has a trivial destructor.
|
||||
static_assert(std::is_trivially_destructible_v<GLTexture>);
|
||||
}
|
||||
} else {
|
||||
assert_invariant(t->gl.target == GL_RENDERBUFFER);
|
||||
glDeleteRenderbuffers(1, &t->gl.id);
|
||||
}
|
||||
if (t->gl.sidecarRenderBufferMS) {
|
||||
glDeleteRenderbuffers(1, &t->gl.sidecarRenderBufferMS);
|
||||
}
|
||||
if (t->asynchronous) {
|
||||
getJobQueue()->push([this, th]() {
|
||||
destroyTextureCommon(th);
|
||||
});
|
||||
} else {
|
||||
gl.unbindTexture(t->gl.target, t->gl.id);
|
||||
destroyTextureCommon(th);
|
||||
}
|
||||
destruct(th, t);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2577,7 +2862,7 @@ bool OpenGLDriver::isStereoSupported() {
|
||||
if (UTILS_UNLIKELY(mContext.isES2())) {
|
||||
return false;
|
||||
}
|
||||
switch (mDriverConfig.stereoscopicType) {
|
||||
switch (getDriverConfig().stereoscopicType) {
|
||||
case StereoscopicType::INSTANCED:
|
||||
return mContext.ext.EXT_clip_cull_distance;
|
||||
case StereoscopicType::MULTIVIEW:
|
||||
@@ -2596,7 +2881,7 @@ bool OpenGLDriver::isParallelShaderCompileSupported() {
|
||||
// GL-specific. It would also be nice to inform the engine that they're working with this fake
|
||||
// amortized system, but this fact will become implicit when we generalize this feature for all
|
||||
// backends.
|
||||
if (mDriverConfig.disableAmortizedShaderCompile) {
|
||||
if (getDriverConfig().disableAmortizedShaderCompile) {
|
||||
return mShaderCompilerService.isParallelShaderCompileSupported();
|
||||
}
|
||||
return true;
|
||||
@@ -2618,6 +2903,10 @@ bool OpenGLDriver::isDepthClampSupported() {
|
||||
return getContext().ext.EXT_depth_clamp;
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isAsynchronousModeEnabled() {
|
||||
return getJobQueue() != nullptr;
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isWorkaroundNeeded(Workaround const workaround) {
|
||||
switch (workaround) {
|
||||
case Workaround::SPLIT_EASU:
|
||||
@@ -2799,10 +3088,8 @@ void OpenGLDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain>
|
||||
// Updating driver objects
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void OpenGLDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh,
|
||||
void OpenGLDriver::setVertexBufferObjectCommon(Handle<HwVertexBuffer> vbh,
|
||||
uint32_t const index, Handle<HwBufferObject> boh) {
|
||||
DEBUG_MARKER()
|
||||
|
||||
GLVertexBuffer* vb = handle_cast<GLVertexBuffer *>(vbh);
|
||||
GLBufferObject const* bo = handle_cast<GLBufferObject *>(boh);
|
||||
|
||||
@@ -2822,10 +3109,28 @@ void OpenGLDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh,
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::updateIndexBuffer(
|
||||
Handle<HwIndexBuffer> ibh, BufferDescriptor&& p, uint32_t const byteOffset) {
|
||||
void OpenGLDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh,
|
||||
uint32_t const index, Handle<HwBufferObject> boh) {
|
||||
DEBUG_MARKER()
|
||||
setVertexBufferObjectCommon(vbh, index, boh);
|
||||
}
|
||||
|
||||
void OpenGLDriver::setVertexBufferObjectAsyncR(AsyncCallId jobId, Handle<HwVertexBuffer> vbh,
|
||||
uint32_t const index, Handle<HwBufferObject> boh, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user) {
|
||||
getJobQueue()->push([this, vbh, index, boh, handler, callback, user]() mutable {
|
||||
DEBUG_MARKER_NAME("setVertexBufferObjectAsyncR")
|
||||
setVertexBufferObjectCommon(vbh, index, boh);
|
||||
// glFlush() should be called when using a shared context for this operation. Without it,
|
||||
// the driver may delay submitting commands to the GPU, preventing other contexts from
|
||||
// seeing the changes immediately. This ensures submitting the current commands right away.
|
||||
glFlush();
|
||||
scheduleCallback(handler, user, callback);
|
||||
}, jobId);
|
||||
}
|
||||
|
||||
void OpenGLDriver::updateIndexBufferCommon(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
|
||||
uint32_t const byteOffset) {
|
||||
auto& gl = mContext;
|
||||
GLIndexBuffer const* ib = handle_cast<GLIndexBuffer *>(ibh);
|
||||
assert_invariant(ib->elementSize == 2 || ib->elementSize == 4);
|
||||
@@ -2839,9 +3144,29 @@ void OpenGLDriver::updateIndexBuffer(
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::updateBufferObject(
|
||||
Handle<HwBufferObject> boh, BufferDescriptor&& bd, uint32_t const byteOffset) {
|
||||
void OpenGLDriver::updateIndexBuffer(
|
||||
Handle<HwIndexBuffer> ibh, BufferDescriptor&& p, uint32_t const byteOffset) {
|
||||
DEBUG_MARKER()
|
||||
updateIndexBufferCommon(ibh, std::move(p), byteOffset);
|
||||
}
|
||||
|
||||
void OpenGLDriver::updateIndexBufferAsyncR(AsyncCallId jobId, Handle<HwIndexBuffer> ibh,
|
||||
BufferDescriptor&& p, uint32_t const byteOffset, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user) {
|
||||
getJobQueue()->push([this, ibh, p=std::move(p), byteOffset, handler, callback,
|
||||
user]() mutable {
|
||||
DEBUG_MARKER_NAME("updateIndexBufferAsyncR")
|
||||
updateIndexBufferCommon(ibh, std::move(p), byteOffset);
|
||||
// glFlush() should be called when using a shared context for this operation. Without it,
|
||||
// the driver may delay submitting commands to the GPU, preventing other contexts from
|
||||
// seeing the changes immediately. This ensures submitting the current commands right away.
|
||||
glFlush();
|
||||
scheduleCallback(handler, user, callback);
|
||||
}, jobId);
|
||||
}
|
||||
|
||||
void OpenGLDriver::updateBufferObjectCommon(
|
||||
Handle<HwBufferObject> boh, BufferDescriptor&& bd, uint32_t const byteOffset) {
|
||||
|
||||
auto& gl = mContext;
|
||||
GLBufferObject* bo = handle_cast<GLBufferObject *>(boh);
|
||||
@@ -2874,6 +3199,27 @@ void OpenGLDriver::updateBufferObject(
|
||||
CHECK_GL_ERROR()
|
||||
}
|
||||
|
||||
void OpenGLDriver::updateBufferObject(
|
||||
Handle<HwBufferObject> boh, BufferDescriptor&& bd, uint32_t const byteOffset) {
|
||||
DEBUG_MARKER()
|
||||
updateBufferObjectCommon(boh, std::move(bd), byteOffset);
|
||||
}
|
||||
|
||||
void OpenGLDriver::updateBufferObjectAsyncR(AsyncCallId jobId, Handle<HwBufferObject> boh,
|
||||
BufferDescriptor&& bd, uint32_t const byteOffset, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user) {
|
||||
getJobQueue()->push([this, boh, bd=std::move(bd), byteOffset, handler, callback,
|
||||
user]() mutable {
|
||||
DEBUG_MARKER_NAME("updateBufferObjectAsyncR")
|
||||
updateBufferObjectCommon(boh, std::move(bd), byteOffset);
|
||||
// glFlush() should be called when using a shared context for this operation. Without it,
|
||||
// the driver may delay submitting commands to the GPU, preventing other contexts from
|
||||
// seeing the changes immediately. This ensures submitting the current commands right away.
|
||||
glFlush();
|
||||
scheduleCallback(handler, user, callback);
|
||||
}, jobId);
|
||||
}
|
||||
|
||||
void OpenGLDriver::updateBufferObjectUnsynchronized(
|
||||
Handle<HwBufferObject> boh, BufferDescriptor&& bd, uint32_t const byteOffset) {
|
||||
DEBUG_MARKER()
|
||||
@@ -2937,12 +3283,10 @@ void OpenGLDriver::resetBufferObject(Handle<HwBufferObject> boh) {
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDriver::update3DImage(Handle<HwTexture> th,
|
||||
void OpenGLDriver::update3DImageCommon(Handle<HwTexture> th,
|
||||
uint32_t const level, uint32_t const xoffset, uint32_t const yoffset, uint32_t const zoffset,
|
||||
uint32_t const width, uint32_t const height, uint32_t const depth,
|
||||
PixelBufferDescriptor&& data) {
|
||||
DEBUG_MARKER()
|
||||
|
||||
GLTexture const* t = handle_cast<GLTexture *>(th);
|
||||
if (data.type == PixelDataType::COMPRESSED) {
|
||||
setCompressedTextureData(t,
|
||||
@@ -2953,6 +3297,33 @@ void OpenGLDriver::update3DImage(Handle<HwTexture> th,
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDriver::update3DImage(Handle<HwTexture> th,
|
||||
uint32_t const level, uint32_t const xoffset, uint32_t const yoffset, uint32_t const zoffset,
|
||||
uint32_t const width, uint32_t const height, uint32_t const depth,
|
||||
PixelBufferDescriptor&& data) {
|
||||
DEBUG_MARKER()
|
||||
update3DImageCommon(th, level, xoffset, yoffset, zoffset, width, height, depth,
|
||||
std::move(data));
|
||||
}
|
||||
|
||||
void OpenGLDriver::update3DImageAsyncR(AsyncCallId jobId, Handle<HwTexture> th,
|
||||
uint32_t const level, uint32_t const xoffset, uint32_t const yoffset, uint32_t const zoffset,
|
||||
uint32_t const width, uint32_t const height, uint32_t const depth,
|
||||
PixelBufferDescriptor&& data, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user) {
|
||||
getJobQueue()->push([this, th, level, xoffset, yoffset, zoffset, width, height, depth,
|
||||
data=std::move(data), handler, callback, user]() mutable {
|
||||
DEBUG_MARKER_NAME("update3DImageAsync")
|
||||
update3DImageCommon(th, level, xoffset, yoffset, zoffset, width, height, depth,
|
||||
std::move(data));
|
||||
// glFlush() should be called when using a shared context for this operation. Without it,
|
||||
// the driver may delay submitting commands to the GPU, preventing other contexts from
|
||||
// seeing the changes immediately. This ensures submitting the current commands right away.
|
||||
glFlush();
|
||||
scheduleCallback(handler, user, callback);
|
||||
}, jobId);
|
||||
}
|
||||
|
||||
void OpenGLDriver::generateMipmaps(Handle<HwTexture> th) {
|
||||
DEBUG_MARKER()
|
||||
|
||||
@@ -3852,6 +4223,10 @@ void OpenGLDriver::tick(int) {
|
||||
#endif
|
||||
executeEveryNowAndThenOps();
|
||||
getShaderCompilerService().tick();
|
||||
if (getJobWorker()) {
|
||||
// This number is randomly/heuristically chosen. Consider making the number optional.
|
||||
getJobWorker()->process(5);
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDriver::beginFrame(
|
||||
@@ -4472,6 +4847,23 @@ void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, uint3 const workGr
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLDriver::queueCommandAsyncR(AsyncCallId jobId, Invocable<void()>&& command, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user) {
|
||||
assert_invariant(getJobQueue());
|
||||
getJobQueue()->push([this, command=std::move(command), handler, callback, user]() {
|
||||
DEBUG_MARKER_NAME("queueCommandAsync")
|
||||
if (command) {
|
||||
command();
|
||||
}
|
||||
scheduleCallback(handler, user, callback);
|
||||
}, jobId);
|
||||
}
|
||||
|
||||
bool OpenGLDriver::cancelAsyncJob(AsyncCallId jobId) {
|
||||
assert_invariant(getJobQueue());
|
||||
return getJobQueue()->cancel(jobId);
|
||||
}
|
||||
|
||||
// explicit instantiation of the Dispatcher
|
||||
template class ConcreteDispatcher<OpenGLDriver>;
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "GLDescriptorSetLayout.h"
|
||||
#include "GLMemoryMappedBuffer.h"
|
||||
#include "GLTexture.h"
|
||||
#include "JobQueue.h"
|
||||
#include "ShaderCompilerService.h"
|
||||
|
||||
#include <backend/AcquiredImage.h>
|
||||
@@ -248,6 +249,9 @@ private:
|
||||
return utils::CString{ mContext.state.renderer };
|
||||
}
|
||||
|
||||
JobQueue* getJobQueue() const noexcept { return mJobQueue.get(); }
|
||||
JobWorker* getJobWorker() const noexcept { return mJobWorker.get(); }
|
||||
|
||||
template<typename T>
|
||||
friend class ConcreteDispatcher;
|
||||
|
||||
@@ -358,6 +362,34 @@ private:
|
||||
static GLsizei getAttachments(AttachmentArray& attachments, TargetBufferFlags buffers,
|
||||
bool isDefaultFramebuffer) noexcept;
|
||||
|
||||
// Common methods
|
||||
void createTextureCommon(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth,
|
||||
TextureUsage usage, utils::ImmutableCString&& tag);
|
||||
void update3DImageCommon(Handle<HwTexture> th,
|
||||
uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
|
||||
uint32_t width, uint32_t height, uint32_t depth,
|
||||
PixelBufferDescriptor&& data);
|
||||
void createTextureViewSwizzleCommon(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
TextureSwizzle r, TextureSwizzle g, TextureSwizzle b,
|
||||
TextureSwizzle a, utils::ImmutableCString&& tag);
|
||||
void importTextureCommon(Handle<HwTexture> th, intptr_t id, SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth,
|
||||
TextureUsage usage, utils::ImmutableCString&& tag);
|
||||
void createBufferObjectCommon(Handle<HwBufferObject> boh, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage, utils::ImmutableCString&& tag);
|
||||
void setVertexBufferObjectCommon(Handle<HwVertexBuffer> vbh, uint32_t index,
|
||||
Handle<HwBufferObject> boh);
|
||||
void updateBufferObjectCommon(Handle<HwBufferObject> boh, BufferDescriptor&& bd,
|
||||
uint32_t byteOffset);
|
||||
void createIndexBufferCommon(Handle<HwIndexBuffer> ibh, ElementType const elementType,
|
||||
uint32_t indexCount, BufferUsage const usage, utils::ImmutableCString&& tag);
|
||||
void updateIndexBufferCommon(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
|
||||
uint32_t const byteOffset);
|
||||
void destroyTextureCommon(Handle<HwTexture> th);
|
||||
void destroyBufferObjectCommon(Handle<HwBufferObject> boh);
|
||||
void destroyIndexBufferCommon(Handle<HwIndexBuffer> ibh);
|
||||
|
||||
// state required to represent the current render pass
|
||||
Handle<HwRenderTarget> mRenderPassTarget;
|
||||
RenderPassParams mRenderPassParams;
|
||||
@@ -413,15 +445,15 @@ private:
|
||||
void executeEveryNowAndThenOps() noexcept;
|
||||
std::vector<std::function<bool()>> mEveryNowAndThenOps;
|
||||
|
||||
const Platform::DriverConfig mDriverConfig;
|
||||
Platform::DriverConfig const& getDriverConfig() const noexcept { return mDriverConfig; }
|
||||
|
||||
// for ES2 sRGB support
|
||||
GLSwapChain* mCurrentDrawSwapChain = nullptr;
|
||||
bool mRec709OutputColorspace = false;
|
||||
|
||||
PushConstantBundle* mCurrentPushConstants = nullptr;
|
||||
PipelineLayout::SetLayout mCurrentSetLayout;
|
||||
|
||||
JobQueue::Ptr mJobQueue;
|
||||
JobWorker::Ptr mJobWorker;
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -19,12 +19,16 @@
|
||||
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class OpenGLDriverBase : public DriverBase {
|
||||
protected:
|
||||
explicit OpenGLDriverBase(const Platform::DriverConfig& driverConfig)
|
||||
: DriverBase(driverConfig) {}
|
||||
~OpenGLDriverBase() override;
|
||||
|
||||
public:
|
||||
|
||||
@@ -254,8 +254,14 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
|
||||
mPushConstants.reserve(totalConstantCount);
|
||||
mPushConstantFragmentStageOffset = vertexConstants.size();
|
||||
auto const transformAndAdd = [&](Program::PushConstant const& constant) {
|
||||
GLint const loc = glGetUniformLocation(program, constant.name.c_str());
|
||||
mPushConstants.push_back({loc, constant.type});
|
||||
if (!constant.name.empty()) {
|
||||
GLint const loc = glGetUniformLocation(program, constant.name.c_str());
|
||||
mPushConstants.push_back({loc, constant.type});
|
||||
} else {
|
||||
// If the constant is not named, then we assume it will not be referenced in this
|
||||
// program.
|
||||
mPushConstants.push_back({ -1, constant.type });
|
||||
}
|
||||
};
|
||||
std::for_each(vertexConstants.cbegin(), vertexConstants.cend(), transformAndAdd);
|
||||
std::for_each(fragmentConstants.cbegin(), fragmentConstants.cend(), transformAndAdd);
|
||||
|
||||
@@ -267,8 +267,6 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
|
||||
eglDupNativeFenceFDANDROID =
|
||||
PFNEGLDUPNATIVEFENCEFDANDROIDPROC(eglGetProcAddress(
|
||||
"eglDupNativeFenceFDANDROID"));
|
||||
} else {
|
||||
LOG(ERROR) << "EGL_ANDROID_native_fence_sync not supported!";
|
||||
}
|
||||
|
||||
mAssertNativeWindowIsValid = driverConfig.assertNativeWindowIsValid;
|
||||
|
||||
@@ -54,17 +54,12 @@ void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* c
|
||||
// Check if we can just memcpy directly to the GPU memory.
|
||||
bool const isMemcopyable = mBuffer->getGpuBuffer()->allocationInfo.pMappedData != nullptr;
|
||||
|
||||
// In the case of UNIFORMS, check that is available to see to know if a memcpy is possible.
|
||||
// This works regardless if it's a full or partial update of the buffer.
|
||||
bool const isUniformAndAvailable = getBinding() == VulkanBufferBinding::UNIFORM && isAvailable;
|
||||
|
||||
// In the case the content is marked as memory mapped or static, is guaranteed to be safe to do
|
||||
// a memcpy if its available.
|
||||
bool const isStaticOrShared =
|
||||
any(mUsage & (BufferUsage::STATIC | BufferUsage::SHARED_WRITE_BIT));
|
||||
bool const useMemcpy =
|
||||
((isUniformAndAvailable && mStagingBufferBypassEnabled) || isStaticOrShared) &&
|
||||
isMemcopyable;
|
||||
((isAvailable && mStagingBufferBypassEnabled) || isStaticOrShared) && isMemcopyable;
|
||||
if (useMemcpy) {
|
||||
char* dest = static_cast<char*>(mBuffer->getGpuBuffer()->allocationInfo.pMappedData) +
|
||||
byteOffset;
|
||||
|
||||
@@ -76,5 +76,4 @@ VkImageSubresourceRange VulkanAttachment::getSubresourceRange() const {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -126,6 +126,10 @@ public:
|
||||
return mDebugUtilsSupported;
|
||||
}
|
||||
|
||||
inline bool isDynamicRenderingSupported() const noexcept {
|
||||
return mDynamicRenderingFeatures.dynamicRendering == VK_TRUE;
|
||||
}
|
||||
|
||||
inline bool isMultiviewEnabled() const noexcept {
|
||||
return mPhysicalDeviceVk11Features.multiview == VK_TRUE;
|
||||
}
|
||||
@@ -150,14 +154,33 @@ public:
|
||||
return mIsUnifiedMemoryArchitecture;
|
||||
}
|
||||
|
||||
inline bool stagingBufferBypassEnabled() const noexcept {
|
||||
return mStagingBufferBypassEnabled;
|
||||
inline bool isVertexInputDynamicStateSupported() const noexcept {
|
||||
return mVertexInputDynamicStateSupported;
|
||||
}
|
||||
|
||||
inline bool pipelineCreationFeedbackSupported() const noexcept {
|
||||
return mPipelineCreationFeedbackSupported;
|
||||
}
|
||||
|
||||
inline bool asyncPipelineCachePrewarmingEnabled() const noexcept {
|
||||
return mAsyncPipelineCachePrewarmingEnabled;
|
||||
}
|
||||
|
||||
inline bool parallelShaderCompilationDisabled() const noexcept {
|
||||
return mParallelShaderCompileDisabled;
|
||||
}
|
||||
|
||||
inline bool stagingBufferBypassEnabled() const noexcept {
|
||||
return mStagingBufferBypassEnabled;
|
||||
}
|
||||
|
||||
inline bool shouldUsePipelineCachePrewarming() const noexcept {
|
||||
return asyncPipelineCachePrewarmingEnabled() &&
|
||||
!parallelShaderCompilationDisabled() &&
|
||||
isVertexInputDynamicStateSupported() &&
|
||||
isDynamicRenderingSupported();
|
||||
}
|
||||
|
||||
private:
|
||||
VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
|
||||
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
|
||||
@@ -169,6 +192,9 @@ private:
|
||||
VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||
};
|
||||
VkPhysicalDeviceDynamicRenderingFeaturesKHR mDynamicRenderingFeatures = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR,
|
||||
};
|
||||
VkPhysicalDevicePortabilitySubsetFeaturesKHR mPortabilitySubsetFeatures = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR,
|
||||
// By default, on platforms where we don't have portability subset, then this feature must
|
||||
@@ -179,13 +205,20 @@ private:
|
||||
|
||||
VkExternalFenceHandleTypeFlags mFenceExportFlags = {};
|
||||
|
||||
// These are options that are either supported or not supported in the current
|
||||
// device and instance.
|
||||
bool mDebugMarkersSupported = false;
|
||||
bool mDebugUtilsSupported = false;
|
||||
bool mLazilyAllocatedMemorySupported = false;
|
||||
bool mProtectedMemorySupported = false;
|
||||
bool mIsUnifiedMemoryArchitecture = false;
|
||||
bool mStagingBufferBypassEnabled = false;
|
||||
bool mLazilyAllocatedMemorySupported = false;
|
||||
bool mPipelineCreationFeedbackSupported = false;
|
||||
bool mProtectedMemorySupported = false;
|
||||
bool mVertexInputDynamicStateSupported = false;
|
||||
|
||||
// These are options that can be enabled or disabled at an application level.
|
||||
bool mAsyncPipelineCachePrewarmingEnabled = false;
|
||||
bool mParallelShaderCompileDisabled = false;
|
||||
bool mStagingBufferBypassEnabled = false;
|
||||
|
||||
fvkutils::VkFormatList mDepthStencilFormats;
|
||||
fvkutils::VkFormatList mBlittableDepthStencilFormats;
|
||||
|
||||
@@ -217,7 +217,8 @@ Dispatcher VulkanDriver::getDispatcher() const noexcept {
|
||||
|
||||
VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context,
|
||||
Platform::DriverConfig const& driverConfig)
|
||||
: mPlatform(platform),
|
||||
: DriverBase(driverConfig),
|
||||
mPlatform(platform),
|
||||
mResourceManager(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck,
|
||||
driverConfig.disableHeapHandleTags),
|
||||
// Note that we always create the default rendertarget before createDefaultRenderTarget(). We
|
||||
@@ -233,7 +234,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context,
|
||||
mPlatform->getGraphicsQueueFamilyIndex(), mPlatform->getProtectedGraphicsQueue(),
|
||||
mPlatform->getProtectedGraphicsQueueFamilyIndex(), mContext, &mSemaphoreManager),
|
||||
mPipelineLayoutCache(mPlatform->getDevice()),
|
||||
mPipelineCache(mPlatform->getDevice(), mContext),
|
||||
mPipelineCache(*this, mPlatform->getDevice(), mContext),
|
||||
mStagePool(mAllocator, &mResourceManager, &mCommands, &mContext.getPhysicalDeviceLimits()),
|
||||
mBufferCache(mContext, mResourceManager, mAllocator),
|
||||
mFramebufferCache(mPlatform->getDevice()),
|
||||
@@ -249,7 +250,9 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context,
|
||||
mStreamedImageManager(&mExternalImageManager),
|
||||
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
|
||||
mIsMSAASwapChainSupported(false), // TODO: support MSAA swapchain
|
||||
mStereoscopicType(driverConfig.stereoscopicType) {
|
||||
mStereoscopicType(driverConfig.stereoscopicType),
|
||||
mStereoscopicEyeCount(driverConfig.stereoscopicEyeCount),
|
||||
mAsynchronousMode(driverConfig.asynchronousMode) {
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
DebugUtils::mSingleton =
|
||||
@@ -601,6 +604,12 @@ void VulkanDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType ele
|
||||
mResourceManager.associateHandle(ibh.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void VulkanDriver::createIndexBufferAsyncR(Handle<HwIndexBuffer> ibh, ElementType elementType,
|
||||
uint32_t indexCount, BufferUsage usage, CallbackHandler* handler,
|
||||
CallbackHandler::Callback callback, void* user, utils::ImmutableCString&& tag) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void VulkanDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
|
||||
if (!ibh) {
|
||||
return;
|
||||
@@ -619,6 +628,12 @@ void VulkanDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byte
|
||||
mResourceManager.associateHandle(boh.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void VulkanDriver::createBufferObjectAsyncR(Handle<HwBufferObject> boh, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage, CallbackHandler* handler,
|
||||
CallbackHandler::Callback callback, void* user, utils::ImmutableCString&& tag) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void VulkanDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
|
||||
if (!boh) {
|
||||
return;
|
||||
@@ -646,6 +661,13 @@ void VulkanDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
mResourceManager.associateHandle(th.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void VulkanDriver::createTextureAsyncR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
|
||||
TextureUsage usage, CallbackHandler* handler, CallbackHandler::Callback callback,
|
||||
void* user, utils::ImmutableCString&& tag) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void VulkanDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
uint8_t baseLevel, uint8_t levelCount, utils::ImmutableCString&& tag) {
|
||||
auto src = resource_ptr<VulkanTexture>::cast(&mResourceManager, srch);
|
||||
@@ -668,6 +690,13 @@ void VulkanDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwText
|
||||
mResourceManager.associateHandle(th.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void VulkanDriver::createTextureViewSwizzleAsyncR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
|
||||
backend::TextureSwizzle a, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user, utils::ImmutableCString&& tag) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
Platform::ExternalImageHandleRef externalImage, utils::ImmutableCString&& tag) {
|
||||
@@ -742,6 +771,16 @@ void VulkanDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
|
||||
mResourceManager.associateHandle(th.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void VulkanDriver::importTextureAsyncR(Handle<HwTexture> th, intptr_t id,
|
||||
SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
|
||||
TextureUsage usage, CallbackHandler* handler, CallbackHandler::Callback callback,
|
||||
void* user, utils::ImmutableCString&& tag) {
|
||||
// not supported in this backend
|
||||
assert_invariant(false && "Not supported in Vulkan backend");
|
||||
mResourceManager.associateHandle(th.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
|
||||
if (!th) {
|
||||
return;
|
||||
@@ -758,6 +797,32 @@ void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program, utils
|
||||
program);
|
||||
vprogram.inc();
|
||||
mResourceManager.associateHandle(ph.getId(), std::move(tag));
|
||||
|
||||
if (!mContext.shouldUsePipelineCachePrewarming()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If async prewarming is enabled, let's find the proper layout and build the pipeline.
|
||||
VulkanDescriptorSetLayout::DescriptorSetLayoutArray vkLayouts {};
|
||||
for (const auto& layoutBinding : program.getDescriptorSetLayouts()) {
|
||||
DescriptorSetLayout layoutDescription = layoutBinding.layout;
|
||||
auto layoutHandle = mResourceManager.allocHandle<VulkanDescriptorSetLayout>();
|
||||
auto layout = mDescriptorSetLayoutCache.createLayout(layoutHandle, std::move(layoutDescription));
|
||||
vkLayouts[layoutBinding.set] = layout->getVkLayout();
|
||||
}
|
||||
|
||||
StereoscopicType stereoscopicType = mStereoscopicType;
|
||||
if (stereoscopicType == StereoscopicType::MULTIVIEW && !program.isMultiview()) {
|
||||
stereoscopicType = StereoscopicType::NONE;
|
||||
}
|
||||
|
||||
VkPipelineLayout layout = mPipelineLayoutCache.getLayout(vkLayouts, vprogram);
|
||||
mPipelineCache.asyncPrewarmCache(
|
||||
*vprogram.get(),
|
||||
layout,
|
||||
stereoscopicType,
|
||||
mStereoscopicEyeCount,
|
||||
program.getPriorityQueue());
|
||||
}
|
||||
|
||||
void VulkanDriver::destroyProgram(Handle<HwProgram> ph) {
|
||||
@@ -772,7 +837,7 @@ void VulkanDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils:
|
||||
assert_invariant(mDefaultRenderTarget); // Default render target should already exist.
|
||||
|
||||
auto renderTarget = resource_ptr<VulkanRenderTarget>::make(&mResourceManager, rth,
|
||||
std::move(std::move(*mDefaultRenderTarget.get())));
|
||||
std::move(*mDefaultRenderTarget.get()));
|
||||
|
||||
mDefaultRenderTarget = renderTarget;
|
||||
mResourceManager.associateHandle(rth.getId(), std::move(tag));
|
||||
@@ -1000,14 +1065,26 @@ Handle<HwIndexBuffer> VulkanDriver::createIndexBufferS() noexcept {
|
||||
return mResourceManager.allocHandle<VulkanIndexBuffer>();
|
||||
}
|
||||
|
||||
Handle<HwIndexBuffer> VulkanDriver::createIndexBufferAsyncS() noexcept {
|
||||
return mResourceManager.allocHandle<VulkanIndexBuffer>();
|
||||
}
|
||||
|
||||
Handle<HwBufferObject> VulkanDriver::createBufferObjectS() noexcept {
|
||||
return mResourceManager.allocHandle<VulkanBufferObject>();
|
||||
}
|
||||
|
||||
Handle<HwBufferObject> VulkanDriver::createBufferObjectAsyncS() noexcept {
|
||||
return mResourceManager.allocHandle<VulkanBufferObject>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> VulkanDriver::createTextureS() noexcept {
|
||||
return mResourceManager.allocHandle<VulkanTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> VulkanDriver::createTextureAsyncS() noexcept {
|
||||
return mResourceManager.allocHandle<VulkanTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> VulkanDriver::createTextureViewS() noexcept {
|
||||
return mResourceManager.allocHandle<VulkanTexture>();
|
||||
}
|
||||
@@ -1016,6 +1093,10 @@ Handle<HwTexture> VulkanDriver::createTextureViewSwizzleS() noexcept {
|
||||
return mResourceManager.allocHandle<VulkanTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> VulkanDriver::createTextureViewSwizzleAsyncS() noexcept {
|
||||
return mResourceManager.allocHandle<VulkanTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> VulkanDriver::createTextureExternalImage2S() noexcept {
|
||||
return mResourceManager.allocHandle<VulkanTexture>();
|
||||
}
|
||||
@@ -1032,6 +1113,30 @@ Handle<HwTexture> VulkanDriver::importTextureS() noexcept {
|
||||
return mResourceManager.allocHandle<VulkanTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> VulkanDriver::importTextureAsyncS() noexcept {
|
||||
return mResourceManager.allocHandle<VulkanTexture>();
|
||||
}
|
||||
|
||||
AsyncCallId VulkanDriver::update3DImageAsyncS() noexcept {
|
||||
// TODO: implement this.
|
||||
return InvalidAsyncCallId;
|
||||
}
|
||||
|
||||
AsyncCallId VulkanDriver::setVertexBufferObjectAsyncS() noexcept {
|
||||
// TODO: implement this.
|
||||
return InvalidAsyncCallId;
|
||||
}
|
||||
|
||||
AsyncCallId VulkanDriver::updateBufferObjectAsyncS() noexcept {
|
||||
// TODO: implement this.
|
||||
return InvalidAsyncCallId;
|
||||
}
|
||||
|
||||
AsyncCallId VulkanDriver::updateIndexBufferAsyncS() noexcept {
|
||||
// TODO: implement this.
|
||||
return InvalidAsyncCallId;
|
||||
}
|
||||
|
||||
Handle<HwRenderPrimitive> VulkanDriver::createRenderPrimitiveS() noexcept {
|
||||
return mResourceManager.allocHandle<VulkanRenderPrimitive>();
|
||||
}
|
||||
@@ -1090,6 +1195,11 @@ MemoryMappedBufferHandle VulkanDriver::mapBufferS() noexcept {
|
||||
return mResourceManager.allocHandle<VulkanMemoryMappedBuffer>();
|
||||
}
|
||||
|
||||
AsyncCallId VulkanDriver::queueCommandAsyncS() noexcept {
|
||||
// TODO: implement this.
|
||||
return InvalidAsyncCallId;
|
||||
}
|
||||
|
||||
void VulkanDriver::unmapBuffer(MemoryMappedBufferHandle mmbh) {
|
||||
if (!mmbh) {
|
||||
return;
|
||||
@@ -1417,7 +1527,7 @@ bool VulkanDriver::isStereoSupported() {
|
||||
}
|
||||
|
||||
bool VulkanDriver::isParallelShaderCompileSupported() {
|
||||
return false;
|
||||
return mContext.shouldUsePipelineCachePrewarming();
|
||||
}
|
||||
|
||||
bool VulkanDriver::isDepthStencilResolveSupported() {
|
||||
@@ -1437,6 +1547,10 @@ bool VulkanDriver::isDepthClampSupported() {
|
||||
return mContext.isDepthClampSupported();
|
||||
}
|
||||
|
||||
bool VulkanDriver::isAsynchronousModeEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
switch (workaround) {
|
||||
case Workaround::SPLIT_EASU: {
|
||||
@@ -1545,6 +1659,12 @@ void VulkanDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t in
|
||||
vb->setBuffer(bo, index);
|
||||
}
|
||||
|
||||
void VulkanDriver::setVertexBufferObjectAsyncR(AsyncCallId jobId, Handle<HwVertexBuffer> vbh,
|
||||
uint32_t index, Handle<HwBufferObject> boh, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
|
||||
uint32_t byteOffset) {
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
@@ -1555,6 +1675,12 @@ void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void VulkanDriver::updateIndexBufferAsyncR(AsyncCallId jobId, Handle<HwIndexBuffer> ibh,
|
||||
BufferDescriptor&& p, uint32_t byteOffset, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& bd,
|
||||
uint32_t byteOffset) {
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
@@ -1567,6 +1693,12 @@ void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescript
|
||||
scheduleDestroy(std::move(bd));
|
||||
}
|
||||
|
||||
void VulkanDriver::updateBufferObjectAsyncR(AsyncCallId jobId, Handle<HwBufferObject> boh,
|
||||
BufferDescriptor&& bd, uint32_t byteOffset, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void VulkanDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> boh,
|
||||
BufferDescriptor&& bd, uint32_t byteOffset) {
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
@@ -1594,6 +1726,13 @@ void VulkanDriver::update3DImage(Handle<HwTexture> th, uint32_t level, uint32_t
|
||||
scheduleDestroy(std::move(data));
|
||||
}
|
||||
|
||||
void VulkanDriver::update3DImageAsyncR(AsyncCallId jobId, Handle<HwTexture> th,
|
||||
uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t width,
|
||||
uint32_t height, uint32_t depth, PixelBufferDescriptor&& data, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void VulkanDriver::setupExternalImage2(Platform::ExternalImageHandleRef image) {
|
||||
}
|
||||
|
||||
@@ -1688,7 +1827,11 @@ void VulkanDriver::generateMipmaps(Handle<HwTexture> th) {
|
||||
void VulkanDriver::compilePrograms(CompilerPriorityQueue priority,
|
||||
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
|
||||
if (callback) {
|
||||
scheduleCallback(handler, user, callback);
|
||||
if (mContext.shouldUsePipelineCachePrewarming()) {
|
||||
mPipelineCache.addCachePrewarmCallback(handler, callback, user);
|
||||
} else {
|
||||
scheduleCallback(handler, user, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1696,9 +1839,6 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
|
||||
auto rt = resource_ptr<VulkanRenderTarget>::cast(&mResourceManager, rth);
|
||||
VkExtent2D const extent = rt->getExtent();
|
||||
|
||||
assert_invariant(rt == mDefaultRenderTarget || extent.width > 0 && extent.height > 0);
|
||||
|
||||
VulkanCommandBuffer* commandBuffer = rt->isProtected() ?
|
||||
&mCommands.getProtected() : &mCommands.get();
|
||||
@@ -1713,9 +1853,16 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
if (sc->isFirstRenderPass()) {
|
||||
discardStart |= TargetBufferFlags::COLOR;
|
||||
sc->markFirstRenderPass();
|
||||
acquireNextSwapchainImage();
|
||||
}
|
||||
}
|
||||
|
||||
// Note that retrieving the extent must come after the acquireNextSwapchainImage() above;
|
||||
// otherwise it might be 0.
|
||||
VkExtent2D const extent = rt->getExtent();
|
||||
|
||||
assert_invariant(rt == mDefaultRenderTarget || extent.width > 0 && extent.height > 0);
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
|
||||
if (rt->hasDepth()) {
|
||||
auto depth = rt->getDepth();
|
||||
@@ -1926,20 +2073,13 @@ void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
|
||||
ASSERT_PRECONDITION_NON_FATAL(drawSch == readSch,
|
||||
"Vulkan driver does not support distinct draw/read swap chains.");
|
||||
|
||||
// Before we can render into the new swapchain, we need to make sure the default render target
|
||||
// releases its old bound swapchain image.
|
||||
mDefaultRenderTarget->releaseSwapchain();
|
||||
|
||||
resource_ptr<VulkanSwapChain> swapChain =
|
||||
resource_ptr<VulkanSwapChain>::cast(&mResourceManager, drawSch);
|
||||
mCurrentSwapChain = swapChain;
|
||||
|
||||
bool resized = false;
|
||||
swapChain->acquire(resized);
|
||||
|
||||
if (resized) {
|
||||
mFramebufferCache.resetFramebuffers();
|
||||
}
|
||||
|
||||
if (UTILS_LIKELY(mDefaultRenderTarget)) {
|
||||
mDefaultRenderTarget->bindToSwapChain(swapChain);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanDriver::commit(Handle<HwSwapChain> sch) {
|
||||
@@ -2110,6 +2250,14 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
auto dstTarget = resource_ptr<VulkanRenderTarget>::cast(&mResourceManager, dst);
|
||||
auto srcTarget = resource_ptr<VulkanRenderTarget>::cast(&mResourceManager, src);
|
||||
|
||||
// This is a valid use of the api. We need to make sure that the swapchain image has been
|
||||
// acquired and associated with the default render target. It's okay to call
|
||||
// acquireNextSwapchainImage() even if a swapchain image has already been bound to the default
|
||||
// render target.
|
||||
if (dstTarget == mDefaultRenderTarget) {
|
||||
acquireNextSwapchainImage();
|
||||
}
|
||||
|
||||
VkFilter const vkfilter = (filter == SamplerMagFilter::NEAREST) ?
|
||||
VK_FILTER_NEAREST : VK_FILTER_LINEAR;
|
||||
|
||||
@@ -2397,6 +2545,16 @@ void VulkanDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
mQueryManager.endQuery(&(mCommands.get()), vtq);
|
||||
}
|
||||
|
||||
void VulkanDriver::queueCommandAsyncR(AsyncCallId jobId, utils::Invocable<void()>&& command,
|
||||
CallbackHandler* handler, CallbackHandler::Callback const callback, void* user) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
bool VulkanDriver::cancelAsyncJob(AsyncCallId jobId) {
|
||||
// TODO: implement this.
|
||||
return false;
|
||||
}
|
||||
|
||||
void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, const char* methodName) noexcept {
|
||||
DriverBase::debugCommandBegin(cmds, synchronous, methodName);
|
||||
#ifndef NDEBUG
|
||||
@@ -2431,6 +2589,25 @@ void VulkanDriver::endCommandRecording() {
|
||||
mDescriptorSetCache.resetCachedState();
|
||||
}
|
||||
|
||||
void VulkanDriver::acquireNextSwapchainImage() {
|
||||
assert_invariant(mCurrentSwapChain);
|
||||
assert_invariant(mDefaultRenderTarget);
|
||||
|
||||
// Swapchain has already been bound to the default render target. We just return.
|
||||
if (mDefaultRenderTarget->isSwapchainBound()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool resized = false;
|
||||
mCurrentSwapChain->acquire(resized);
|
||||
if (resized) {
|
||||
mFramebufferCache.resetFramebuffers();
|
||||
}
|
||||
// Note that ordering this after the above lines is necessary since we set the swapchain image
|
||||
// to the render target in bindSwapChain().
|
||||
mDefaultRenderTarget->bindSwapChain(mCurrentSwapChain);
|
||||
}
|
||||
|
||||
// explicit instantiation of the Dispatcher
|
||||
template class ConcreteDispatcher<VulkanDriver>;
|
||||
|
||||
|
||||
@@ -132,6 +132,8 @@ private:
|
||||
// Flush the current command buffer and reset the pipeline state.
|
||||
void endCommandRecording();
|
||||
|
||||
void acquireNextSwapchainImage();
|
||||
|
||||
VulkanPlatform* mPlatform = nullptr;
|
||||
fvkmemory::ResourceManager mResourceManager;
|
||||
|
||||
@@ -160,7 +162,6 @@ private:
|
||||
VulkanExternalImageManager mExternalImageManager;
|
||||
VulkanStreamedImageManager mStreamedImageManager;
|
||||
|
||||
|
||||
// This maps a VulkanSwapchain to a native swapchain. VulkanSwapchain should have a copy of the
|
||||
// Platform::Swapchain pointer, but queryFrameTimestamps() and queryCompositorTiming() are
|
||||
// synchronous calls, making access to VulkanSwapchain unsafe (this difference vs other backends
|
||||
@@ -205,6 +206,8 @@ private:
|
||||
bool const mIsSRGBSwapChainSupported;
|
||||
bool const mIsMSAASwapChainSupported;
|
||||
backend::StereoscopicType const mStereoscopicType;
|
||||
uint8_t const mStereoscopicEyeCount;
|
||||
backend::AsynchronousMode const mAsynchronousMode;
|
||||
|
||||
// setAcquiredImage is a DECL_DRIVER_API_SYNCHRONOUS_N which means we don't necessarily have the
|
||||
// data to process it at call time. So we store it and process it during updateStreams.
|
||||
|
||||
@@ -100,18 +100,18 @@ inline VkShaderStageFlags getVkStage(backend::ShaderStage stage) {
|
||||
using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask;
|
||||
BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) {
|
||||
BitmaskGroup mask;
|
||||
for (auto const& binding: layout.bindings) {
|
||||
switch (binding.type) {
|
||||
for (auto const& descriptor: layout.descriptors) {
|
||||
switch (descriptor.type) {
|
||||
case DescriptorType::UNIFORM_BUFFER: {
|
||||
if ((binding.flags & DescriptorFlags::DYNAMIC_OFFSET) != DescriptorFlags::NONE) {
|
||||
fromStageFlags(binding.stageFlags, binding.binding, mask.dynamicUbo);
|
||||
if ((descriptor.flags & DescriptorFlags::DYNAMIC_OFFSET) != DescriptorFlags::NONE) {
|
||||
fromStageFlags(descriptor.stageFlags, descriptor.binding, mask.dynamicUbo);
|
||||
} else {
|
||||
fromStageFlags(binding.stageFlags, binding.binding, mask.ubo);
|
||||
fromStageFlags(descriptor.stageFlags, descriptor.binding, mask.ubo);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DescriptorType::SAMPLER_EXTERNAL:
|
||||
fromStageFlags(binding.stageFlags, binding.binding, mask.externalSampler);
|
||||
fromStageFlags(descriptor.stageFlags, descriptor.binding, mask.externalSampler);
|
||||
UTILS_FALLTHROUGH;
|
||||
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
@@ -139,11 +139,11 @@ BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) {
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT: {
|
||||
fromStageFlags(binding.stageFlags, binding.binding, mask.sampler);
|
||||
fromStageFlags(descriptor.stageFlags, descriptor.binding, mask.sampler);
|
||||
break;
|
||||
}
|
||||
case DescriptorType::INPUT_ATTACHMENT: {
|
||||
fromStageFlags(binding.stageFlags, binding.binding, mask.inputAttachment);
|
||||
fromStageFlags(descriptor.stageFlags, descriptor.binding, mask.inputAttachment);
|
||||
break;
|
||||
}
|
||||
case DescriptorType::SHADER_STORAGE_BUFFER:
|
||||
@@ -387,8 +387,9 @@ VulkanRenderTarget::VulkanRenderTarget()
|
||||
|
||||
VulkanRenderTarget::~VulkanRenderTarget() = default;
|
||||
|
||||
void VulkanRenderTarget::bindToSwapChain(fvkmemory::resource_ptr<VulkanSwapChain> swapchain) {
|
||||
void VulkanRenderTarget::bindSwapChain(fvkmemory::resource_ptr<VulkanSwapChain> swapchain) {
|
||||
assert_invariant(!mOffscreen);
|
||||
assert_invariant(!mInfo->colors[0]);
|
||||
|
||||
VkExtent2D const extent = swapchain->getExtent();
|
||||
width = extent.width;
|
||||
@@ -396,7 +397,8 @@ void VulkanRenderTarget::bindToSwapChain(fvkmemory::resource_ptr<VulkanSwapChain
|
||||
mProtected = swapchain->isProtected();
|
||||
|
||||
VulkanAttachment color = createSwapchainAttachment(swapchain->getCurrentColor());
|
||||
mInfo->attachments = {color};
|
||||
assert_invariant(mInfo->attachments.size() == 0);
|
||||
mInfo->attachments.push_back(color);
|
||||
|
||||
auto& fbkey = mInfo->fbkey;
|
||||
auto& rpkey = mInfo->rpkey;
|
||||
@@ -422,6 +424,11 @@ void VulkanRenderTarget::bindToSwapChain(fvkmemory::resource_ptr<VulkanSwapChain
|
||||
mInfo->colors.set(0);
|
||||
}
|
||||
|
||||
void VulkanRenderTarget::releaseSwapchain() {
|
||||
mInfo->colors = {};
|
||||
mInfo->attachments.clear();
|
||||
}
|
||||
|
||||
VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
VulkanContext const& context, fvkmemory::ResourceManager* resourceManager,
|
||||
VmaAllocator allocator, VulkanCommands* commands, uint32_t width, uint32_t height,
|
||||
@@ -675,7 +682,7 @@ void VulkanVertexBuffer::setBuffer(fvkmemory::resource_ptr<VulkanBufferObject> b
|
||||
VulkanBufferObject::VulkanBufferObject(VulkanContext const& context, VmaAllocator allocator,
|
||||
VulkanStagePool& stagePool, VulkanBufferCache& bufferCache, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage)
|
||||
: HwBufferObject(byteCount),
|
||||
: HwBufferObject(byteCount, false),
|
||||
bindingType(bindingType),
|
||||
mBuffer(context, allocator, stagePool, bufferCache, getBufferObjectBinding(bindingType),
|
||||
usage, byteCount) {}
|
||||
|
||||
@@ -366,7 +366,13 @@ struct VulkanRenderTarget : private HwRenderTarget, fvkmemory::Resource {
|
||||
inline bool isSwapChain() const { return !mOffscreen; }
|
||||
inline bool isProtected() const { return mProtected; }
|
||||
|
||||
void bindToSwapChain(fvkmemory::resource_ptr<VulkanSwapChain> swapchain);
|
||||
void bindSwapChain(fvkmemory::resource_ptr<VulkanSwapChain> swapchain);
|
||||
|
||||
void releaseSwapchain();
|
||||
|
||||
bool isSwapchainBound() const {
|
||||
return isSwapChain() && mInfo->colors[0];
|
||||
}
|
||||
|
||||
void emitBarriersBeginRenderPass(VulkanCommandBuffer& commands);
|
||||
|
||||
@@ -470,7 +476,7 @@ struct VulkanIndexBuffer : public HwIndexBuffer, fvkmemory::Resource {
|
||||
VulkanIndexBuffer(VulkanContext const& context, VmaAllocator allocator,
|
||||
VulkanStagePool& stagePool, VulkanBufferCache& bufferCache, uint8_t elementSize,
|
||||
uint32_t indexCount)
|
||||
: HwIndexBuffer(elementSize, indexCount),
|
||||
: HwIndexBuffer(elementSize, indexCount, false),
|
||||
indexType(elementSize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32),
|
||||
mBuffer(context, allocator, stagePool, bufferCache, VulkanBufferBinding::INDEX,
|
||||
BufferUsage::STATIC, elementSize * indexCount) {}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "VulkanPipelineCache.h"
|
||||
|
||||
#include <utils/JobSystem.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
@@ -36,6 +37,8 @@ namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
using utils::JobSystem;
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
void printPipelineFeedbackInfo(VkPipelineCreationFeedbackCreateInfo const& feedbackInfo) {
|
||||
VkPipelineCreationFeedback const& pipelineInfo = *feedbackInfo.pPipelineCreationFeedback;
|
||||
@@ -66,13 +69,26 @@ void printPipelineFeedbackInfo(VkPipelineCreationFeedbackCreateInfo const& feedb
|
||||
|
||||
} // namespace
|
||||
|
||||
VulkanPipelineCache::VulkanPipelineCache(VkDevice device, VulkanContext const& context)
|
||||
VulkanPipelineCache::VulkanPipelineCache(DriverBase& driver, VkDevice device, VulkanContext const& context)
|
||||
: mDevice(device),
|
||||
mCallbackManager(driver),
|
||||
mContext(context) {
|
||||
VkPipelineCacheCreateInfo createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
|
||||
};
|
||||
bluevk::vkCreatePipelineCache(mDevice, &createInfo, VKALLOC, &mPipelineCache);
|
||||
|
||||
if (mContext.shouldUsePipelineCachePrewarming()) {
|
||||
mCompilerThreadPool.init(
|
||||
/*threadCount=*/1,
|
||||
[]() {
|
||||
JobSystem::setThreadName("CompilerThreadPool");
|
||||
// This thread should be lower priority than the main thread.
|
||||
JobSystem::setThreadPriority(JobSystem::Priority::DISPLAY);
|
||||
}, []() {
|
||||
// No cleanup required.
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::bindLayout(VkPipelineLayout layout) noexcept {
|
||||
@@ -87,9 +103,12 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::getOrCreatePipelin
|
||||
pipeline.lastUsed = mCurrentTime;
|
||||
return &pipeline;
|
||||
}
|
||||
auto ret = createPipeline();
|
||||
ret->lastUsed = mCurrentTime;
|
||||
return ret;
|
||||
PipelineCacheEntry cacheEntry {
|
||||
.handle = createPipeline(mPipelineRequirements),
|
||||
.lastUsed = mCurrentTime,
|
||||
};
|
||||
assert_invariant(cacheEntry.handle != VK_NULL_HANDLE && "Pipeline handle is VK_NULL_HANDLE");
|
||||
return &mPipelines.emplace(mPipelineRequirements, cacheEntry).first.value();
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::bindPipeline(VulkanCommandBuffer* commands) {
|
||||
@@ -107,70 +126,139 @@ void VulkanPipelineCache::bindPipeline(VulkanCommandBuffer* commands) {
|
||||
}
|
||||
}
|
||||
|
||||
VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() noexcept {
|
||||
assert_invariant(mPipelineRequirements.shaders[0] && "Vertex shader is not bound.");
|
||||
assert_invariant(mPipelineRequirements.layout && "No pipeline layout specified");
|
||||
void VulkanPipelineCache::asyncPrewarmCache(const VulkanProgram& program,
|
||||
VkPipelineLayout layout,
|
||||
StereoscopicType stereoscopicType,
|
||||
uint8_t stereoscopicViewCount, CompilerPriorityQueue priority) {
|
||||
PipelineKey key {
|
||||
.shaders = {
|
||||
program.getVertexShader(),
|
||||
program.getFragmentShader(),
|
||||
},
|
||||
// We're using dynamic rendering, so this should be empty.
|
||||
.renderPass = VK_NULL_HANDLE,
|
||||
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
||||
.subpassIndex = 0,
|
||||
// We're using vertex input dynamic state, so these should be empty.
|
||||
.vertexAttributes = {},
|
||||
.vertexBuffers = {},
|
||||
// Create a reasonable default raster state; we're assuming this is not cached.
|
||||
.rasterState = {
|
||||
.cullMode = VK_CULL_MODE_NONE,
|
||||
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
|
||||
.depthBiasEnable = VK_FALSE,
|
||||
.blendEnable = VK_FALSE,
|
||||
.depthWriteEnable = VK_FALSE,
|
||||
.alphaToCoverageEnable = VK_FALSE,
|
||||
.srcColorBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||
.dstColorBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||
.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||
.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE,
|
||||
.colorWriteMask = 0,
|
||||
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.depthClamp = VK_FALSE,
|
||||
.colorTargetCount = 1,
|
||||
.colorBlendOp = BlendEquation::SUBTRACT,
|
||||
.alphaBlendOp = BlendEquation::SUBTRACT,
|
||||
.depthCompareOp = SamplerCompareFunc::L,
|
||||
.depthBiasConstantFactor = 0.f,
|
||||
.depthBiasSlopeFactor = 0.f,
|
||||
},
|
||||
.layout = layout,
|
||||
};
|
||||
PipelineDynamicOptions dynamicOptions {
|
||||
.useDynamicVertexInputState = true,
|
||||
.useDynamicRenderPasses = true,
|
||||
.stereoscopicType = stereoscopicType,
|
||||
.stereoscopicViewCount = stereoscopicViewCount,
|
||||
};
|
||||
|
||||
CallbackManager::Handle cmh = mCallbackManager.get();
|
||||
auto token = std::make_shared<ProgramToken>();
|
||||
mCompilerThreadPool.queue(priority, token, [this, key, dynamicOptions, cmh]() mutable {
|
||||
VkPipeline pipeline = createPipeline(key, dynamicOptions);
|
||||
mCallbackManager.put(cmh);
|
||||
// We don't actually need this pipeline, we just wanted to force the driver to cache
|
||||
// the pipeline's information.
|
||||
vkDestroyPipeline(mDevice, pipeline, VKALLOC);
|
||||
});
|
||||
}
|
||||
|
||||
VkPipeline VulkanPipelineCache::createPipeline(
|
||||
const PipelineKey& key, const PipelineDynamicOptions& dynamicOptions) noexcept {
|
||||
assert_invariant(key.shaders[0] && "Vertex shader is not bound.");
|
||||
assert_invariant(key.layout && "No pipeline layout specified");
|
||||
|
||||
VkPipelineShaderStageCreateInfo shaderStages[SHADER_MODULE_COUNT];
|
||||
shaderStages[0] = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.stage = VK_SHADER_STAGE_VERTEX_BIT,
|
||||
.module = mPipelineRequirements.shaders[0],
|
||||
.module = key.shaders[0],
|
||||
.pName = "main",
|
||||
};
|
||||
shaderStages[1] = shaderStages[0];
|
||||
shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
shaderStages[1].module = mPipelineRequirements.shaders[1];
|
||||
shaderStages[1].module = key.shaders[1];
|
||||
|
||||
bool const hasFragmentShader = shaderStages[1].module != VK_NULL_HANDLE;
|
||||
|
||||
VkPipelineColorBlendAttachmentState colorBlendAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT];
|
||||
VkPipelineColorBlendStateCreateInfo colorBlendState = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
|
||||
.attachmentCount = mPipelineRequirements.rasterState.colorTargetCount,
|
||||
.attachmentCount = key.rasterState.colorTargetCount,
|
||||
.pAttachments = colorBlendAttachments,
|
||||
};
|
||||
|
||||
// Expand our size-optimized structs into the proper Vk structs.
|
||||
uint32_t numVertexAttribs = 0;
|
||||
uint32_t numVertexBuffers = 0;
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertexInputState;
|
||||
VkVertexInputAttributeDescription vertexAttributes[VERTEX_ATTRIBUTE_COUNT];
|
||||
VkVertexInputBindingDescription vertexBuffers[VERTEX_ATTRIBUTE_COUNT];
|
||||
for (uint32_t i = 0; i < VERTEX_ATTRIBUTE_COUNT; i++) {
|
||||
if (mPipelineRequirements.vertexAttributes[i].format > 0) {
|
||||
vertexAttributes[numVertexAttribs++] = mPipelineRequirements.vertexAttributes[i];
|
||||
}
|
||||
if (mPipelineRequirements.vertexBuffers[i].stride > 0) {
|
||||
vertexBuffers[numVertexBuffers++] = mPipelineRequirements.vertexBuffers[i];
|
||||
if (!dynamicOptions.useDynamicVertexInputState) {
|
||||
uint32_t numVertexAttribs = 0;
|
||||
uint32_t numVertexBuffers = 0;
|
||||
|
||||
for (uint32_t i = 0; i < VERTEX_ATTRIBUTE_COUNT; i++) {
|
||||
if (key.vertexAttributes[i].format > 0) {
|
||||
vertexAttributes[numVertexAttribs++] = key.vertexAttributes[i];
|
||||
}
|
||||
if (key.vertexBuffers[i].stride > 0) {
|
||||
vertexBuffers[numVertexBuffers++] = key.vertexBuffers[i];
|
||||
}
|
||||
}
|
||||
vertexInputState = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = numVertexBuffers,
|
||||
.pVertexBindingDescriptions = vertexBuffers,
|
||||
.vertexAttributeDescriptionCount = numVertexAttribs,
|
||||
.pVertexAttributeDescriptions = vertexAttributes,
|
||||
};
|
||||
}
|
||||
VkPipelineVertexInputStateCreateInfo vertexInputState = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
|
||||
.vertexBindingDescriptionCount = numVertexBuffers,
|
||||
.pVertexBindingDescriptions = vertexBuffers,
|
||||
.vertexAttributeDescriptionCount = numVertexAttribs,
|
||||
.pVertexAttributeDescriptions = vertexAttributes,
|
||||
};
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
|
||||
.topology = (VkPrimitiveTopology) mPipelineRequirements.topology,
|
||||
.topology = (VkPrimitiveTopology) key.topology,
|
||||
};
|
||||
VkPipelineViewportStateCreateInfo viewportState = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
|
||||
.viewportCount = 1,
|
||||
.scissorCount = 1,
|
||||
};
|
||||
VkDynamicState dynamicStateEnables[] = {
|
||||
|
||||
constexpr size_t maxDynamicStates = 3;
|
||||
size_t numDynamicStates = 2;
|
||||
VkDynamicState enabledDynamicStates[maxDynamicStates] = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR,
|
||||
};
|
||||
if (dynamicOptions.useDynamicVertexInputState) {
|
||||
enabledDynamicStates[numDynamicStates++] = VK_DYNAMIC_STATE_VERTEX_INPUT_EXT;
|
||||
}
|
||||
VkPipelineDynamicStateCreateInfo dynamicState = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
|
||||
.dynamicStateCount = 2,
|
||||
.pDynamicStates = dynamicStateEnables,
|
||||
.dynamicStateCount = static_cast<uint32_t>(numDynamicStates),
|
||||
.pDynamicStates = enabledDynamicStates,
|
||||
};
|
||||
auto const& raster = mPipelineRequirements.rasterState;
|
||||
|
||||
auto const& raster = key.rasterState;
|
||||
VkPipelineRasterizationStateCreateInfo vkRaster = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
|
||||
.depthClampEnable = raster.depthClamp,
|
||||
@@ -218,7 +306,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
|
||||
.stageCount = hasFragmentShader ? SHADER_MODULE_COUNT : 1,
|
||||
.pStages = shaderStages,
|
||||
.pVertexInputState = &vertexInputState,
|
||||
.pVertexInputState = dynamicOptions.useDynamicVertexInputState ? nullptr : &vertexInputState,
|
||||
.pInputAssemblyState = &inputAssemblyState,
|
||||
.pViewportState = &viewportState,
|
||||
.pRasterizationState = &vkRaster,
|
||||
@@ -226,9 +314,9 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
.pDepthStencilState = &vkDs,
|
||||
.pColorBlendState = &colorBlendState,
|
||||
.pDynamicState = &dynamicState,
|
||||
.layout = mPipelineRequirements.layout,
|
||||
.renderPass = mPipelineRequirements.renderPass,
|
||||
.subpass = mPipelineRequirements.subpassIndex,
|
||||
.layout = key.layout,
|
||||
.renderPass = key.renderPass,
|
||||
.subpass = key.subpassIndex,
|
||||
};
|
||||
|
||||
// There are no color attachments if there is no bound fragment shader. (e.g. shadow map gen)
|
||||
@@ -252,6 +340,27 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
}
|
||||
}
|
||||
|
||||
VkPipelineRenderingCreateInfoKHR renderingInfo {};
|
||||
VkFormat pipelineRenderingColorFormats[] = {VK_FORMAT_UNDEFINED};
|
||||
if (dynamicOptions.useDynamicRenderPasses) {
|
||||
renderingInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR;
|
||||
renderingInfo.pNext = pipelineCreateInfo.pNext;
|
||||
// Fill values in with empty values where we do not already have values.
|
||||
renderingInfo.depthAttachmentFormat = VK_FORMAT_UNDEFINED;
|
||||
renderingInfo.stencilAttachmentFormat = VK_FORMAT_UNDEFINED;
|
||||
// If multiview, create a bitmask with bits representing each enabled view set to 1;
|
||||
// otherwise, set the view mask to 0.
|
||||
renderingInfo.viewMask = dynamicOptions.stereoscopicType == StereoscopicType::MULTIVIEW ?
|
||||
(1 << dynamicOptions.stereoscopicViewCount) - 1 : 0;
|
||||
|
||||
if (hasFragmentShader) {
|
||||
renderingInfo.colorAttachmentCount = 1;
|
||||
renderingInfo.pColorAttachmentFormats = pipelineRenderingColorFormats;
|
||||
}
|
||||
|
||||
pipelineCreateInfo.pNext = &renderingInfo;
|
||||
}
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
FVK_LOGD << "vkCreateGraphicsPipelines with shaders = (" << shaderStages[0].module << ", "
|
||||
<< shaderStages[1].module << ")";
|
||||
@@ -271,11 +380,9 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
pipelineCreateInfo.pNext = &feedbackInfo;
|
||||
}
|
||||
#endif
|
||||
PipelineCacheEntry cacheEntry = {
|
||||
.lastUsed = mCurrentTime,
|
||||
};
|
||||
VkPipeline pipeline;
|
||||
VkResult error = vkCreateGraphicsPipelines(mDevice, mPipelineCache, 1, &pipelineCreateInfo,
|
||||
VKALLOC, &cacheEntry.handle);
|
||||
VKALLOC, &pipeline);
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
FVK_LOGD << "vkCreateGraphicsPipelines with shaders = (" << shaderStages[0].module << ", "
|
||||
@@ -289,9 +396,9 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
assert_invariant(error == VK_SUCCESS);
|
||||
if (error != VK_SUCCESS) {
|
||||
FVK_LOGE << "vkCreateGraphicsPipelines error " << error;
|
||||
return nullptr;
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
return &mPipelines.emplace(mPipelineRequirements, cacheEntry).first.value();
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept {
|
||||
@@ -334,6 +441,14 @@ void VulkanPipelineCache::bindVertexArray(VkVertexInputAttributeDescription cons
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::addCachePrewarmCallback(CallbackHandler* handler,
|
||||
const CallbackHandler::Callback callback,
|
||||
void* user) {
|
||||
if (callback) {
|
||||
mCallbackManager.setCallback(handler, callback, user);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::resetBoundPipeline() {
|
||||
mBoundPipeline = {};
|
||||
}
|
||||
@@ -345,6 +460,9 @@ void VulkanPipelineCache::terminate() noexcept {
|
||||
mPipelines.clear();
|
||||
resetBoundPipeline();
|
||||
|
||||
mCallbackManager.terminate();
|
||||
mCompilerThreadPool.terminate();
|
||||
|
||||
vkDestroyPipelineCache(mDevice, mPipelineCache, VKALLOC);
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANPIPELINECACHE_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANPIPELINECACHE_H
|
||||
|
||||
#include "CallbackManager.h"
|
||||
#include "CompilerThreadPool.h"
|
||||
#include "VulkanCommands.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
@@ -86,14 +88,38 @@ public:
|
||||
|
||||
static_assert(sizeof(RasterState) == 16, "RasterState must not have implicit padding.");
|
||||
|
||||
VulkanPipelineCache(VkDevice device, VulkanContext const& context);
|
||||
/**
|
||||
* Creates a new instance of a pipeline cache for graphics pipelines.
|
||||
*
|
||||
* @param driver The driver this is being instantiated for. This is used only for construction of
|
||||
* the callback manager, which references the driver for scheduling callbacks.
|
||||
* @param device The device that the pipelines will be created and run on.
|
||||
* @param context Information about the current instance of Vulkan, such as supported extensions,
|
||||
* and enabled features.
|
||||
*/
|
||||
VulkanPipelineCache(DriverBase& driver, VkDevice device, VulkanContext const& context);
|
||||
|
||||
void bindLayout(VkPipelineLayout layout) noexcept;
|
||||
// Loads a fake pipeline into memory on a separate thread, with the intent of
|
||||
// preloading the Vulkan cache with enough information to have a cache hit when
|
||||
// compiling the pipeline on the main thread at draw time. This is very dependent
|
||||
// on the implementation of the driver on the current device; it's expected to work
|
||||
// on devices with VK_EXT_vertex_input_dynamic_state and VK_KHR_dynamic_rendering.
|
||||
void asyncPrewarmCache(const VulkanProgram& program, VkPipelineLayout layout,
|
||||
StereoscopicType stereoscopicType, uint8_t stereoscopicViewCount,
|
||||
CompilerPriorityQueue priority);
|
||||
|
||||
// Notifies the callback once all in-flight async cache prewarm jobs are complete.
|
||||
// This typically signals that it is safe to use a material at draw time without
|
||||
// hitching.
|
||||
void addCachePrewarmCallback(CallbackHandler* handler,
|
||||
const CallbackHandler::Callback callback,
|
||||
void* user);
|
||||
|
||||
// Creates a new pipeline if necessary and binds it using vkCmdBindPipeline.
|
||||
void bindPipeline(VulkanCommandBuffer* commands);
|
||||
|
||||
// Each of the following methods are fast and do not make Vulkan calls.
|
||||
void bindLayout(VkPipelineLayout layout) noexcept;
|
||||
void bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept;
|
||||
void bindRasterState(RasterState const& rasterState) noexcept;
|
||||
void bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept;
|
||||
@@ -164,6 +190,19 @@ private:
|
||||
VkPipelineLayout layout; // 8 : 304
|
||||
};
|
||||
|
||||
// Provides information about any dynamic state that should be used in creation of the
|
||||
// pipeline (if supported).
|
||||
struct PipelineDynamicOptions {
|
||||
// Requires `VK_EXT_vertex_input_dynamic_state` to enable.
|
||||
bool useDynamicVertexInputState = false;
|
||||
// Requires `VK_KHR_dynamic_rendering` to enable.
|
||||
bool useDynamicRenderPasses = false;
|
||||
// Only used if `useDynamicRenderPasses` is true.
|
||||
StereoscopicType stereoscopicType = StereoscopicType::NONE;
|
||||
// Only used if `stereoscopicType` is `StereoscopicType::MULTIVIEW`.
|
||||
uint8_t stereoscopicViewCount = 2;
|
||||
};
|
||||
|
||||
static_assert(sizeof(PipelineKey) == 312, "PipelineKey must not have implicit padding.");
|
||||
|
||||
using PipelineHashFn = utils::hash::MurmurHashFn<PipelineKey>;
|
||||
@@ -195,8 +234,15 @@ private:
|
||||
|
||||
PipelineMap mPipelines;
|
||||
|
||||
// Creates a pipeline, with all optional dynamic pipeline states diabled.
|
||||
// Note - because PipelineDynamicOptions is defined within VulkanPipelineCache,
|
||||
// we cannot define the function with a default arg for it explicitly.
|
||||
inline VkPipeline createPipeline(const PipelineKey& key) noexcept {
|
||||
return createPipeline(key, {});
|
||||
}
|
||||
|
||||
// These helpers all return unstable pointers that should not be stored.
|
||||
PipelineCacheEntry* createPipeline() noexcept;
|
||||
VkPipeline createPipeline(const PipelineKey& key, const PipelineDynamicOptions& dynamicOptions) noexcept;
|
||||
|
||||
// Immutable state.
|
||||
VkDevice mDevice = VK_NULL_HANDLE;
|
||||
@@ -211,6 +257,14 @@ private:
|
||||
// Current bindings for the pipeline and descriptor sets.
|
||||
PipelineKey mBoundPipeline = {};
|
||||
|
||||
// Thread pool that allows us to "prewarm" the pipeline cache, reducing draw-time
|
||||
// pipeline compilation time.
|
||||
CompilerThreadPool mCompilerThreadPool;
|
||||
|
||||
// Callback manager that allows us to notify the frontend when a set of pipelines have
|
||||
// been prewarmed, signifying that it is safe to compile pipelines at draw time.
|
||||
CallbackManager mCallbackManager;
|
||||
|
||||
[[maybe_unused]] VulkanContext const& mContext;
|
||||
};
|
||||
|
||||
|
||||
@@ -346,7 +346,7 @@ VulkanTexture::VulkanTexture(VulkanContext const& context, VkDevice device, VmaA
|
||||
uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage tusage,
|
||||
VulkanStagePool& stagePool)
|
||||
: HwTexture(getSamplerTypeFromDepth(depth), 1, samples, width, height, depth,
|
||||
TextureFormat::UNUSED, tusage),
|
||||
TextureFormat::UNUSED, tusage, false),
|
||||
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, stagePool,
|
||||
commands, allocator, device, image, memory, format,
|
||||
fvkutils::getViewType(SamplerType::SAMPLER_2D),
|
||||
@@ -362,7 +362,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, SamplerType target,
|
||||
uint8_t levels, TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h,
|
||||
uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool)
|
||||
: HwTexture(target, levels, samples, w, h, depth, tformat, tusage) {
|
||||
: HwTexture(target, levels, samples, w, h, depth, tformat, tusage, false) {
|
||||
// Create an appropriately-sized device-only VkImage, but do not fill it yet.
|
||||
VkFormat const vkFormat = fvkutils::getVkFormat(tformat);
|
||||
bool const isProtected = any(tusage & TextureUsage::PROTECTED);
|
||||
@@ -492,7 +492,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
fvkmemory::resource_ptr<VulkanTexture> src, uint8_t baseLevel,
|
||||
uint8_t levelCount)
|
||||
: HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth,
|
||||
src->format, src->usage),
|
||||
src->format, src->usage, src->asynchronous),
|
||||
mState(src->mState) {
|
||||
mPrimaryViewRange = src->mPrimaryViewRange;
|
||||
mPrimaryViewRange.baseMipLevel = src->mPrimaryViewRange.baseMipLevel + baseLevel;
|
||||
@@ -504,7 +504,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
|
||||
fvkmemory::resource_ptr<VulkanTexture> src, VkComponentMapping swizzle)
|
||||
: HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth,
|
||||
src->format, src->usage),
|
||||
src->format, src->usage, src->asynchronous),
|
||||
mState(src->mState),
|
||||
mPrimaryViewRange(src->mPrimaryViewRange),
|
||||
mSwizzle(composeSwizzle(src->mSwizzle, swizzle)) {}
|
||||
|
||||
@@ -227,7 +227,11 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
|
||||
#if defined(__APPLE__)
|
||||
VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,
|
||||
#endif
|
||||
VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME,
|
||||
VK_KHR_MULTIVIEW_EXTENSION_NAME,
|
||||
VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME,
|
||||
// Required for dynamic rendering, enable this too.
|
||||
VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME,
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
|
||||
VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME,
|
||||
@@ -255,188 +259,6 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
|
||||
return exts;
|
||||
}
|
||||
|
||||
VkInstance createInstance(ExtensionSet const& requiredExts) {
|
||||
// Create the Vulkan instance.
|
||||
VkApplicationInfo appInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||
.pEngineName = "Filament",
|
||||
.apiVersion =
|
||||
VK_MAKE_API_VERSION(0, FVK_REQUIRED_VERSION_MAJOR, FVK_REQUIRED_VERSION_MINOR, 0),
|
||||
};
|
||||
|
||||
VkInstance instance;
|
||||
VkInstanceCreateInfo instanceCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||||
.pApplicationInfo = &appInfo,
|
||||
};
|
||||
bool validationFeaturesSupported = false;
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
|
||||
auto const enabledLayers = getEnabledLayers();
|
||||
if (!enabledLayers.empty()) {
|
||||
// If layers are supported, Check if VK_EXT_validation_features is supported.
|
||||
FixedCapacityVector<VkExtensionProperties> const availableValidationExts
|
||||
= fvkutils::enumerate(vkEnumerateInstanceExtensionProperties,
|
||||
"VK_LAYER_KHRONOS_validation");
|
||||
for (auto const& extProps: availableValidationExts) {
|
||||
if (!strcmp(extProps.extensionName, VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME)) {
|
||||
validationFeaturesSupported = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
instanceCreateInfo.enabledLayerCount = (uint32_t) enabledLayers.size();
|
||||
instanceCreateInfo.ppEnabledLayerNames = enabledLayers.data();
|
||||
} else {
|
||||
#if defined(__ANDROID__)
|
||||
FVK_LOGD << "Validation layers are not available; did you set jniLibs in your "
|
||||
<< "gradle file?";
|
||||
#else
|
||||
FVK_LOGD << "Validation layer not available; did you install the Vulkan SDK?\n"
|
||||
<< "Please ensure that VK_LAYER_PATH is set correctly.";
|
||||
#endif // __ANDROID__
|
||||
|
||||
}
|
||||
#endif // FVK_ENABLED(FVK_DEBUG_VALIDATION)
|
||||
|
||||
// The Platform class can require 1 or 2 instance extensions, plus we'll request at most 6
|
||||
// instance extensions here in the common code. So that's a max of 8.
|
||||
constexpr uint32_t MAX_INSTANCE_EXTENSION_COUNT = 8;
|
||||
char const* ppEnabledExtensions[MAX_INSTANCE_EXTENSION_COUNT];
|
||||
uint32_t enabledExtensionCount = 0;
|
||||
|
||||
if (validationFeaturesSupported) {
|
||||
ppEnabledExtensions[enabledExtensionCount++] = VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME;
|
||||
}
|
||||
// Request platform-specific extensions.
|
||||
for (auto const& requiredExt: requiredExts) {
|
||||
assert_invariant(enabledExtensionCount < MAX_INSTANCE_EXTENSION_COUNT);
|
||||
ppEnabledExtensions[enabledExtensionCount++] = requiredExt.data();
|
||||
}
|
||||
|
||||
instanceCreateInfo.enabledExtensionCount = enabledExtensionCount;
|
||||
instanceCreateInfo.ppEnabledExtensionNames = ppEnabledExtensions;
|
||||
if (setContains(requiredExts, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) {
|
||||
instanceCreateInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
||||
}
|
||||
|
||||
// Validation features
|
||||
VkValidationFeatureEnableEXT enables[] = {
|
||||
VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT,
|
||||
VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT,
|
||||
};
|
||||
VkValidationFeaturesEXT features = {
|
||||
.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT,
|
||||
.enabledValidationFeatureCount = sizeof(enables) / sizeof(enables[0]),
|
||||
.pEnabledValidationFeatures = enables,
|
||||
};
|
||||
if (validationFeaturesSupported) {
|
||||
chainStruct(&instanceCreateInfo, &features);
|
||||
}
|
||||
|
||||
VkResult result = vkCreateInstance(&instanceCreateInfo, VKALLOC, &instance);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create Vulkan instance. error=" << static_cast<int32_t>(result);
|
||||
return instance;
|
||||
}
|
||||
|
||||
VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
|
||||
VkPhysicalDeviceFeatures2 const& features,
|
||||
VkPhysicalDeviceVulkan11Features const& vk11Features,
|
||||
uint32_t graphicsQueueFamilyIndex,
|
||||
uint32_t protectedGraphicsQueueFamilyIndex, ExtensionSet const& deviceExtensions,
|
||||
bool requestImageView2DOn3DImage) {
|
||||
VkDevice device;
|
||||
float queuePriority[] = {1.0f};
|
||||
VkDeviceCreateInfo deviceCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
};
|
||||
FixedCapacityVector<const char*> requestExtensions;
|
||||
requestExtensions.reserve(deviceExtensions.size() + 1);
|
||||
|
||||
// TODO: We don't really need this if we only ever expect headless swapchains.
|
||||
requestExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
for (auto const& ext: deviceExtensions) {
|
||||
requestExtensions.push_back(ext.data());
|
||||
}
|
||||
VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = {};
|
||||
deviceQueueCreateInfo[0] = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||
.queueFamilyIndex = graphicsQueueFamilyIndex,
|
||||
.queueCount = 1,
|
||||
.pQueuePriorities = &queuePriority[0],
|
||||
};
|
||||
// Protected queue
|
||||
deviceQueueCreateInfo[1] = deviceQueueCreateInfo[0];
|
||||
deviceQueueCreateInfo[1].flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT;
|
||||
|
||||
bool const hasProtectedQueue = protectedGraphicsQueueFamilyIndex != INVALID_VK_INDEX;
|
||||
deviceCreateInfo.queueCreateInfoCount = hasProtectedQueue ? 2 : 1;
|
||||
deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
|
||||
|
||||
// We could simply enable all supported features, but since that may have performance
|
||||
// consequences let's just enable the features we need.
|
||||
VkPhysicalDeviceFeatures enabledFeatures = {
|
||||
.depthClamp = features.features.depthClamp,
|
||||
.samplerAnisotropy = features.features.samplerAnisotropy,
|
||||
.textureCompressionETC2 = features.features.textureCompressionETC2,
|
||||
.textureCompressionBC = features.features.textureCompressionBC,
|
||||
.shaderClipDistance = features.features.shaderClipDistance,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceFeatures2 enabledFeatures2 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||
.features = enabledFeatures,
|
||||
};
|
||||
chainStruct(&deviceCreateInfo, &enabledFeatures2);
|
||||
|
||||
VkPhysicalDeviceVulkan11Features enabledVk11Features = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
|
||||
.multiview = vk11Features.multiview,
|
||||
#if defined(__ANDROID__)
|
||||
.samplerYcbcrConversion = vk11Features.samplerYcbcrConversion,
|
||||
#endif
|
||||
};
|
||||
chainStruct(&deviceCreateInfo, &enabledVk11Features);
|
||||
|
||||
deviceCreateInfo.enabledExtensionCount = (uint32_t) requestExtensions.size();
|
||||
deviceCreateInfo.ppEnabledExtensionNames = requestExtensions.data();
|
||||
|
||||
VkPhysicalDevicePortabilitySubsetFeaturesKHR portability = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR,
|
||||
.imageViewFormatSwizzle = VK_TRUE,
|
||||
.imageView2DOn3DImage = requestImageView2DOn3DImage ? VK_TRUE : VK_FALSE,
|
||||
.mutableComparisonSamplers = VK_TRUE,
|
||||
};
|
||||
if (setContains(deviceExtensions, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) {
|
||||
chainStruct(&deviceCreateInfo, &portability);
|
||||
}
|
||||
|
||||
VkPhysicalDeviceMultiviewFeaturesKHR multiview = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR,
|
||||
.multiview = vk11Features.multiview,
|
||||
.multiviewGeometryShader = VK_FALSE,
|
||||
.multiviewTessellationShader = VK_FALSE,
|
||||
};
|
||||
if (setContains(deviceExtensions, VK_KHR_MULTIVIEW_EXTENSION_NAME)) {
|
||||
chainStruct(&deviceCreateInfo, &multiview);
|
||||
}
|
||||
|
||||
VkPhysicalDeviceProtectedMemoryFeatures protectedMemory = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES,
|
||||
.protectedMemory = VK_TRUE,
|
||||
};
|
||||
if (hasProtectedQueue) {
|
||||
// Enable protected memory, if requested.
|
||||
chainStruct(&deviceCreateInfo, &protectedMemory);
|
||||
}
|
||||
|
||||
VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, VKALLOC, &device);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkCreateDevice error=" << static_cast<int32_t>(result);
|
||||
|
||||
return device;
|
||||
}
|
||||
|
||||
// This method is used to enable/disable extensions based on external factors (i.e.
|
||||
// driver/device workarounds).
|
||||
std::tuple<ExtensionSet, ExtensionSet> pruneExtensions(VkPhysicalDevice device,
|
||||
@@ -719,11 +541,6 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
mImpl->mSharedContext = true;
|
||||
}
|
||||
|
||||
VulkanContext& context = mImpl->mContext;
|
||||
|
||||
// Pass along relevant driver config (feature flags)
|
||||
context.mStagingBufferBypassEnabled = driverConfig.vulkanEnableStagingBufferBypass;
|
||||
|
||||
ExtensionSet instExts;
|
||||
// If using a shared context, we do not assume any extensions.
|
||||
if (!mImpl->mSharedContext) {
|
||||
@@ -749,7 +566,7 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
instExts.merge(getRequiredInstanceExtensions());
|
||||
}
|
||||
if (mImpl->mInstance == VK_NULL_HANDLE) {
|
||||
mImpl->mInstance = createInstance(instExts);
|
||||
createInstance(instExts);
|
||||
}
|
||||
assert_invariant(mImpl->mInstance != VK_NULL_HANDLE);
|
||||
|
||||
@@ -761,27 +578,29 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
<< "Cannot both share context and indicate GPU preference";
|
||||
|
||||
if (mImpl->mPhysicalDevice == VK_NULL_HANDLE) {
|
||||
mImpl->mPhysicalDevice = selectPhysicalDevice(mImpl->mInstance, pref);
|
||||
mImpl->mPhysicalDevice = selectVkPhysicalDevice(mImpl->mInstance);
|
||||
}
|
||||
assert_invariant(mImpl->mPhysicalDevice != VK_NULL_HANDLE);
|
||||
|
||||
printDeviceInfo(mImpl->mInstance, mImpl->mPhysicalDevice);
|
||||
|
||||
VkPhysicalDeviceProtectedMemoryFeatures queryProtectedMemoryFeatures = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES,
|
||||
};
|
||||
// Note that we're chaining a struct on the stack into a struct in context, which will then be
|
||||
// passed to the driver. However, this should be ok since the use of
|
||||
// queryProtectedMemoryFeatures is only in this function.
|
||||
chainStruct(&context.mPhysicalDeviceFeatures, &queryProtectedMemoryFeatures);
|
||||
chainStruct(&context.mPhysicalDeviceFeatures, &context.mPhysicalDeviceVk11Features);
|
||||
ExtensionSet deviceExts;
|
||||
// If a shared context is not used, we will use our own provided list; otherwise, we do not
|
||||
// assume any extensions.
|
||||
if (!mImpl->mSharedContext) {
|
||||
deviceExts = getDeviceExtensions(mImpl->mPhysicalDevice);
|
||||
auto [prunedInstExts, prunedDeviceExts] =
|
||||
pruneExtensions(mImpl->mPhysicalDevice, driverConfig, instExts, deviceExts);
|
||||
instExts = prunedInstExts;
|
||||
deviceExts = prunedDeviceExts;
|
||||
}
|
||||
|
||||
// Initialize the following fields: physicalDeviceProperties, memoryProperties,
|
||||
// physicalDeviceFeatures, graphicsQueueFamilyIndex.
|
||||
vkGetPhysicalDeviceProperties2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceProperties);
|
||||
vkGetPhysicalDeviceFeatures2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceFeatures);
|
||||
vkGetPhysicalDeviceMemoryProperties(mImpl->mPhysicalDevice, &context.mMemoryProperties);
|
||||
// Query all the supported physical device features and enable/disable any feature as needed
|
||||
queryAndSetDeviceFeatures(driverConfig, instExts, deviceExts, sharedContext);
|
||||
|
||||
VulkanContext const& context = mImpl->mContext;
|
||||
|
||||
// Initialize the required queues
|
||||
if (mImpl->mGraphicsQueueFamilyIndex == INVALID_VK_INDEX) {
|
||||
mImpl->mGraphicsQueueFamilyIndex =
|
||||
identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice, VK_QUEUE_GRAPHICS_BIT);
|
||||
@@ -795,10 +614,6 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
mImpl->mGraphicsQueueIndex = 0;
|
||||
}
|
||||
|
||||
// We know we need to allocate the protected version of the VK objects
|
||||
context.mProtectedMemorySupported =
|
||||
static_cast<bool>(queryProtectedMemoryFeatures.protectedMemory);
|
||||
|
||||
if (context.mProtectedMemorySupported) {
|
||||
mImpl->mProtectedGraphicsQueueFamilyIndex = identifyGraphicsQueueFamilyIndex(
|
||||
mImpl->mPhysicalDevice, (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_PROTECTED_BIT));
|
||||
@@ -809,39 +624,25 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
}
|
||||
}
|
||||
|
||||
// Only enable shaderClipDistance if we are doing instanced stereoscopic rendering.
|
||||
if (driverConfig.stereoscopicType != StereoscopicType::INSTANCED) {
|
||||
context.mPhysicalDeviceFeatures.features.shaderClipDistance = VK_FALSE;
|
||||
}
|
||||
|
||||
// TODO: Add support of VK_KHR_global_priority with `driverConfig.gpuContextPriority`
|
||||
// in VulkanPlatform::createDriver.
|
||||
|
||||
ExtensionSet deviceExts;
|
||||
// If using a shared context, we do not assume any extensions.
|
||||
if (!mImpl->mSharedContext) {
|
||||
deviceExts = getDeviceExtensions(mImpl->mPhysicalDevice);
|
||||
auto [prunedInstExts, prunedDeviceExts] =
|
||||
pruneExtensions(mImpl->mPhysicalDevice, driverConfig, instExts, deviceExts);
|
||||
instExts = prunedInstExts;
|
||||
deviceExts = prunedDeviceExts;
|
||||
MiscDeviceFeatures requestedFeatures {};
|
||||
|
||||
if (setContains(deviceExts, VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME)) {
|
||||
requestedFeatures.dynamicRendering =
|
||||
context.mDynamicRenderingFeatures.dynamicRendering == VK_TRUE;
|
||||
}
|
||||
|
||||
bool requestPortabilitySubsetImageView2DOn3DImage = false;
|
||||
if (setContains(deviceExts, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) {
|
||||
// We are on a non-conformant vulkan implementation so we need to ascertain if the features
|
||||
// we need are available.
|
||||
chainStruct(&context.mPhysicalDeviceFeatures, &context.mPortabilitySubsetFeatures);
|
||||
vkGetPhysicalDeviceFeatures2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceFeatures);
|
||||
requestPortabilitySubsetImageView2DOn3DImage =
|
||||
requestedFeatures.imageView2Don3DImage =
|
||||
context.mPortabilitySubsetFeatures.imageView2DOn3DImage == VK_TRUE;
|
||||
}
|
||||
|
||||
if (mImpl->mDevice == VK_NULL_HANDLE) {
|
||||
mImpl->mDevice = createLogicalDevice(mImpl->mPhysicalDevice,
|
||||
context.mPhysicalDeviceFeatures, context.mPhysicalDeviceVk11Features,
|
||||
mImpl->mGraphicsQueueFamilyIndex, mImpl->mProtectedGraphicsQueueFamilyIndex,
|
||||
deviceExts, requestPortabilitySubsetImageView2DOn3DImage);
|
||||
createLogicalDeviceAndQueues(deviceExts, context.mPhysicalDeviceFeatures.features,
|
||||
context.mPhysicalDeviceVk11Features, context.mProtectedMemorySupported,
|
||||
requestedFeatures);
|
||||
}
|
||||
|
||||
assert_invariant(mImpl->mDevice != VK_NULL_HANDLE);
|
||||
@@ -865,55 +666,17 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
||||
assert_invariant(mImpl->mProtectedGraphicsQueue != VK_NULL_HANDLE);
|
||||
}
|
||||
|
||||
// Store the extension support in the context
|
||||
if (!mImpl->mSharedContext) {
|
||||
context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
|
||||
context.mPipelineCreationFeedbackSupported =
|
||||
setContains(deviceExts, VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME);
|
||||
} else {
|
||||
VulkanSharedContext const* scontext = (VulkanSharedContext const*) sharedContext;
|
||||
context.mDebugUtilsSupported = scontext->debugUtilsSupported;
|
||||
context.mDebugMarkersSupported = scontext->debugMarkersSupported;
|
||||
}
|
||||
|
||||
// Check the availability of lazily allocated memory
|
||||
context.mLazilyAllocatedMemorySupported = false;
|
||||
// RenderDoc doesn't support lazy allocated memory
|
||||
if constexpr (!FVK_RENDERDOC_CAPTURE_MODE) {
|
||||
for (uint32_t i = 0, typeCount = context.mMemoryProperties.memoryTypeCount; i < typeCount;
|
||||
++i) {
|
||||
VkMemoryType const type = context.mMemoryProperties.memoryTypes[i];
|
||||
if (type.propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
|
||||
context.mLazilyAllocatedMemorySupported = true;
|
||||
assert_invariant(type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.mIsUnifiedMemoryArchitecture = hasUnifiedMemoryArchitecture(context.mMemoryProperties);
|
||||
|
||||
#ifdef NDEBUG
|
||||
// If we are in release build, we should not have turned on debug extensions
|
||||
FILAMENT_CHECK_POSTCONDITION(!context.mDebugUtilsSupported && !context.mDebugMarkersSupported)
|
||||
<< "Debug utils should not be enabled in release build.";
|
||||
#endif
|
||||
|
||||
context.mDepthStencilFormats = findAttachmentDepthStencilFormats(mImpl->mPhysicalDevice);
|
||||
context.mBlittableDepthStencilFormats =
|
||||
findBlittableDepthStencilFormats(mImpl->mPhysicalDevice);
|
||||
|
||||
context.mFenceExportFlags = getFenceExportFlags();
|
||||
|
||||
assert_invariant(context.mDepthStencilFormats.size() > 0);
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
|
||||
printDepthFormats(mImpl->mPhysicalDevice);
|
||||
#endif
|
||||
|
||||
// Note that `context` is an alias of mImpl->mContext.
|
||||
return VulkanDriver::create(this, context, driverConfig);
|
||||
return VulkanDriver::create(this, mImpl->mContext, driverConfig);
|
||||
}
|
||||
|
||||
// This needs to be explictly written for
|
||||
@@ -1035,4 +798,299 @@ bool VulkanPlatform::isTransientAttachmentSupported() const noexcept {
|
||||
return mImpl->mContext.isLazilyAllocatedMemorySupported();
|
||||
}
|
||||
|
||||
VkInstance VulkanPlatform::createVkInstance(const VkInstanceCreateInfo& createInfo) noexcept {
|
||||
VkInstance instance = VK_NULL_HANDLE;
|
||||
VkResult result = vkCreateInstance(&createInfo, VKALLOC, &instance);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create Vulkan instance. error=" << static_cast<int32_t>(result);
|
||||
return instance;
|
||||
}
|
||||
|
||||
VkPhysicalDevice VulkanPlatform::selectVkPhysicalDevice(VkInstance instance) noexcept {
|
||||
VulkanPlatform::Customization::GPUPreference const pref = getCustomization().gpu;
|
||||
return selectPhysicalDevice(mImpl->mInstance, pref);
|
||||
}
|
||||
|
||||
VkDevice VulkanPlatform::createVkDevice(const VkDeviceCreateInfo& createInfo) noexcept {
|
||||
VkDevice device = VK_NULL_HANDLE;
|
||||
VkResult result = vkCreateDevice(mImpl->mPhysicalDevice, &createInfo, VKALLOC, &device);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkCreateDevice error=" << static_cast<int32_t>(result);
|
||||
return device;
|
||||
}
|
||||
|
||||
void VulkanPlatform::createInstance(ExtensionSet const& requiredExts) noexcept {
|
||||
// Create the Vulkan instance.
|
||||
VkApplicationInfo appInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
||||
.pEngineName = "Filament",
|
||||
.apiVersion =
|
||||
VK_MAKE_API_VERSION(0, FVK_REQUIRED_VERSION_MAJOR, FVK_REQUIRED_VERSION_MINOR, 0),
|
||||
};
|
||||
|
||||
VkInstanceCreateInfo instanceCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
|
||||
.pApplicationInfo = &appInfo,
|
||||
};
|
||||
bool validationFeaturesSupported = false;
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
|
||||
auto const enabledLayers = getEnabledLayers();
|
||||
if (!enabledLayers.empty()) {
|
||||
// If layers are supported, Check if VK_EXT_validation_features is supported.
|
||||
FixedCapacityVector<VkExtensionProperties> const availableValidationExts =
|
||||
fvkutils::enumerate(vkEnumerateInstanceExtensionProperties,
|
||||
"VK_LAYER_KHRONOS_validation");
|
||||
for (auto const& extProps: availableValidationExts) {
|
||||
if (!strcmp(extProps.extensionName, VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME)) {
|
||||
validationFeaturesSupported = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
instanceCreateInfo.enabledLayerCount = (uint32_t) enabledLayers.size();
|
||||
instanceCreateInfo.ppEnabledLayerNames = enabledLayers.data();
|
||||
} else {
|
||||
#if defined(__ANDROID__)
|
||||
FVK_LOGD << "Validation layers are not available; did you set jniLibs in your "
|
||||
<< "gradle file?";
|
||||
#else
|
||||
FVK_LOGD << "Validation layer not available; did you install the Vulkan SDK?\n"
|
||||
<< "Please ensure that VK_LAYER_PATH is set correctly.";
|
||||
#endif // __ANDROID__
|
||||
}
|
||||
#endif // FVK_ENABLED(FVK_DEBUG_VALIDATION)
|
||||
|
||||
// The Platform class can require 1 or 2 instance extensions, plus we'll request at most 6
|
||||
// instance extensions here in the common code. So that's a max of 8.
|
||||
constexpr uint32_t MAX_INSTANCE_EXTENSION_COUNT = 8;
|
||||
char const* ppEnabledExtensions[MAX_INSTANCE_EXTENSION_COUNT];
|
||||
uint32_t enabledExtensionCount = 0;
|
||||
|
||||
if (validationFeaturesSupported) {
|
||||
ppEnabledExtensions[enabledExtensionCount++] = VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME;
|
||||
}
|
||||
// Request platform-specific extensions.
|
||||
for (auto const& requiredExt: requiredExts) {
|
||||
assert_invariant(enabledExtensionCount < MAX_INSTANCE_EXTENSION_COUNT);
|
||||
ppEnabledExtensions[enabledExtensionCount++] = requiredExt.data();
|
||||
}
|
||||
|
||||
instanceCreateInfo.enabledExtensionCount = enabledExtensionCount;
|
||||
instanceCreateInfo.ppEnabledExtensionNames = ppEnabledExtensions;
|
||||
if (setContains(requiredExts, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) {
|
||||
instanceCreateInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
||||
}
|
||||
|
||||
// Validation features
|
||||
VkValidationFeatureEnableEXT enables[] = {
|
||||
VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT,
|
||||
VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT,
|
||||
};
|
||||
VkValidationFeaturesEXT features = {
|
||||
.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT,
|
||||
.enabledValidationFeatureCount = sizeof(enables) / sizeof(enables[0]),
|
||||
.pEnabledValidationFeatures = enables,
|
||||
};
|
||||
if (validationFeaturesSupported) {
|
||||
chainStruct(&instanceCreateInfo, &features);
|
||||
}
|
||||
|
||||
mImpl->mInstance = createVkInstance(instanceCreateInfo);
|
||||
}
|
||||
|
||||
void VulkanPlatform::queryAndSetDeviceFeatures(Platform::DriverConfig const& driverConfig,
|
||||
ExtensionSet const& instExts, ExtensionSet const& deviceExts,
|
||||
void* sharedContext) noexcept {
|
||||
VulkanContext& context = mImpl->mContext;
|
||||
|
||||
VkPhysicalDeviceProtectedMemoryFeatures queryProtectedMemoryFeatures = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES,
|
||||
};
|
||||
// Note that we're chaining a struct on the stack into a struct in context, which will then be
|
||||
// passed to the driver. However, this should be ok since the use of
|
||||
// queryProtectedMemoryFeatures is only in this function.
|
||||
chainStruct(&context.mPhysicalDeviceFeatures, &queryProtectedMemoryFeatures);
|
||||
chainStruct(&context.mPhysicalDeviceFeatures, &context.mPhysicalDeviceVk11Features);
|
||||
|
||||
if (setContains(deviceExts, VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME)) {
|
||||
chainStruct(&context.mPhysicalDeviceFeatures, &context.mDynamicRenderingFeatures);
|
||||
}
|
||||
|
||||
if (setContains(deviceExts, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) {
|
||||
// We are on a non-conformant vulkan implementation so we need to ascertain if the features
|
||||
// we need are available.
|
||||
chainStruct(&context.mPhysicalDeviceFeatures, &context.mPortabilitySubsetFeatures);
|
||||
}
|
||||
|
||||
// Initialize the following fields: physicalDeviceProperties, memoryProperties,
|
||||
// physicalDeviceFeatures.
|
||||
vkGetPhysicalDeviceProperties2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceProperties);
|
||||
vkGetPhysicalDeviceFeatures2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceFeatures);
|
||||
vkGetPhysicalDeviceMemoryProperties(mImpl->mPhysicalDevice, &context.mMemoryProperties);
|
||||
|
||||
// Store the extension support in the context
|
||||
if (!mImpl->mSharedContext) {
|
||||
context.mDebugUtilsSupported =
|
||||
setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
context.mDebugMarkersSupported =
|
||||
setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
|
||||
context.mPipelineCreationFeedbackSupported =
|
||||
setContains(deviceExts, VK_EXT_PIPELINE_CREATION_FEEDBACK_EXTENSION_NAME);
|
||||
context.mVertexInputDynamicStateSupported =
|
||||
setContains(deviceExts, VK_EXT_VERTEX_INPUT_DYNAMIC_STATE_EXTENSION_NAME);
|
||||
} else {
|
||||
VulkanSharedContext const* scontext = (VulkanSharedContext const*) sharedContext;
|
||||
context.mDebugUtilsSupported = scontext->debugUtilsSupported;
|
||||
context.mDebugMarkersSupported = scontext->debugMarkersSupported;
|
||||
}
|
||||
|
||||
// Pass along relevant driver config (feature flags)
|
||||
context.mAsyncPipelineCachePrewarmingEnabled = driverConfig.vulkanEnableAsyncPipelineCachePrewarming;
|
||||
context.mParallelShaderCompileDisabled = driverConfig.disableParallelShaderCompile;
|
||||
context.mStagingBufferBypassEnabled = driverConfig.vulkanEnableStagingBufferBypass;
|
||||
|
||||
// We know we need to allocate the protected version of the VK objects
|
||||
context.mProtectedMemorySupported =
|
||||
static_cast<bool>(queryProtectedMemoryFeatures.protectedMemory);
|
||||
|
||||
// Only enable shaderClipDistance if we are doing instanced stereoscopic rendering.
|
||||
if (driverConfig.stereoscopicType != StereoscopicType::INSTANCED) {
|
||||
context.mPhysicalDeviceFeatures.features.shaderClipDistance = VK_FALSE;
|
||||
}
|
||||
|
||||
// Check the availability of lazily allocated memory
|
||||
context.mLazilyAllocatedMemorySupported = false;
|
||||
// RenderDoc doesn't support lazy allocated memory
|
||||
if constexpr (!FVK_RENDERDOC_CAPTURE_MODE) {
|
||||
for (uint32_t i = 0, typeCount = context.mMemoryProperties.memoryTypeCount; i < typeCount;
|
||||
++i) {
|
||||
VkMemoryType const type = context.mMemoryProperties.memoryTypes[i];
|
||||
if (type.propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
|
||||
context.mLazilyAllocatedMemorySupported = true;
|
||||
assert_invariant(type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.mIsUnifiedMemoryArchitecture = hasUnifiedMemoryArchitecture(context.mMemoryProperties);
|
||||
context.mDepthStencilFormats = findAttachmentDepthStencilFormats(mImpl->mPhysicalDevice);
|
||||
assert_invariant(context.mDepthStencilFormats.size() > 0);
|
||||
context.mBlittableDepthStencilFormats =
|
||||
findBlittableDepthStencilFormats(mImpl->mPhysicalDevice);
|
||||
context.mFenceExportFlags = getFenceExportFlags();
|
||||
}
|
||||
|
||||
void VulkanPlatform::createLogicalDeviceAndQueues(const ExtensionSet& deviceExtensions,
|
||||
VkPhysicalDeviceFeatures const& features,
|
||||
VkPhysicalDeviceVulkan11Features const& vk11Features, bool createProtectedQueue,
|
||||
MiscDeviceFeatures const& requestedFeatures) noexcept {
|
||||
|
||||
// Identify and select all the required queues
|
||||
mImpl->mGraphicsQueueFamilyIndex =
|
||||
identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice, VK_QUEUE_GRAPHICS_BIT);
|
||||
assert_invariant(mImpl->mGraphicsQueueFamilyIndex != INVALID_VK_INDEX);
|
||||
mImpl->mGraphicsQueueIndex = 0;
|
||||
|
||||
if (createProtectedQueue) {
|
||||
mImpl->mProtectedGraphicsQueueFamilyIndex = identifyGraphicsQueueFamilyIndex(
|
||||
mImpl->mPhysicalDevice, (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_PROTECTED_BIT));
|
||||
mImpl->mProtectedGraphicsQueueIndex = 0;
|
||||
}
|
||||
|
||||
float queuePriority[] = { 1.0f };
|
||||
VkDeviceCreateInfo deviceCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
|
||||
};
|
||||
FixedCapacityVector<const char*> requestExtensions;
|
||||
requestExtensions.reserve(deviceExtensions.size() + 1);
|
||||
|
||||
// TODO: We don't really need this if we only ever expect headless swapchains.
|
||||
requestExtensions.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
|
||||
for (auto const& ext: deviceExtensions) {
|
||||
requestExtensions.push_back(ext.data());
|
||||
}
|
||||
VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = {};
|
||||
deviceQueueCreateInfo[0] = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
|
||||
.queueFamilyIndex = mImpl->mGraphicsQueueFamilyIndex,
|
||||
.queueCount = 1,
|
||||
.pQueuePriorities = &queuePriority[0],
|
||||
};
|
||||
// Protected queue
|
||||
deviceQueueCreateInfo[1] = deviceQueueCreateInfo[0];
|
||||
deviceQueueCreateInfo[1].flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT;
|
||||
|
||||
bool const hasProtectedQueue = mImpl->mProtectedGraphicsQueueFamilyIndex != INVALID_VK_INDEX;
|
||||
deviceCreateInfo.queueCreateInfoCount = hasProtectedQueue ? 2 : 1;
|
||||
deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
|
||||
|
||||
// We could simply enable all supported features, but since that may have performance
|
||||
// consequences let's just enable the features we need.
|
||||
VkPhysicalDeviceFeatures enabledFeatures = {
|
||||
.depthClamp = features.depthClamp,
|
||||
.samplerAnisotropy = features.samplerAnisotropy,
|
||||
.textureCompressionETC2 = features.textureCompressionETC2,
|
||||
.textureCompressionBC = features.textureCompressionBC,
|
||||
.shaderClipDistance = features.shaderClipDistance,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceFeatures2 enabledFeatures2 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||
.features = enabledFeatures,
|
||||
};
|
||||
chainStruct(&deviceCreateInfo, &enabledFeatures2);
|
||||
|
||||
VkPhysicalDeviceVulkan11Features enabledVk11Features = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
|
||||
.multiview = vk11Features.multiview,
|
||||
#if defined(__ANDROID__)
|
||||
.samplerYcbcrConversion = vk11Features.samplerYcbcrConversion,
|
||||
#endif
|
||||
};
|
||||
chainStruct(&deviceCreateInfo, &enabledVk11Features);
|
||||
|
||||
deviceCreateInfo.enabledExtensionCount = (uint32_t) requestExtensions.size();
|
||||
deviceCreateInfo.ppEnabledExtensionNames = requestExtensions.data();
|
||||
|
||||
VkPhysicalDeviceDynamicRenderingFeaturesKHR dynamicRendering = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR,
|
||||
.dynamicRendering = requestedFeatures.dynamicRendering ? VK_TRUE : VK_FALSE,
|
||||
};
|
||||
if (setContains(deviceExtensions, VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME)) {
|
||||
chainStruct(&deviceCreateInfo, &dynamicRendering);
|
||||
}
|
||||
|
||||
VkPhysicalDevicePortabilitySubsetFeaturesKHR portability = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR,
|
||||
.imageViewFormatSwizzle = VK_TRUE,
|
||||
.imageView2DOn3DImage = requestedFeatures.imageView2Don3DImage ? VK_TRUE : VK_FALSE,
|
||||
.mutableComparisonSamplers = VK_TRUE,
|
||||
};
|
||||
if (setContains(deviceExtensions, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) {
|
||||
chainStruct(&deviceCreateInfo, &portability);
|
||||
}
|
||||
|
||||
VkPhysicalDeviceMultiviewFeaturesKHR multiview = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR,
|
||||
.multiview = vk11Features.multiview,
|
||||
.multiviewGeometryShader = VK_FALSE,
|
||||
.multiviewTessellationShader = VK_FALSE,
|
||||
};
|
||||
if (setContains(deviceExtensions, VK_KHR_MULTIVIEW_EXTENSION_NAME)) {
|
||||
chainStruct(&deviceCreateInfo, &multiview);
|
||||
}
|
||||
|
||||
VkPhysicalDeviceProtectedMemoryFeatures protectedMemory = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES,
|
||||
.protectedMemory = VK_TRUE,
|
||||
};
|
||||
if (hasProtectedQueue) {
|
||||
// Enable protected memory, if requested.
|
||||
chainStruct(&deviceCreateInfo, &protectedMemory);
|
||||
}
|
||||
|
||||
mImpl->mDevice = createVkDevice(deviceCreateInfo);
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -44,7 +44,7 @@ namespace {
|
||||
// updating the buffer.
|
||||
WebGPUBufferObject::WebGPUBufferObject(wgpu::Device const& device,
|
||||
const BufferObjectBinding bindingType, const uint32_t byteCount)
|
||||
: HwBufferObject{ byteCount },
|
||||
: HwBufferObject{ byteCount, false },
|
||||
WebGPUBufferBase{ device, wgpu::BufferUsage::CopyDst | getBufferObjectUsage(bindingType),
|
||||
byteCount, "buffer_object" } {}
|
||||
|
||||
|
||||
@@ -73,15 +73,15 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
|
||||
}
|
||||
|
||||
const unsigned int samplerCount =
|
||||
std::count_if(layout.bindings.begin(), layout.bindings.end(), [](auto& fEntry) {
|
||||
return DescriptorSetLayoutBinding::isSampler(fEntry.type);
|
||||
std::count_if(layout.descriptors.begin(), layout.descriptors.end(), [](auto& fEntry) {
|
||||
return DescriptorSetLayoutDescriptor::isSampler(fEntry.type);
|
||||
});
|
||||
|
||||
std::vector<wgpu::BindGroupLayoutEntry> wEntries;
|
||||
wEntries.reserve(layout.bindings.size() + samplerCount);
|
||||
wEntries.reserve(layout.descriptors.size() + samplerCount);
|
||||
mBindGroupEntries.reserve(wEntries.capacity());
|
||||
|
||||
for (auto fEntry: layout.bindings) {
|
||||
for (auto fEntry: layout.descriptors) {
|
||||
auto& wEntry = wEntries.emplace_back();
|
||||
auto& entryInfo = mBindGroupEntries.emplace_back();
|
||||
wEntry.visibility = filamentStageToWGPUStage(fEntry.stageFlags);
|
||||
@@ -198,13 +198,13 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
|
||||
FWGPU_LOGD << "WebGPUDescriptorSetLayout:";
|
||||
FWGPU_LOGD << " label: " << label;
|
||||
FWGPU_LOGD << " wgpu::BindGroupLayout handle: " << mLayout.Get();
|
||||
FWGPU_LOGD << " Filament bindings (" << layout.bindings.size() << "):";
|
||||
for (DescriptorSetLayoutBinding const& layoutBinding: layout.bindings) {
|
||||
FWGPU_LOGD << " binding:" << +layoutBinding.binding
|
||||
<< " type:" << filamentDescriptorTypeToString(layoutBinding.type)
|
||||
<< " stageFlags:" << filamentStageFlagsToString(layoutBinding.stageFlags)
|
||||
<< " flags:" << filamentDescriptorFlagsToString(layoutBinding.flags)
|
||||
<< " count:" << layoutBinding.count;
|
||||
FWGPU_LOGD << " Filament bindings (" << layout.descriptors.size() << "):";
|
||||
for (DescriptorSetLayoutDescriptor const& descriptor: layout.descriptors) {
|
||||
FWGPU_LOGD << " binding:" << +descriptor.binding
|
||||
<< " type:" << filamentDescriptorTypeToString(descriptor.type)
|
||||
<< " stageFlags:" << filamentStageFlagsToString(descriptor.stageFlags)
|
||||
<< " flags:" << filamentDescriptorFlagsToString(descriptor.flags)
|
||||
<< " count:" << descriptor.count;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -103,7 +103,8 @@ Driver* WebGPUDriver::create(WebGPUPlatform& platform, const Platform::DriverCon
|
||||
|
||||
WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform,
|
||||
const Platform::DriverConfig& driverConfig) noexcept
|
||||
: mPlatform{ platform },
|
||||
: DriverBase(driverConfig),
|
||||
mPlatform{ platform },
|
||||
mAdapter{ mPlatform.requestAdapter(nullptr) },
|
||||
mDevice{ mPlatform.requestDevice(mAdapter) },
|
||||
mQueueManager{ mDevice },
|
||||
@@ -322,8 +323,36 @@ Handle<HwTexture> WebGPUDriver::createTextureS() noexcept {
|
||||
return allocHandle<WebGPUTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureAsyncS() noexcept {
|
||||
return allocHandle<WebGPUTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept { return allocHandle<WebGPUTexture>(); }
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::importTextureAsyncS() noexcept {
|
||||
return allocHandle<WebGPUTexture>();
|
||||
}
|
||||
|
||||
AsyncCallId WebGPUDriver::update3DImageAsyncS() noexcept {
|
||||
// TODO: implement this.
|
||||
return InvalidAsyncCallId;
|
||||
}
|
||||
|
||||
AsyncCallId WebGPUDriver::setVertexBufferObjectAsyncS() noexcept {
|
||||
// TODO: implement this.
|
||||
return InvalidAsyncCallId;
|
||||
}
|
||||
|
||||
AsyncCallId WebGPUDriver::updateBufferObjectAsyncS() noexcept {
|
||||
// TODO: implement this.
|
||||
return InvalidAsyncCallId;
|
||||
}
|
||||
|
||||
AsyncCallId WebGPUDriver::updateIndexBufferAsyncS() noexcept {
|
||||
// TODO: implement this.
|
||||
return InvalidAsyncCallId;
|
||||
}
|
||||
|
||||
Handle<HwProgram> WebGPUDriver::createProgramS() noexcept {
|
||||
return allocHandle<WebGPUProgram>();
|
||||
}
|
||||
@@ -346,6 +375,10 @@ Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
|
||||
return allocHandle<WebGPUIndexBuffer>();
|
||||
}
|
||||
|
||||
Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferAsyncS() noexcept {
|
||||
return allocHandle<WebGPUIndexBuffer>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
|
||||
return allocHandle<WebGPUTexture>();
|
||||
}
|
||||
@@ -354,6 +387,10 @@ Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
|
||||
return allocHandle<WebGPUBufferObject>();
|
||||
}
|
||||
|
||||
Handle<HwBufferObject> WebGPUDriver::createBufferObjectAsyncS() noexcept {
|
||||
return allocHandle<WebGPUBufferObject>();
|
||||
}
|
||||
|
||||
Handle<HwRenderTarget> WebGPUDriver::createRenderTargetS() noexcept {
|
||||
return allocHandle<WebGPURenderTarget>();
|
||||
}
|
||||
@@ -378,6 +415,10 @@ Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
|
||||
return allocHandle<WebGPUTexture>();
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleAsyncS() noexcept {
|
||||
return allocHandle<WebGPUTexture>();
|
||||
}
|
||||
|
||||
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
|
||||
return allocHandle<WebGPURenderTarget>();
|
||||
}
|
||||
@@ -398,6 +439,11 @@ Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
|
||||
return allocHandle<WebGPUTexture>();
|
||||
}
|
||||
|
||||
AsyncCallId WebGPUDriver::queueCommandAsyncS() noexcept {
|
||||
// TODO: implement this.
|
||||
return InvalidAsyncCallId;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Asynchronous resource creation methods (R-methods)
|
||||
//
|
||||
@@ -475,6 +521,13 @@ void WebGPUDriver::createIndexBufferR(Handle<HwIndexBuffer> indexBufferHandle,
|
||||
setDebugTag(indexBufferHandle.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void WebGPUDriver::createIndexBufferAsyncR(Handle<HwIndexBuffer> indexBufferHandle,
|
||||
const ElementType elementType, const uint32_t indexCount, const BufferUsage usage,
|
||||
CallbackHandler* handler, CallbackHandler::Callback callback, void* user,
|
||||
utils::ImmutableCString&& tag) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> bufferObjectHandle,
|
||||
const uint32_t byteCount, const BufferObjectBinding bindingType, const BufferUsage usage,
|
||||
utils::ImmutableCString&& tag) {
|
||||
@@ -483,6 +536,13 @@ void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> bufferObjectHandle
|
||||
setDebugTag(bufferObjectHandle.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void WebGPUDriver::createBufferObjectAsyncR(Handle<HwBufferObject> bufferObjectHandle,
|
||||
const uint32_t byteCount, const BufferObjectBinding bindingType, const BufferUsage usage,
|
||||
CallbackHandler* handler, CallbackHandler::Callback callback, void* user,
|
||||
utils::ImmutableCString&& tag) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureR(Handle<HwTexture> textureHandle, const SamplerType target,
|
||||
const uint8_t levels, const TextureFormat format, const uint8_t samples,
|
||||
const uint32_t width, const uint32_t height, const uint32_t depth,
|
||||
@@ -493,6 +553,15 @@ void WebGPUDriver::createTextureR(Handle<HwTexture> textureHandle, const Sampler
|
||||
setDebugTag(textureHandle.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureAsyncR(Handle<HwTexture> textureHandle, const SamplerType target,
|
||||
const uint8_t levels, const TextureFormat format, const uint8_t samples,
|
||||
const uint32_t width, const uint32_t height, const uint32_t depth,
|
||||
const TextureUsage usage, CallbackHandler* handler, CallbackHandler::Callback callback,
|
||||
void* user, utils::ImmutableCString&& tag) {
|
||||
// TODO: implement this.
|
||||
FWGPU_SYSTRACE_SCOPE();
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureViewR(Handle<HwTexture> textureHandle,
|
||||
Handle<HwTexture> sourceTextureHandle, const uint8_t baseLevel, const uint8_t levelCount,
|
||||
utils::ImmutableCString&& tag) {
|
||||
@@ -530,6 +599,14 @@ void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> textureHandle,
|
||||
setDebugTag(textureHandle.getId(), std::move(tag));
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureViewSwizzleAsyncR(Handle<HwTexture> textureHandle,
|
||||
Handle<HwTexture> sourceTextureHandle, const backend::TextureSwizzle r,
|
||||
const backend::TextureSwizzle g, const backend::TextureSwizzle b,
|
||||
const backend::TextureSwizzle a, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user, utils::ImmutableCString&& tag) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> textureHandle,
|
||||
const backend::SamplerType target, const backend::TextureFormat format,
|
||||
const uint32_t width, const uint32_t height, const backend::TextureUsage usage,
|
||||
@@ -559,6 +636,15 @@ void WebGPUDriver::importTextureR(Handle<HwTexture> textureHandle, const intptr_
|
||||
PANIC_POSTCONDITION("Import WebGPU Texture is not supported");
|
||||
}
|
||||
|
||||
void WebGPUDriver::importTextureAsyncR(Handle<HwTexture> textureHandle, const intptr_t id,
|
||||
const SamplerType target, const uint8_t levels, const TextureFormat format,
|
||||
const uint8_t samples, const uint32_t width, const uint32_t height, const uint32_t depth,
|
||||
const TextureUsage usage, CallbackHandler* handler, CallbackHandler::Callback callback,
|
||||
void* user, utils::ImmutableCString&& tag) {
|
||||
PANIC_POSTCONDITION("Import WebGPU Texture is not supported");
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> renderPrimitiveHandle,
|
||||
Handle<HwVertexBuffer> vertexBufferHandle, Handle<HwIndexBuffer> indexBufferHandle,
|
||||
const PrimitiveType primitiveType, utils::ImmutableCString&& tag) {
|
||||
@@ -787,6 +873,10 @@ bool WebGPUDriver::isDepthClampSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isAsynchronousModeEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isWorkaroundNeeded(Workaround) {
|
||||
return false;
|
||||
}
|
||||
@@ -859,6 +949,13 @@ void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> indexBufferHandle,
|
||||
scheduleDestroy(std::move(bufferDescriptor));
|
||||
}
|
||||
|
||||
void WebGPUDriver::updateIndexBufferAsyncR(AsyncCallId jobId,
|
||||
Handle<HwIndexBuffer> indexBufferHandle, BufferDescriptor&& bufferDescriptor,
|
||||
const uint32_t byteOffset, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void WebGPUDriver::updateBufferObject(Handle<HwBufferObject> bufferObjectHandle,
|
||||
BufferDescriptor&& bufferDescriptor, const uint32_t byteOffset) {
|
||||
// make sure command elements (draws, etc.) prior to the buffer update are processed before the
|
||||
@@ -870,6 +967,12 @@ void WebGPUDriver::updateBufferObject(Handle<HwBufferObject> bufferObjectHandle,
|
||||
scheduleDestroy(std::move(bufferDescriptor));
|
||||
}
|
||||
|
||||
void WebGPUDriver::updateBufferObjectAsyncR(AsyncCallId jobId, Handle<HwBufferObject> bufferObjectHandle,
|
||||
BufferDescriptor&& bufferDescriptor, const uint32_t byteOffset, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> bufferObjectHandle,
|
||||
BufferDescriptor&& bufferDescriptor, const uint32_t byteOffset) {
|
||||
handleCast<WebGPUBufferObject>(bufferObjectHandle)
|
||||
@@ -890,6 +993,13 @@ void WebGPUDriver::setVertexBufferObject(Handle<HwVertexBuffer> vertexBufferHand
|
||||
vertexBuffer->getBuffers()[index] = bufferObject->getBuffer();
|
||||
}
|
||||
|
||||
void WebGPUDriver::setVertexBufferObjectAsyncR(AsyncCallId jobId,
|
||||
Handle<HwVertexBuffer> vertexBufferHandle, const uint32_t index,
|
||||
Handle<HwBufferObject> bufferObjectHandle, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
// Updates a 3D texture region with pixel data from a buffer.
|
||||
// This function handles various pixel formats and performs a blit operation if the source
|
||||
// format needs to be converted to the destination format. For compressed textures, it ensures
|
||||
@@ -1036,6 +1146,15 @@ void WebGPUDriver::update3DImage(Handle<HwTexture> textureHandle, const uint32_t
|
||||
scheduleDestroy(std::move(pixelBufferDescriptor));
|
||||
}
|
||||
|
||||
void WebGPUDriver::update3DImageAsyncR(AsyncCallId jobId,
|
||||
Handle<HwTexture> textureHandle, const uint32_t level,
|
||||
const uint32_t xoffset, const uint32_t yoffset, const uint32_t zoffset,
|
||||
const uint32_t width, const uint32_t height, const uint32_t depth,
|
||||
PixelBufferDescriptor&& pixelBufferDescriptor, CallbackHandler* handler,
|
||||
CallbackHandler::Callback const callback, void* user) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
void WebGPUDriver::setupExternalImage(void* image) {
|
||||
//todo
|
||||
}
|
||||
@@ -2051,4 +2170,14 @@ void WebGPUDriver::copyToMemoryMappedBuffer(MemoryMappedBufferHandle mmbh, size_
|
||||
// TODO: MetalDriver::copyToMemoryMappedBuffer
|
||||
}
|
||||
|
||||
void WebGPUDriver::queueCommandAsyncR(AsyncCallId jobId, utils::Invocable<void()>&& command,
|
||||
CallbackHandler* handler, CallbackHandler::Callback const callback, void* user) {
|
||||
// TODO: implement this.
|
||||
}
|
||||
|
||||
bool WebGPUDriver::cancelAsyncJob(AsyncCallId jobId) {
|
||||
// TODO: implement this.
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace filament::backend {
|
||||
|
||||
WebGPUIndexBuffer::WebGPUIndexBuffer(wgpu::Device const& device, const uint8_t elementSize,
|
||||
const uint32_t indexCount)
|
||||
: HwIndexBuffer{ elementSize, indexCount },
|
||||
: HwIndexBuffer{ elementSize, indexCount, false },
|
||||
WebGPUBufferBase{ device, wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Index,
|
||||
elementSize * indexCount, "index_buffer" },
|
||||
mIndexFormat{ elementSize == 2 ? wgpu::IndexFormat::Uint16 : wgpu::IndexFormat::Uint32 } {}
|
||||
|
||||
@@ -198,7 +198,7 @@ WebGPUTexture::WebGPUTexture(const SamplerType samplerType, const uint8_t levels
|
||||
const TextureFormat format, const uint8_t samples, const uint32_t width,
|
||||
const uint32_t height, const uint32_t depth, const TextureUsage usage,
|
||||
wgpu::Device const& device) noexcept
|
||||
: HwTexture{ samplerType, levels, samples, width, height, depth, format, usage },
|
||||
: HwTexture{ samplerType, levels, samples, width, height, depth, format, usage, false },
|
||||
mViewFormat{ toWGPUTextureFormat(format) },
|
||||
mMipmapGenerationStrategy{ determineMipmapGenerationStrategy(mViewFormat, samplerType,
|
||||
samples, levels) },
|
||||
@@ -268,7 +268,7 @@ WebGPUTexture::WebGPUTexture(const SamplerType samplerType, const uint8_t levels
|
||||
WebGPUTexture::WebGPUTexture(WebGPUTexture const* src, const uint8_t baseLevel,
|
||||
const uint8_t levelCount) noexcept
|
||||
: HwTexture{ src->target, levelCount, src->samples, src->width, src->height, src->depth,
|
||||
src->format, src->usage },
|
||||
src->format, src->usage, static_cast<bool>(src->asynchronous) },
|
||||
mViewFormat{ src->mViewFormat },
|
||||
mMipmapGenerationStrategy{ src->mMipmapGenerationStrategy },
|
||||
mWebGPUFormat{ src->mWebGPUFormat },
|
||||
@@ -312,7 +312,7 @@ WebGPUTexture::WebGPUTexture(WebGPUTexture const* src, const uint8_t baseLevel,
|
||||
WebGPUTexture::WebGPUTexture(const WebGPUTexture* src,
|
||||
const wgpu::TextureComponentSwizzle nextSwizzle) noexcept
|
||||
: HwTexture{ src->target, src->levels, src->samples, src->width, src->height, src->depth,
|
||||
src->format, src->usage},
|
||||
src->format, src->usage, static_cast<bool>(src->asynchronous) },
|
||||
mViewFormat{ src->mViewFormat },
|
||||
mMipmapGenerationStrategy{ src->mMipmapGenerationStrategy },
|
||||
mWebGPUFormat{ src->mWebGPUFormat },
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace test {
|
||||
using namespace filament::backend;
|
||||
|
||||
Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) : mCleanup(cleanup) {
|
||||
utils::FixedCapacityVector<DescriptorSetLayoutBinding> kLayouts(config.uniforms.size());
|
||||
utils::FixedCapacityVector<DescriptorSetLayoutDescriptor> kLayouts(config.uniforms.size());
|
||||
for (unsigned char i = 0; i < config.uniforms.size(); ++i) {
|
||||
kLayouts[i] = {
|
||||
config.uniforms[i].type.value_or(DescriptorType::UNIFORM_BUFFER),
|
||||
@@ -59,7 +59,7 @@ Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) : mCleanup
|
||||
|
||||
if (!kLayouts.empty()) {
|
||||
mDescriptorSetLayout = cleanup.add(
|
||||
api.createDescriptorSetLayout(DescriptorSetLayout{ .bindings = kLayouts }));
|
||||
api.createDescriptorSetLayout(DescriptorSetLayout{ .descriptors = kLayouts }));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -52,17 +52,17 @@ layout(push_constant) uniform Constants {
|
||||
float triangleScale;
|
||||
float triangleOffsetX;
|
||||
float triangleOffsetY;
|
||||
} pushConstants;
|
||||
} pushConstantsV;
|
||||
|
||||
layout(location = 0) in vec4 mesh_position;
|
||||
void main() {
|
||||
if (pushConstants.hideTriangle) {
|
||||
if (pushConstantsV.hideTriangle) {
|
||||
// Test that bools are written correctly. All bits must be 0 if the bool is false.
|
||||
gl_Position = vec4(0.0);
|
||||
return;
|
||||
}
|
||||
gl_Position = vec4(mesh_position.xy * pushConstants.triangleScale +
|
||||
vec2(pushConstants.triangleOffsetX, pushConstants.triangleOffsetY), 0.0, 1.0);
|
||||
gl_Position = vec4(mesh_position.xy * pushConstantsV.triangleScale +
|
||||
vec2(pushConstantsV.triangleOffsetX, pushConstantsV.triangleOffsetY), 0.0, 1.0);
|
||||
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||
gl_Position.y = -gl_Position.y;
|
||||
@@ -76,12 +76,12 @@ layout(push_constant) uniform Constants {
|
||||
bool padding; // test correct bool padding
|
||||
float green;
|
||||
float blue;
|
||||
} pushConstants;
|
||||
} pushConstantsF;
|
||||
|
||||
precision mediump int; precision highp float;
|
||||
layout(location = 0) out vec4 fragColor;
|
||||
void main() {
|
||||
fragColor = vec4(pushConstants.red, pushConstants.green, pushConstants.blue, 1.0);
|
||||
fragColor = vec4(pushConstantsF.red, pushConstantsF.green, pushConstantsF.blue, 1.0);
|
||||
})";
|
||||
|
||||
void initPushConstants() {
|
||||
@@ -91,20 +91,19 @@ void initPushConstants() {
|
||||
|
||||
gVertConstants.reserve(4);
|
||||
gVertConstants.resize(4);
|
||||
gVertConstants[pushConstantIndex.TRIANGLE_HIDE] = {"hideTriangle", backend::ConstantType::BOOL};
|
||||
gVertConstants[pushConstantIndex.TRIANGLE_SCALE] = {"triangleScale", backend::ConstantType::FLOAT};
|
||||
gVertConstants[pushConstantIndex.TRIANGLE_OFFSET_X] = {"triangleOffsetX", backend::ConstantType::FLOAT};
|
||||
gVertConstants[pushConstantIndex.TRIANGLE_OFFSET_Y] = {"triangleOffsetY", backend::ConstantType::FLOAT};
|
||||
gVertConstants[pushConstantIndex.TRIANGLE_HIDE] = {"pushConstantsV.hideTriangle", backend::ConstantType::BOOL};
|
||||
gVertConstants[pushConstantIndex.TRIANGLE_SCALE] = {"pushConstantsV.triangleScale", backend::ConstantType::FLOAT};
|
||||
gVertConstants[pushConstantIndex.TRIANGLE_OFFSET_X] = {"pushConstantsV.triangleOffsetX", backend::ConstantType::FLOAT};
|
||||
gVertConstants[pushConstantIndex.TRIANGLE_OFFSET_Y] = {"pushConstantsV.triangleOffsetY", backend::ConstantType::FLOAT};
|
||||
|
||||
gFragConstants.reserve(4);
|
||||
gFragConstants.resize(4);
|
||||
gFragConstants[pushConstantIndex.RED] = {"red", backend::ConstantType::FLOAT};
|
||||
gFragConstants[pushConstantIndex.GREEN] = {"green", backend::ConstantType::FLOAT};
|
||||
gFragConstants[pushConstantIndex.BLUE] = {"blue", backend::ConstantType::FLOAT};
|
||||
gFragConstants[pushConstantIndex.RED] = {"pushConstantsF.red", backend::ConstantType::FLOAT};
|
||||
gFragConstants[pushConstantIndex.GREEN] = {"pushConstantsF.green", backend::ConstantType::FLOAT};
|
||||
gFragConstants[pushConstantIndex.BLUE] = {"pushConstantsF.blue", backend::ConstantType::FLOAT};
|
||||
}
|
||||
|
||||
TEST_F(BackendTest, PushConstants) {
|
||||
SKIP_IF(Backend::OPENGL, "see b/453757504");
|
||||
SKIP_IF(SkipEnvironment(OperatingSystem::CI, Backend::VULKAN), "see b/453776664");
|
||||
SKIP_IF(Backend::WEBGPU, "Push constants not supported on WebGPU");
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/Slice.h>
|
||||
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <optional>
|
||||
|
||||
@@ -193,6 +194,8 @@ public:
|
||||
using Driver = backend::Driver;
|
||||
using GpuContextPriority = backend::Platform::GpuContextPriority;
|
||||
using AsynchronousMode = backend::AsynchronousMode;
|
||||
using AsyncCompletionCallback = std::function<void(void* UTILS_NULLABLE)>;
|
||||
using AsyncCallId = backend::AsyncCallId;
|
||||
|
||||
/**
|
||||
* Config is used to define the memory footprint used by the engine, such as the
|
||||
@@ -432,6 +435,10 @@ public:
|
||||
|
||||
/**
|
||||
* Asynchronous mode for the engine. Defines how asynchronous operations are handled.
|
||||
* Note that selecting a non-NONE mode does not guarantee asynchronous methods are
|
||||
* supported, as the underlying backend or the feature flag may override this configuration.
|
||||
* Always validate availability via Engine::isAsynchronousModeEnabled() before
|
||||
* invoking asynchronous methods.
|
||||
*/
|
||||
AsynchronousMode asynchronousMode = AsynchronousMode::NONE;
|
||||
};
|
||||
@@ -449,7 +456,7 @@ public:
|
||||
char const* UTILS_NONNULL name; //!< name of the feature flag
|
||||
char const* UTILS_NONNULL description; //!< short description
|
||||
bool const* UTILS_NONNULL value; //!< pointer to the value of the flag
|
||||
bool constant; //!< whether the flag is constant after construction
|
||||
bool constant = true; //!< whether the flag is constant after construction
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -754,7 +761,7 @@ public:
|
||||
*
|
||||
* @return true if the engine supports asynchronous operation.
|
||||
*/
|
||||
bool isAsynchronousOperationSupported() const noexcept;
|
||||
bool isAsynchronousModeEnabled() const noexcept;
|
||||
|
||||
/**
|
||||
* Retrieves the configuration settings of this Engine.
|
||||
@@ -1023,6 +1030,47 @@ public:
|
||||
size_t getRenderTargetCount() const noexcept;
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* This asynchronously executes user-defined commands. The commands are queued sequentially
|
||||
* alongside other asynchronous operations (see Texture, VertexBuffer, and IndexBuffer) and
|
||||
* guaranteed to be executed in the exact order they were invoked.
|
||||
*
|
||||
* Beware of overusing this method. It shares the execution queue with other asynchronous tasks
|
||||
* like texture updates, so flooding it can delay those critical engine tasks. The recommended
|
||||
* practice is to use this method for resource preparation, such as asset loading(images/meshes).
|
||||
* This facilitates an efficient chaining pattern, where subsequent asynchronous operations
|
||||
* (e.g., creating textures/vertex buffers) can be initiated directly within the completion
|
||||
* callback.
|
||||
*
|
||||
* Users can call the `Engine::cancelAsyncCall()` method with the returned ID to cancel the
|
||||
* asynchronous call.
|
||||
*
|
||||
* To use this method, the engine must be configured for asynchronous operation. Otherwise,
|
||||
* calling async method will cause the program to terminate.
|
||||
*
|
||||
* @param command The custom command to be executed.
|
||||
* @param handler The handler from which `onComplete` is invoked. If null, it's called from the
|
||||
* main thread.
|
||||
* @param onComplete The callback function that runs once the command has finished.
|
||||
* @param user The custom data that will be passed as an argument to the `onComplete`.
|
||||
* @return A unique identifier for the asynchronous call.
|
||||
*/
|
||||
AsyncCallId runCommandAsync(utils::Invocable<void()>&& command,
|
||||
backend::CallbackHandler* UTILS_NULLABLE handler, AsyncCompletionCallback onComplete,
|
||||
void* UTILS_NULLABLE user = nullptr);
|
||||
|
||||
/**
|
||||
* Cancel the pending asynchronous call pointed to by `id`, which is retrieved whenever you
|
||||
* invoke a non-blocking version of method on an object, such as `Texture::setImageAsync` or
|
||||
* `BufferObject::setBufferAsync`.
|
||||
*
|
||||
* @param id The unique identifier for the asynchronous call to be canceled.
|
||||
* @return Returns true upon successful cancellation. It returns false if the asynchronous
|
||||
* operation cannot be canceled because it is currently running, has finished, or has previously
|
||||
* been canceled.
|
||||
*/
|
||||
bool cancelAsyncCall(AsyncCallId id);
|
||||
|
||||
/**
|
||||
* Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) and blocks until
|
||||
* all commands to this point are executed. Note that does guarantee that the
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
#include <functional>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
@@ -51,6 +52,9 @@ class UTILS_PUBLIC IndexBuffer : public FilamentAPI {
|
||||
|
||||
public:
|
||||
using BufferDescriptor = backend::BufferDescriptor;
|
||||
using AsyncCompletionCallback =
|
||||
std::function<void(IndexBuffer* UTILS_NONNULL, void* UTILS_NULLABLE)>;
|
||||
using AsyncCallId = backend::AsyncCallId;
|
||||
|
||||
/**
|
||||
* Type of the index buffer
|
||||
@@ -111,6 +115,29 @@ public:
|
||||
*/
|
||||
Builder& name(utils::StaticString const& name) noexcept;
|
||||
|
||||
/**
|
||||
* Specifies a callback that will execute once the resource's data has been fully allocated
|
||||
* within the GPU memory. This enables the resource creation process to be handled
|
||||
* asynchronously.
|
||||
*
|
||||
* Any asynchronous calls made during a resource's asynchronous creation (using this method)
|
||||
* are safe because they are queued and executed in sequence. However, invoking regular
|
||||
* methods on the same resource before it's fully ready is unsafe and may cause undefined
|
||||
* behavior. Users can call the `isCreationComplete()` method for the resource to confirm
|
||||
* when the resource is ready for regular API calls.
|
||||
*
|
||||
* To use this method, the engine must be configured for asynchronous operation. Otherwise,
|
||||
* calling async method will cause the program to terminate.
|
||||
*
|
||||
* @param handler Handler to dispatch the callback or nullptr for the default handler
|
||||
* @param callback A function to be called upon the completion of an asynchronous creation.
|
||||
* @param user The custom data that will be passed as the second argument to the `callback`.
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
Builder& async(backend::CallbackHandler* UTILS_NULLABLE handler,
|
||||
AsyncCompletionCallback callback = nullptr,
|
||||
void* UTILS_NULLABLE user = nullptr) noexcept;
|
||||
|
||||
/**
|
||||
* Creates the IndexBuffer object and returns a pointer to it. After creation, the index
|
||||
* buffer is uninitialized. Use IndexBuffer::setBuffer() to initialize the IndexBuffer.
|
||||
@@ -131,7 +158,7 @@ public:
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously copy-initializes a region of this IndexBuffer from the data provided.
|
||||
* Copy-initializes a region of this IndexBuffer from the data provided.
|
||||
*
|
||||
* @param engine Reference to the filament::Engine to associate this IndexBuffer with.
|
||||
* @param buffer A BufferDescriptor representing the data used to initialize the IndexBuffer.
|
||||
@@ -141,12 +168,49 @@ public:
|
||||
*/
|
||||
void setBuffer(Engine& engine, BufferDescriptor&& buffer, uint32_t byteOffset = 0);
|
||||
|
||||
/**
|
||||
* An asynchronous version of `setBuffer()`.
|
||||
* Copy-initializes a region of this IndexBuffer from the data provided.
|
||||
*
|
||||
* Users can call the `Engine::cancelAsyncCall()` method with the returned ID to cancel the
|
||||
* asynchronous call.
|
||||
*
|
||||
* To use this method, the engine must be configured for asynchronous operation. Otherwise,
|
||||
* calling async method will cause the program to terminate.
|
||||
*
|
||||
* @param engine Reference to the filament::Engine to associate this IndexBuffer with.
|
||||
* @param buffer A BufferDescriptor representing the data used to initialize the IndexBuffer.
|
||||
* BufferDescriptor points to raw, untyped data that will be interpreted as
|
||||
* either 16-bit or 32-bits indices based on the Type of this IndexBuffer.
|
||||
* @param byteOffset Offset in *bytes* into the IndexBuffer. Must be multiple of 4.
|
||||
* @param handler Handler to dispatch the callback or nullptr for the default handler
|
||||
* @param callback A function to be called upon the completion of an asynchronous creation.
|
||||
* @param user The custom data that will be passed as the second argument to the `callback`.
|
||||
*
|
||||
* @return An ID that the caller can use to cancel the operation.
|
||||
*/
|
||||
AsyncCallId setBufferAsync(Engine& engine, BufferDescriptor&& buffer, uint32_t byteOffset,
|
||||
backend::CallbackHandler* UTILS_NULLABLE handler, AsyncCompletionCallback callback,
|
||||
void* UTILS_NULLABLE user = nullptr);
|
||||
|
||||
/**
|
||||
* Returns the size of this IndexBuffer in elements.
|
||||
* @return The number of indices the IndexBuffer holds.
|
||||
*/
|
||||
size_t getIndexCount() const noexcept;
|
||||
|
||||
/**
|
||||
* This non-blocking method checks if the resource has finished creation. If the resource
|
||||
* creation was initiated asynchronously, it will return true only after all related
|
||||
* asynchronous tasks are complete. If the resource was created normally without using async
|
||||
* method, it will always return true.
|
||||
*
|
||||
* @return Whether the resource is created.
|
||||
*
|
||||
* @see Builder::async()
|
||||
*/
|
||||
bool isCreationComplete() const noexcept;
|
||||
|
||||
protected:
|
||||
// prevent heap allocation
|
||||
~IndexBuffer() = default;
|
||||
|
||||
@@ -489,16 +489,15 @@ struct MultiSampleAntiAliasingOptions {
|
||||
* @see setTemporalAntiAliasingOptions()
|
||||
*/
|
||||
struct TemporalAntiAliasingOptions {
|
||||
float filterWidth = 1.0f; //!< reconstruction filter width typically between 1 (sharper) and 2 (smoother)
|
||||
float filterWidth = 1.0f; //!< @deprecated has no effect.
|
||||
float feedback = 0.12f; //!< history feedback, between 0 (maximum temporal AA) and 1 (no temporal AA).
|
||||
float lodBias = -1.0f; //!< texturing lod bias (typically -1 or -2)
|
||||
float sharpness = 0.0f; //!< post-TAA sharpen, especially useful when upscaling is true.
|
||||
bool enabled = false; //!< enables or disables temporal anti-aliasing
|
||||
bool upscaling = false; //!< 4x TAA upscaling. Disables Dynamic Resolution. [BETA]
|
||||
float upscaling = 1.0f; //!< Upscaling factor. Disables Dynamic Resolution. [BETA]
|
||||
|
||||
enum class BoxType : uint8_t {
|
||||
AABB, //!< use an AABB neighborhood
|
||||
VARIANCE, //!< use the variance of the neighborhood (not recommended)
|
||||
AABB_VARIANCE //!< use both AABB and variance
|
||||
};
|
||||
|
||||
@@ -519,6 +518,7 @@ struct TemporalAntiAliasingOptions {
|
||||
bool filterHistory = true; //!< whether to filter the history buffer
|
||||
bool filterInput = true; //!< whether to apply the reconstruction filter to the input
|
||||
bool useYCoCg = false; //!< whether to use the YcoCg color-space for history rejection
|
||||
bool hdr = true; //!< set to true for HDR content
|
||||
BoxType boxType = BoxType::AABB; //!< type of color gamut box
|
||||
BoxClipping boxClipping = BoxClipping::ACCURATE; //!< clipping algorithm
|
||||
JitterPattern jitterPattern = JitterPattern::HALTON_23_X16; //! Jitter Pattern
|
||||
|
||||
@@ -26,10 +26,12 @@
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <functional>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -88,6 +90,9 @@ public:
|
||||
using Swizzle = backend::TextureSwizzle; //!< Texture swizzle
|
||||
using ExternalImageHandle = backend::Platform::ExternalImageHandle;
|
||||
using ExternalImageHandleRef = backend::Platform::ExternalImageHandleRef;
|
||||
using AsyncCompletionCallback =
|
||||
std::function<void(Texture* UTILS_NONNULL, void* UTILS_NULLABLE)>;
|
||||
using AsyncCallId = backend::AsyncCallId;
|
||||
|
||||
/** @return Whether a backend supports a particular format. */
|
||||
static bool isTextureFormatSupported(Engine& engine, InternalFormat format) noexcept;
|
||||
@@ -261,6 +266,32 @@ public:
|
||||
*/
|
||||
Builder& external() noexcept;
|
||||
|
||||
/**
|
||||
* Specifies a callback that will execute once the resource's data has been fully allocated
|
||||
* within the GPU memory. This enables the resource creation process to be handled
|
||||
* asynchronously.
|
||||
*
|
||||
* Any asynchronous calls made during a resource's asynchronous creation (using this method)
|
||||
* are safe because they are queued and executed in sequence. However, invoking regular
|
||||
* methods on the same resource before it's fully ready is unsafe and may cause undefined
|
||||
* behavior. Users can call the `isCreationComplete()` method for the resource to confirm
|
||||
* when the resource is ready for regular API calls.
|
||||
*
|
||||
* To use this method, the engine must be configured for asynchronous operation. Otherwise,
|
||||
* calling async method will cause the program to terminate.
|
||||
*
|
||||
* This method and the `external()` method are mutually exclusive. You cannot use both
|
||||
* because external texture's contents are filled later by calling `setExternalImage()`.
|
||||
*
|
||||
* @param handler Handler to dispatch the callback or nullptr for the default handler
|
||||
* @param callback A function to be called upon the completion of an asynchronous creation.
|
||||
* @param user The custom data that will be passed as the second argument to the `callback`.
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
Builder& async(backend::CallbackHandler* UTILS_NULLABLE handler,
|
||||
AsyncCompletionCallback callback = nullptr,
|
||||
void* UTILS_NULLABLE user = nullptr) noexcept;
|
||||
|
||||
/**
|
||||
* Creates the Texture object and returns a pointer to it.
|
||||
*
|
||||
@@ -437,6 +468,85 @@ public:
|
||||
void setImage(Engine& engine, size_t level,
|
||||
PixelBufferDescriptor&& buffer, const FaceOffsets& faceOffsets) const;
|
||||
|
||||
/**
|
||||
* An asynchronous version of `setImage()`.
|
||||
* Updates a sub-image of a 3D texture or 2D texture array for a level. Cubemaps are treated
|
||||
* like a 2D array of six layers.
|
||||
*
|
||||
* Users can call the `Engine::cancelAsyncCall()` method with the returned ID to cancel the
|
||||
* asynchronous call.
|
||||
*
|
||||
* To use this method, the engine must be configured for asynchronous operation. Otherwise,
|
||||
* calling async method will cause the program to terminate.
|
||||
*
|
||||
* @param engine Engine this texture is associated to.
|
||||
* @param level Level to set the image for.
|
||||
* @param xoffset Left offset of the sub-region to update.
|
||||
* @param yoffset Bottom offset of the sub-region to update.
|
||||
* @param zoffset Depth offset of the sub-region to update.
|
||||
* @param width Width of the sub-region to update.
|
||||
* @param height Height of the sub-region to update.
|
||||
* @param depth Depth of the sub-region to update.
|
||||
* @param buffer Client-side buffer containing the image to set.
|
||||
* @param handler Handler to dispatch the callback or nullptr for the default handler
|
||||
* @param callback A function to be called upon the completion of an asynchronous creation.
|
||||
* @param user The custom data that will be passed as the second argument to the `callback`.
|
||||
*
|
||||
* @return An ID that the caller can use to cancel the operation.
|
||||
*
|
||||
* @attention \p engine must be the instance passed to Builder::build()
|
||||
* @attention \p level must be less than getLevels().
|
||||
* @attention \p buffer's Texture::Format must match that of getFormat().
|
||||
* @attention This Texture instance must use Sampler::SAMPLER_3D, Sampler::SAMPLER_2D_ARRAY
|
||||
* or Sampler::SAMPLER_CUBEMAP.
|
||||
*
|
||||
* @see Builder::sampler()
|
||||
*/
|
||||
AsyncCallId setImageAsync(Engine& engine, size_t level,
|
||||
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
|
||||
uint32_t width, uint32_t height, uint32_t depth,
|
||||
PixelBufferDescriptor&& buffer,
|
||||
backend::CallbackHandler* UTILS_NULLABLE handler,
|
||||
AsyncCompletionCallback callback,
|
||||
void* UTILS_NULLABLE user = nullptr) const;
|
||||
|
||||
/**
|
||||
* inline helper to update a 2D texture asynchronously
|
||||
*
|
||||
* @see setImageAsync(Engine& engine, size_t level,
|
||||
* uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
|
||||
* uint32_t width, uint32_t height, uint32_t depth,
|
||||
* PixelBufferDescriptor&& buffer,
|
||||
* backend::CallbackHandler* UTILS_NULLABLE handler,
|
||||
* AsyncCompletionCallback callback, void* user)
|
||||
*/
|
||||
AsyncCallId setImageAsync(Engine& engine, size_t level, PixelBufferDescriptor&& buffer,
|
||||
backend::CallbackHandler* UTILS_NULLABLE handler, AsyncCompletionCallback callback,
|
||||
void* UTILS_NULLABLE user = nullptr) const {
|
||||
return setImageAsync(engine, level, 0, 0, 0,
|
||||
uint32_t(getWidth(level)), uint32_t(getHeight(level)), 1, std::move(buffer),
|
||||
handler, std::move(callback), user);
|
||||
}
|
||||
|
||||
/**
|
||||
* inline helper to update a 2D texture asynchronously
|
||||
*
|
||||
* @see setImageAsync(Engine& engine, size_t level,
|
||||
* uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
|
||||
* uint32_t width, uint32_t height, uint32_t depth,
|
||||
* PixelBufferDescriptor&& buffer,
|
||||
* backend::CallbackHandler* UTILS_NULLABLE handler,
|
||||
* AsyncCompletionCallback callback, void* user)
|
||||
*/
|
||||
AsyncCallId setImageAsync(Engine& engine, size_t level,
|
||||
uint32_t xoffset, uint32_t yoffset, uint32_t width, uint32_t height,
|
||||
PixelBufferDescriptor&& buffer,
|
||||
backend::CallbackHandler* UTILS_NULLABLE handler,
|
||||
AsyncCompletionCallback callback,
|
||||
void* UTILS_NULLABLE user = nullptr) const {
|
||||
return setImageAsync(engine, level, xoffset, yoffset, 0, width, height, 1, std::move(buffer),
|
||||
handler, std::move(callback), user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the external image to associate with this Texture. Typically, the external
|
||||
@@ -547,6 +657,18 @@ public:
|
||||
*/
|
||||
void generateMipmaps(Engine& engine) const noexcept;
|
||||
|
||||
/**
|
||||
* This non-blocking method checks if the resource has finished creation. If the resource
|
||||
* creation was initiated asynchronously, it will return true only after all related
|
||||
* asynchronous tasks are complete. If the resource was created normally without using async
|
||||
* method, it will always return true.
|
||||
*
|
||||
* @return Whether the resource is created.
|
||||
*
|
||||
* @see Builder::async()
|
||||
*/
|
||||
bool isCreationComplete() const noexcept;
|
||||
|
||||
/** @deprecated */
|
||||
struct FaceOffsets {
|
||||
using size_type = size_t;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/StaticString.h>
|
||||
|
||||
#include <functional>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -61,6 +62,10 @@ class UTILS_PUBLIC VertexBuffer : public FilamentAPI {
|
||||
public:
|
||||
using AttributeType = backend::ElementType;
|
||||
using BufferDescriptor = backend::BufferDescriptor;
|
||||
using AsyncCompletionCallback =
|
||||
std::function<void(VertexBuffer* UTILS_NONNULL, void* UTILS_NULLABLE)>;
|
||||
using AsyncCallId = backend::AsyncCallId;
|
||||
|
||||
|
||||
class Builder : public BuilderBase<BuilderDetails>, public BuilderNameMixin<Builder> {
|
||||
friend struct BuilderDetails;
|
||||
@@ -186,6 +191,29 @@ public:
|
||||
*/
|
||||
Builder& name(utils::StaticString const& name) noexcept;
|
||||
|
||||
/**
|
||||
* Specifies a callback that will execute once the resource's data has been fully allocated
|
||||
* within the GPU memory. This enables the resource creation process to be handled
|
||||
* asynchronously.
|
||||
*
|
||||
* Any asynchronous calls made during a resource's asynchronous creation (using this method)
|
||||
* are safe because they are queued and executed in sequence. However, invoking regular
|
||||
* methods on the same resource before it's fully ready is unsafe and may cause undefined
|
||||
* behavior. Users can call the `isCreationComplete()` method for the resource to confirm
|
||||
* when the resource is ready for regular API calls.
|
||||
*
|
||||
* To use this method, the engine must be configured for asynchronous operation. Otherwise,
|
||||
* calling async method will cause the program to terminate.
|
||||
*
|
||||
* @param handler Handler to dispatch the callback or nullptr for the default handler
|
||||
* @param callback A function to be called upon the completion of an asynchronous creation.
|
||||
* @param user The custom data that will be passed as the second argument to the `callback`.
|
||||
* @return This Builder, for chaining calls.
|
||||
*/
|
||||
Builder& async(backend::CallbackHandler* UTILS_NULLABLE handler,
|
||||
AsyncCompletionCallback callback = nullptr,
|
||||
void* UTILS_NULLABLE user = nullptr) noexcept;
|
||||
|
||||
/**
|
||||
* Creates the VertexBuffer object and returns a pointer to it.
|
||||
*
|
||||
@@ -210,7 +238,7 @@ public:
|
||||
size_t getVertexCount() const noexcept;
|
||||
|
||||
/**
|
||||
* Asynchronously copy-initializes the specified buffer from the given buffer data.
|
||||
* copy-initializes the specified buffer from the given buffer data.
|
||||
*
|
||||
* Do not use this if you called enableBufferObjects() on the Builder.
|
||||
*
|
||||
@@ -226,6 +254,36 @@ public:
|
||||
void setBufferAt(Engine& engine, uint8_t bufferIndex, BufferDescriptor&& buffer,
|
||||
uint32_t byteOffset = 0);
|
||||
|
||||
/**
|
||||
* An asynchronous version of `setBufferAt()`.
|
||||
* Asynchronously copy-initializes the specified buffer from the given buffer data.
|
||||
*
|
||||
* Users can call the `Engine::cancelAsyncCall()` method with the returned ID to cancel the
|
||||
* asynchronous call.
|
||||
*
|
||||
* To use this method, the engine must be configured for asynchronous operation. Otherwise,
|
||||
* calling async method will cause the program to terminate.
|
||||
*
|
||||
* Do not use this if you called enableBufferObjects() on the Builder.
|
||||
*
|
||||
* @param engine Reference to the filament::Engine to associate this VertexBuffer with.
|
||||
* @param bufferIndex Index of the buffer to initialize. Must be between 0
|
||||
* and Builder::bufferCount() - 1.
|
||||
* @param buffer A BufferDescriptor representing the data used to initialize the buffer at
|
||||
* index \p bufferIndex. BufferDescriptor points to raw, untyped data that will
|
||||
* be copied as-is into the buffer.
|
||||
* @param byteOffset Offset in *bytes* into the buffer at index \p bufferIndex of this vertex
|
||||
* buffer set. Must be multiple of 4.
|
||||
* @param handler Handler to dispatch the callback or nullptr for the default handler
|
||||
* @param callback A function to be called upon the completion of an asynchronous creation.
|
||||
* @param user The custom data that will be passed as the second argument to the `callback`.
|
||||
*
|
||||
* @return An ID that the caller can use to cancel the operation.
|
||||
*/
|
||||
AsyncCallId setBufferAtAsync(Engine& engine, uint8_t bufferIndex, BufferDescriptor&& buffer,
|
||||
uint32_t byteOffset, backend::CallbackHandler* UTILS_NULLABLE handler,
|
||||
AsyncCompletionCallback callback, void* UTILS_NULLABLE user = nullptr);
|
||||
|
||||
/**
|
||||
* Swaps in the given buffer object.
|
||||
*
|
||||
@@ -239,6 +297,45 @@ public:
|
||||
void setBufferObjectAt(Engine& engine, uint8_t bufferIndex,
|
||||
BufferObject const* UTILS_NONNULL bufferObject);
|
||||
|
||||
/**
|
||||
* An asynchronous version of `setBufferObjectAt()`.
|
||||
* Swaps in the given buffer object.
|
||||
*
|
||||
* Users can call the `Engine::cancelAsyncCall()` method with the returned ID to cancel the
|
||||
* asynchronous call.
|
||||
*
|
||||
* To use this method, the engine must be configured for asynchronous operation. Otherwise,
|
||||
* calling async method will cause the program to terminate.
|
||||
*
|
||||
* To use this, you must first call enableBufferObjects() on the Builder.
|
||||
*
|
||||
* @param engine Reference to the filament::Engine to associate this VertexBuffer with.
|
||||
* @param bufferIndex Index of the buffer to initialize. Must be between 0
|
||||
* and Builder::bufferCount() - 1.
|
||||
* @param bufferObject The handle to the GPU data that will be used in this buffer slot.
|
||||
* @param handler Handler to dispatch the callback or nullptr for the default handler
|
||||
* @param callback A function to be called upon the completion of an asynchronous creation.
|
||||
* @param user The custom data that will be passed as the second argument to the `callback`.
|
||||
*
|
||||
* @return An ID that the caller can use to cancel the operation.
|
||||
*/
|
||||
AsyncCallId setBufferObjectAtAsync(Engine& engine, uint8_t bufferIndex,
|
||||
BufferObject const* UTILS_NONNULL bufferObject,
|
||||
backend::CallbackHandler* UTILS_NULLABLE handler,
|
||||
AsyncCompletionCallback callback, void* UTILS_NULLABLE user = nullptr);
|
||||
|
||||
/**
|
||||
* This non-blocking method checks if the resource has finished creation. If the resource
|
||||
* creation was initiated asynchronously, it will return true only after all related
|
||||
* asynchronous tasks are complete. If the resource was created normally without using async
|
||||
* method, it will always return true.
|
||||
*
|
||||
* @return Whether the resource is created.
|
||||
*
|
||||
* @see Builder::async()
|
||||
*/
|
||||
bool isCreationComplete() const noexcept;
|
||||
|
||||
protected:
|
||||
// prevent heap allocation
|
||||
~VertexBuffer() = default;
|
||||
|
||||
+12
-3
@@ -16,7 +16,7 @@
|
||||
|
||||
#include "details/Engine.h"
|
||||
|
||||
#include "ResourceAllocator.h"
|
||||
#include "TextureCache.h"
|
||||
|
||||
#include "details/BufferObject.h"
|
||||
#include "details/Camera.h"
|
||||
@@ -349,6 +349,15 @@ size_t Engine::getRenderTargetCount() const noexcept {
|
||||
return downcast(this)->getRenderTargetCount();
|
||||
}
|
||||
|
||||
AsyncCallId Engine::runCommandAsync(Invocable<void()>&& command, CallbackHandler* handler,
|
||||
AsyncCompletionCallback onComplete, void* user) {
|
||||
return downcast(this)->runCommandAsync(std::move(command), handler, std::move(onComplete),
|
||||
user);
|
||||
}
|
||||
|
||||
bool Engine::cancelAsyncCall(AsyncCallId const id) {
|
||||
return downcast(this)->cancelAsyncCall(id);
|
||||
}
|
||||
|
||||
void Engine::flushAndWait() {
|
||||
downcast(this)->flushAndWait();
|
||||
@@ -455,8 +464,8 @@ bool Engine::isStereoSupported(StereoscopicType) const noexcept {
|
||||
return downcast(this)->isStereoSupported();
|
||||
}
|
||||
|
||||
bool Engine::isAsynchronousOperationSupported() const noexcept {
|
||||
return downcast(this)->isAsynchronousOperationSupported();
|
||||
bool Engine::isAsynchronousModeEnabled() const noexcept {
|
||||
return downcast(this)->isAsynchronousModeEnabled();
|
||||
}
|
||||
|
||||
size_t Engine::getMaxStereoscopicEyes() noexcept {
|
||||
|
||||
@@ -40,17 +40,17 @@ using namespace backend;
|
||||
|
||||
size_t HwDescriptorSetLayoutFactory::Parameters::hash() const noexcept {
|
||||
return hash::murmurSlow(
|
||||
reinterpret_cast<uint8_t const *>(dsl.bindings.data()),
|
||||
dsl.bindings.size() * sizeof(DescriptorSetLayoutBinding),
|
||||
reinterpret_cast<uint8_t const *>(dsl.descriptors.data()),
|
||||
dsl.descriptors.size() * sizeof(DescriptorSetLayoutDescriptor),
|
||||
42);
|
||||
}
|
||||
|
||||
bool operator==(HwDescriptorSetLayoutFactory::Parameters const& lhs,
|
||||
HwDescriptorSetLayoutFactory::Parameters const& rhs) noexcept {
|
||||
return (lhs.dsl.bindings.size() == rhs.dsl.bindings.size()) &&
|
||||
return (lhs.dsl.descriptors.size() == rhs.dsl.descriptors.size()) &&
|
||||
std::equal(
|
||||
lhs.dsl.bindings.begin(), lhs.dsl.bindings.end(),
|
||||
rhs.dsl.bindings.begin());
|
||||
lhs.dsl.descriptors.begin(), lhs.dsl.descriptors.end(),
|
||||
rhs.dsl.descriptors.begin());
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
@@ -70,7 +70,7 @@ void HwDescriptorSetLayoutFactory::terminate(DriverApi&) noexcept {
|
||||
auto HwDescriptorSetLayoutFactory::create(DriverApi& driver,
|
||||
DescriptorSetLayout dsl) noexcept -> Handle {
|
||||
|
||||
std::sort(dsl.bindings.begin(), dsl.bindings.end(),
|
||||
std::sort(dsl.descriptors.begin(), dsl.descriptors.end(),
|
||||
[](auto&& lhs, auto&& rhs) {
|
||||
return lhs.binding < rhs.binding;
|
||||
});
|
||||
|
||||
@@ -25,8 +25,19 @@ void IndexBuffer::setBuffer(Engine& engine,
|
||||
downcast(this)->setBuffer(downcast(engine), std::move(buffer), byteOffset);
|
||||
}
|
||||
|
||||
backend::AsyncCallId IndexBuffer::setBufferAsync(Engine& engine, BufferDescriptor&& buffer,
|
||||
uint32_t byteOffset, backend::CallbackHandler* handler,
|
||||
AsyncCompletionCallback callback, void* user) {
|
||||
return downcast(this)->setBufferAsync(downcast(engine), std::move(buffer), byteOffset,
|
||||
handler, std::move(callback), user);
|
||||
}
|
||||
|
||||
size_t IndexBuffer::getIndexCount() const noexcept {
|
||||
return downcast(this)->getIndexCount();
|
||||
}
|
||||
|
||||
bool IndexBuffer::isCreationComplete() const noexcept {
|
||||
return downcast(this)->isCreationComplete();
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -467,8 +467,7 @@ void MaterialDefinition::processDescriptorSets(FEngine& engine) {
|
||||
success = mMaterialParser->getDescriptorBindings(&programDescriptorBindings);
|
||||
assert_invariant(success);
|
||||
|
||||
backend::DescriptorSetLayout descriptorSetLayout;
|
||||
success = mMaterialParser->getDescriptorSetLayout(&descriptorSetLayout);
|
||||
success = mMaterialParser->getDescriptorSetLayout(&this->descriptorSetLayoutDescription);
|
||||
assert_invariant(success);
|
||||
|
||||
// get the PER_VIEW descriptor binding info
|
||||
@@ -477,26 +476,26 @@ void MaterialDefinition::processDescriptorSets(FEngine& engine) {
|
||||
refractionMode == RefractionMode::SCREEN_SPACE;
|
||||
bool const hasFog = !(variantFilterMask & UserVariantFilterMask(UserVariantFilterBit::FOG));
|
||||
|
||||
auto perViewDescriptorSetLayout = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
this->perViewDescriptorSetLayoutDescription = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
materialDomain, isLit, isSSR, hasFog, false);
|
||||
|
||||
auto perViewDescriptorSetLayoutVsm = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
this->perViewDescriptorSetLayoutVsmDescription = descriptor_sets::getPerViewDescriptorSetLayout(
|
||||
materialDomain, isLit, isSSR, hasFog, true);
|
||||
|
||||
// set the labels
|
||||
descriptorSetLayout.label = CString{ name }.append("_perMat");
|
||||
perViewDescriptorSetLayout.label = CString{ name }.append("_perView");
|
||||
perViewDescriptorSetLayoutVsm.label = CString{ name }.append("_perViewVsm");
|
||||
this->descriptorSetLayoutDescription.label = CString{ name }.append("_perMat");
|
||||
this->perViewDescriptorSetLayoutDescription.label = CString{ name }.append("_perView");
|
||||
this->perViewDescriptorSetLayoutVsmDescription.label = CString{ name }.append("_perViewVsm");
|
||||
|
||||
// get the PER_RENDERABLE and PER_VIEW descriptor binding info
|
||||
for (auto&& [bindingPoint, descriptorSetLayout] : {
|
||||
for (auto&& [bindingPoint, dsl] : {
|
||||
std::pair{ DescriptorSetBindingPoints::PER_RENDERABLE,
|
||||
descriptor_sets::getPerRenderableLayout() },
|
||||
std::pair{ DescriptorSetBindingPoints::PER_VIEW,
|
||||
perViewDescriptorSetLayout }}) {
|
||||
this->perViewDescriptorSetLayoutDescription }}) {
|
||||
Program::DescriptorBindingsInfo& descriptors = programDescriptorBindings[+bindingPoint];
|
||||
descriptors.reserve(descriptorSetLayout.bindings.size());
|
||||
for (auto const& entry: descriptorSetLayout.bindings) {
|
||||
descriptors.reserve(dsl.descriptors.size());
|
||||
for (auto const& entry: dsl.descriptors) {
|
||||
auto const& name = descriptor_sets::getDescriptorName(bindingPoint, entry.binding);
|
||||
descriptors.push_back({ name, entry.type, entry.binding });
|
||||
}
|
||||
@@ -504,15 +503,15 @@ void MaterialDefinition::processDescriptorSets(FEngine& engine) {
|
||||
|
||||
this->descriptorSetLayout = {
|
||||
engine.getDescriptorSetLayoutFactory(),
|
||||
engine.getDriverApi(), std::move(descriptorSetLayout) };
|
||||
engine.getDriverApi(), this->descriptorSetLayoutDescription };
|
||||
|
||||
this->perViewDescriptorSetLayout = {
|
||||
engine.getDescriptorSetLayoutFactory(),
|
||||
engine.getDriverApi(), std::move(perViewDescriptorSetLayout) };
|
||||
engine.getDriverApi(), this->perViewDescriptorSetLayoutDescription };
|
||||
|
||||
this->perViewDescriptorSetLayoutVsm = {
|
||||
engine.getDescriptorSetLayoutFactory(),
|
||||
engine.getDriverApi(), std::move(perViewDescriptorSetLayoutVsm) };
|
||||
engine.getDriverApi(), this->perViewDescriptorSetLayoutVsmDescription };
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <ds/DescriptorSetLayout.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Program.h>
|
||||
|
||||
namespace filament {
|
||||
@@ -56,10 +57,16 @@ struct MaterialDefinition {
|
||||
|
||||
MaterialParser const& getMaterialParser() const noexcept { return *mMaterialParser; }
|
||||
|
||||
// Keep track of the definitions of the descriptor set layouts, as these
|
||||
// may be used by some backends in parallel compilation of programs.
|
||||
backend::DescriptorSetLayout perViewDescriptorSetLayoutDescription;
|
||||
backend::DescriptorSetLayout perViewDescriptorSetLayoutVsmDescription;
|
||||
backend::DescriptorSetLayout descriptorSetLayoutDescription;
|
||||
|
||||
// try to order by frequency of use
|
||||
DescriptorSetLayout perViewDescriptorSetLayout;
|
||||
DescriptorSetLayout perViewDescriptorSetLayoutVsm;
|
||||
DescriptorSetLayout descriptorSetLayout;
|
||||
filament::DescriptorSetLayout perViewDescriptorSetLayout;
|
||||
filament::DescriptorSetLayout perViewDescriptorSetLayoutVsm;
|
||||
filament::DescriptorSetLayout descriptorSetLayout;
|
||||
backend::Program::DescriptorSetInfo programDescriptorBindings;
|
||||
|
||||
backend::RasterState rasterState;
|
||||
|
||||
@@ -792,7 +792,7 @@ bool ChunkDescriptorSetLayoutInfo::unflatten(Unflattener& unflattener,
|
||||
if (!unflattener.read(&descriptorCount)) {
|
||||
return false;
|
||||
}
|
||||
auto& descriptors = container->bindings;
|
||||
auto& descriptors = container->descriptors;
|
||||
descriptors.reserve(descriptorCount);
|
||||
for (size_t i = 0; i < descriptorCount; i++) {
|
||||
uint8_t type;
|
||||
|
||||
@@ -599,7 +599,8 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
|
||||
passBuilder.variant(structureVariant);
|
||||
passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::SSAO);
|
||||
|
||||
RenderPass const pass{ passBuilder.build(mEngine, driver) };
|
||||
RenderPass pass{ passBuilder.build(mEngine) };
|
||||
pass.finalize(mEngine, driver);
|
||||
auto const out = resources.getRenderPassInfo();
|
||||
driver.beginRenderPass(out.target, out.params);
|
||||
pass.getExecutor().execute(mEngine, driver);
|
||||
@@ -710,7 +711,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::transparentPicking(FrameGrap
|
||||
passBuilder.variant(pickingVariant);
|
||||
passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::DEPTH);
|
||||
|
||||
RenderPass const pass{ passBuilder.build(mEngine, driver) };
|
||||
RenderPass pass{ passBuilder.build(mEngine) };
|
||||
pass.finalize(mEngine, driver);
|
||||
driver.beginRenderPass(target, params);
|
||||
pass.getExecutor().execute(mEngine, driver);
|
||||
driver.endRenderPass();
|
||||
@@ -808,7 +810,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::ssr(FrameGraph& fg,
|
||||
// generate all our drawing commands, except blended objects.
|
||||
passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::SCREEN_SPACE_REFLECTIONS);
|
||||
|
||||
RenderPass const pass{ passBuilder.build(mEngine, driver) };
|
||||
RenderPass pass{ passBuilder.build(mEngine) };
|
||||
pass.finalize(mEngine, driver);
|
||||
driver.beginRenderPass(out.target, out.params);
|
||||
pass.getExecutor().execute(mEngine, driver);
|
||||
driver.endRenderPass();
|
||||
@@ -2530,11 +2533,12 @@ static float4 getVignetteParameters(VignetteOptions const& options,
|
||||
}
|
||||
|
||||
void PostProcessManager::colorGradingPrepareSubpass(DriverApi& driver,
|
||||
const FColorGrading* colorGrading, ColorGradingConfig const& colorGradingConfig,
|
||||
VignetteOptions const& vignetteOptions, uint32_t const width, uint32_t const height) noexcept {
|
||||
|
||||
FColorGrading const* colorGrading,
|
||||
ColorGradingConfig const& colorGradingConfig,
|
||||
VignetteOptions const& vignetteOptions,
|
||||
uint32_t const width, uint32_t const height) noexcept {
|
||||
auto& material = getPostProcessMaterial("colorGradingAsSubpass");
|
||||
FMaterialInstance* const mi =
|
||||
FMaterialInstance const* const mi =
|
||||
configureColorGradingMaterial(material, colorGrading, colorGradingConfig,
|
||||
vignetteOptions, width, height);
|
||||
mi->commit(driver, getUboManager());
|
||||
@@ -2566,12 +2570,11 @@ void PostProcessManager::colorGradingSubpass(DriverApi& driver,
|
||||
|
||||
void PostProcessManager::customResolvePrepareSubpass(DriverApi& driver, CustomResolveOp const op) noexcept {
|
||||
auto const& material = getPostProcessMaterial("customResolveAsSubpass");
|
||||
auto ma = material.getMaterial(mEngine, PostProcessVariant::OPAQUE);
|
||||
auto const ma = material.getMaterial(mEngine, PostProcessVariant::OPAQUE);
|
||||
auto [mi, fixedIndex] = mMaterialInstanceManager.getFixedMaterialInstance(ma);
|
||||
mFixedMaterialInstanceIndex.customResolve = fixedIndex;
|
||||
mi->setParameter("direction", op == CustomResolveOp::COMPRESS ? 1.0f : -1.0f),
|
||||
mi->commit(driver, getUboManager());
|
||||
material.getMaterial(mEngine);
|
||||
}
|
||||
|
||||
void PostProcessManager::customResolveSubpass(DriverApi& driver) noexcept {
|
||||
@@ -2624,7 +2627,6 @@ void PostProcessManager::clearAncillaryBuffersPrepare(DriverApi& driver) noexcep
|
||||
auto [mi, fixedIndex] = mMaterialInstanceManager.getFixedMaterialInstance(ma);
|
||||
mFixedMaterialInstanceIndex.clearDepth = fixedIndex;
|
||||
mi->commit(driver, getUboManager());
|
||||
material.getMaterial(mEngine);
|
||||
}
|
||||
|
||||
void PostProcessManager::clearAncillaryBuffers(DriverApi& driver,
|
||||
@@ -2909,11 +2911,12 @@ void PostProcessManager::configureTemporalAntiAliasingMaterial(
|
||||
FMaterial* const ma = getPostProcessMaterial("taa").getMaterial(mEngine);
|
||||
bool dirty = false;
|
||||
|
||||
setConstantParameter(ma, "upscaling", taaOptions.upscaling, dirty);
|
||||
setConstantParameter(ma, "upscaling", taaOptions.upscaling > 1.0f, dirty);
|
||||
setConstantParameter(ma, "historyReprojection", taaOptions.historyReprojection, dirty);
|
||||
setConstantParameter(ma, "filterHistory", taaOptions.filterHistory, dirty);
|
||||
setConstantParameter(ma, "filterInput", taaOptions.filterInput, dirty);
|
||||
setConstantParameter(ma, "useYCoCg", taaOptions.useYCoCg, dirty);
|
||||
setConstantParameter(ma, "hdr", taaOptions.hdr, dirty);
|
||||
setConstantParameter(ma, "preventFlickering", taaOptions.preventFlickering, dirty);
|
||||
setConstantParameter(ma, "boxType", int32_t(taaOptions.boxType), dirty);
|
||||
setConstantParameter(ma, "boxClipping", int32_t(taaOptions.boxClipping), dirty);
|
||||
@@ -2982,6 +2985,8 @@ FMaterialInstance* PostProcessManager::configureColorGradingMaterial(
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
|
||||
FrameGraphId<FrameGraphTexture> input,
|
||||
FrameGraphId<FrameGraphTexture> const depth,
|
||||
filament::Viewport const& xvp,
|
||||
filament::Viewport const& vp,
|
||||
FrameHistory& frameHistory,
|
||||
FrameHistoryEntry::TemporalAA FrameHistoryEntry::*pTaa,
|
||||
TemporalAntiAliasingOptions const& taaOptions,
|
||||
@@ -3011,9 +3016,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
|
||||
auto const& taaPass = fg.addPass<TAAData>("TAA",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
auto desc = fg.getDescriptor(input);
|
||||
if (taaOptions.upscaling) {
|
||||
desc.width *= 2;
|
||||
desc.height *= 2;
|
||||
if (taaOptions.upscaling > 1.0f) {
|
||||
desc.width *= taaOptions.upscaling;
|
||||
desc.height *= taaOptions.upscaling;
|
||||
}
|
||||
data.color = builder.sample(input);
|
||||
data.depth = builder.sample(depth);
|
||||
@@ -3045,55 +3050,15 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
|
||||
0, 0, 0, 1
|
||||
}};
|
||||
|
||||
constexpr float2 sampleOffsets[9] = {
|
||||
{ -1.0f, -1.0f }, { 0.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 0.0f },
|
||||
{ 0.0f, 0.0f },
|
||||
{ 1.0f, 0.0f }, { -1.0f, 1.0f }, { 0.0f, 1.0f }, { 1.0f, 1.0f },
|
||||
};
|
||||
|
||||
constexpr float2 subSampleOffsets[4] = {
|
||||
{ -0.25f, 0.25f },
|
||||
{ 0.25f, 0.25f },
|
||||
{ 0.25f, -0.25f },
|
||||
{ -0.25f, -0.25f }
|
||||
};
|
||||
|
||||
UTILS_UNUSED
|
||||
auto const lanczos = [](float const x, float const a) -> float {
|
||||
if (x <= std::numeric_limits<float>::epsilon()) {
|
||||
return 1.0f;
|
||||
}
|
||||
if (std::abs(x) <= a) {
|
||||
return (a * std::sin(f::PI * x) * std::sin(f::PI * x / a))
|
||||
/ ((f::PI * f::PI) * (x * x));
|
||||
}
|
||||
return 0.0f;
|
||||
};
|
||||
|
||||
float const filterWidth = std::clamp(taaOptions.filterWidth, 1.0f, 2.0f);
|
||||
float4 sum = 0.0;
|
||||
float4 weights[9];
|
||||
|
||||
// this doesn't get vectorized (probably because of exp()), so don't bother
|
||||
// unrolling it.
|
||||
UTILS_NOUNROLL
|
||||
for (size_t i = 0; i < 9; i++) {
|
||||
float2 const o = sampleOffsets[i];
|
||||
for (size_t j = 0; j < 4; j++) {
|
||||
float2 const subPixelOffset = taaOptions.upscaling ? subSampleOffsets[j] : float2{ 0 };
|
||||
float2 const d = (o - (current.jitter - subPixelOffset)) / filterWidth;
|
||||
weights[i][j] = lanczos(length(d), filterWidth);
|
||||
}
|
||||
sum += weights[i];
|
||||
}
|
||||
for (auto& w : weights) {
|
||||
w /= sum;
|
||||
}
|
||||
|
||||
auto out = resources.getRenderPassInfo();
|
||||
auto color = resources.getTexture(data.color);
|
||||
auto depth = resources.getTexture(data.depth);
|
||||
auto history = resources.getTexture(data.history);
|
||||
|
||||
auto const color = resources.getTexture(data.color);
|
||||
auto const depth = resources.getTexture(data.depth);
|
||||
auto const history = resources.getTexture(data.history);
|
||||
|
||||
auto const& colorDesc = resources.getDescriptor(data.color);
|
||||
auto const& historyDesc = resources.getDescriptor(data.history);
|
||||
|
||||
auto const& material = getPostProcessMaterial("taa");
|
||||
|
||||
PostProcessVariant const variant = colorGradingConfig.translucent ?
|
||||
@@ -3104,17 +3069,37 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
|
||||
FMaterialInstance* mi = getMaterialInstance(ma);
|
||||
mi->setParameter("color", color, SamplerParams{}); // nearest
|
||||
mi->setParameter("depth", depth, SamplerParams{}); // nearest
|
||||
mi->setParameter("alpha", taaOptions.feedback);
|
||||
mi->setParameter("history", history, SamplerParams{
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
.filterMin = SamplerMinFilter::LINEAR
|
||||
});
|
||||
mi->setParameter("filterWeights", weights, 9);
|
||||
mi->setParameter("jitter", current.jitter);
|
||||
mi->setParameter("alpha", taaOptions.feedback);
|
||||
|
||||
// jitter is negated here because the material wants the sample jitter offset
|
||||
// not the projection matrix offset
|
||||
mi->setParameter("jitter", -current.jitter);
|
||||
mi->setParameter("scale", taaOptions.upscaling);
|
||||
|
||||
mi->setParameter("reprojection",
|
||||
mat4f{ historyProjection * inverse(current.projection) } *
|
||||
normalizedToClip);
|
||||
|
||||
mi->setParameter("colorViewport",
|
||||
float4{
|
||||
float(xvp.left) / colorDesc.width,
|
||||
float(xvp.bottom) / colorDesc.height,
|
||||
float(xvp.width) / colorDesc.width,
|
||||
float(xvp.height) / colorDesc.height,
|
||||
});
|
||||
|
||||
mi->setParameter("colorResolution",
|
||||
float4{ colorDesc.width, colorDesc.height,
|
||||
1.0f / colorDesc.width, 1.0f / colorDesc.height });
|
||||
|
||||
mi->setParameter("historyResolution",
|
||||
float4{ historyDesc.width, historyDesc.height,
|
||||
1.0f / historyDesc.width, 1.0f / historyDesc.height });
|
||||
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->use(driver);
|
||||
|
||||
@@ -3152,7 +3137,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
|
||||
// an "import".
|
||||
builder.sideEffect();
|
||||
data.color = builder.sample(history); // FIXME: an access must be declared for detach(), why?
|
||||
}, [¤t](FrameGraphResources const& resources, auto const& data, auto&) {
|
||||
}, [¤t](FrameGraphResources const& resources, auto const& data) {
|
||||
resources.detach(data.color, ¤t.color, ¤t.desc);
|
||||
});
|
||||
|
||||
@@ -3803,7 +3788,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::vsmMipmapPass(FrameGraph& fg
|
||||
|
||||
auto const& depthMipmapPass = fg.addPass<VsmMipData>("VSM Generate Mipmap Pass",
|
||||
[&](FrameGraph::Builder& builder, auto& data) {
|
||||
utils::StaticString name = builder.getName(input);
|
||||
StaticString const name = builder.getName(input);
|
||||
data.in = builder.sample(input);
|
||||
|
||||
auto out = builder.createSubresource(data.in, "Mip level", {
|
||||
|
||||
@@ -235,6 +235,8 @@ public:
|
||||
FrameGraphId<FrameGraphTexture> taa(FrameGraph& fg,
|
||||
FrameGraphId<FrameGraphTexture> input,
|
||||
FrameGraphId<FrameGraphTexture> depth,
|
||||
Viewport const& xvp,
|
||||
Viewport const& vp,
|
||||
FrameHistory& frameHistory,
|
||||
FrameHistoryEntry::TemporalAA FrameHistoryEntry::*pTaa,
|
||||
TemporalAntiAliasingOptions const& taaOptions,
|
||||
|
||||
+111
-135
@@ -16,9 +16,9 @@
|
||||
|
||||
#include "RenderPass.h"
|
||||
|
||||
#include "compact.h"
|
||||
#include "RenderPrimitive.h"
|
||||
#include "ShadowMap.h"
|
||||
#include "SharedHandle.h"
|
||||
|
||||
#include "details/Material.h"
|
||||
#include "details/MaterialInstance.h"
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
#include <filament/MaterialEnums.h>
|
||||
|
||||
#include <backend/BufferDescriptor.h>
|
||||
#include <backend/DriverApiForward.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
@@ -43,6 +44,7 @@
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#include <utils/JobSystem.h>
|
||||
#include <utils/Logger.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Range.h>
|
||||
#include <utils/Slice.h>
|
||||
@@ -51,8 +53,10 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <limits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -80,21 +84,20 @@ RenderPassBuilder& RenderPassBuilder::customCommand(
|
||||
return *this;
|
||||
}
|
||||
|
||||
RenderPass RenderPassBuilder::build(FEngine const& engine, DriverApi& driver) const {
|
||||
RenderPass RenderPassBuilder::build(FEngine const& engine) const {
|
||||
assert_invariant(mRenderableSoa);
|
||||
return RenderPass{ engine, driver, *this };
|
||||
return RenderPass{ engine, *this };
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void RenderPass::BufferObjectHandleDeleter::operator()(BufferObjectHandle handle) noexcept {
|
||||
void RenderPass::BufferObjectHandleDeleter::operator()(BufferObjectHandle handle) const noexcept {
|
||||
if (handle) { // this is common case
|
||||
driver.get().destroyBufferObject(handle);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderPass::DescriptorSetHandleDeleter::operator()(
|
||||
DescriptorSetHandle handle) noexcept {
|
||||
void RenderPass::DescriptorSetHandleDeleter::operator()(DescriptorSetHandle handle) const noexcept {
|
||||
if (handle) { // this is common case
|
||||
driver.get().destroyDescriptorSet(handle);
|
||||
}
|
||||
@@ -102,7 +105,7 @@ void RenderPass::DescriptorSetHandleDeleter::operator()(
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
RenderPass::RenderPass(FEngine const& engine, DriverApi& driver,
|
||||
RenderPass::RenderPass(FEngine const& engine,
|
||||
RenderPassBuilder const& builder) noexcept
|
||||
: mRenderableSoa(*builder.mRenderableSoa),
|
||||
mColorPassDescriptorSet(builder.mColorPassDescriptorSet) {
|
||||
@@ -163,9 +166,7 @@ RenderPass::RenderPass(FEngine const& engine, DriverApi& driver,
|
||||
stereoscopicEyeCount *= engine.getConfig().stereoscopicEyeCount;
|
||||
}
|
||||
commandEnd = resize(builder.mArena,
|
||||
instanceify(driver,
|
||||
engine.getPerRenderableDescriptorSetLayout().getHandle(),
|
||||
commandBegin, commandEnd, stereoscopicEyeCount));
|
||||
instanceify(commandBegin, commandEnd, stereoscopicEyeCount));
|
||||
}
|
||||
|
||||
// these are `const` from this point on...
|
||||
@@ -250,7 +251,7 @@ void RenderPass::appendCommands(FEngine const& engine,
|
||||
|
||||
void RenderPass::appendCustomCommand(Command* commands,
|
||||
uint8_t channel, Pass pass, CustomCommand custom, uint32_t const order,
|
||||
Executor::CustomCommandFn command) {
|
||||
Executor::CustomCommandFn command) const {
|
||||
|
||||
assert_invariant((uint64_t(order) << CUSTOM_ORDER_SHIFT) <= CUSTOM_ORDER_MASK);
|
||||
|
||||
@@ -283,8 +284,7 @@ RenderPass::Command* RenderPass::sortCommands(
|
||||
return last;
|
||||
}
|
||||
|
||||
RenderPass::Command* RenderPass::instanceify(DriverApi& driver,
|
||||
DescriptorSetLayoutHandle perRenderableDescriptorSetLayoutHandle,
|
||||
RenderPass::Command* RenderPass::instanceify(
|
||||
Command* curr, Command* const last,
|
||||
int32_t const eyeCount) const noexcept {
|
||||
FILAMENT_TRACING_NAME(FILAMENT_TRACING_CATEGORY_FILAMENT, "instanceify");
|
||||
@@ -298,138 +298,112 @@ RenderPass::Command* RenderPass::instanceify(DriverApi& driver,
|
||||
// hash of those parameters.
|
||||
|
||||
UTILS_UNUSED uint32_t drawCallsSavedCount = 0;
|
||||
|
||||
Command* firstSentinel = nullptr;
|
||||
PerRenderableData const* uboData = nullptr;
|
||||
PerRenderableData* stagingBuffer = nullptr;
|
||||
uint32_t stagingBufferSize = 0;
|
||||
uint32_t instancedPrimitiveOffset = 0;
|
||||
size_t const count = last - curr;
|
||||
size_t const count = std::distance(curr, last);
|
||||
|
||||
// TODO: for the case of instancing we could actually use 128 instead of 64 instances
|
||||
constexpr size_t maxInstanceCount = CONFIG_MAX_INSTANCES;
|
||||
auto equivalent = [](Command const& lhs, Command const& rhs) {
|
||||
// This predicate must also filter out commands that are not eligible for auto-instancing.
|
||||
if (UTILS_UNLIKELY(lhs.info.hasSkinning || lhs.info.hasMorphing || lhs.info.instanceCount > 1)) {
|
||||
return false;
|
||||
}
|
||||
if (UTILS_UNLIKELY(rhs.info.hasSkinning || rhs.info.hasMorphing || rhs.info.instanceCount > 1)) {
|
||||
return false;
|
||||
}
|
||||
// Now check for equality
|
||||
return lhs.info.mi == rhs.info.mi &&
|
||||
lhs.info.rph == rhs.info.rph &&
|
||||
lhs.info.vbih == rhs.info.vbih &&
|
||||
lhs.info.indexOffset == rhs.info.indexOffset &&
|
||||
lhs.info.indexCount == rhs.info.indexCount &&
|
||||
lhs.info.rasterState == rhs.info.rasterState;
|
||||
};
|
||||
|
||||
while (curr != last) {
|
||||
// Currently, if we have skinning or morphing, we can't use auto instancing. This is
|
||||
// because the morphing/skinning data for comparison is not easily accessible; and also
|
||||
// because we're assuming that the per-renderable descriptor-set only has the
|
||||
// OBJECT_UNIFORMS descriptor active (i.e. the skinning/morphing descriptors are unused).
|
||||
// We also can't use auto-instancing if manual- or hybrid- instancing is used.
|
||||
// TODO: support auto-instancing for skinning/morphing
|
||||
Command const* e = curr + 1;
|
||||
if (UTILS_LIKELY(
|
||||
!curr->info.hasSkinning && !curr->info.hasMorphing &&
|
||||
curr->info.instanceCount <= 1))
|
||||
{
|
||||
assert_invariant(!curr->info.hasHybridInstancing);
|
||||
// we can't have nice things! No more than maxInstanceCount due to UBO size limits
|
||||
e = std::find_if_not(curr, std::min(last, curr + maxInstanceCount),
|
||||
[lhs = *curr](Command const& rhs) {
|
||||
// primitives must be identical to be instanced
|
||||
// Currently, instancing doesn't support skinning/morphing.
|
||||
return lhs.info.mi == rhs.info.mi &&
|
||||
lhs.info.rph == rhs.info.rph &&
|
||||
lhs.info.vbih == rhs.info.vbih &&
|
||||
lhs.info.indexOffset == rhs.info.indexOffset &&
|
||||
lhs.info.indexCount == rhs.info.indexCount &&
|
||||
lhs.info.rasterState == rhs.info.rasterState;
|
||||
});
|
||||
auto processor = [this, eyeCount, count,
|
||||
&uboData, &instancedPrimitiveOffset, &drawCallsSavedCount,
|
||||
&stagingBuffer = mInstancingStagingBuffer,
|
||||
&descriptorSetPatch = mInstancingDescriptorSetPatch]
|
||||
(Command* const dst, Command* const begin, Command* const end) mutable {
|
||||
|
||||
uint32_t const instanceCount = std::distance(begin, end);
|
||||
drawCallsSavedCount += instanceCount - 1;
|
||||
|
||||
if (UTILS_UNLIKELY(stagingBuffer.empty())) {
|
||||
stagingBuffer.resize(count);
|
||||
descriptorSetPatch.reserve(count);
|
||||
uboData = mRenderableSoa.data<FScene::UBO>();
|
||||
assert_invariant(uboData);
|
||||
}
|
||||
|
||||
uint32_t const instanceCount = e - curr;
|
||||
assert_invariant(instanceCount > 0);
|
||||
assert_invariant(instanceCount <= CONFIG_MAX_INSTANCES);
|
||||
|
||||
if (UTILS_UNLIKELY(instanceCount > 1)) {
|
||||
drawCallsSavedCount += instanceCount - 1;
|
||||
|
||||
// allocate our staging buffer only if needed
|
||||
if (UTILS_UNLIKELY(!stagingBuffer)) {
|
||||
// Create a temporary UBO for holding the per-renderable data of each primitive,
|
||||
// The `curr->info.index` is updated so that this (now instanced) command can
|
||||
// bind the UBO in the right place (where the per-instance data is).
|
||||
// The lifetime of this object is the longest of this RenderPass and all its
|
||||
// executors.
|
||||
|
||||
// create a temporary UBO for instancing
|
||||
mInstancedUboHandle = BufferObjectSharedHandle{
|
||||
driver.createBufferObject(
|
||||
count * sizeof(PerRenderableData) + sizeof(PerRenderableUib),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC), driver };
|
||||
|
||||
// TODO: use stream inline buffer for small sizes
|
||||
// TODO: use a pool for larger heap buffers
|
||||
// buffer large enough for all instances data
|
||||
stagingBufferSize = count * sizeof(PerRenderableData);
|
||||
stagingBuffer = static_cast<PerRenderableData*>(malloc(stagingBufferSize));
|
||||
uboData = mRenderableSoa.data<FScene::UBO>();
|
||||
assert_invariant(uboData);
|
||||
|
||||
// We also need a descriptor-set to hold the custom UBO. This works because
|
||||
// we currently assume the descriptor-set only needs to hold this UBO in the
|
||||
// instancing case (it wouldn't be true if we supported skinning/morphing, and
|
||||
// in this case we would need to preserve the default descriptor-set content).
|
||||
// This has the same lifetime as the UBO (see above).
|
||||
mInstancedDescriptorSetHandle = DescriptorSetSharedHandle{
|
||||
driver.createDescriptorSet(perRenderableDescriptorSetLayoutHandle),
|
||||
driver
|
||||
};
|
||||
driver.updateDescriptorSetBuffer(mInstancedDescriptorSetHandle,
|
||||
+PerRenderableBindingPoints::OBJECT_UNIFORMS,
|
||||
mInstancedUboHandle, 0, sizeof(PerRenderableUib));
|
||||
}
|
||||
|
||||
// copy the ubo data to a staging buffer
|
||||
assert_invariant(instancedPrimitiveOffset + instanceCount
|
||||
<= stagingBufferSize / sizeof(PerRenderableData));
|
||||
for (uint32_t i = 0; i < instanceCount; i++) {
|
||||
stagingBuffer[instancedPrimitiveOffset + i] = uboData[curr[i].info.index];
|
||||
}
|
||||
|
||||
// make the first command instanced
|
||||
curr[0].info.instanceCount = instanceCount * eyeCount;
|
||||
curr[0].info.index = instancedPrimitiveOffset;
|
||||
curr[0].info.dsh = mInstancedDescriptorSetHandle;
|
||||
|
||||
instancedPrimitiveOffset += instanceCount;
|
||||
|
||||
// cancel commands that are now instances
|
||||
firstSentinel = !firstSentinel ? curr : firstSentinel;
|
||||
for (uint32_t i = 1; i < instanceCount; i++) {
|
||||
curr[i].key = uint64_t(Pass::SENTINEL);
|
||||
}
|
||||
assert_invariant(instancedPrimitiveOffset + instanceCount <= stagingBuffer.size());
|
||||
for (uint32_t i = 0; i < instanceCount; i++) {
|
||||
stagingBuffer[instancedPrimitiveOffset + i] = uboData[begin[i].info.index];
|
||||
}
|
||||
|
||||
curr = const_cast<Command*>(e);
|
||||
}
|
||||
// Modify the command at its final destination
|
||||
dst->info.instanceCount = instanceCount * eyeCount;
|
||||
dst->info.index = instancedPrimitiveOffset;
|
||||
dst->info.dsh = {}; // this will be filled up in finalize
|
||||
descriptorSetPatch.push_back(dst); // Store the stable iterator
|
||||
|
||||
if (UTILS_UNLIKELY(firstSentinel)) {
|
||||
// DLOG(INFO) << "auto-instancing, saving " << drawCallsSavedCount << " draw calls, out of "
|
||||
// << count;
|
||||
// we have instanced primitives
|
||||
// copy our instanced ubo data
|
||||
driver.updateBufferObjectUnsynchronized(mInstancedUboHandle, {
|
||||
stagingBuffer, sizeof(PerRenderableData) * instancedPrimitiveOffset,
|
||||
+[](void* buffer, size_t, void*) {
|
||||
free(buffer);
|
||||
}
|
||||
}, 0);
|
||||
instancedPrimitiveOffset += instanceCount;
|
||||
};
|
||||
|
||||
stagingBuffer = nullptr;
|
||||
|
||||
// remove all the canceled commands
|
||||
auto const lastCommand = std::remove_if(firstSentinel, last, [](auto const& command) {
|
||||
return command.key == uint64_t(Pass::SENTINEL);
|
||||
});
|
||||
|
||||
return lastCommand;
|
||||
}
|
||||
|
||||
assert_invariant(stagingBuffer == nullptr);
|
||||
return last;
|
||||
auto const end = compact<CONFIG_MAX_INSTANCES>(curr, last, equivalent, processor);
|
||||
// if (!mInstancingStagingBuffer.empty()) {
|
||||
// slog.d << "auto-instancing, saving " << drawCallsSavedCount << " draw calls, out of "
|
||||
// << count << io::endl;
|
||||
// }
|
||||
return end;
|
||||
}
|
||||
|
||||
|
||||
void RenderPass::finalize(FEngine const& engine, DriverApi& driver) {
|
||||
mFinalized = true;
|
||||
if (UTILS_UNLIKELY(!mInstancingStagingBuffer.empty())) {
|
||||
auto const* p = mInstancingStagingBuffer.data();
|
||||
size_t const size = sizeof(PerRenderableData) * mInstancingStagingBuffer.size();
|
||||
|
||||
// Create a temporary UBO for holding the per-renderable data of each primitive,
|
||||
// The `curr->info.index` is updated so that this (now instanced) command can
|
||||
// bind the UBO in the right place (where the per-instance data is).
|
||||
// The lifetime of this object is the longest of this RenderPass and all its
|
||||
// executors.
|
||||
|
||||
// create a temporary UBO for instancing
|
||||
mInstancedUboHandle = BufferObjectSharedHandle{
|
||||
driver.createBufferObject(size + sizeof(PerRenderableUib),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC), driver };
|
||||
|
||||
// We also need a descriptor-set to hold the custom UBO. This works because
|
||||
// we currently assume the descriptor-set only needs to hold this UBO in the
|
||||
// instancing case (it wouldn't be true if we supported skinning/morphing, and
|
||||
// in this case we would need to preserve the default descriptor-set content).
|
||||
// This has the same lifetime as the UBO (see above).
|
||||
|
||||
mInstancedDescriptorSetHandle = DescriptorSetSharedHandle{
|
||||
driver.createDescriptorSet(engine.getPerRenderableDescriptorSetLayout().getHandle()),
|
||||
driver };
|
||||
|
||||
driver.updateDescriptorSetBuffer(mInstancedDescriptorSetHandle,
|
||||
+PerRenderableBindingPoints::OBJECT_UNIFORMS,
|
||||
mInstancedUboHandle, 0, sizeof(PerRenderableUib));
|
||||
|
||||
driver.updateBufferObjectUnsynchronized(mInstancedUboHandle,
|
||||
BufferDescriptor::make(p, size,
|
||||
[data = std::move(mInstancingStagingBuffer)](void*, size_t) {}), 0);
|
||||
|
||||
// patch the commands with the descriptor set
|
||||
for (Command* const curr : mInstancingDescriptorSetPatch) {
|
||||
assert_invariant(curr[0].info.instanceCount > 1);
|
||||
assert_invariant(!curr[0].info.dsh);
|
||||
curr[0].info.dsh = mInstancedDescriptorSetHandle;
|
||||
}
|
||||
mInstancingDescriptorSetPatch.clear();
|
||||
assert_invariant(mInstancingStagingBuffer.empty());
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
UTILS_ALWAYS_INLINE // This function exists only to make the code more readable. we want it inlined.
|
||||
inline // and we don't need it in the compilation unit
|
||||
@@ -581,6 +555,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(CommandTypeFlags extraFlag
|
||||
|
||||
auto const* const UTILS_RESTRICT soaWorldAABBCenter = soa.data<FScene::WORLD_AABB_CENTER>();
|
||||
auto const* const UTILS_RESTRICT soaVisibility = soa.data<FScene::VISIBILITY_STATE>();
|
||||
auto const* const UTILS_RESTRICT soaSkinningData = soa.data<FScene::SKINNING_STATE>();
|
||||
auto const* const UTILS_RESTRICT soaPrimitives = soa.data<FScene::PRIMITIVES>();
|
||||
auto const* const UTILS_RESTRICT soaSkinning = soa.data<FScene::SKINNING_BUFFER>();
|
||||
auto const* const UTILS_RESTRICT soaMorphing = soa.data<FScene::MORPHING_BUFFER>();
|
||||
@@ -636,8 +611,8 @@ RenderPass::Command* RenderPass::generateCommandsImpl(CommandTypeFlags extraFlag
|
||||
|
||||
// calculate the per-primitive face winding order inversion
|
||||
bool const inverseFrontFaces = viewInverseFrontFaces ^ soaVisibility[i].reversedWindingOrder;
|
||||
bool const hasMorphing = soaVisibility[i].morphing;
|
||||
bool const hasSkinning = soaVisibility[i].skinning;
|
||||
bool const hasMorphing = soaSkinningData[i].morphing;
|
||||
bool const hasSkinning = soaSkinningData[i].skinning;
|
||||
bool const hasSkinningOrMorphing = hasSkinning || hasMorphing;
|
||||
|
||||
// if we are already an SSR variant, the SRE bit is already set
|
||||
@@ -693,7 +668,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(CommandTypeFlags extraFlag
|
||||
const bool shadowCaster = soaVisibility[i].castShadows & hasShadowing;
|
||||
const bool writeDepthForShadowCasters = depthContainsShadowCasters & shadowCaster;
|
||||
|
||||
Slice<const FRenderPrimitive> primitives = soaPrimitives[i];
|
||||
Slice<const FRenderPrimitive> const primitives = soaPrimitives[i];
|
||||
/*
|
||||
* This is our hot loop. It's written to avoid branches.
|
||||
* When modifying this code, always ensure it stays efficient.
|
||||
@@ -1160,6 +1135,7 @@ RenderPass::Executor::Executor(RenderPass const& pass, Command const* b, Command
|
||||
mHasScissorViewport = mScissor != backend::Viewport{ 0, 0, INT32_MAX, INT32_MAX };
|
||||
assert_invariant(b >= pass.begin());
|
||||
assert_invariant(e <= pass.end());
|
||||
assert_invariant(pass.isFinalized());
|
||||
}
|
||||
|
||||
RenderPass::Executor::Executor() noexcept
|
||||
|
||||
+19
-12
@@ -61,6 +61,7 @@ class FMaterialInstance;
|
||||
class FRenderPrimitive;
|
||||
class RenderPassBuilder;
|
||||
class ColorPassDescriptorSet;
|
||||
struct PerRenderableData;
|
||||
|
||||
class RenderPass {
|
||||
public:
|
||||
@@ -310,6 +311,10 @@ public:
|
||||
// allocated commands ARE NOT freed, they're owned by the Arena
|
||||
~RenderPass() noexcept;
|
||||
|
||||
// this must be called before calling getExecutor(), but can't be called from within
|
||||
// a render pass
|
||||
void finalize(FEngine const& engine, backend::DriverApi& driver);
|
||||
|
||||
// Specifies the viewport for the scissor rectangle, that is, the final scissor rect is
|
||||
// offset by the viewport's left-top and clipped to the viewport's width/height.
|
||||
void setScissorViewport(backend::Viewport const viewport) noexcept {
|
||||
@@ -324,14 +329,14 @@ public:
|
||||
std::reference_wrapper<backend::DriverApi> driver;
|
||||
public:
|
||||
explicit BufferObjectHandleDeleter(backend::DriverApi& driver) noexcept : driver(driver) { }
|
||||
void operator()(backend::BufferObjectHandle handle) noexcept;
|
||||
void operator()(backend::BufferObjectHandle handle) const noexcept;
|
||||
};
|
||||
|
||||
class DescriptorSetHandleDeleter {
|
||||
std::reference_wrapper<backend::DriverApi> driver;
|
||||
public:
|
||||
explicit DescriptorSetHandleDeleter(backend::DriverApi& driver) noexcept : driver(driver) { }
|
||||
void operator()(backend::DescriptorSetHandle handle) noexcept;
|
||||
void operator()(backend::DescriptorSetHandle handle) const noexcept;
|
||||
};
|
||||
|
||||
using BufferObjectSharedHandle = SharedHandle<
|
||||
@@ -340,6 +345,8 @@ public:
|
||||
using DescriptorSetSharedHandle = SharedHandle<
|
||||
backend::HwDescriptorSet, DescriptorSetHandleDeleter>;
|
||||
|
||||
bool isFinalized() const noexcept { return mFinalized; }
|
||||
|
||||
/*
|
||||
* Executor holds the range of commands to execute for a given pass
|
||||
*/
|
||||
@@ -409,8 +416,7 @@ public:
|
||||
private:
|
||||
friend class FRenderer;
|
||||
friend class RenderPassBuilder;
|
||||
RenderPass(FEngine const& engine, backend::DriverApi& driver,
|
||||
RenderPassBuilder const& builder) noexcept;
|
||||
RenderPass(FEngine const& engine, RenderPassBuilder const& builder) noexcept;
|
||||
|
||||
// This is the main function of this class, this appends commands to the pass using
|
||||
// the current camera, geometry and flags set. This can be called multiple times if needed.
|
||||
@@ -427,7 +433,7 @@ private:
|
||||
// Appends a custom command.
|
||||
void appendCustomCommand(Command* commands,
|
||||
uint8_t channel, Pass pass, CustomCommand custom, uint32_t order,
|
||||
Executor::CustomCommandFn command);
|
||||
Executor::CustomCommandFn command) const;
|
||||
|
||||
static Command* resize(Arena& arena, Command* last) noexcept;
|
||||
|
||||
@@ -436,10 +442,7 @@ private:
|
||||
Command* begin, Command* end) noexcept;
|
||||
|
||||
// instanceify commands then trims sentinels
|
||||
Command* instanceify(backend::DriverApi& driver,
|
||||
backend::DescriptorSetLayoutHandle perRenderableDescriptorSetLayoutHandle,
|
||||
Command* begin, Command* end,
|
||||
int32_t eyeCount) const noexcept;
|
||||
Command* instanceify(Command* begin, Command* end, int32_t eyeCount) const noexcept;
|
||||
|
||||
// We choose the command count per job to minimize JobSystem overhead.
|
||||
static constexpr size_t JOBS_PARALLEL_FOR_COMMANDS_COUNT = 128;
|
||||
@@ -474,8 +477,12 @@ private:
|
||||
backend::Viewport mScissorViewport{ 0, 0, INT32_MAX, INT32_MAX };
|
||||
Command const* /* const */ mCommandBegin = nullptr; // Pointer to the first command
|
||||
Command const* /* const */ mCommandEnd = nullptr; // Pointer to one past the last command
|
||||
mutable BufferObjectSharedHandle mInstancedUboHandle; // ubo for instanced primitives
|
||||
mutable DescriptorSetSharedHandle mInstancedDescriptorSetHandle; // a descriptor-set to hold the ubo
|
||||
mutable std::vector<PerRenderableData> mInstancingStagingBuffer;
|
||||
mutable std::vector<Command*> mInstancingDescriptorSetPatch;
|
||||
BufferObjectSharedHandle mInstancedUboHandle; // ubo for instanced primitives
|
||||
DescriptorSetSharedHandle mInstancedDescriptorSetHandle; // a descriptor-set to hold the ubo
|
||||
bool mFinalized = false;
|
||||
|
||||
// a vector for our custom commands
|
||||
using CustomCommandVector = utils::FixedCapacityVector<Executor::CustomCommandFn>;
|
||||
mutable CustomCommandVector mCustomCommands;
|
||||
@@ -573,7 +580,7 @@ public:
|
||||
uint32_t order,
|
||||
const RenderPass::Executor::CustomCommandFn& command);
|
||||
|
||||
RenderPass build(FEngine const& engine, backend::DriverApi& driver) const;
|
||||
RenderPass build(FEngine const& engine) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "details/Engine.h"
|
||||
#include "details/VertexBuffer.h"
|
||||
#include "details/IndexBuffer.h"
|
||||
#include "details/Material.h"
|
||||
|
||||
using namespace utils;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include <filament/Renderer.h>
|
||||
|
||||
#include "ResourceAllocator.h"
|
||||
#include "TextureCache.h"
|
||||
|
||||
#include "details/Engine.h"
|
||||
#include "details/Renderer.h"
|
||||
|
||||
@@ -415,13 +415,15 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
|
||||
renderPassFlags |= RenderPass::HAS_DEPTH_CLAMP;
|
||||
}
|
||||
|
||||
RenderPass const pass = passBuilder
|
||||
RenderPass pass = passBuilder
|
||||
.renderFlags(RenderPass::HAS_DEPTH_CLAMP, renderPassFlags)
|
||||
.camera(cameraInfo.getPosition(), cameraInfo.getForwardVector())
|
||||
.visibilityMask(entry.visibilityMask)
|
||||
.geometry(scene->getRenderableData(), entry.range)
|
||||
.commandTypeFlags(RenderPass::CommandTypeFlags::SHADOW)
|
||||
.build(engine, driver);
|
||||
.build(engine);
|
||||
|
||||
pass.finalize(engine, driver);
|
||||
|
||||
entry.executor = pass.getExecutor();
|
||||
|
||||
|
||||
@@ -58,6 +58,16 @@ void Texture::setImage(Engine& engine, size_t const level,
|
||||
downcast(this)->setImage(downcast(engine), level, std::move(buffer), faceOffsets);
|
||||
}
|
||||
|
||||
backend::AsyncCallId Texture::setImageAsync(Engine& engine, size_t const level,
|
||||
uint32_t const xoffset, uint32_t const yoffset, uint32_t const zoffset,
|
||||
uint32_t const width, uint32_t const height, uint32_t const depth,
|
||||
PixelBufferDescriptor&& buffer, backend::CallbackHandler* handler,
|
||||
AsyncCompletionCallback callback, void* user) const {
|
||||
return downcast(this)->setImageAsync(downcast(engine),
|
||||
level, xoffset, yoffset, zoffset, width, height, depth, std::move(buffer),
|
||||
handler, std::move(callback), user);
|
||||
}
|
||||
|
||||
void Texture::setExternalImage(Engine& engine, ExternalImageHandleRef image) noexcept {
|
||||
downcast(this)->setExternalImage(downcast(engine), image);
|
||||
}
|
||||
@@ -78,6 +88,10 @@ void Texture::generateMipmaps(Engine& engine) const noexcept {
|
||||
downcast(this)->generateMipmaps(downcast(engine));
|
||||
}
|
||||
|
||||
bool Texture::isCreationComplete() const noexcept {
|
||||
return downcast(this)->isCreationComplete();
|
||||
}
|
||||
|
||||
bool Texture::isTextureFormatSupported(Engine& engine, InternalFormat const format) noexcept {
|
||||
return FTexture::isTextureFormatSupported(downcast(engine), format);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "ResourceAllocator.h"
|
||||
#include "TextureCache.h"
|
||||
|
||||
#include <filament/Engine.h>
|
||||
|
||||
@@ -56,31 +56,31 @@ using namespace backend;
|
||||
|
||||
template<typename K, typename V, typename H>
|
||||
UTILS_NOINLINE
|
||||
ResourceAllocator::AssociativeContainer<K, V, H>::AssociativeContainer() {
|
||||
TextureCache::AssociativeContainer<K, V, H>::AssociativeContainer() {
|
||||
mContainer.reserve(128);
|
||||
}
|
||||
|
||||
template<typename K, typename V, typename H>
|
||||
UTILS_NOINLINE
|
||||
ResourceAllocator::AssociativeContainer<K, V, H>::~AssociativeContainer() noexcept = default;
|
||||
TextureCache::AssociativeContainer<K, V, H>::~AssociativeContainer() noexcept = default;
|
||||
|
||||
template<typename K, typename V, typename H>
|
||||
UTILS_NOINLINE
|
||||
typename ResourceAllocator::AssociativeContainer<K, V, H>::iterator
|
||||
ResourceAllocator::AssociativeContainer<K, V, H>::erase(iterator it) {
|
||||
typename TextureCache::AssociativeContainer<K, V, H>::iterator
|
||||
TextureCache::AssociativeContainer<K, V, H>::erase(iterator it) {
|
||||
return mContainer.erase(it);
|
||||
}
|
||||
|
||||
template<typename K, typename V, typename H>
|
||||
typename ResourceAllocator::AssociativeContainer<K, V, H>::const_iterator
|
||||
ResourceAllocator::AssociativeContainer<K, V, H>::find(key_type const& key) const {
|
||||
typename TextureCache::AssociativeContainer<K, V, H>::const_iterator
|
||||
TextureCache::AssociativeContainer<K, V, H>::find(key_type const& key) const {
|
||||
return const_cast<AssociativeContainer*>(this)->find(key);
|
||||
}
|
||||
|
||||
template<typename K, typename V, typename H>
|
||||
UTILS_NOINLINE
|
||||
typename ResourceAllocator::AssociativeContainer<K, V, H>::iterator
|
||||
ResourceAllocator::AssociativeContainer<K, V, H>::find(key_type const& key) {
|
||||
typename TextureCache::AssociativeContainer<K, V, H>::iterator
|
||||
TextureCache::AssociativeContainer<K, V, H>::find(key_type const& key) {
|
||||
return std::find_if(mContainer.begin(), mContainer.end(), [&key](auto const& v) {
|
||||
return v.first == key;
|
||||
});
|
||||
@@ -89,21 +89,21 @@ ResourceAllocator::AssociativeContainer<K, V, H>::find(key_type const& key) {
|
||||
template<typename K, typename V, typename H>
|
||||
template<typename... ARGS>
|
||||
UTILS_NOINLINE
|
||||
void ResourceAllocator::AssociativeContainer<K, V, H>::emplace(ARGS&& ... args) {
|
||||
void TextureCache::AssociativeContainer<K, V, H>::emplace(ARGS&& ... args) {
|
||||
mContainer.emplace_back(std::forward<ARGS>(args)...);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
ResourceAllocatorInterface::~ResourceAllocatorInterface() = default;
|
||||
TextureCacheInterface::~TextureCacheInterface() = default;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
ResourceAllocatorDisposerInterface::~ResourceAllocatorDisposerInterface() = default;
|
||||
TextureCacheDisposerInterface::~TextureCacheDisposerInterface() = default;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
size_t ResourceAllocator::TextureKey::getSize() const noexcept {
|
||||
size_t TextureCache::TextureKey::getSize() const noexcept {
|
||||
size_t const pixelCount = width * height * depth;
|
||||
size_t size = pixelCount * FTexture::getFormatSize(format);
|
||||
size_t const s = std::max(uint8_t(1), samples);
|
||||
@@ -120,24 +120,24 @@ size_t ResourceAllocator::TextureKey::getSize() const noexcept {
|
||||
return size;
|
||||
}
|
||||
|
||||
ResourceAllocator::ResourceAllocator(Engine::Config const& config, DriverApi& driverApi) noexcept
|
||||
TextureCache::TextureCache(Engine::Config const& config, DriverApi& driverApi) noexcept
|
||||
: mCacheMaxAge(config.resourceAllocatorCacheMaxAge),
|
||||
mBackend(driverApi),
|
||||
mDisposer(std::make_shared<ResourceAllocatorDisposer>(driverApi)) {
|
||||
mDisposer(std::make_shared<TextureCacheDisposer>(driverApi)) {
|
||||
}
|
||||
|
||||
ResourceAllocator::ResourceAllocator(std::shared_ptr<ResourceAllocatorDisposer> disposer,
|
||||
TextureCache::TextureCache(std::shared_ptr<TextureCacheDisposer> disposer,
|
||||
Engine::Config const& config, DriverApi& driverApi) noexcept
|
||||
: mCacheMaxAge(config.resourceAllocatorCacheMaxAge),
|
||||
mBackend(driverApi),
|
||||
mDisposer(std::move(disposer)) {
|
||||
}
|
||||
|
||||
ResourceAllocator::~ResourceAllocator() noexcept {
|
||||
TextureCache::~TextureCache() noexcept {
|
||||
assert_invariant(mTextureCache.empty());
|
||||
}
|
||||
|
||||
void ResourceAllocator::terminate() noexcept {
|
||||
void TextureCache::terminate() noexcept {
|
||||
auto& textureCache = mTextureCache;
|
||||
for (auto it = textureCache.begin(); it != textureCache.end();) {
|
||||
mBackend.destroyTexture(it->second.handle);
|
||||
@@ -145,7 +145,7 @@ void ResourceAllocator::terminate() noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
RenderTargetHandle ResourceAllocator::createRenderTarget(StaticString name,
|
||||
RenderTargetHandle TextureCache::createRenderTarget(StaticString name,
|
||||
TargetBufferFlags const targetBufferFlags, uint32_t const width, uint32_t const height,
|
||||
uint8_t const samples, uint8_t const layerCount, MRT const color, TargetBufferInfo const depth,
|
||||
TargetBufferInfo const stencil) noexcept {
|
||||
@@ -154,11 +154,11 @@ RenderTargetHandle ResourceAllocator::createRenderTarget(StaticString name,
|
||||
return handle;
|
||||
}
|
||||
|
||||
void ResourceAllocator::destroyRenderTarget(RenderTargetHandle const h) noexcept {
|
||||
void TextureCache::destroyRenderTarget(RenderTargetHandle const h) noexcept {
|
||||
mBackend.destroyRenderTarget(h);
|
||||
}
|
||||
|
||||
TextureHandle ResourceAllocator::createTexture(StaticString name,
|
||||
TextureHandle TextureCache::createTexture(StaticString name,
|
||||
SamplerType const target, uint8_t const levels, TextureFormat const format, uint8_t samples,
|
||||
uint32_t const width, uint32_t const height, uint32_t const depth,
|
||||
std::array<TextureSwizzle, 4> const swizzle,
|
||||
@@ -207,7 +207,7 @@ TextureHandle ResourceAllocator::createTexture(StaticString name,
|
||||
return handle;
|
||||
}
|
||||
|
||||
void ResourceAllocator::destroyTexture(TextureHandle const h) noexcept {
|
||||
void TextureCache::destroyTexture(TextureHandle const h) noexcept {
|
||||
auto const key = mDisposer->checkin(h);
|
||||
if constexpr (mEnabled) {
|
||||
if (UTILS_LIKELY(key.has_value())) {
|
||||
@@ -221,11 +221,11 @@ void ResourceAllocator::destroyTexture(TextureHandle const h) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
ResourceAllocatorDisposerInterface& ResourceAllocator::getDisposer() noexcept {
|
||||
TextureCacheDisposerInterface& TextureCache::getDisposer() noexcept {
|
||||
return *mDisposer;
|
||||
}
|
||||
|
||||
void ResourceAllocator::gc(bool const skippedFrame) noexcept {
|
||||
void TextureCache::gc(bool const skippedFrame) noexcept {
|
||||
// this is called regularly -- usually once per frame
|
||||
|
||||
// increase our age at each (non-skipped) frame
|
||||
@@ -289,7 +289,7 @@ void ResourceAllocator::gc(bool const skippedFrame) noexcept {
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
void ResourceAllocator::dump(bool const brief) const noexcept {
|
||||
void TextureCache::dump(bool const brief) const noexcept {
|
||||
constexpr float MiB = 1.0f / float(1u << 20u);
|
||||
DLOG(INFO) << "# entries=" << mTextureCache.size() << ", sz=" << (float) mCacheSize * MiB
|
||||
<< " MiB"
|
||||
@@ -305,8 +305,8 @@ void ResourceAllocator::dump(bool const brief) const noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
ResourceAllocator::CacheContainer::iterator
|
||||
ResourceAllocator::purge(
|
||||
TextureCache::CacheContainer::iterator
|
||||
TextureCache::purge(
|
||||
CacheContainer::iterator const& pos) {
|
||||
// DLOG(INFO) << "purging " << pos->second.handle.getId() << ", age=" << pos->second.age;
|
||||
mBackend.destroyTexture(pos->second.handle);
|
||||
@@ -316,19 +316,19 @@ ResourceAllocator::purge(
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
ResourceAllocatorDisposer::ResourceAllocatorDisposer(DriverApi& driverApi) noexcept
|
||||
TextureCacheDisposer::TextureCacheDisposer(DriverApi& driverApi) noexcept
|
||||
: mBackend(driverApi) {
|
||||
}
|
||||
|
||||
ResourceAllocatorDisposer::~ResourceAllocatorDisposer() noexcept {
|
||||
TextureCacheDisposer::~TextureCacheDisposer() noexcept {
|
||||
assert_invariant(mInUseTextures.empty());
|
||||
}
|
||||
|
||||
void ResourceAllocatorDisposer::terminate() noexcept {
|
||||
void TextureCacheDisposer::terminate() noexcept {
|
||||
assert_invariant(mInUseTextures.empty());
|
||||
}
|
||||
|
||||
void ResourceAllocatorDisposer::destroy(TextureHandle const handle) noexcept {
|
||||
void TextureCacheDisposer::destroy(TextureHandle const handle) noexcept {
|
||||
if (handle) {
|
||||
auto r = checkin(handle);
|
||||
if (r.has_value()) {
|
||||
@@ -337,12 +337,12 @@ void ResourceAllocatorDisposer::destroy(TextureHandle const handle) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceAllocatorDisposer::checkout(TextureHandle handle,
|
||||
ResourceAllocator::TextureKey key) {
|
||||
void TextureCacheDisposer::checkout(TextureHandle handle,
|
||||
TextureCache::TextureKey key) {
|
||||
mInUseTextures.emplace(handle, key);
|
||||
}
|
||||
|
||||
std::optional<ResourceAllocator::TextureKey> ResourceAllocatorDisposer::checkin(
|
||||
std::optional<TextureCache::TextureKey> TextureCacheDisposer::checkin(
|
||||
TextureHandle handle) {
|
||||
// find the texture in the in-use list (it must be there!)
|
||||
auto it = mInUseTextures.find(handle);
|
||||
@@ -40,19 +40,19 @@
|
||||
|
||||
namespace filament {
|
||||
|
||||
class ResourceAllocatorDisposer;
|
||||
class TextureCacheDisposer;
|
||||
|
||||
// The only reason we use an interface here is for unit-tests, so we can mock this allocator.
|
||||
// This is not too time-critical, so that's okay.
|
||||
|
||||
class ResourceAllocatorDisposerInterface {
|
||||
class TextureCacheDisposerInterface {
|
||||
public:
|
||||
virtual void destroy(backend::TextureHandle handle) noexcept = 0;
|
||||
protected:
|
||||
virtual ~ResourceAllocatorDisposerInterface();
|
||||
virtual ~TextureCacheDisposerInterface();
|
||||
};
|
||||
|
||||
class ResourceAllocatorInterface {
|
||||
class TextureCacheInterface {
|
||||
public:
|
||||
virtual backend::RenderTargetHandle createRenderTarget(utils::StaticString name,
|
||||
backend::TargetBufferFlags targetBufferFlags,
|
||||
@@ -74,21 +74,21 @@ public:
|
||||
|
||||
virtual void destroyTexture(backend::TextureHandle h) noexcept = 0;
|
||||
|
||||
virtual ResourceAllocatorDisposerInterface& getDisposer() noexcept = 0;
|
||||
virtual TextureCacheDisposerInterface& getDisposer() noexcept = 0;
|
||||
|
||||
protected:
|
||||
virtual ~ResourceAllocatorInterface();
|
||||
virtual ~TextureCacheInterface();
|
||||
};
|
||||
|
||||
class ResourceAllocator final : public ResourceAllocatorInterface {
|
||||
class TextureCache final : public TextureCacheInterface {
|
||||
public:
|
||||
explicit ResourceAllocator(std::shared_ptr<ResourceAllocatorDisposer> disposer,
|
||||
explicit TextureCache(std::shared_ptr<TextureCacheDisposer> disposer,
|
||||
Engine::Config const& config, backend::DriverApi& driverApi) noexcept;
|
||||
|
||||
explicit ResourceAllocator(
|
||||
explicit TextureCache(
|
||||
Engine::Config const& config, backend::DriverApi& driverApi) noexcept;
|
||||
|
||||
~ResourceAllocator() noexcept override;
|
||||
~TextureCache() noexcept override;
|
||||
|
||||
void terminate() noexcept;
|
||||
|
||||
@@ -112,7 +112,7 @@ public:
|
||||
|
||||
void destroyTexture(backend::TextureHandle h) noexcept override;
|
||||
|
||||
ResourceAllocatorDisposerInterface& getDisposer() noexcept override;
|
||||
TextureCacheDisposerInterface& getDisposer() noexcept override;
|
||||
|
||||
void gc(bool skippedFrame = false) noexcept;
|
||||
|
||||
@@ -220,30 +220,30 @@ private:
|
||||
purge(CacheContainer::iterator const& pos);
|
||||
|
||||
backend::DriverApi& mBackend;
|
||||
std::shared_ptr<ResourceAllocatorDisposer> mDisposer;
|
||||
std::shared_ptr<TextureCacheDisposer> mDisposer;
|
||||
CacheContainer mTextureCache;
|
||||
size_t mAge = 0;
|
||||
uint32_t mCacheSize = 0;
|
||||
uint32_t mCacheSizeHiWaterMark = 0;
|
||||
static constexpr bool mEnabled = true;
|
||||
|
||||
friend class ResourceAllocatorDisposer;
|
||||
friend class TextureCacheDisposer;
|
||||
};
|
||||
|
||||
class ResourceAllocatorDisposer final : public ResourceAllocatorDisposerInterface {
|
||||
using TextureKey = ResourceAllocator::TextureKey;
|
||||
class TextureCacheDisposer final : public TextureCacheDisposerInterface {
|
||||
using TextureKey = TextureCache::TextureKey;
|
||||
public:
|
||||
explicit ResourceAllocatorDisposer(backend::DriverApi& driverApi) noexcept;
|
||||
~ResourceAllocatorDisposer() noexcept override;
|
||||
explicit TextureCacheDisposer(backend::DriverApi& driverApi) noexcept;
|
||||
~TextureCacheDisposer() noexcept override;
|
||||
void terminate() noexcept;
|
||||
void destroy(backend::TextureHandle handle) noexcept override;
|
||||
|
||||
private:
|
||||
friend class ResourceAllocator;
|
||||
friend class TextureCache;
|
||||
void checkout(backend::TextureHandle handle, TextureKey key);
|
||||
std::optional<TextureKey> checkin(backend::TextureHandle handle);
|
||||
|
||||
using InUseContainer = ResourceAllocator::AssociativeContainer<backend::TextureHandle, TextureKey>;
|
||||
using InUseContainer = TextureCache::AssociativeContainer<backend::TextureHandle, TextureKey>;
|
||||
backend::DriverApi& mBackend;
|
||||
InUseContainer mInUseTextures;
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user