Compare commits

..
Author SHA1 Message Date
Eliza Velasquez 3308ec7e34 wip: editor stuff 2025-03-13 17:59:50 -07:00
9884 changed files with 2099281 additions and 650648 deletions
+10 -2
View File
@@ -1,7 +1,15 @@
;;; Directory Local Variables -*- no-byte-compile: t -*- ;;; Directory Local Variables -*- no-byte-compile: t -*-
;;; For more information see (info "(emacs) Directory Variables") ;;; For more information see (info "(emacs) Directory Variables")
((c++-mode . ((c-file-style . "filament") ((c++-mode . ((eval . (progn
(make-local-variable 'eglot-ignored-server-capabilities)
(add-to-list 'eglot-ignored-server-capabilities
:documentOnTypeFormattingProvider)))
(c-file-style . "filament")
(apheleia-inhibit . t))) (apheleia-inhibit . t)))
(c-mode . ((c-file-style . "filament") (c-mode . ((eval . (progn
(make-local-variable 'eglot-ignored-server-capabilities)
(add-to-list 'eglot-ignored-server-capabilities
:documentOnTypeFormattingProvider)))
(c-file-style . "filament")
(apheleia-inhibit . t)))) (apheleia-inhibit . t))))
+1 -1
View File
@@ -5,7 +5,7 @@ end_of_line = lf
insert_final_newline = true insert_final_newline = true
charset = utf-8 charset = utf-8
[*.{c,cpp,h,inc,kt,java,js,md}] [*.{c,cpp,h,inc,kt,java,js,md,fs,vs}]
indent_style = space indent_style = space
indent_size = 4 indent_size = 4
max_line_length = 100 max_line_length = 100
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
name: build-android name: build-android
# We intentially use a larger runner here to enable larger disk space # We intentially use a larger runner here to enable larger disk space
# (standard linux runner will fail on disk space and faster build time). # (standard linux runner will fail on disk space and faster build time).
runs-on: 'ubuntu-24.04-32core' runs-on: ubuntu-22.04-32core
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.6
+1 -1
View File
@@ -10,7 +10,7 @@ on:
jobs: jobs:
build-linux: build-linux:
name: build-linux name: build-linux
runs-on: 'ubuntu-24.04-16core' runs-on: ubuntu-22.04-16core
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.6
+12 -65
View File
@@ -19,8 +19,6 @@ jobs:
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- name: Run build script - name: Run build script
run: | run: |
WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]` WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]`
@@ -42,12 +40,10 @@ jobs:
build-android: build-android:
name: build-android name: build-android
runs-on: 'ubuntu-24.04-16core' runs-on: ubuntu-22.04-16core
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: actions/setup-java@v3 - uses: actions/setup-java@v3
with: with:
distribution: 'temurin' distribution: 'temurin'
@@ -64,8 +60,6 @@ jobs:
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- name: Run build script - name: Run build script
run: | run: |
cd build/ios && printf "y" | ./build.sh presubmit cd build/ios && printf "y" | ./build.sh presubmit
@@ -75,7 +69,7 @@ jobs:
build-web: build-web:
name: build-web name: build-web
runs-on: 'ubuntu-24.04-16core' runs-on: ubuntu-22.04-16core
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.6
@@ -85,7 +79,7 @@ jobs:
validate-docs: validate-docs:
name: validate-docs name: validate-docs
runs-on: 'ubuntu-24.04-4core' runs-on: ubuntu-22.04-4core
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.6
with: with:
@@ -98,73 +92,26 @@ jobs:
test-renderdiff: test-renderdiff:
name: test-renderdiff name: test-renderdiff
runs-on: macos-14-xlarge runs-on: ubuntu-22.04-16core
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.6
- name: Set up Homebrew - uses: ./.github/actions/ubuntu-apt-add-src
id: set-up-homebrew - name: Run script
uses: Homebrew/actions/setup-homebrew@master
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Cache Mesa and deps
id: mesa-cache
uses: actions/cache@v4 # Use a specific version
with:
path: |
$HOME/Library/Caches/Homebrew
mesa
key: ${{ runner.os }}-mesa-deps-${{ vars.MESA_VERSION }}
- name: Get Mesa
id: mesa-prereq
env:
MESA_VERSION: ${{ vars.MESA_VERSION }}
run: | run: |
bash test/utils/get_mesa.sh source ./build/linux/ci-common.sh && bash test/renderdiff/test.sh
- name: Run Test
run: |
bash test/renderdiff/test.sh
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: presubmit-renderdiff-result name: presubmit-renderdiff-result
path: ./out/renderdiff_tests path: ./out/renderdiff_tests
validate-wgsl-webgpu: validate-wgsl-pipeline:
name: validate-wgsl-webgpu name: validate-wgsl-pipeline
runs-on: 'ubuntu-24.04-8core' runs-on: macos-14-xlarge
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.6
- name: Run build script - name: Run build script
run: source ./build/linux/ci-common.sh && ./build.sh -W debug test_filamat filament run: ./build.sh -W debug test_filamat
- name: Run test - name: Run test
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl* run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
code-correcteness:
name: code-correctness
runs-on: 'macos-14-xlarge'
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- name: Set up Homebrew
id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install prerequisites
run: |
pip install pyyaml
brew install llvm
sudo ln -s "$(brew --prefix llvm)/bin/clang-tidy" "/usr/local/bin/clang-tidy"
- name: Run build script
# We need to build before clang-tidy can run analysis
run: |
# This will build for all three desktop backends on mac
./build.sh -p desktop debug gltf_viewer
- name: Run test
run: |
bash test/code-correctness/test.sh
+2 -2
View File
@@ -70,7 +70,7 @@ jobs:
build-web: build-web:
name: build-web name: build-web
runs-on: 'ubuntu-24.04-16core' runs-on: ubuntu-22.04-16core
if: github.event_name == 'release' || github.event.inputs.platform == 'web' if: github.event_name == 'release' || github.event.inputs.platform == 'web'
steps: steps:
@@ -103,7 +103,7 @@ jobs:
build-android: build-android:
name: build-android name: build-android
runs-on: 'ubuntu-24.04-16core' runs-on: ubuntu-22.04-16core
if: github.event_name == 'release' || github.event.inputs.platform == 'android' if: github.event_name == 'release' || github.event.inputs.platform == 'android'
steps: steps:
+1 -1
View File
@@ -10,7 +10,7 @@ on:
jobs: jobs:
build-web: build-web:
name: build-web name: build-web
runs-on: 'ubuntu-24.04-16core' runs-on: ubuntu-22.04-16core
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.6
-15
View File
@@ -18,18 +18,3 @@ test*.json
results results
/compile_commands.json /compile_commands.json
/.cache /.cache
build/.cmake/
build/CMakeCache.txt
build/CMakeFiles/
build/Makefile
build/SPIRV-Tools*
build/cmake_install.cmake
build/compile_commands.json
build/filament/
build/include/
build/libs/
build/mac/ninja
build/samples/
build/shaders/
build/third_party/
build/tools/
+1 -19
View File
@@ -139,6 +139,7 @@ else()
set(LINUX FALSE) set(LINUX FALSE)
endif() endif()
if (LINUX)
if (NOT FILAMENT_OSMESA_PATH STREQUAL "") if (NOT FILAMENT_OSMESA_PATH STREQUAL "")
if (NOT EXISTS ${FILAMENT_OSMESA_PATH}/) if (NOT EXISTS ${FILAMENT_OSMESA_PATH}/)
message(FATAL_ERROR "Cannot find specified OSMesa build directory: ${FILAMENT_OSMESA_PATH}") message(FATAL_ERROR "Cannot find specified OSMesa build directory: ${FILAMENT_OSMESA_PATH}")
@@ -146,7 +147,6 @@ if (NOT FILAMENT_OSMESA_PATH STREQUAL "")
set(FILAMENT_SUPPORTS_OSMESA TRUE) set(FILAMENT_SUPPORTS_OSMESA TRUE)
endif() endif()
if (LINUX)
if (FILAMENT_SUPPORTS_WAYLAND) if (FILAMENT_SUPPORTS_WAYLAND)
add_definitions(-DFILAMENT_SUPPORTS_WAYLAND) add_definitions(-DFILAMENT_SUPPORTS_WAYLAND)
set(FILAMENT_SUPPORTS_X11 FALSE) set(FILAMENT_SUPPORTS_X11 FALSE)
@@ -184,12 +184,6 @@ if (NOT ANDROID AND NOT WEBGL AND NOT IOS AND NOT FILAMENT_LINUX_IS_MOBILE)
set(IS_HOST_PLATFORM TRUE) set(IS_HOST_PLATFORM TRUE)
endif() endif()
if (APPLE)
if (FILAMENT_SUPPORTS_OSMESA)
add_definitions(-DFILAMENT_SUPPORTS_OSMESA)
endif()
endif()
if (WIN32) if (WIN32)
# Link statically against c/c++ lib to avoid missing redistriburable such as # Link statically against c/c++ lib to avoid missing redistriburable such as
# "VCRUNTIME140.dll not found. Try reinstalling the app.", but give users # "VCRUNTIME140.dll not found. Try reinstalling the app.", but give users
@@ -334,9 +328,6 @@ if (MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} /W0 /Zc:__cplusplus") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} /W0 /Zc:__cplusplus")
else() else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} -fstrict-aliasing -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} -fstrict-aliasing -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations")
if (APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-nullability-extension")
endif()
endif() endif()
if (FILAMENT_USE_EXTERNAL_GLES3) if (FILAMENT_USE_EXTERNAL_GLES3)
@@ -610,14 +601,6 @@ if (FILAMENT_SUPPORTS_METAL)
set(MATC_API_FLAGS ${MATC_API_FLAGS} -a metal) set(MATC_API_FLAGS ${MATC_API_FLAGS} -a metal)
endif() endif()
# With WebGPU, push constants are not supported. Skinning uses them.
# WebGPU has a proposal to add push constants at https://github.com/gpuweb/gpuweb/blob/main/proposals/push-constants.md
# With WebGPU, Tint does not support ClipDistance which is used in Stereo. Mentioned in comment
# https://github.com/google/dawn/blob/855d17b08abdf02f9142bf5a8f14d0ea088810a4/src/tint/lang/spirv/reader/ast_parser/function.cc#L4434
if (FILAMENT_SUPPORTS_WEBGPU)
set(MATC_API_FLAGS ${MATC_API_FLAGS} -a webgpu --variant-filter=skinning,stereo)
endif()
# Disable ESSL 1.0 code generation. # Disable ESSL 1.0 code generation.
if (NOT FILAMENT_ENABLE_FEATURE_LEVEL_0) if (NOT FILAMENT_ENABLE_FEATURE_LEVEL_0)
set(MATC_API_FLAGS ${MATC_API_FLAGS} -1) set(MATC_API_FLAGS ${MATC_API_FLAGS} -1)
@@ -801,7 +784,6 @@ add_subdirectory(${EXTERNAL}/draco/tnt)
add_subdirectory(${EXTERNAL}/jsmn/tnt) add_subdirectory(${EXTERNAL}/jsmn/tnt)
add_subdirectory(${EXTERNAL}/stb/tnt) add_subdirectory(${EXTERNAL}/stb/tnt)
add_subdirectory(${EXTERNAL}/getopt) add_subdirectory(${EXTERNAL}/getopt)
add_subdirectory(${EXTERNAL}/perfetto/tnt)
# Note that this has to be placed after mikktspace in order for combine_static_libs to work. # Note that this has to be placed after mikktspace in order for combine_static_libs to work.
add_subdirectory(${LIBRARIES}/geometry) add_subdirectory(${LIBRARIES}/geometry)
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
} }
dependencies { dependencies {
implementation 'com.google.android.filament:filament-android:1.59.3' implementation 'com.google.android.filament:filament-android:1.58.0'
} }
``` ```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release: iOS projects can use CocoaPods to install the latest release:
```shell ```shell
pod 'Filament', '~> 1.59.3' pod 'Filament', '~> 1.58.0'
``` ```
## Documentation ## Documentation
-21
View File
@@ -7,27 +7,6 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md). [NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.59.4
## v1.59.3
## v1.59.2
- Fix build/compile errors when upgrading to MacOS 15.4
## v1.59.1
## v1.59.0
- materials: five custom variables (varyings) are now available on the condition that the `color` attribute is not requested (b/404930099). [⚠️ **New Material Version**]
## v1.58.2
- engine: Generate 1D instead of 3D LUTs for color grading whenever possible.
## v1.58.1 ## v1.58.1
@@ -142,12 +142,6 @@ abstract class MaterialCompiler extends TaskWithBinary {
if (!exclude_vulkan) { if (!exclude_vulkan) {
matcArgs += ['-a', 'vulkan'] matcArgs += ['-a', 'vulkan']
} }
def include_webgpu = providers
.gradleProperty("com.google.android.filament.include-webgpu")
.forUseAtConfigurationTime().present
if (include_webgpu) {
matcArgs += ['-a', 'webgpu', '--variant-filter=skinning,stereo']
}
def mat_no_opt = providers def mat_no_opt = providers
.gradleProperty("com.google.android.filament.matnopt") .gradleProperty("com.google.android.filament.matnopt")
+5 -11
View File
@@ -12,12 +12,12 @@ if (FILAMENT_SUPPORTS_VULKAN)
endif() endif()
if (FILAMENT_SUPPORTS_WEBGPU) if (FILAMENT_SUPPORTS_WEBGPU)
# See third_party/dawn/tnt/CMakeLists.txt and libs/filamat/CMakeLists.txt add_library(webgpu_dawn STATIC IMPORTED)
add_library(tint STATIC IMPORTED) set_target_properties(webgpu_dawn PROPERTIES IMPORTED_LOCATION
set_target_properties(tint PROPERTIES IMPORTED_LOCATION ${FILAMENT_DIR}/lib/${ANDROID_ABI}/libwebgpu_dawn.a)
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libtint_combined.a)
endif() endif()
add_library(${FILAMAT_FLAVOR} STATIC IMPORTED) add_library(${FILAMAT_FLAVOR} STATIC IMPORTED)
set_target_properties(${FILAMAT_FLAVOR} PROPERTIES IMPORTED_LOCATION set_target_properties(${FILAMAT_FLAVOR} PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/lib${FILAMAT_FLAVOR}.a) ${FILAMENT_DIR}/lib/${ANDROID_ABI}/lib${FILAMAT_FLAVOR}.a)
@@ -26,10 +26,6 @@ add_library(utils STATIC IMPORTED)
set_target_properties(utils PROPERTIES IMPORTED_LOCATION set_target_properties(utils PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a) ${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
add_library(perfetto STATIC IMPORTED)
set_target_properties(perfetto PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libperfetto.a)
add_library(filabridge STATIC IMPORTED) add_library(filabridge STATIC IMPORTED)
set_target_properties(filabridge PROPERTIES IMPORTED_LOCATION set_target_properties(filabridge PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilabridge.a) ${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilabridge.a)
@@ -44,7 +40,6 @@ set_target_properties(shaders PROPERTIES IMPORTED_LOCATION
set(FILAMAT_INCLUDE_DIRS set(FILAMAT_INCLUDE_DIRS
../../libs/utils/include ../../libs/utils/include
../../third_party/perfetto
) )
include_directories(${FILAMENT_DIR}/include) include_directories(${FILAMENT_DIR}/include)
@@ -60,8 +55,7 @@ target_link_libraries(filamat-jni
filabridge filabridge
shaders shaders
utils utils
perfetto
log log
smol-v smol-v
$<$<STREQUAL:${FILAMENT_SUPPORTS_WEBGPU},ON>:tint> $<$<STREQUAL:${FILAMENT_SUPPORTS_WEBGPU},ON>:webgpu_dawn> # for tint
) )
+4 -6
View File
@@ -21,10 +21,6 @@ add_library(utils STATIC IMPORTED)
set_target_properties(utils PROPERTIES IMPORTED_LOCATION set_target_properties(utils PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a) ${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
add_library(perfetto STATIC IMPORTED)
set_target_properties(perfetto PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libperfetto.a)
add_library(ibl-lite STATIC IMPORTED) add_library(ibl-lite STATIC IMPORTED)
set_target_properties(ibl-lite PROPERTIES IMPORTED_LOCATION set_target_properties(ibl-lite PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libibl-lite.a) ${FILAMENT_DIR}/lib/${ANDROID_ABI}/libibl-lite.a)
@@ -33,6 +29,10 @@ add_library(filaflat STATIC IMPORTED)
set_target_properties(filaflat PROPERTIES IMPORTED_LOCATION set_target_properties(filaflat PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilaflat.a) ${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilaflat.a)
add_library(filamat STATIC IMPORTED)
set_target_properties(filamat PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilamat.a)
add_library(geometry STATIC IMPORTED) add_library(geometry STATIC IMPORTED)
set_target_properties(geometry PROPERTIES IMPORTED_LOCATION set_target_properties(geometry PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libgeometry.a) ${FILAMENT_DIR}/lib/${ANDROID_ABI}/libgeometry.a)
@@ -127,7 +127,6 @@ target_link_libraries(filament-jni
PRIVATE android PRIVATE android
PRIVATE jnigraphics PRIVATE jnigraphics
PRIVATE utils PRIVATE utils
PRIVATE perfetto
# libgeometry is PUBLIC because gltfio uses it. # libgeometry is PUBLIC because gltfio uses it.
PUBLIC geometry PUBLIC geometry
@@ -146,7 +145,6 @@ target_include_directories(filament-jni PRIVATE
${FILAMENT_DIR}/include ${FILAMENT_DIR}/include
../../filament/backend/include ../../filament/backend/include
../../third_party/robin-map ../../third_party/robin-map
../../third_party/perfetto
../../libs/utils/include) ../../libs/utils/include)
# Force a relink when the version script is changed: # Force a relink when the version script is changed:
@@ -68,21 +68,6 @@ Java_com_google_android_filament_Texture_nIsTextureSwizzleSupported(JNIEnv*, jcl
return (jboolean) Texture::isTextureSwizzleSupported(*engine); return (jboolean) Texture::isTextureSwizzleSupported(*engine);
} }
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nGetMaxTextureSize(JNIEnv *, jclass,
jlong nativeEngine, jint sampler) {
Engine *engine = (Engine *) nativeEngine;
return Texture::getMaxTextureSize(*engine, (Texture::Sampler)sampler);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nGetMaxArrayTextureLayers(JNIEnv *, jclass,
jlong nativeEngine) {
Engine *engine = (Engine *) nativeEngine;
return Texture::getMaxArrayTextureLayers(*engine);
}
extern "C" JNIEXPORT jboolean JNICALL extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Texture_nValidatePixelFormatAndType(JNIEnv*, jclass, Java_com_google_android_filament_Texture_nValidatePixelFormatAndType(JNIEnv*, jclass,
jint internalFormat, jint pixelDataFormat, jint pixelDataType) { jint internalFormat, jint pixelDataFormat, jint pixelDataType) {
@@ -46,8 +46,9 @@ import static com.google.android.filament.Asserts.assertFloat4In;
* *
* <h1>Performance</h1> * <h1>Performance</h1>
* *
* Creating a new ColorGrading object may be more expensive than other Filament objects as a LUT may * Creating a new ColorGrading object may be more expensive than other Filament objects as a
* need to be generated. The generation of this LUT, if necessary, may happen on the CPU. * 3D LUT may need to be generated. The generation of a 3D LUT, if necessary, may happen on
* the CPU.
* *
* <h1>Ordering</h1> * <h1>Ordering</h1>
* *
@@ -159,10 +160,6 @@ public class ColorGrading {
* quality level will use a 32x32x32 10 bit LUT, a high quality will use a 32x32x32 16 bit * quality level will use a 32x32x32 10 bit LUT, a high quality will use a 32x32x32 16 bit
* LUT, and a ultra quality will use a 64x64x64 16 bit LUT. * LUT, and a ultra quality will use a 64x64x64 16 bit LUT.
* *
* This setting has no effect if generating a 1D LUT.
*
* This overrides the values set by format() and dimensions().
*
* The default quality is {@link QualityLevel#MEDIUM}. * The default quality is {@link QualityLevel#MEDIUM}.
* *
* @param qualityLevel The desired quality of the color grading process * @param qualityLevel The desired quality of the color grading process
@@ -178,8 +175,6 @@ public class ColorGrading {
* When color grading is implemented using a 3D LUT, this sets the texture format of * When color grading is implemented using a 3D LUT, this sets the texture format of
* of the LUT. This overrides the value set by quality(). * of the LUT. This overrides the value set by quality().
* *
* This setting has no effect if generating a 1D LUT.
*
* The default is INTEGER * The default is INTEGER
* *
* @param format The desired format of the 3D LUT. * @param format The desired format of the 3D LUT.
@@ -195,8 +190,6 @@ public class ColorGrading {
* When color grading is implemented using a 3D LUT, this sets the dimension of the LUT. * When color grading is implemented using a 3D LUT, this sets the dimension of the LUT.
* This overrides the value set by quality(). * This overrides the value set by quality().
* *
* This setting has no effect if generating a 1D LUT.
*
* The default is 32 * The default is 32
* *
* @param dim The desired dimension of the LUT. Between 16 and 64. * @param dim The desired dimension of the LUT. Between 16 and 64.
@@ -623,3 +616,4 @@ public class ColorGrading {
private static native long nBuilderBuild(long nativeBuilder, long nativeEngine); private static native long nBuilderBuild(long nativeBuilder, long nativeEngine);
} }
@@ -691,24 +691,6 @@ public class Texture {
pixelDataType.ordinal()); pixelDataType.ordinal());
} }
/**
* @param engine {@link Engine}
* @param type Texture sampler type
* @return The maximum size in texels of a texture of type \p type. At least 2048 for
* 2D textures, 256 for 3D textures
*/
public static int getMaxTextureSize(@NonNull Engine engine, Sampler type) {
return nGetMaxTextureSize(engine.getNativeObject(), type.ordinal());
}
/**
* @param engine {@link Engine}
* @return The maximum number of layers supported by texture arrays. At least 256.
*/
public static int getMaxArrayTextureLayers(@NonNull Engine engine) {
return nGetMaxArrayTextureLayers(engine.getNativeObject());
}
/** /**
* Use <code>Builder</code> to construct a <code>Texture</code> object instance. * Use <code>Builder</code> to construct a <code>Texture</code> object instance.
*/ */
@@ -1307,8 +1289,6 @@ public class Texture {
private static native boolean nIsTextureFormatSupported(long nativeEngine, int internalFormat); private static native boolean nIsTextureFormatSupported(long nativeEngine, int internalFormat);
private static native boolean nIsTextureFormatMipmappable(long nativeEngine, int internalFormat); private static native boolean nIsTextureFormatMipmappable(long nativeEngine, int internalFormat);
private static native boolean nIsTextureSwizzleSupported(long nativeEngine); private static native boolean nIsTextureSwizzleSupported(long nativeEngine);
private static native int nGetMaxTextureSize(long nativeObject, int ordinal);
private static native int nGetMaxArrayTextureLayers(long nativeObject);
private static native boolean nValidatePixelFormatAndType(int internalFormat, int pixelDataFormat, private static native boolean nValidatePixelFormatAndType(int internalFormat, int pixelDataFormat,
int pixelDataType); int pixelDataType);
@@ -31,6 +31,8 @@ package com.google.android.filament;
* <li>DisplayRangeToneMapper</li> * <li>DisplayRangeToneMapper</li>
* </ul> * </ul>
* </ul> * </ul>
*
* You can create custom tone mapping operators by subclassing ToneMapper.
*/ */
public class ToneMapper { public class ToneMapper {
private final long mNativeObject; private final long mNativeObject;
+1 -6
View File
@@ -35,10 +35,6 @@ add_library(utils STATIC IMPORTED)
set_target_properties(utils PROPERTIES IMPORTED_LOCATION set_target_properties(utils PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a) ${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
add_library(perfetto STATIC IMPORTED)
set_target_properties(perfetto PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libperfetto.a)
add_library(uberzlib STATIC IMPORTED) add_library(uberzlib STATIC IMPORTED)
set_target_properties(uberzlib PROPERTIES IMPORTED_LOCATION set_target_properties(uberzlib PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libuberzlib.a) ${FILAMENT_DIR}/lib/${ANDROID_ABI}/libuberzlib.a)
@@ -125,7 +121,6 @@ set(GLTFIO_INCLUDE_DIRS
../../third_party/meshoptimizer/src ../../third_party/meshoptimizer/src
../../third_party/robin-map ../../third_party/robin-map
../../third_party/stb ../../third_party/stb
../../third_party/perfetto
../../libs/utils/include ../../libs/utils/include
../../libs/ktxreader/include ../../libs/ktxreader/include
) )
@@ -134,7 +129,7 @@ add_library(gltfio-jni SHARED ${GLTFIO_SRCS})
target_include_directories(gltfio-jni PRIVATE ${GLTFIO_INCLUDE_DIRS}) target_include_directories(gltfio-jni PRIVATE ${GLTFIO_INCLUDE_DIRS})
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.symbols) set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.symbols)
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.map) set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.map)
target_link_libraries(gltfio-jni filament-jni utils perfetto uberzlib log stb ktxreader basis_transcoder zstd uberarchive) target_link_libraries(gltfio-jni filament-jni utils uberzlib log stb ktxreader basis_transcoder zstd uberarchive)
target_link_libraries(gltfio-jni dracodec meshoptimizer) target_link_libraries(gltfio-jni dracodec meshoptimizer)
target_compile_definitions(gltfio-jni PUBLIC GLTFIO_DRACO_SUPPORTED=1) target_compile_definitions(gltfio-jni PUBLIC GLTFIO_DRACO_SUPPORTED=1)
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/src) target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/src)
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament GROUP=com.google.android.filament
VERSION_NAME=1.59.3 VERSION_NAME=1.58.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android. POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+1 -4
View File
@@ -200,9 +200,8 @@ WEBGPU_ANDROID_GRADLE_OPTION=""
EGL_ON_LINUX_OPTION="-DFILAMENT_SUPPORTS_EGL_ON_LINUX=OFF" EGL_ON_LINUX_OPTION="-DFILAMENT_SUPPORTS_EGL_ON_LINUX=OFF"
MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=OFF" MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=OFF"
MATDBG_GRADLE_OPTION=""
FGVIEWER_OPTION="-DFILAMENT_ENABLE_FGVIEWER=OFF" FGVIEWER_OPTION="-DFILAMENT_ENABLE_FGVIEWER=OFF"
FGVIEWER_GRADLE_OPTION="" MATDBG_GRADLE_OPTION=""
MATOPT_OPTION="" MATOPT_OPTION=""
MATOPT_GRADLE_OPTION="" MATOPT_GRADLE_OPTION=""
@@ -534,7 +533,6 @@ function build_android {
${VULKAN_ANDROID_GRADLE_OPTION} \ ${VULKAN_ANDROID_GRADLE_OPTION} \
${WEBGPU_ANDROID_GRADLE_OPTION} \ ${WEBGPU_ANDROID_GRADLE_OPTION} \
${MATDBG_GRADLE_OPTION} \ ${MATDBG_GRADLE_OPTION} \
${FGVIEWER_GRADLE_OPTION} \
${MATOPT_GRADLE_OPTION} \ ${MATOPT_GRADLE_OPTION} \
:filament-android:assembleDebug \ :filament-android:assembleDebug \
:gltfio-android:assembleDebug \ :gltfio-android:assembleDebug \
@@ -586,7 +584,6 @@ function build_android {
${VULKAN_ANDROID_GRADLE_OPTION} \ ${VULKAN_ANDROID_GRADLE_OPTION} \
${WEBGPU_ANDROID_GRADLE_OPTION} \ ${WEBGPU_ANDROID_GRADLE_OPTION} \
${MATDBG_GRADLE_OPTION} \ ${MATDBG_GRADLE_OPTION} \
${FGVIEWER_GRADLE_OPTION} \
${MATOPT_GRADLE_OPTION} \ ${MATOPT_GRADLE_OPTION} \
:filament-android:assembleRelease \ :filament-android:assembleRelease \
:gltfio-android:assembleRelease \ :gltfio-android:assembleRelease \
-3
View File
@@ -30,9 +30,6 @@ if [[ "$GITHUB_WORKFLOW" ]]; then
sudo apt-get install clang-$GITHUB_CLANG_VERSION libc++-$GITHUB_CLANG_VERSION-dev libc++abi-$GITHUB_CLANG_VERSION-dev sudo apt-get install clang-$GITHUB_CLANG_VERSION libc++-$GITHUB_CLANG_VERSION-dev libc++abi-$GITHUB_CLANG_VERSION-dev
sudo apt-get install mesa-common-dev libxi-dev libxxf86vm-dev sudo apt-get install mesa-common-dev libxi-dev libxxf86vm-dev
# For dawn
sudo apt-get install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libx11-xcb-dev
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${GITHUB_CLANG_VERSION} 100 sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${GITHUB_CLANG_VERSION} 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${GITHUB_CLANG_VERSION} 100 sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${GITHUB_CLANG_VERSION} 100
fi fi
+16 -24
View File
@@ -13,7 +13,7 @@ This document is part of the [Filament project](https://github.com/google/filame
## Authors ## Authors
- [Romain Guy](https://github.com/romainguy), [@romainguy](https://twitter.com/romainguy) - [Romain Guy](https://github.com/romainguy), [@romainguy](https://twitter.com/romainguy)
- [Mathias Agopian](https://github.com/pixelflinger), [@pixelflinger](https://bsky.app/profile/pixelflinger.bsky.social) - [Mathias Agopian](https://github.com/pixelflinger), [@darthmoosious](https://twitter.com/darthmoosious)
# Overview # Overview
@@ -78,27 +78,26 @@ in table [standardProperties].
Property | Definition Property | Definition
-----------------------:|:--------------------- -----------------------:|:---------------------
**baseColor** | Diffuse albedo for non-metallic surfaces, and specular color for metallic surfaces **baseColor** | Diffuse albedo for non-metallic surfaces, and specular color for metallic surfaces
**roughness** | Perceived smoothness (1.0) or roughness (0.0) of a surface. Smooth surfaces exhibit sharp reflections
**metallic** | Whether a surface appears to be dielectric (0.0) or conductor (1.0). Often used as a binary value (0 or 1) **metallic** | Whether a surface appears to be dielectric (0.0) or conductor (1.0). Often used as a binary value (0 or 1)
**roughness** | Perceived smoothness (1.0) or roughness (0.0) of a surface. Smooth surfaces exhibit sharp reflections
**reflectance** | Fresnel reflectance at normal incidence for dielectric surfaces. This directly controls the strength of the reflections **reflectance** | Fresnel reflectance at normal incidence for dielectric surfaces. This directly controls the strength of the reflections
**ambientOcclusion** | Defines how much of the ambient light is accessible to a surface point. It is a per-pixel shadowing factor between 0.0 and 1.0
**clearCoat** | Strength of the clear coat layer
**clearCoatRoughness** | Perceived smoothness or roughness of the clear coat layer
**clearCoatNormal** | A detail normal used to perturb the clear coat layer using _bump mapping_ (_normal mapping_)
**anisotropy** | Amount of anisotropy in either the tangent or bitangent direction
**anisotropyDirection** | Local surface direction in tangent space
**thickness** | Thickness of the solid volume of refractive objects
**sheenColor** | Strength of the sheen layer **sheenColor** | Strength of the sheen layer
**sheenRoughness** | Perceived smoothness or roughness of the sheen layer **sheenRoughness** | Perceived smoothness or roughness of the sheen layer
**emissive** | Additional diffuse albedo to simulate emissive surfaces (such as neons, etc.) This property is mostly useful in an HDR pipeline with a bloom pass **clearCoat** | Strength of the clear coat layer
**clearCoatRoughness** | Perceived smoothness or roughness of the clear coat layer
**anisotropy** | Amount of anisotropy in either the tangent or bitangent direction
**anisotropyDirection** | Local surface direction in tangent space
**ambientOcclusion** | Defines how much of the ambient light is accessible to a surface point. It is a per-pixel shadowing factor between 0.0 and 1.0
**normal** | A detail normal used to perturb the surface using _bump mapping_ (_normal mapping_) **normal** | A detail normal used to perturb the surface using _bump mapping_ (_normal mapping_)
**postLightingColor** | Additional color that can be blended with the result of the lighting computations. See `postLightingBlending`
**absorption** | Absorption factor for refractive objects
**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
**bentNormal** | A normal pointing in the average unoccluded direction. Can be used to improve indirect lighting quality **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 **clearCoatNormal** | A detail normal used to perturb the clear coat layer using _bump mapping_ (_normal mapping_)
**emissive** | Additional diffuse albedo to simulate emissive surfaces (such as neons, etc.) This property is mostly useful in an HDR pipeline with a bloom pass
**postLightingColor** | Additional color that can be blended with the result of the lighting computations. See `postLightingBlending`
**ior** | Index of refraction, either for refractive objects or as an alternative to reflectance
**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
**absorption** | Absorption factor for refractive objects
**microThickness** | Thickness of the thin layer of refractive objects
**thickness** | Thickness of the solid volume of refractive objects
[Table [standardProperties]: Properties of the standard model] [Table [standardProperties]: Properties of the standard model]
The type and range of each property is described in table [standardPropertiesTypes]. The type and range of each property is described in table [standardPropertiesTypes].
@@ -1273,9 +1272,6 @@ Description
when selecting any shading model that is not `unlit`. See the shader sections of this document when selecting any shading model that is not `unlit`. See the shader sections of this document
for more information on how to access these attributes from the shaders. for more information on how to access these attributes from the shaders.
!!! Note: Interaction with custom variables
When the `color` attribute is specified, only four custom variables are available instead of five.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material { material {
parameters : [ parameters : [
@@ -1306,7 +1302,7 @@ Type
: array of `string` : array of `string`
Value Value
: Up to 5 strings, each must be a valid GLSL identifier. : Up to 4 strings, each must be a valid GLSL identifier.
Description Description
: Defines custom interpolants (or variables) that are output by the material's vertex shader. : Defines custom interpolants (or variables) that are output by the material's vertex shader.
@@ -1322,10 +1318,6 @@ Description
particular if `default` is specified the default precision is used is the fragment shader particular if `default` is specified the default precision is used is the fragment shader
(`mediump`) and in the vertex shader (`highp`). (`mediump`) and in the vertex shader (`highp`).
!!! Warning: Interaction with required attributes
If the `color` attribute is specified in the `required` list, then only four variables can be used
instead of five.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material { material {
name : Skybox, name : Skybox,
File diff suppressed because one or more lines are too long
Binary file not shown.
+30 -84
View File
@@ -108,12 +108,8 @@ if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3)
list(APPEND SRCS src/opengl/platforms/PlatformCocoaTouchGL.mm) list(APPEND SRCS src/opengl/platforms/PlatformCocoaTouchGL.mm)
list(APPEND SRCS src/opengl/platforms/CocoaTouchExternalImage.mm) list(APPEND SRCS src/opengl/platforms/CocoaTouchExternalImage.mm)
elseif (APPLE) elseif (APPLE)
if (FILAMENT_SUPPORTS_OSMESA)
list(APPEND SRCS src/opengl/platforms/PlatformOSMesa.cpp)
else()
list(APPEND SRCS src/opengl/platforms/PlatformCocoaGL.mm) list(APPEND SRCS src/opengl/platforms/PlatformCocoaGL.mm)
list(APPEND SRCS src/opengl/platforms/CocoaExternalImage.mm) list(APPEND SRCS src/opengl/platforms/CocoaExternalImage.mm)
endif()
elseif (WEBGL) elseif (WEBGL)
list(APPEND SRCS src/opengl/platforms/PlatformWebGL.cpp) list(APPEND SRCS src/opengl/platforms/PlatformWebGL.cpp)
elseif (LINUX) elseif (LINUX)
@@ -177,6 +173,27 @@ endif()
if (FILAMENT_SUPPORTS_VULKAN) if (FILAMENT_SUPPORTS_VULKAN)
list(APPEND SRCS list(APPEND SRCS
include/backend/platforms/VulkanPlatform.h include/backend/platforms/VulkanPlatform.h
src/vulkan/caching/VulkanDescriptorSetManager.cpp
src/vulkan/caching/VulkanDescriptorSetManager.h
src/vulkan/caching/VulkanPipelineLayoutCache.cpp
src/vulkan/caching/VulkanPipelineLayoutCache.h
src/vulkan/memory/ResourceManager.cpp
src/vulkan/memory/ResourceManager.h
src/vulkan/memory/ResourcePointer.h
src/vulkan/memory/Resource.cpp
src/vulkan/memory/Resource.h
src/vulkan/platform/VulkanPlatform.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
src/vulkan/utils/Conversion.cpp
src/vulkan/utils/Conversion.h
src/vulkan/utils/Definitions.h
src/vulkan/utils/Helper.h
src/vulkan/utils/Image.h
src/vulkan/utils/Image.cpp
src/vulkan/utils/Spirv.h
src/vulkan/utils/Spirv.cpp
src/vulkan/utils/StaticVector.h
src/vulkan/VulkanAsyncHandles.h src/vulkan/VulkanAsyncHandles.h
src/vulkan/VulkanBlitter.cpp src/vulkan/VulkanBlitter.cpp
src/vulkan/VulkanBlitter.h src/vulkan/VulkanBlitter.h
@@ -187,10 +204,6 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/VulkanConstants.h src/vulkan/VulkanConstants.h
src/vulkan/VulkanContext.cpp src/vulkan/VulkanContext.cpp
src/vulkan/VulkanContext.h src/vulkan/VulkanContext.h
src/vulkan/VulkanDescriptorSetCache.cpp
src/vulkan/VulkanDescriptorSetCache.h
src/vulkan/VulkanDescriptorSetLayoutCache.cpp
src/vulkan/VulkanDescriptorSetLayoutCache.h
src/vulkan/VulkanDriver.cpp src/vulkan/VulkanDriver.cpp
src/vulkan/VulkanDriver.h src/vulkan/VulkanDriver.h
src/vulkan/VulkanDriverFactory.h src/vulkan/VulkanDriverFactory.h
@@ -198,43 +211,22 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/VulkanFboCache.h src/vulkan/VulkanFboCache.h
src/vulkan/VulkanHandles.cpp src/vulkan/VulkanHandles.cpp
src/vulkan/VulkanHandles.h src/vulkan/VulkanHandles.h
src/vulkan/VulkanMemory.cpp
src/vulkan/VulkanMemory.h src/vulkan/VulkanMemory.h
src/vulkan/VulkanMemory.cpp
src/vulkan/VulkanPipelineCache.cpp src/vulkan/VulkanPipelineCache.cpp
src/vulkan/VulkanPipelineCache.h src/vulkan/VulkanPipelineCache.h
src/vulkan/VulkanPipelineLayoutCache.cpp
src/vulkan/VulkanPipelineLayoutCache.h
src/vulkan/VulkanQueryManager.cpp src/vulkan/VulkanQueryManager.cpp
src/vulkan/VulkanQueryManager.h src/vulkan/VulkanQueryManager.h
src/vulkan/VulkanReadPixels.cpp
src/vulkan/VulkanReadPixels.h
src/vulkan/VulkanSamplerCache.cpp src/vulkan/VulkanSamplerCache.cpp
src/vulkan/VulkanSamplerCache.h src/vulkan/VulkanSamplerCache.h
src/vulkan/VulkanStagePool.cpp src/vulkan/VulkanStagePool.cpp
src/vulkan/VulkanStagePool.h src/vulkan/VulkanStagePool.h
src/vulkan/VulkanSwapChain.cpp src/vulkan/VulkanSwapChain.cpp
src/vulkan/VulkanSwapChain.h src/vulkan/VulkanSwapChain.h
src/vulkan/VulkanReadPixels.cpp
src/vulkan/VulkanReadPixels.h
src/vulkan/VulkanTexture.cpp src/vulkan/VulkanTexture.cpp
src/vulkan/VulkanTexture.h src/vulkan/VulkanTexture.h
src/vulkan/VulkanYcbcrConversionCache.cpp
src/vulkan/VulkanYcbcrConversionCache.h
src/vulkan/memory/Resource.cpp
src/vulkan/memory/Resource.h
src/vulkan/memory/ResourceManager.cpp
src/vulkan/memory/ResourceManager.h
src/vulkan/memory/ResourcePointer.h
src/vulkan/platform/VulkanPlatform.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
src/vulkan/utils/Conversion.cpp
src/vulkan/utils/Conversion.h
src/vulkan/utils/Definitions.h
src/vulkan/utils/Helper.h
src/vulkan/utils/Image.cpp
src/vulkan/utils/Image.h
src/vulkan/utils/Spirv.cpp
src/vulkan/utils/Spirv.h
src/vulkan/utils/StaticVector.h
) )
if (LINUX OR WIN32) if (LINUX OR WIN32)
list(APPEND SRCS src/vulkan/platform/VulkanPlatformLinuxWindows.cpp) list(APPEND SRCS src/vulkan/platform/VulkanPlatformLinuxWindows.cpp)
@@ -250,36 +242,11 @@ endif()
if (FILAMENT_SUPPORTS_WEBGPU) if (FILAMENT_SUPPORTS_WEBGPU)
list(APPEND SRCS list(APPEND SRCS
include/backend/platforms/WebGPUPlatform.h
src/webgpu/platform/WebGPUPlatform.cpp
src/webgpu/WebGPUConstants.h
src/webgpu/WebGPUDriver.cpp src/webgpu/WebGPUDriver.cpp
src/webgpu/WebGPUDriver.h src/webgpu/WebGPUDriver.h
src/webgpu/WebGPUHandles.cpp src/webgpu/WebGPUPlatform.cpp
src/webgpu/WebGPUHandles.h src/webgpu/WebGPUPlatform.h
src/webgpu/WebGPUSwapChain.cpp
src/webgpu/WebGPUSwapChain.h
src/webgpu/WGPUProgram.cpp
) )
if (WIN32)
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
elseif (LINUX)
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformLinux.cpp)
elseif (APPLE OR IOS)
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformApple.mm)
elseif (ANDROID)
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformAndroid.cpp)
endif()
if (TNT_DEV)
set(FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING_DEFAULT ON)
else()
set(FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING_DEFAULT OFF)
endif()
option(FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING
"Enable immediate_error_handling for the WebGPU backend Dawn implementation"
${FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING_DEFAULT})
endif() endif()
if (ANDROID) if (ANDROID)
@@ -309,11 +276,6 @@ target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
# add this subproject to the Filament folder # add this subproject to the Filament folder
set_target_properties(${TARGET} PROPERTIES FOLDER Filament) set_target_properties(${TARGET} PROPERTIES FOLDER Filament)
# we need to export the headers properly for backend.lib on windows
if (WIN32 AND FILAMENT_SUPPORTS_WEBGPU)
target_compile_definitions(${TARGET} PRIVATE "WGPU_IMPLEMENTATION")
endif()
# ================================================================================================== # ==================================================================================================
# Expose a header-only target to minimize dependencies. # Expose a header-only target to minimize dependencies.
# ================================================================================================== # ==================================================================================================
@@ -434,12 +396,9 @@ set(LINUX_LINKER_OPTIMIZATION_FLAGS
-Wl,--exclude-libs,bluegl -Wl,--exclude-libs,bluegl
) )
if (FILAMENT_SUPPORTS_OSMESA) if (LINUX AND FILAMENT_SUPPORTS_OSMESA)
if (LINUX) set(OSMESA_COMPILE_FLAGS
set(OSMESA_COMPILE_FLAGS -I${FILAMENT_OSMESA_PATH}/include/GL) -I${FILAMENT_OSMESA_PATH}/include/GL)
elseif (APPLE)
set(OSMESA_COMPILE_FLAGS -I${FILAMENT_OSMESA_PATH}/include)
endif()
endif() endif()
if (MSVC) if (MSVC)
@@ -474,10 +433,6 @@ if (FILAMENT_SUPPORTS_METAL)
target_compile_definitions(${TARGET} PRIVATE $<$<BOOL:${FILAMENT_METAL_PROFILING}>:FILAMENT_METAL_PROFILING>) target_compile_definitions(${TARGET} PRIVATE $<$<BOOL:${FILAMENT_METAL_PROFILING}>:FILAMENT_METAL_PROFILING>)
endif() endif()
if (FILAMENT_SUPPORTS_WEBGPU)
target_compile_definitions(${TARGET} PRIVATE $<$<BOOL:${FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING}>:FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING>)
endif()
target_link_libraries(${TARGET} PRIVATE target_link_libraries(${TARGET} PRIVATE
${OSMESA_LINKER_FLAGS} ${OSMESA_LINKER_FLAGS}
$<$<AND:$<PLATFORM_ID:Linux>,$<CONFIG:Release>>:${LINUX_LINKER_OPTIMIZATION_FLAGS}> $<$<AND:$<PLATFORM_ID:Linux>,$<CONFIG:Release>>:${LINUX_LINKER_OPTIMIZATION_FLAGS}>
@@ -506,12 +461,7 @@ if (APPLE OR LINUX)
test/ShaderGenerator.cpp test/ShaderGenerator.cpp
test/TrianglePrimitive.cpp test/TrianglePrimitive.cpp
test/Arguments.cpp test/Arguments.cpp
test/ImageExpectations.cpp
test/Lifetimes.cpp test/Lifetimes.cpp
test/PlatformRunner.cpp
test/Shader.cpp
test/SharedShaders.cpp
test/Skip.cpp
test/test_FeedbackLoops.cpp test/test_FeedbackLoops.cpp
test/test_Blit.cpp test/test_Blit.cpp
test/test_MissingRequiredAttributes.cpp test/test_MissingRequiredAttributes.cpp
@@ -527,7 +477,6 @@ if (APPLE OR LINUX)
test/test_Handles.cpp test/test_Handles.cpp
) )
set(BACKEND_TEST_LIBS set(BACKEND_TEST_LIBS
absl::str_format
backend backend
getopt getopt
gtest gtest
@@ -535,9 +484,6 @@ if (APPLE OR LINUX)
filamat filamat
SPIRV SPIRV
spirv-cross-glsl) spirv-cross-glsl)
# Create input/output directories for test result images.
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/images/actual_images)
file(COPY test/expected_images DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/images)
endif() endif()
# TODO: Disabling IOS test due to breakage wrt glslang update # TODO: Disabling IOS test due to breakage wrt glslang update
@@ -546,7 +492,7 @@ if (APPLE AND NOT IOS)
list(APPEND BACKEND_TEST_SRC list(APPEND BACKEND_TEST_SRC
test/test_RenderExternalImage.cpp) test/test_RenderExternalImage.cpp)
add_library(backend_test STATIC ${BACKEND_TEST_SRC}) add_library(backend_test STATIC ${BACKEND_TEST_SRC})
target_link_libraries(backend_test PUBLIC ${BACKEND_TEST_LIBS}) target_link_libraries(backend_test PRIVATE ${BACKEND_TEST_LIBS})
set(BACKEND_TEST_DEPS set(BACKEND_TEST_DEPS
OSDependent OSDependent
@@ -55,9 +55,4 @@ public:
} // namespace filament::backend } // namespace filament::backend
#if !defined(NDEBUG)
utils::io::ostream& operator<<(utils::io::ostream& out,
const filament::backend::BufferObjectStreamDescriptor& b);
#endif
#endif // TNT_FILAMENT_BACKEND_BUFFEROBJECTSTREAMDESCRIPTOR_H #endif // TNT_FILAMENT_BACKEND_BUFFEROBJECTSTREAMDESCRIPTOR_H
+7 -121
View File
@@ -126,10 +126,6 @@ static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT,
static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 9; // This is guaranteed by OpenGL ES. static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 9; // This is guaranteed by OpenGL ES.
static constexpr size_t CONFIG_SAMPLER_BINDING_COUNT = 4; // This is guaranteed by OpenGL ES. static constexpr size_t CONFIG_SAMPLER_BINDING_COUNT = 4; // This is guaranteed by OpenGL ES.
static constexpr uint8_t EXTERNAL_SAMPLER_DATA_INDEX_UNUSED =
uint8_t(-1);// Case where the descriptor set binding isnt using any external sampler state
// and therefore doesn't have a valid entry.
/** /**
* Defines the backend's feature levels. * Defines the backend's feature levels.
*/ */
@@ -256,21 +252,21 @@ struct DescriptorSetLayoutBinding {
DescriptorFlags flags = DescriptorFlags::NONE; DescriptorFlags flags = DescriptorFlags::NONE;
uint16_t count = 0; uint16_t count = 0;
// TODO: uncomment when needed. Note that this class is used as hash key. We need to ensure friend inline bool operator==(
// no uninitialized padding bytes. DescriptorSetLayoutBinding const& lhs,
// uint8_t externalSamplerDataIndex = EXTERNAL_SAMPLER_DATA_INDEX_UNUSED;
friend inline bool operator==(DescriptorSetLayoutBinding const& lhs,
DescriptorSetLayoutBinding const& rhs) noexcept { DescriptorSetLayoutBinding const& rhs) noexcept {
return lhs.type == rhs.type && return lhs.type == rhs.type &&
lhs.flags == rhs.flags && lhs.flags == rhs.flags &&
lhs.count == rhs.count && lhs.count == rhs.count &&
lhs.stageFlags == rhs.stageFlags; lhs.stageFlags == rhs.stageFlags;
// lhs.stageFlags == rhs.stageFlags &&
// lhs.externalSamplerDataIndex == rhs.externalSamplerDataIndex;
} }
}; };
struct DescriptorSetLayout {
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
};
/** /**
* Bitmask for selecting render buffers * Bitmask for selecting render buffers
*/ */
@@ -972,26 +968,6 @@ enum class SamplerCompareFunc : uint8_t {
N //!< Never. The depth / stencil test always fails. N //!< Never. The depth / stencil test always fails.
}; };
//! this API is copied from (and only applies to) the Vulkan spec.
//! These specify YUV to RGB conversions.
enum class SamplerYcbcrModelConversion : uint8_t {
RGB_IDENTITY = 0,
YCBCR_IDENTITY = 1,
YCBCR_709 = 2,
YCBCR_601 = 3,
YCBCR_2020 = 4,
};
enum class SamplerYcbcrRange : uint8_t {
ITU_FULL = 0,
ITU_NARROW = 1,
};
enum class ChromaLocation : uint8_t {
COSITED_EVEN = 0,
MIDPOINT = 1,
};
//! Sampler parameters //! Sampler parameters
struct SamplerParams { // NOLINT struct SamplerParams { // NOLINT
SamplerMagFilter filterMag : 1; //!< magnification filter (NEAREST) SamplerMagFilter filterMag : 1; //!< magnification filter (NEAREST)
@@ -1048,7 +1024,6 @@ private:
return SamplerParams::LessThan{}(lhs, rhs); return SamplerParams::LessThan{}(lhs, rhs);
} }
}; };
static_assert(sizeof(SamplerParams) == 4); static_assert(sizeof(SamplerParams) == 4);
// The limitation to 64-bits max comes from how we store a SamplerParams in our JNI code // The limitation to 64-bits max comes from how we store a SamplerParams in our JNI code
@@ -1056,95 +1031,6 @@ static_assert(sizeof(SamplerParams) == 4);
static_assert(sizeof(SamplerParams) <= sizeof(uint64_t), static_assert(sizeof(SamplerParams) <= sizeof(uint64_t),
"SamplerParams must be no more than 64 bits"); "SamplerParams must be no more than 64 bits");
//! Sampler parameters
struct SamplerYcbcrConversion {// NOLINT
SamplerYcbcrModelConversion ycbcrModel : 4;
TextureSwizzle r : 4;
TextureSwizzle g : 4;
TextureSwizzle b : 4;
TextureSwizzle a : 4;
SamplerYcbcrRange ycbcrRange : 1;
ChromaLocation xChromaOffset : 1;
ChromaLocation yChromaOffset : 1;
SamplerMagFilter chromaFilter : 1;
uint8_t padding;
struct Hasher {
size_t operator()(const SamplerYcbcrConversion p) const noexcept {
// we don't use std::hash<> here, so we don't have to include <functional>
return *reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&p));
}
};
struct EqualTo {
bool operator()(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) const noexcept {
assert_invariant(lhs.padding == 0);
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
return *pLhs == *pRhs;
}
};
struct LessThan {
bool operator()(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) const noexcept {
assert_invariant(lhs.padding == 0);
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
return *pLhs < *pRhs;
}
};
private:
friend inline bool operator == (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
noexcept {
return SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
}
friend inline bool operator != (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
noexcept {
return !SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
}
friend inline bool operator < (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
noexcept {
return SamplerYcbcrConversion::LessThan{}(lhs, rhs);
}
};
static_assert(sizeof(SamplerYcbcrConversion) == 4);
static_assert(sizeof(SamplerYcbcrConversion) <= sizeof(uint64_t),
"SamplerYcbcrConversion must be no more than 64 bits");
struct ExternalSamplerDatum {
ExternalSamplerDatum(SamplerYcbcrConversion ycbcr, SamplerParams spm, uint32_t extFmt)
: YcbcrConversion(ycbcr),
samplerParams(spm),
externalFormat(extFmt) {}
bool operator==(ExternalSamplerDatum const& rhs) const {
return (YcbcrConversion == rhs.YcbcrConversion && samplerParams == rhs.samplerParams &&
externalFormat == rhs.externalFormat);
}
struct EqualTo {
bool operator()(const ExternalSamplerDatum& lhs,
const ExternalSamplerDatum& rhs) const noexcept {
return (lhs.YcbcrConversion == rhs.YcbcrConversion &&
lhs.samplerParams == rhs.samplerParams &&
lhs.externalFormat == rhs.externalFormat);
}
};
SamplerYcbcrConversion YcbcrConversion;
SamplerParams samplerParams;
uint32_t externalFormat;
};
// No implicit padding allowed due to it being a hash key.
static_assert(sizeof(ExternalSamplerDatum) == 12);
struct DescriptorSetLayout {
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
// TODO: uncomment when needed
// utils::FixedCapacityVector<ExternalSamplerDatum> externalSamplerData;
};
//! blending equation function //! blending equation function
enum class BlendEquation : uint8_t { enum class BlendEquation : uint8_t {
ADD, //!< the fragment is added to the color buffer ADD, //!< the fragment is added to the color buffer
@@ -33,7 +33,7 @@ public:
PlatformCocoaGL(); PlatformCocoaGL();
~PlatformCocoaGL() noexcept override; ~PlatformCocoaGL() noexcept override;
ExternalImageHandle UTILS_PUBLIC createExternalImage(void* cvPixelBuffer) noexcept; ExternalImageHandle createExternalImage(void* cvPixelBuffer) noexcept;
protected: protected:
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
@@ -32,7 +32,7 @@ public:
PlatformCocoaTouchGL(); PlatformCocoaTouchGL();
~PlatformCocoaTouchGL() noexcept override; ~PlatformCocoaTouchGL() noexcept override;
ExternalImageHandle UTILS_PUBLIC createExternalImage(void* cvPixelBuffer) noexcept; ExternalImageHandle createExternalImage(void* cvPixelBuffer) noexcept;
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
// Platform Interface // Platform Interface
@@ -50,7 +50,7 @@ public:
/** /**
* Creates an ExternalImage from a EGLImageKHR * Creates an ExternalImage from a EGLImageKHR
*/ */
ExternalImageHandle UTILS_PUBLIC createExternalImage(EGLImageKHR eglImage) noexcept; ExternalImageHandle createExternalImage(EGLImageKHR eglImage) noexcept;
protected: protected:
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
@@ -23,7 +23,6 @@
#include <backend/platforms/PlatformEGL.h> #include <backend/platforms/PlatformEGL.h>
#include <utils/android/PerformanceHintManager.h> #include <utils/android/PerformanceHintManager.h>
#include <utils/compiler.h>
#include <chrono> #include <chrono>
@@ -44,21 +43,8 @@ public:
PlatformEGLAndroid() noexcept; PlatformEGLAndroid() noexcept;
~PlatformEGLAndroid() noexcept override; ~PlatformEGLAndroid() noexcept override;
/**
* Creates an ExternalImage from a EGLImageKHR
*/
ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer, bool sRGB) noexcept;
struct UTILS_PUBLIC ExternalImageDescAndroid {
uint32_t width; // Texture width
uint32_t height; // Texture height
TextureFormat format;// Texture format
TextureUsage usage; // Texture usage flags
};
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(ExternalImageHandle externalImage) noexcept;
protected: protected:
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
// Platform Interface // Platform Interface
@@ -71,6 +57,11 @@ protected:
Driver* createDriver(void* sharedContext, Driver* createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) noexcept override; const Platform::DriverConfig& driverConfig) noexcept override;
/**
* Creates an ExternalImage from a EGLImageKHR
*/
ExternalImageHandle createExternalImage(AHardwareBuffer const *buffer, bool sRGB) noexcept;
// -------------------------------------------------------------------------------------------- // --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface // OpenGLPlatform Interface
@@ -104,26 +95,15 @@ protected:
*/ */
AcquiredImage transformAcquiredImage(AcquiredImage source) noexcept override; AcquiredImage transformAcquiredImage(AcquiredImage source) noexcept override;
OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override; bool setExternalImage(ExternalImageHandleRef externalImage, ExternalTexture* texture) noexcept override;
void destroyExternalImageTexture(ExternalTexture* texture) noexcept override;
struct ExternalImageEGLAndroid : public ExternalImageEGL { struct ExternalImageEGLAndroid : public ExternalImageEGL {
AHardwareBuffer* aHardwareBuffer = nullptr; AHardwareBuffer* aHardwareBuffer = nullptr;
uint32_t width; // Texture width
uint32_t height; // Texture height
TextureFormat format;// Texture format
TextureUsage usage; // Texture usage flags
bool sRGB = false; bool sRGB = false;
protected: protected:
~ExternalImageEGLAndroid() override; ~ExternalImageEGLAndroid() override;
}; };
bool setExternalImage(ExternalImageHandleRef externalImage,
ExternalTexture* texture) noexcept override;
bool setImage(ExternalImageEGLAndroid const* eglExternalImage,
ExternalTexture* texture) noexcept;
protected: protected:
bool makeCurrent(ContextType type, bool makeCurrent(ContextType type,
SwapChain* drawSwapChain, SwapChain* drawSwapChain,
@@ -21,12 +21,7 @@
#include "bluegl/BlueGL.h" #include "bluegl/BlueGL.h"
#if defined(__linux__)
#include <osmesa.h> #include <osmesa.h>
#elif defined(__APPLE__)
#undef GLAPI
#include <GL/osmesa.h>
#endif
#include <backend/platforms/OpenGLPlatform.h> #include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
@@ -18,7 +18,6 @@
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H #define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H
#include <backend/Platform.h> #include <backend/Platform.h>
#include <backend/DriverEnums.h>
#include <bluevk/BlueVK.h> #include <bluevk/BlueVK.h>
@@ -104,6 +103,10 @@ public:
// Semaphore to be signaled once the image is available. // Semaphore to be signaled once the image is available.
VkSemaphore imageReadySemaphore = VK_NULL_HANDLE; VkSemaphore imageReadySemaphore = VK_NULL_HANDLE;
// A function called right before vkQueueSubmit. After this call, the image must be
// available. This pointer can be null if imageReadySemaphore is not VK_NULL_HANDLE.
std::function<void(SwapChainPtr handle)> explicitImageReadyWait = nullptr;
}; };
VulkanPlatform(); VulkanPlatform();
@@ -314,11 +317,6 @@ public:
*/ */
uint32_t layers; uint32_t layers;
/**
* The numbers of samples per texel
*/
VkSampleCountFlagBits samples;
/** /**
* The format of the external image * The format of the external image
*/ */
@@ -353,43 +351,20 @@ public:
using ImageData = std::pair<VkImage, VkDeviceMemory>; using ImageData = std::pair<VkImage, VkDeviceMemory>;
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage, virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, const ExternalImageMetadata& metadata);
VkImageUsageFlags usage);
virtual VkSampler createExternalSampler(SamplerYcbcrConversion chroma,
SamplerParams sampler, uint32_t internalFormat);
virtual VkImageView createExternalImageView(SamplerYcbcrConversion chroma,
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
VkImageViewType viewType, VkComponentMapping swizzle);
protected:
virtual ExtensionSet getSwapchainInstanceExtensions() const;
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept;
private: private:
// Platform dependent helper methods static ExtensionSet getSwapchainInstanceExtensions();
static ExtensionSet getSwapchainInstanceExtensionsImpl();
static ExternalImageMetadata getExternalImageMetadataImpl(ExternalImageHandleRef externalImage, static ExternalImageMetadata getExternalImageMetadataImpl(ExternalImageHandleRef externalImage,
VkDevice device); VkDevice device);
static ImageData createExternalImageDataImpl(ExternalImageHandleRef externalImage, static ImageData createExternalImageDataImpl(ExternalImageHandleRef externalImage,
VkDevice device, const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkDevice device, const ExternalImageMetadata& metadata);
VkImageUsageFlags usage);
static VkSampler createExternalSamplerImpl(VkDevice device,
SamplerYcbcrConversion chroma, SamplerParams sampler,
uint32_t internalFormat);
static VkImageView createExternalImageViewImpl(VkDevice device,
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
VkImageSubresourceRange range, VkImageViewType viewType,
VkComponentMapping swizzle);
// Platform dependent helper methods // Platform dependent helper methods
static SurfaceBundle createVkSurfaceKHRImpl(void* nativeWindow, VkInstance instance, using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
static SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) noexcept; uint64_t flags) noexcept;
friend struct VulkanPlatformPrivate; friend struct VulkanPlatformPrivate;
@@ -17,29 +17,12 @@
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H #ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H #define TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
#include <backend/DriverEnums.h> #include <backend/Platform.h>
#include <backend/platforms/VulkanPlatform.h>
#include <android/hardware_buffer.h> #include <android/hardware_buffer.h>
namespace filament::backend { namespace filament::backend::fvkandroid {
class VulkanPlatformAndroid : public VulkanPlatform {
public:
Platform::ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept;
struct UTILS_PUBLIC ExternalImageDescAndroid {
uint32_t width; // Texture width
uint32_t height; // Texture height
TextureFormat format;// Texture format
TextureUsage usage; // Texture usage flags
};
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(
ExternalImageHandleRef externalImage) const noexcept;
protected:
struct ExternalImageVulkanAndroid : public Platform::ExternalImage { struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
AHardwareBuffer* aHardwareBuffer = nullptr; AHardwareBuffer* aHardwareBuffer = nullptr;
bool sRGB = false; bool sRGB = false;
@@ -52,20 +35,9 @@ protected:
~ExternalImageVulkanAndroid() override; ~ExternalImageVulkanAndroid() override;
}; };
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage); Platform::ExternalImageHandle createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept;
using ImageData = VulkanPlatform::ImageData; } // namespace filament::backend::fvkandroid
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage);
virtual ExtensionSet getSwapchainInstanceExtensions() const;
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept;
};
}// namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H #endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
@@ -1,65 +0,0 @@
/*
* 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.
*/
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H
#include <backend/Platform.h>
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
namespace filament::backend {
/**
* A Platform interface, handling the environment-specific concerns, e.g. OS, for creating a WebGPU
* driver (backend).
*/
class WebGPUPlatform final : public Platform {
public:
WebGPUPlatform();
~WebGPUPlatform() override = default;
[[nodiscard]] int getOSVersion() const noexcept final { return 0; }
[[nodiscard]] wgpu::Instance& getInstance() noexcept { return mInstance; }
// TODO consider that this functionality is not WebGPU-specific, and thus could be
// placed in a generic place and even reused across backends. Alternatively,
// a 3rd party library could be considered. However, this was a simple and
// quick change and works for now.
// gets the size (height and width) of the surface/window
[[nodiscard]] wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const;
// either returns a valid surface or panics
[[nodiscard]] wgpu::Surface createSurface(void* nativeWindow, uint64_t flags);
// either returns a valid adapter or panics
[[nodiscard]] wgpu::Adapter requestAdapter(wgpu::Surface const& surface);
// either returns a valid device or panics
[[nodiscard]] wgpu::Device requestDevice(wgpu::Adapter const& adapter);
protected:
[[nodiscard]] Driver* createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) noexcept override;
private:
// we may consider having the driver own this in the future
wgpu::Instance mInstance;
};
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H
@@ -362,8 +362,6 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedTexturesSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthClampSupported) DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthClampSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers) DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize) DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
DECL_DRIVER_API_SYNCHRONOUS_N(size_t, getMaxTextureSize, backend::SamplerType, target)
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxArrayTextureLayers)
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams) DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage2, backend::Platform::ExternalImageHandleRef, image) DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage2, backend::Platform::ExternalImageHandleRef, image)
DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage, void*, image) DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage, void*, image)
@@ -43,8 +43,6 @@
#define HandleAllocatorGL HandleAllocator<32, 96, 184> // ~4520 / pool / MiB #define HandleAllocatorGL HandleAllocator<32, 96, 184> // ~4520 / pool / MiB
#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB #define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB
#define HandleAllocatorMTL HandleAllocator<32, 64, 552> // ~1660 / pool / MiB #define HandleAllocatorMTL HandleAllocator<32, 64, 552> // ~1660 / pool / MiB
// TODO WebGPU examine right size of handles
#define HandleAllocatorWGPU HandleAllocator<64, 160, 552> // ~1820 / pool / MiB
namespace filament::backend { namespace filament::backend {
+2 -2
View File
@@ -84,7 +84,7 @@ void CommandStream::execute(void* buffer) {
Profiler profiler; Profiler profiler;
if constexpr (SYSTRACE_TAG) { if (SYSTRACE_TAG) {
if (UTILS_UNLIKELY(mUsePerformanceCounter)) { if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
// we want to remove all this when tracing is completely disabled // we want to remove all this when tracing is completely disabled
profiler.resetEvents(Profiler::EV_CPU_CYCLES | Profiler::EV_BPU_MISSES); profiler.resetEvents(Profiler::EV_CPU_CYCLES | Profiler::EV_BPU_MISSES);
@@ -100,7 +100,7 @@ void CommandStream::execute(void* buffer) {
} }
}); });
if constexpr (SYSTRACE_TAG) { if (SYSTRACE_TAG) {
if (UTILS_UNLIKELY(mUsePerformanceCounter)) { if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
// we want to remove all this when tracing is completely disabled // we want to remove all this when tracing is completely disabled
profiler.stop(); profiler.stop();
-4
View File
@@ -211,8 +211,4 @@ template class HandleAllocatorVK;
template class HandleAllocatorMTL; template class HandleAllocatorMTL;
#endif #endif
#if defined (FILAMENT_SUPPORTS_WEBGPU)
template class HandleAllocatorWGPU;
#endif
} // namespace filament::backend } // namespace filament::backend
+3 -21
View File
@@ -19,11 +19,6 @@
#include <utils/Systrace.h> #include <utils/Systrace.h>
#include <utils/debug.h> #include <utils/debug.h>
// We need to keep this up top for the linux (X11) name collisions.
#if defined(FILAMENT_SUPPORTS_WEBGPU)
#include "backend/platforms/WebGPUPlatform.h"
#endif
#if defined(__ANDROID__) #if defined(__ANDROID__)
#include <sys/system_properties.h> #include <sys/system_properties.h>
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) #if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3)
@@ -35,12 +30,8 @@
#endif #endif
#elif defined(__APPLE__) #elif defined(__APPLE__)
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) #if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3)
#if defined(FILAMENT_SUPPORTS_OSMESA)
#include <backend/platforms/PlatformOSMesa.h>
#else
#include <backend/platforms/PlatformCocoaGL.h> #include <backend/platforms/PlatformCocoaGL.h>
#endif #endif
#endif
#elif defined(__linux__) #elif defined(__linux__)
#if defined(FILAMENT_SUPPORTS_X11) #if defined(FILAMENT_SUPPORTS_X11)
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) #if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3)
@@ -64,12 +55,8 @@
#endif #endif
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN) #if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
#if defined(__ANDROID__)
#include "backend/platforms/VulkanPlatformAndroid.h"
#else
#include "backend/platforms/VulkanPlatform.h" #include "backend/platforms/VulkanPlatform.h"
#endif #endif
#endif
#if defined (FILAMENT_SUPPORTS_METAL) #if defined (FILAMENT_SUPPORTS_METAL)
namespace filament::backend { namespace filament::backend {
@@ -78,6 +65,9 @@ filament::backend::Platform* createDefaultMetalPlatform();
#endif #endif
#include "noop/PlatformNoop.h" #include "noop/PlatformNoop.h"
#if defined(FILAMENT_SUPPORTS_WEBGPU)
#include "webgpu/WebGPUPlatform.h"
#endif
namespace filament::backend { namespace filament::backend {
@@ -114,11 +104,7 @@ Platform* PlatformFactory::create(Backend* backend) noexcept {
} }
if (*backend == Backend::VULKAN) { if (*backend == Backend::VULKAN) {
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN) #if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
#if defined(__ANDROID__)
return new VulkanPlatformAndroid();
#else
return new VulkanPlatform(); return new VulkanPlatform();
#endif
#else #else
return nullptr; return nullptr;
#endif #endif
@@ -146,11 +132,7 @@ Platform* PlatformFactory::create(Backend* backend) noexcept {
#elif defined(FILAMENT_IOS) #elif defined(FILAMENT_IOS)
return new PlatformCocoaTouchGL(); return new PlatformCocoaTouchGL();
#elif defined(__APPLE__) #elif defined(__APPLE__)
#if defined(FILAMENT_SUPPORTS_OSMESA)
return new PlatformOSMesa();
#else
return new PlatformCocoaGL(); return new PlatformCocoaGL();
#endif
#elif defined(__linux__) #elif defined(__linux__)
#if defined(FILAMENT_SUPPORTS_X11) #if defined(FILAMENT_SUPPORTS_X11)
return new PlatformGLX(); return new PlatformGLX();
-18
View File
@@ -1177,24 +1177,6 @@ size_t MetalDriver::getMaxUniformBufferSize() {
return 256 * 1024 * 1024; // TODO: return the actual size instead of hardcoding the minspec return 256 * 1024 * 1024; // TODO: return the actual size instead of hardcoding the minspec
} }
size_t MetalDriver::getMaxTextureSize(SamplerType type) {
if (type == SamplerType::SAMPLER_3D) {
return 2048;
}
if (mContext->highestSupportedGpuFamily.apple >= 3 ||
mContext->highestSupportedGpuFamily.mac >= 2) {
return 16384;
} else if (mContext->highestSupportedGpuFamily.apple >= 2) {
return 8192;
}
return 4096;
}
size_t MetalDriver::getMaxArrayTextureLayers() {
// TODO: return the actual size instead of hardcoding the minspec
return 256;
}
void MetalDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& data, void MetalDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& data,
uint32_t byteOffset) { uint32_t byteOffset) {
FILAMENT_CHECK_PRECONDITION(data.buffer) FILAMENT_CHECK_PRECONDITION(data.buffer)
-10
View File
@@ -240,16 +240,6 @@ size_t NoopDriver::getMaxUniformBufferSize() {
return 16384u; return 16384u;
} }
size_t NoopDriver::getMaxTextureSize(SamplerType target) {
// NoopDriver is being actively used for other purposes. This needs to be resolved before we
// can change it to 2048. b/406832484
return 16384u;
}
size_t NoopDriver::getMaxArrayTextureLayers() {
return 256u;
}
void NoopDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p, void NoopDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
uint32_t byteOffset) { uint32_t byteOffset) {
scheduleDestroy(std::move(p)); scheduleDestroy(std::move(p));
+1 -1
View File
@@ -20,7 +20,7 @@
namespace filament::backend { namespace filament::backend {
Driver* PlatformNoop::createDriver(void* sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept { Driver* PlatformNoop::createDriver(void* const sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
return NoopDriver::create(); return NoopDriver::create();
} }
+7 -34
View File
@@ -94,22 +94,18 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
} }
#endif #endif
initExtensions(&ext, state.major, state.minor); OpenGLContext::initExtensions(&ext, state.major, state.minor);
initProcs(&procs, ext, state.major, state.minor); OpenGLContext::initProcs(&procs, ext, state.major, state.minor);
initBugs(&bugs, ext, state.major, state.minor, OpenGLContext::initBugs(&bugs, ext, state.major, state.minor,
state.vendor, state.renderer, state.version, state.shader); state.vendor, state.renderer, state.version, state.shader);
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &gets.max_renderbuffer_size); glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &gets.max_renderbuffer_size);
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &gets.max_texture_image_units); glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &gets.max_texture_image_units);
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gets.max_combined_texture_image_units); glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gets.max_combined_texture_image_units);
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gets.max_texture_size);
glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &gets.max_cubemap_texture_size);
glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gets.max_3d_texture_size);
glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &gets.max_array_texture_layers);
mFeatureLevel = resolveFeatureLevel(state.major, state.minor, ext, gets, bugs); mFeatureLevel = OpenGLContext::resolveFeatureLevel(state.major, state.minor, ext, gets, bugs);
#ifdef BACKEND_OPENGL_VERSION_GLES #ifdef BACKEND_OPENGL_VERSION_GLES
mShaderModel = ShaderModel::MOBILE; mShaderModel = ShaderModel::MOBILE;
@@ -181,14 +177,6 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
<< gets.max_anisotropy << '\n' << gets.max_anisotropy << '\n'
<< "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = " << "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = "
<< gets.max_combined_texture_image_units << '\n' << gets.max_combined_texture_image_units << '\n'
<< "GL_MAX_TEXTURE_SIZE = "
<< gets.max_texture_size << '\n'
<< "GL_MAX_CUBE_MAP_TEXTURE_SIZE = "
<< gets.max_cubemap_texture_size << '\n'
<< "GL_MAX_3D_TEXTURE_SIZE = "
<< gets.max_3d_texture_size << '\n'
<< "GL_MAX_ARRAY_TEXTURE_LAYERS = "
<< gets.max_array_texture_layers << '\n'
<< "GL_MAX_DRAW_BUFFERS = " << "GL_MAX_DRAW_BUFFERS = "
<< gets.max_draw_buffers << '\n' << gets.max_draw_buffers << '\n'
<< "GL_MAX_RENDERBUFFER_SIZE = " << "GL_MAX_RENDERBUFFER_SIZE = "
@@ -566,11 +554,7 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
} else if (strstr(renderer, "AMD") || } else if (strstr(renderer, "AMD") ||
strstr(renderer, "ATI")) { strstr(renderer, "ATI")) {
// AMD/ATI GPU // AMD/ATI GPU
} else if (strstr(renderer, "Mozilla")) { } else if (strstr(vendor, "Mesa")) {
bugs->disable_invalidate_framebuffer = true;
}
if (strstr(vendor, "Mesa")) {
// Seen on // Seen on
// [Mesa], // [Mesa],
// [llvmpipe (LLVM 17.0.6, 256 bits)], // [llvmpipe (LLVM 17.0.6, 256 bits)],
@@ -578,6 +562,8 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
// [4.50] // [4.50]
// not known which version are affected // not known which version are affected
bugs->rebind_buffer_after_deletion = true; bugs->rebind_buffer_after_deletion = true;
} else if (strstr(renderer, "Mozilla")) {
bugs->disable_invalidate_framebuffer = true;
} }
} else { } else {
// When running under ANGLE, it's a different set of workaround that we need. // When running under ANGLE, it's a different set of workaround that we need.
@@ -589,19 +575,6 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
} }
} }
if (strstr(vendor, "Mozilla")) {
// Seen on
// [Mozilla],
// [GeForce GTX 980, or similar]
// or [ANGLE (NVIDIA, NVIDIA GeForce GTX 980 Direct3D11 vs_5_0 ps_5_0), or similar]
// or anything else,
// [OpenGL ES 3.0 (WebGL 2.0)],
// [OpenGL ES GLSL ES 3.00 (WebGL GLSL ES 3.00)]
// For Mozilla, the issue appears to be observed regardless of whether the renderer is
// ANGLE or not. (b/376125497)
bugs->rebind_buffer_after_deletion = true;
}
#ifdef BACKEND_OPENGL_VERSION_GLES #ifdef BACKEND_OPENGL_VERSION_GLES
# ifndef FILAMENT_IOS // FILAMENT_IOS is guaranteed to have ES3.x # ifndef FILAMENT_IOS // FILAMENT_IOS is guaranteed to have ES3.x
if (UTILS_UNLIKELY(major == 2)) { if (UTILS_UNLIKELY(major == 2)) {
+2 -6
View File
@@ -203,10 +203,6 @@ public:
GLint max_renderbuffer_size; GLint max_renderbuffer_size;
GLint max_samples; GLint max_samples;
GLint max_texture_image_units; GLint max_texture_image_units;
GLint max_texture_size;
GLint max_cubemap_texture_size;
GLint max_3d_texture_size;
GLint max_array_texture_layers;
GLint max_transform_feedback_separate_attribs; GLint max_transform_feedback_separate_attribs;
GLint max_uniform_block_size; GLint max_uniform_block_size;
GLint max_uniform_buffer_bindings; GLint max_uniform_buffer_bindings;
@@ -320,8 +316,8 @@ public:
// a glFinish. So we must delay the destruction until we know the GPU is finished. // a glFinish. So we must delay the destruction until we know the GPU is finished.
bool delay_fbo_destruction; bool delay_fbo_destruction;
// Mesa and Mozilla(web) sometimes clear the generic buffer binding when *another* buffer // Mesa sometimes clears the generic buffer binding when *another* buffer is destroyed,
// is destroyed, if that other buffer is bound on an *indexed* buffer binding. // if that other buffer is bound on an *indexed* buffer binding.
bool rebind_buffer_after_deletion; bool rebind_buffer_after_deletion;
// Force feature level 0. Typically used for low end ES3 devices with significant driver // Force feature level 0. Typically used for low end ES3 devices with significant driver
+6 -26
View File
@@ -148,8 +148,8 @@ using namespace utils;
namespace filament::backend { namespace filament::backend {
Driver* OpenGLDriverFactory::create( Driver* OpenGLDriverFactory::create(
OpenGLPlatform* platform, OpenGLPlatform* const platform,
void* sharedGLContext, void* const sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept { const Platform::DriverConfig& driverConfig) noexcept {
return OpenGLDriver::create(platform, sharedGLContext, driverConfig); return OpenGLDriver::create(platform, sharedGLContext, driverConfig);
} }
@@ -159,10 +159,10 @@ using namespace GLUtils;
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
UTILS_NOINLINE UTILS_NOINLINE
OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* platform, OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* const platform,
void* /*sharedGLContext*/, const Platform::DriverConfig& driverConfig) noexcept { void* const /*sharedGLContext*/, const Platform::DriverConfig& driverConfig) noexcept {
assert_invariant(platform); assert_invariant(platform);
OpenGLPlatform* ec = platform; OpenGLPlatform* const ec = platform;
#if 0 #if 0
// this is useful for development, but too verbose even for debug builds // this is useful for development, but too verbose even for debug builds
@@ -230,7 +230,7 @@ OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* platform,
constexpr size_t defaultSize = FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U; constexpr size_t defaultSize = FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
Platform::DriverConfig validConfig{ driverConfig }; Platform::DriverConfig validConfig{ driverConfig };
validConfig.handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize); validConfig.handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize);
OpenGLDriver* driver = new(std::nothrow) OpenGLDriver(ec, validConfig); OpenGLDriver* const driver = new(std::nothrow) OpenGLDriver(ec, validConfig);
return driver; return driver;
} }
@@ -2460,26 +2460,6 @@ size_t OpenGLDriver::getMaxUniformBufferSize() {
return mContext.gets.max_uniform_block_size; return mContext.gets.max_uniform_block_size;
} }
size_t OpenGLDriver::getMaxTextureSize(SamplerType target) {
switch (target) {
case SamplerType::SAMPLER_2D:
case SamplerType::SAMPLER_2D_ARRAY:
case SamplerType::SAMPLER_EXTERNAL:
return mContext.gets.max_texture_size;
case SamplerType::SAMPLER_CUBEMAP:
return mContext.gets.max_cubemap_texture_size;
case SamplerType::SAMPLER_3D:
return mContext.gets.max_3d_texture_size;
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
return mContext.gets.max_cubemap_texture_size;
}
return 0;
}
size_t OpenGLDriver::getMaxArrayTextureLayers() {
return mContext.gets.max_array_texture_layers;
}
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
// Swap chains // Swap chains
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
@@ -85,7 +85,6 @@ OpenGLProgram::~OpenGLProgram() noexcept {
delete lazyInitializationData; delete lazyInitializationData;
ShaderCompilerService::terminate(mToken); ShaderCompilerService::terminate(mToken);
assert_invariant(!mToken);
} }
delete [] mUniformsRecords; delete [] mUniformsRecords;
File diff suppressed because it is too large Load Diff
@@ -24,23 +24,23 @@
#include "OpenGLBlobCache.h" #include "OpenGLBlobCache.h"
#include <backend/CallbackHandler.h> #include <backend/CallbackHandler.h>
#include <backend/DriverEnums.h>
#include <backend/Program.h> #include <backend/Program.h>
#include <utils/CString.h> #include <utils/CString.h>
#include <utils/FixedCapacityVector.h> #include <utils/FixedCapacityVector.h>
#include <utils/Invocable.h>
#include <utils/JobSystem.h> #include <utils/JobSystem.h>
#include <array> #include <atomic>
#include <condition_variable>
#include <deque>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <tuple> #include <thread>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <stdint.h>
namespace filament::backend { namespace filament::backend {
class OpenGLDriver; class OpenGLDriver;
@@ -57,8 +57,6 @@ class ShaderCompilerService {
public: public:
using program_token_t = std::shared_ptr<OpenGLProgramToken>; using program_token_t = std::shared_ptr<OpenGLProgramToken>;
using shaders_t = std::array<GLuint, Program::SHADER_TYPE_COUNT>;
using shaders_source_t = std::array<utils::CString, Program::SHADER_TYPE_COUNT>;
explicit ShaderCompilerService(OpenGLDriver& driver); explicit ShaderCompilerService(OpenGLDriver& driver);
@@ -84,7 +82,6 @@ public:
void tick(); void tick();
// Destroys a valid token and all associated resources. Used to "cancel" a program compilation. // Destroys a valid token and all associated resources. Used to "cancel" a program compilation.
// This function is not called if `initialize(token)` is already invoked.
static void terminate(program_token_t& token); static void terminate(program_token_t& token);
// stores a user data pointer in the token // stores a user data pointer in the token
@@ -93,12 +90,6 @@ public:
// retrieves the user data pointer stored in the token // retrieves the user data pointer stored in the token
static void* getUserData(const program_token_t& token) noexcept; static void* getUserData(const program_token_t& token) noexcept;
// Issue one callback handle.
CallbackManager::Handle issueCallbackHandle() const noexcept;
// Return a callback handle to the callback manager.
void submitCallbackHandle(CallbackManager::Handle handle) noexcept;
// call the callback when all active programs are ready // call the callback when all active programs are ready
void notifyWhenAllProgramsAreReady( void notifyWhenAllProgramsAreReady(
CallbackHandler* handler, CallbackHandler::Callback callback, void* user); CallbackHandler* handler, CallbackHandler::Callback callback, void* user);
@@ -106,7 +97,7 @@ public:
private: private:
struct Job { struct Job {
template<typename FUNC> template<typename FUNC>
Job(FUNC&& fn) : fn(std::forward<FUNC>(fn)) {} // NOLINT(*-explicit-constructor) Job(FUNC&& fn) : fn(std::forward<FUNC>(fn)) {}
Job(std::function<bool(Job const& job)> fn, Job(std::function<bool(Job const& job)> fn,
CallbackHandler* handler, void* user, CallbackHandler::Callback callback) CallbackHandler* handler, void* user, CallbackHandler::Callback callback)
: fn(std::move(fn)), handler(handler), user(user), callback(callback) { : fn(std::move(fn)), handler(handler), user(user), callback(callback) {
@@ -135,49 +126,39 @@ private:
using ContainerType = std::tuple<CompilerPriorityQueue, program_token_t, Job>; using ContainerType = std::tuple<CompilerPriorityQueue, program_token_t, Job>;
std::vector<ContainerType> mRunAtNextTickOps; std::vector<ContainerType> mRunAtNextTickOps;
GLuint initialize(program_token_t& token); GLuint initialize(ShaderCompilerService::program_token_t& token) noexcept;
void ensureTokenIsReady(program_token_t const& token);
void runAtNextTick(CompilerPriorityQueue priority, program_token_t const& token, static void getProgramFromCompilerPool(program_token_t& token) noexcept;
Job job) noexcept;
static void compileShaders(
OpenGLContext& context,
Program::ShaderSource shadersSource,
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
bool multiview,
std::array<GLuint, Program::SHADER_TYPE_COUNT>& outShaders,
std::array<utils::CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept;
static void process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
char* source, size_t len) noexcept;
static void process_OVR_multiview2(OpenGLContext& context, int32_t eyeCount,
char* source, size_t len) noexcept;
static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept;
static std::array<std::string_view, 3> splitShaderSource(std::string_view source) noexcept;
static GLuint linkProgram(OpenGLContext& context,
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders,
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> const& attributes) noexcept;
static bool checkProgramStatus(program_token_t const& token) noexcept;
void runAtNextTick(CompilerPriorityQueue priority,
const program_token_t& token, Job job) noexcept;
void executeTickOps() noexcept; void executeTickOps() noexcept;
bool cancelTickOp(program_token_t const& token) noexcept; bool cancelTickOp(program_token_t token) noexcept;
// order of insertion is important
// Compile shaders with the given `shaderSource`. `gl.shaders` is always populated with valid
// shader IDs after this method. But this doesn't necessarily mean the shaders are successfully
// compiled. Errors can be checked by calling `checkCompileStatus` later.
static void compileShaders(OpenGLContext& context, Program::ShaderSource shadersSource,
utils::FixedCapacityVector<Program::SpecializationConstant> const&
specializationConstants,
bool multiview, program_token_t const& token) noexcept;
// Check if the shader compilation is completed. You may want to call this when the extension
// `KHR_parallel_shader_compile` is enabled.
static bool isCompileCompleted(program_token_t const& token) noexcept;
// Check compilation status of the shaders and log errors on failure.
static void checkCompileStatus(program_token_t const& token) noexcept;
// Create a program by linking the compiled shaders. `gl.program` is always populated with a
// valid program ID after this method. But this doesn't necessarily mean the program is
// successfully linked. Errors can be checked by calling `checkLinkStatusAndCleanupShaders`
// later.
static void linkProgram(OpenGLContext const& context, program_token_t const& token) noexcept;
// Check if the program link is completed. You may want to call this when the extension
// `KHR_parallel_shader_compile` is enabled.
static bool isLinkCompleted(program_token_t const& token) noexcept;
// Check link status of the program and log errors on failure. Return the result of the link.
// Also cleanup shaders regardless of the result.
static bool checkLinkStatusAndCleanupShaders(program_token_t const& token) noexcept;
// Try caching the program if we haven't done it yet. Cache it only when the program is valid.
static void tryCachingProgram(OpenGLBlobCache& cache, OpenGLPlatform& platform,
program_token_t const& token) noexcept;
// Cleanup GL resources.
static void cleanupProgramAndShaders(program_token_t const& token) noexcept;
}; };
} // namespace filament::backend } // namespace filament::backend
@@ -64,7 +64,7 @@ PlatformCocoaTouchGL::~PlatformCocoaTouchGL() noexcept {
delete pImpl; delete pImpl;
} }
Driver* PlatformCocoaTouchGL::createDriver(void* sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept { Driver* PlatformCocoaTouchGL::createDriver(void* const sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
EAGLSharegroup* sharegroup = (__bridge EAGLSharegroup*) sharedGLContext; EAGLSharegroup* sharegroup = (__bridge EAGLSharegroup*) sharedGLContext;
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 sharegroup:sharegroup]; EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 sharegroup:sharegroup];
@@ -19,7 +19,6 @@
#include <backend/platforms/PlatformEGL.h> #include <backend/platforms/PlatformEGL.h>
#include <backend/platforms/PlatformEGLAndroid.h> #include <backend/platforms/PlatformEGLAndroid.h>
#include <private/backend/BackendUtilsAndroid.h>
#include <private/backend/VirtualMachineEnv.h> #include <private/backend/VirtualMachineEnv.h>
#include "opengl/GLUtils.h" #include "opengl/GLUtils.h"
@@ -35,8 +34,6 @@
#include <utils/ostream.h> #include <utils/ostream.h>
#include <utils/Panic.h> #include <utils/Panic.h>
#include <utils/Log.h> #include <utils/Log.h>
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <EGL/egl.h> #include <EGL/egl.h>
#include <EGL/eglext.h> #include <EGL/eglext.h>
@@ -234,133 +231,26 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
return driver; return driver;
} }
PlatformEGLAndroid::ExternalImageEGLAndroid::~ExternalImageEGLAndroid() { PlatformEGLAndroid::ExternalImageEGLAndroid::~ExternalImageEGLAndroid() = default;
if (__builtin_available(android 26, *)) {
if (aHardwareBuffer) {
AHardwareBuffer_release(aHardwareBuffer);
}
}
}
Platform::ExternalImageHandle PlatformEGLAndroid::createExternalImage(AHardwareBuffer const* buffer, Platform::ExternalImageHandle PlatformEGLAndroid::createExternalImage(AHardwareBuffer const* buffer, bool sRGB) noexcept {
bool sRGB) noexcept {
if (__builtin_available(android 26, *)) {
auto* const p = new(std::nothrow) ExternalImageEGLAndroid; auto* const p = new(std::nothrow) ExternalImageEGLAndroid;
auto hardwareBuffer = const_cast<AHardwareBuffer*>(buffer); p->aHardwareBuffer = const_cast<AHardwareBuffer*>(buffer);
AHardwareBuffer_acquire(hardwareBuffer);
p->aHardwareBuffer = hardwareBuffer;
p->sRGB = sRGB; p->sRGB = sRGB;
AHardwareBuffer_Desc hardwareBufferDescription = {};
AHardwareBuffer_describe(hardwareBuffer, &hardwareBufferDescription);
p->height = hardwareBufferDescription.height;
p->width = hardwareBufferDescription.width;
auto textureFormat = mapToFilamentFormat(hardwareBufferDescription.format, sRGB);
p->usage = mapToFilamentUsage(hardwareBufferDescription.usage, textureFormat);
return ExternalImageHandle{ p }; return ExternalImageHandle{ p };
} }
return Platform::ExternalImageHandle{};
}
PlatformEGLAndroid::ExternalImageDescAndroid PlatformEGLAndroid::getExternalImageDesc(
ExternalImageHandle externalImage) noexcept {
auto const* const eglExternalImage =
static_cast<ExternalImageEGLAndroid const*>(externalImage.get());
ExternalImageDescAndroid metadata = {};
if (!eglExternalImage) {
return metadata;
}
metadata.height = eglExternalImage->height;
metadata.width = eglExternalImage->width;
metadata.format = eglExternalImage->format;
metadata.usage = eglExternalImage->usage;
return metadata;
}
bool PlatformEGLAndroid::setExternalImage(ExternalImageHandleRef externalImage, bool PlatformEGLAndroid::setExternalImage(ExternalImageHandleRef externalImage,
UTILS_UNUSED_IN_RELEASE ExternalTexture* texture) noexcept { UTILS_UNUSED_IN_RELEASE ExternalTexture* texture) noexcept {
auto const* const eglExternalImage = auto const* const eglExternalImage = static_cast<ExternalImageEGLAndroid const*>(externalImage.get());
static_cast<ExternalImageEGLAndroid const*>(externalImage.get());
if (eglExternalImage->aHardwareBuffer) { if (eglExternalImage->aHardwareBuffer) {
return PlatformEGLAndroid::setImage(eglExternalImage, texture); // TODO: implement PlatformEGLAndroid::setExternalImage w/ AHardwareBuffer
return true;
} }
// not a AHardwareBuffer, fallback to the inherited version // not a AHardwareBuffer, fallback to the inherited version
return PlatformEGL::setExternalImage(externalImage, texture); return PlatformEGL::setExternalImage(externalImage, texture);
} }
OpenGLPlatform::ExternalTexture* PlatformEGLAndroid::createExternalImageTexture() noexcept {
ExternalTexture* outTexture = new (std::nothrow) ExternalTexture{};
glGenTextures(1, &outTexture->id);
return outTexture;
}
void PlatformEGLAndroid::destroyExternalImageTexture(ExternalTexture* texture) noexcept {
glDeleteTextures(1, &texture->id);
delete texture;
}
bool PlatformEGLAndroid::setImage(ExternalImageEGLAndroid const* eglExternalImage,
UTILS_UNUSED_IN_RELEASE ExternalTexture* texture) noexcept {
AHardwareBuffer* hardwareBuffer = eglExternalImage->aHardwareBuffer;
// Get the EGL client buffer from AHardwareBuffer
EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(hardwareBuffer);
EGLint imageAttrs[] = {
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
EGL_NONE, EGL_NONE, // Reserve space
EGL_NONE, EGL_NONE, // Reserve space
EGL_NONE // Ensure the list always ends with EGL_NONE
};
int attrIndex = 2;
if (eglExternalImage->sRGB) {
imageAttrs[attrIndex++] = EGL_GL_COLORSPACE;
imageAttrs[attrIndex++] = EGL_GL_COLORSPACE_SRGB;
}
if (static_cast<bool>(eglExternalImage->usage & TextureUsage::PROTECTED)) {
imageAttrs[attrIndex++] = EGL_PROTECTED_CONTENT_EXT;
imageAttrs[attrIndex++] = EGL_TRUE;
}
// Create an EGLImage from the client buffer
EGLImageKHR eglImage = eglCreateImageKHR(eglGetCurrentDisplay(), EGL_NO_CONTEXT,
EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs);
if (eglImage == EGL_NO_IMAGE_KHR) {
// Handle error
slog.e << "Failed to create EGL image" << io::endl;
glDeleteTextures(1, &texture->id);
return false;
}
// Create and bind the OpenGL texture
GLint prevActiveTexture, prevTexture;
glGetIntegerv(GL_ACTIVE_TEXTURE, &prevActiveTexture);
glGetIntegerv(GL_TEXTURE_BINDING_2D, &prevTexture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(texture->target, texture->id);
GLenum error = glGetError();
if (UTILS_UNLIKELY(error != GL_NO_ERROR)) {
slog.e << "Error after glBindTexture: " << error << io::endl;
glDeleteTextures(1, &texture->id);
eglDestroyImageKHR(eglGetCurrentDisplay(), eglImage);
glActiveTexture(prevActiveTexture);
glBindTexture(GL_TEXTURE_2D, prevTexture);
return false;
}
glEGLImageTargetTexture2DOES(texture->target, static_cast<GLeglImageOES>(eglImage));
error = glGetError();
if (UTILS_UNLIKELY(error != GL_NO_ERROR)) {
slog.e << "Error after glEGLImageTargetTexture2DOES: " << error << io::endl;
glDeleteTextures(1, &texture->id);
eglDestroyImageKHR(eglGetCurrentDisplay(), eglImage);
glActiveTexture(prevActiveTexture);
glBindTexture(GL_TEXTURE_2D, prevTexture);
return false;
}
glActiveTexture(prevActiveTexture);
glBindTexture(GL_TEXTURE_2D, prevTexture);
return true;
}
void PlatformEGLAndroid::setPresentationTime(int64_t presentationTimeInNanosecond) noexcept { void PlatformEGLAndroid::setPresentationTime(int64_t presentationTimeInNanosecond) noexcept {
EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW); EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW);
if (currentDrawSurface != EGL_NO_SURFACE) { if (currentDrawSurface != EGL_NO_SURFACE) {
@@ -128,7 +128,7 @@ namespace filament::backend {
using namespace backend; using namespace backend;
Driver* PlatformGLX::createDriver(void* sharedGLContext, Driver* PlatformGLX::createDriver(void* const sharedGLContext,
const DriverConfig& driverConfig) noexcept { const DriverConfig& driverConfig) noexcept {
loadLibraries(); loadLibraries();
// Get the display device // Get the display device
@@ -22,11 +22,9 @@
#include <dlfcn.h> #include <dlfcn.h>
#include <memory> #include <memory>
#if defined(__linux__)
// This is to ensure that linking during compilation will not fail even if // This is to ensure that linking during compilation will not fail even if
// OSMesaGetProcAddress is not linked. // OSMesaGetProcAddress is not linked.
__attribute__((weak)) OSMESAproc OSMesaGetProcAddress(char const*); __attribute__((weak)) OSMESAproc OSMesaGetProcAddress(char const*);
#endif
namespace filament::backend { namespace filament::backend {
@@ -50,27 +48,20 @@ struct OSMesaSwapchain {
struct OSMesaAPI { struct OSMesaAPI {
private: private:
using CreateContextAttribsFunc = OSMesaContext (*)(const int *, OSMesaContext); using CreateContextFunc = OSMesaContext (*)(GLenum format, OSMesaContext);
using DestroyContextFunc = GLboolean (*)(OSMesaContext); using DestroyContextFunc = GLboolean (*)(OSMesaContext);
using MakeCurrentFunc = GLboolean (*)(OSMesaContext ctx, void* buffer, GLenum type, using MakeCurrentFunc = GLboolean (*)(OSMesaContext ctx, void* buffer, GLenum type,
GLsizei width, GLsizei height); GLsizei width, GLsizei height);
using GetProcAddressFunc = OSMESAproc (*)(const char* funcName); using GetProcAddressFunc = OSMESAproc (*)(const char* funcName);
public: public:
CreateContextAttribsFunc fOSMesaCreateContextAttribs; CreateContextFunc fOSMesaCreateContext;
DestroyContextFunc fOSMesaDestroyContext; DestroyContextFunc fOSMesaDestroyContext;
MakeCurrentFunc fOSMesaMakeCurrent; MakeCurrentFunc fOSMesaMakeCurrent;
GetProcAddressFunc fOSMesaGetProcAddress; GetProcAddressFunc fOSMesaGetProcAddress;
OSMesaAPI() { OSMesaAPI() {
static constexpr char const* libraryNames[] = { constexpr char const* libraryNames[] = {"libOSMesa.so", "libosmesa.so"};
#if defined(__linux__)
"libOSMesa.so",
"libosmesa.so",
#elif defined(__APPLE__)
"libOSMesa.dylib",
#endif
};
for (char const* libName: libraryNames) { for (char const* libName: libraryNames) {
mLib = dlopen(libName, RTLD_GLOBAL | RTLD_NOW); mLib = dlopen(libName, RTLD_GLOBAL | RTLD_NOW);
if (mLib) { if (mLib) {
@@ -80,24 +71,22 @@ public:
if (mLib) { if (mLib) {
// Loading from a libosmesa.os // Loading from a libosmesa.os
fOSMesaGetProcAddress = (GetProcAddressFunc) dlsym(mLib, "OSMesaGetProcAddress"); fOSMesaGetProcAddress = (GetProcAddressFunc) dlsym(mLib, "OSMesaGetProcAddress");
} } else {
#if defined(__linux__)
else {
// Filament is built into a .so // Filament is built into a .so
fOSMesaGetProcAddress = (GetProcAddressFunc) dlsym(RTLD_LOCAL, "OSMesaGetProcAddress"); fOSMesaGetProcAddress = (GetProcAddressFunc) dlsym(RTLD_LOCAL, "OSMesaGetProcAddress");
} }
if (!fOSMesaGetProcAddress) { if (!fOSMesaGetProcAddress) {
// Statically linking osmesa // Statically linking osmesa
fOSMesaGetProcAddress = OSMesaGetProcAddress; fOSMesaGetProcAddress = OSMesaGetProcAddress;
} }
#endif // __linux__
FILAMENT_CHECK_PRECONDITION(fOSMesaGetProcAddress) FILAMENT_CHECK_PRECONDITION(fOSMesaGetProcAddress)
<< "Unable to link against libOSMesa to create a software GL context"; << "Unable to link against libOSMesa to create a software GL context";
fOSMesaCreateContextAttribs = fOSMesaCreateContext = (CreateContextFunc) fOSMesaGetProcAddress("OSMesaCreateContext");
(CreateContextAttribsFunc) fOSMesaGetProcAddress("OSMesaCreateContextAttribs"); fOSMesaDestroyContext =
fOSMesaDestroyContext = (DestroyContextFunc) fOSMesaGetProcAddress("OSMesaDestroyContext"); (DestroyContextFunc) fOSMesaGetProcAddress("OSMesaDestroyContext");
fOSMesaMakeCurrent = (MakeCurrentFunc) fOSMesaGetProcAddress("OSMesaMakeCurrent"); fOSMesaMakeCurrent = (MakeCurrentFunc) fOSMesaGetProcAddress("OSMesaMakeCurrent");
} }
@@ -112,24 +101,14 @@ private:
}// anonymous namespace }// anonymous namespace
Driver* PlatformOSMesa::createDriver(void* sharedGLContext, Driver* PlatformOSMesa::createDriver(void* const sharedGLContext,
const DriverConfig& driverConfig) noexcept { const DriverConfig& driverConfig) noexcept {
OSMesaAPI* api = new OSMesaAPI(); OSMesaAPI* api = new OSMesaAPI();
mOsMesaApi = api; mOsMesaApi = api;
static constexpr int attribs[] = {
OSMESA_FORMAT, GL_RGBA,
OSMESA_DEPTH_BITS, 24,
OSMESA_STENCIL_BITS, 8,
OSMESA_ACCUM_BITS, 0,
OSMESA_PROFILE, OSMESA_CORE_PROFILE,
0,
};
FILAMENT_CHECK_PRECONDITION(sharedGLContext == nullptr) FILAMENT_CHECK_PRECONDITION(sharedGLContext == nullptr)
<< "shared GL context is not supported with PlatformOSMesa"; << "shared GL context is not supported with PlatformOSMesa";
mContext = api->fOSMesaCreateContextAttribs(attribs, NULL); mContext = api->fOSMesaCreateContext(GL_RGBA, NULL);
// We need to do a no-op makecurrent here so that the context will be in a correct state before // We need to do a no-op makecurrent here so that the context will be in a correct state before
// any GL calls. // any GL calls.
@@ -75,7 +75,7 @@ struct WGLSwapChain {
static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs = nullptr; static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs = nullptr;
Driver* PlatformWGL::createDriver(void* sharedGLContext, Driver* PlatformWGL::createDriver(void* const sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept { const Platform::DriverConfig& driverConfig) noexcept {
int result = 0; int result = 0;
int pixelFormat = 0; int pixelFormat = 0;
@@ -20,7 +20,7 @@ namespace filament::backend {
using namespace backend; using namespace backend;
Driver* PlatformWebGL::createDriver(void* sharedGLContext, Driver* PlatformWebGL::createDriver(void* const sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept { const Platform::DriverConfig& driverConfig) noexcept {
return OpenGLPlatform::createDefaultDriver(this, sharedGLContext, driverConfig); return OpenGLPlatform::createDefaultDriver(this, sharedGLContext, driverConfig);
} }
-5
View File
@@ -15,7 +15,6 @@
*/ */
#include <backend/BufferDescriptor.h> #include <backend/BufferDescriptor.h>
#include <backend/BufferObjectStreamDescriptor.h>
#include <backend/DescriptorSetOffsetArray.h> #include <backend/DescriptorSetOffsetArray.h>
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <backend/PipelineState.h> #include <backend/PipelineState.h>
@@ -438,10 +437,6 @@ io::ostream& operator<<(io::ostream& out, BufferDescriptor const& b) {
<< ", user=" << b.getUser() << " }"; << ", user=" << b.getUser() << " }";
} }
io::ostream& operator<<(io::ostream& out, const BufferObjectStreamDescriptor& b) {
return out << "BufferObjectStreamDescriptor{ streams(" << b.mStreams.size() << ")=... }";
}
io::ostream& operator<<(io::ostream& out, PixelBufferDescriptor const& b) { io::ostream& operator<<(io::ostream& out, PixelBufferDescriptor const& b) {
BufferDescriptor const& base = static_cast<BufferDescriptor const&>(b); BufferDescriptor const& base = static_cast<BufferDescriptor const&>(b);
return out << "PixelBufferDescriptor{ " << base return out << "PixelBufferDescriptor{ " << base
@@ -17,6 +17,9 @@
#include "VulkanBlitter.h" #include "VulkanBlitter.h"
#include "VulkanCommands.h" #include "VulkanCommands.h"
#include "VulkanContext.h" #include "VulkanContext.h"
#include "VulkanFboCache.h"
#include "VulkanHandles.h"
#include "VulkanSamplerCache.h"
#include "VulkanTexture.h" #include "VulkanTexture.h"
#include "vulkan/utils/Image.h" #include "vulkan/utils/Image.h"
@@ -24,6 +24,13 @@
namespace filament::backend { namespace filament::backend {
class VulkanBuffer;
class VulkanFboCache;
class VulkanPipelineCache;
class VulkanSamplerCache;
struct VulkanProgram;
class VulkanBlitter { class VulkanBlitter {
public: public:
VulkanBlitter(VkPhysicalDevice physicalDevice, VulkanCommands* commands) noexcept; VulkanBlitter(VkPhysicalDevice physicalDevice, VulkanCommands* commands) noexcept;
@@ -156,8 +156,8 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
#define FVK_SYSTRACE_CONTEXT() SYSTRACE_CONTEXT() #define FVK_SYSTRACE_CONTEXT() SYSTRACE_CONTEXT()
#define FVK_SYSTRACE_START(marker) SYSTRACE_NAME_BEGIN(marker) #define FVK_SYSTRACE_START(marker) SYSTRACE_NAME_BEGIN(marker)
#define FVK_SYSTRACE_END() SYSTRACE_NAME_END() #define FVK_SYSTRACE_END() SYSTRACE_NAME_END()
#define FVK_SYSTRACE_SCOPE() SYSTRACE_CALL() #define FVK_SYSTRACE_SCOPE() SYSTRACE_NAME(__func__)
#define FVK_PROFILE_MARKER(marker) SYSTRACE_CALL() #define FVK_PROFILE_MARKER(marker) FVK_SYSTRACE_SCOPE()
#else #else
#define FVK_SYSTRACE_CONTEXT() #define FVK_SYSTRACE_CONTEXT()
@@ -56,9 +56,9 @@ VkImageView VulkanAttachment::getImageView() {
assert_invariant(texture); assert_invariant(texture);
VkImageSubresourceRange range = getSubresourceRange(); VkImageSubresourceRange range = getSubresourceRange();
if (range.layerCount > 1) { if (range.layerCount > 1) {
return texture->getAttachmentView(range, VK_IMAGE_VIEW_TYPE_2D_ARRAY); return texture->getMultiviewAttachmentView(range);
} }
return texture->getAttachmentView(range, VK_IMAGE_VIEW_TYPE_2D); return texture->getAttachmentView(range);
} }
bool VulkanAttachment::isDepth() const { bool VulkanAttachment::isDepth() const {
+10 -14
View File
@@ -17,6 +17,7 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANCONTEXT_H #ifndef TNT_FILAMENT_BACKEND_VULKANCONTEXT_H
#define TNT_FILAMENT_BACKEND_VULKANCONTEXT_H #define TNT_FILAMENT_BACKEND_VULKANCONTEXT_H
#include "VulkanConstants.h"
#include "vulkan/utils/Image.h" #include "vulkan/utils/Image.h"
#include "vulkan/utils/Definitions.h" #include "vulkan/utils/Definitions.h"
@@ -29,6 +30,8 @@
#include <bluevk/BlueVK.h> #include <bluevk/BlueVK.h>
#include <memory>
VK_DEFINE_HANDLE(VmaAllocator) VK_DEFINE_HANDLE(VmaAllocator)
VK_DEFINE_HANDLE(VmaPool) VK_DEFINE_HANDLE(VmaPool)
@@ -70,11 +73,13 @@ struct VulkanRenderPass {
// context are stored in VulkanPlatform. // context are stored in VulkanPlatform.
struct VulkanContext { struct VulkanContext {
public: public:
static uint32_t selectMemoryType(VkPhysicalDeviceMemoryProperties const& memoryProperties, inline uint32_t selectMemoryType(uint32_t flags, VkFlags reqs) const {
uint32_t flags, VkFlags reqs) { if ((reqs & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) {
assert_invariant(isProtectedMemorySupported() == true);
}
for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) { for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) {
if (flags & 1) { if (flags & 1) {
if ((memoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) { if ((mMemoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) {
return i; return i;
} }
} }
@@ -83,13 +88,6 @@ public:
return (uint32_t) VK_MAX_MEMORY_TYPES; return (uint32_t) VK_MAX_MEMORY_TYPES;
} }
inline uint32_t selectMemoryType(uint32_t flags, VkFlags reqs) const {
if ((reqs & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) {
assert_invariant(isProtectedMemorySupported());
}
return selectMemoryType(mMemoryProperties, flags, reqs);
}
inline fvkutils::VkFormatList const& getAttachmentDepthStencilFormats() const { inline fvkutils::VkFormatList const& getAttachmentDepthStencilFormats() const {
return mDepthStencilFormats; return mDepthStencilFormats;
} }
@@ -123,7 +121,7 @@ public:
} }
inline bool isMultiviewEnabled() const noexcept { inline bool isMultiviewEnabled() const noexcept {
return mPhysicalDeviceVk11Features.multiview == VK_TRUE; return mMultiviewEnabled;
} }
inline bool isClipDistanceSupported() const noexcept { inline bool isClipDistanceSupported() const noexcept {
@@ -147,9 +145,6 @@ private:
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = { VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
}; };
VkPhysicalDeviceVulkan11Features mPhysicalDeviceVk11Features = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
};
VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures = { VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
}; };
@@ -162,6 +157,7 @@ private:
}; };
bool mDebugMarkersSupported = false; bool mDebugMarkersSupported = false;
bool mDebugUtilsSupported = false; bool mDebugUtilsSupported = false;
bool mMultiviewEnabled = false;
bool mLazilyAllocatedMemorySupported = false; bool mLazilyAllocatedMemorySupported = false;
bool mProtectedMemorySupported = false; bool mProtectedMemorySupported = false;
@@ -1,416 +0,0 @@
/*
* Copyright (C) 2024 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 "VulkanDescriptorSetCache.h"
#include "VulkanCommands.h"
#include "VulkanHandles.h"
#include "VulkanConstants.h"
#include <utils/FixedCapacityVector.h>
#include <utils/Panic.h>
#include <algorithm>
#include <memory>
#include <vector>
namespace filament::backend {
namespace {
using DescriptorCount = VulkanDescriptorSetLayout::Count;
using DescriptorSetLayoutArray = VulkanDescriptorSetCache::DescriptorSetLayoutArray;
// We create a pool for each layout as defined by the number of descriptors of each type. For
// example, a layout of
// 'A' =>
// layout(binding = 0, set = 1) uniform {};
// layout(binding = 1, set = 1) sampler1;
// layout(binding = 2, set = 1) sampler2;
//
// would be equivalent to
// 'B' =>
// layout(binding = 1, set = 2) uniform {};
// layout(binding = 2, set = 2) sampler2;
// layout(binding = 3, set = 2) sampler3;
//
// TODO: we might do better if we understand the types of unique layouts and can combine them in a
// single pool without too much waste.
class DescriptorPool {
public:
DescriptorPool(VkDevice device, DescriptorCount const& count, uint16_t capacity)
: mDevice(device),
mCount(count),
mCapacity(capacity),
mSize(0),
mUnusedCount(0) {
DescriptorCount const actual = mCount * capacity;
VkDescriptorPoolSize sizes[4];
uint8_t npools = 0;
if (actual.ubo) {
sizes[npools++] = {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = actual.ubo,
};
}
if (actual.dynamicUbo) {
sizes[npools++] = {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
.descriptorCount = actual.dynamicUbo,
};
}
if (actual.sampler) {
sizes[npools++] = {
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = actual.sampler,
};
}
if (actual.inputAttachment) {
sizes[npools++] = {
.type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
.descriptorCount = actual.inputAttachment,
};
}
VkDescriptorPoolCreateInfo info{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.maxSets = capacity,
.poolSizeCount = npools,
.pPoolSizes = sizes,
};
vkCreateDescriptorPool(mDevice, &info, VKALLOC, &mPool);
}
DescriptorPool(DescriptorPool const&) = delete;
DescriptorPool& operator=(DescriptorPool const&) = delete;
~DescriptorPool() {
vkDestroyDescriptorPool(mDevice, mPool, VKALLOC);
}
uint16_t const& capacity() {
return mCapacity;
}
// A convenience method for checking if this pool can allocate sets for a given layout.
inline bool canAllocate(DescriptorCount const& count) {
return count == mCount;
}
VkDescriptorSet obtainSet(VkDescriptorSetLayout vklayout) {
auto itr = findSets(vklayout);
if (itr != mUnused.end()) {
// If we don't have any unused, then just return an empty handle.
if (itr->second.empty()) {
return VK_NULL_HANDLE;
}
std::vector<VkDescriptorSet>& sets = itr->second;
auto set = sets.back();
sets.pop_back();
mUnusedCount--;
return set;
}
if (mSize + 1 > mCapacity) {
return VK_NULL_HANDLE;
}
// Creating a new set
VkDescriptorSetLayout layouts[1] = {vklayout};
VkDescriptorSetAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.pNext = nullptr,
.descriptorPool = mPool,
.descriptorSetCount = 1,
.pSetLayouts = layouts,
};
VkDescriptorSet vkSet;
UTILS_UNUSED VkResult result = vkAllocateDescriptorSets(mDevice, &allocInfo, &vkSet);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Failed to allocate descriptor set code=" << result << " size=" << mSize
<< " capacity=" << mCapacity << " count=" << mUnusedCount;
mSize++;
return vkSet;
}
void recycle(VkDescriptorSetLayout vklayout, VkDescriptorSet vkSet) {
// We are recycling - release the set back into the pool. Note that the
// vk handle has not changed, but we need to change the backend handle to allow
// for proper refcounting of resources referenced in this set.
auto itr = findSets(vklayout);
if (itr != mUnused.end()) {
itr->second.push_back(vkSet);
} else {
mUnused.push_back(std::make_pair(vklayout, std::vector<VkDescriptorSet> {vkSet}));
}
mUnusedCount++;
}
private:
using UnusedSets = std::pair<VkDescriptorSetLayout, std::vector<VkDescriptorSet>>;
using UnusedSetMap = std::vector<UnusedSets>;
inline UnusedSetMap::iterator findSets(VkDescriptorSetLayout vklayout) {
return std::find_if(mUnused.begin(), mUnused.end(), [vklayout](auto const& value) {
return value.first == vklayout;
});
}
VkDevice mDevice;
VkDescriptorPool mPool;
DescriptorCount const mCount;
uint16_t const mCapacity;
// Tracks the number of allocated descriptor sets.
uint16_t mSize;
// Tracks the number of in-use descriptor sets.
uint16_t mUnusedCount;
// This maps a layout to a list of descriptor sets allocated for that layout.
UnusedSetMap mUnused;
};
template<typename Key>
struct Equal {
bool operator()(Key const& k1, Key const& k2) const {
return 0 == memcmp((const void*) &k1, (const void*) &k2, sizeof(Key));
}
};
} // anonymous namespace
// This is an ever-expanding pool of sets where it
// 1. Keeps a list of smaller pools of different layout-dimensions.
// 2. Will add a pool if existing pool are not compatible with the requested layout o runs out.
class VulkanDescriptorSetCache::DescriptorInfinitePool {
private:
static constexpr uint16_t EXPECTED_SET_COUNT = 10;
static constexpr float SET_COUNT_GROWTH_FACTOR = 1.5;
public:
DescriptorInfinitePool(VkDevice device)
: mDevice(device) {}
VkDescriptorSet obtainSet(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
auto const vklayout = layout->getVkLayout();
DescriptorPool* sameTypePool = nullptr;
for (auto& pool: mPools) {
if (!pool->canAllocate(layout->count)) {
continue;
}
if (auto set = pool->obtainSet(vklayout); set != VK_NULL_HANDLE) {
return set;
}
if (!sameTypePool || sameTypePool->capacity() < pool->capacity()) {
sameTypePool = pool.get();
}
}
uint16_t capacity = EXPECTED_SET_COUNT;
if (sameTypePool) {
// Exponentially increase the size of the pool to ensure we don't hit this too often.
capacity = std::ceil(sameTypePool->capacity() * SET_COUNT_GROWTH_FACTOR);
}
// We need to increase the set of pools by one.
mPools.push_back(std::make_unique<DescriptorPool>(mDevice,
DescriptorCount::fromLayoutBitmask(layout->bitmask), capacity));
auto& pool = mPools.back();
auto ret = pool->obtainSet(vklayout);
assert_invariant(ret != VK_NULL_HANDLE && "failed to obtain a set?");
return ret;
}
void recycle(DescriptorCount const& count, VkDescriptorSetLayout vklayout,
VkDescriptorSet vkSet) {
for (auto& pool: mPools) {
if (!pool->canAllocate(count)) {
continue;
}
pool->recycle(vklayout, vkSet);
break;
}
}
private:
VkDevice mDevice;
std::vector<std::unique_ptr<DescriptorPool>> mPools;
};
VulkanDescriptorSetCache::VulkanDescriptorSetCache(VkDevice device,
fvkmemory::ResourceManager* resourceManager)
: mDevice(device),
mResourceManager(resourceManager),
mDescriptorPool(std::make_unique<DescriptorInfinitePool>(device)) {}
VulkanDescriptorSetCache::~VulkanDescriptorSetCache() = default;
void VulkanDescriptorSetCache::terminate() noexcept{
mDescriptorPool.reset();
}
// bind() is not really binding the set but just stashing until we have all the info
// (pipelinelayout).
void VulkanDescriptorSetCache::bind(uint8_t setIndex,
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
backend::DescriptorSetOffsetArray&& offsets) {
set->setOffsets(std::move(offsets));
mStashedSets[setIndex] = set;
}
void VulkanDescriptorSetCache::unbind(uint8_t setIndex) {
mStashedSets[setIndex] = {};
}
void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
VkPipelineLayout pipelineLayout, fvkutils::DescriptorSetMask const& setMask) {
// setMask indicates the set of descriptor sets the driver wants to bind, curMask is the
// actual set of sets that *needs* to be bound.
fvkutils::DescriptorSetMask curMask = setMask;
auto& updateSets = mStashedSets;
bool const pipelineLayoutIsSame = mLastBoundInfo.pipelineLayout == pipelineLayout;
if (pipelineLayoutIsSame) {
auto& lastBoundSets = mLastBoundInfo.boundSets;
setMask.forEachSetBit([&](size_t index) {
if (!updateSets[index] || updateSets[index] == lastBoundSets[index]) {
curMask.unset(index);
}
});
if (curMask.none() &&
mLastBoundInfo.setMask == setMask && mLastBoundInfo.boundSets == updateSets) {
return;
}
} else {
setMask.forEachSetBit([&](size_t index) {
if (!updateSets[index]) {
curMask.unset(index);
}
});
}
curMask.forEachSetBit([&updateSets, commands, pipelineLayout](size_t index) {
// This code actually binds the descriptor sets.
auto set = updateSets[index];
VkCommandBuffer const cmdbuffer = commands->buffer();
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index,
1, &set->getVkSet(), set->uniqueDynamicUboCount, set->getOffsets()->data());
commands->acquire(set);
});
mStashedSets = {};
mLastBoundInfo = {
pipelineLayout,
setMask,
updateSets,
};
}
void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanBufferObject> bufferObject,
VkDeviceSize offset, VkDeviceSize size) noexcept {
VkDescriptorBufferInfo const info = {
.buffer = bufferObject->buffer.getGpuBuffer(),
.offset = offset,
.range = size,
};
VkDescriptorType type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
if (set->dynamicUboMask.test(binding)) {
type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
}
VkWriteDescriptorSet const descriptorWrite = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.dstSet = set->getVkSet(),
.dstBinding = binding,
.descriptorCount = 1,
.descriptorType = type,
.pBufferInfo = &info,
};
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
set->acquire(bufferObject);
}
void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
VkSampler sampler) noexcept {
VkImageSubresourceRange range = texture->getPrimaryViewRange();
VkImageViewType const expectedType = texture->getViewType();
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
expectedType == VK_IMAGE_VIEW_TYPE_2D) {
// If the sampler is part of a mipmapped depth texture, where one of the level *can* be
// an attachment, then the range for this view has exactly one level and one layer.
range.levelCount = 1;
range.layerCount = 1;
}
VkDescriptorImageInfo info{
.sampler = sampler,
.imageView = texture->getView(range),
.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout()),
};
VkWriteDescriptorSet const descriptorWrite = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = nullptr,
.dstSet = set->getVkSet(),
.dstBinding = binding,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &info,
};
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
set->acquire(texture);
}
void VulkanDescriptorSetCache::updateInputAttachment(
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
VulkanAttachment const& attachment) noexcept {
// TOOD: fill this in.
}
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet(
Handle<HwDescriptorSet> handle, fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
auto const vkSet = getVkSet(layout);
auto const& count = layout->count;
auto const vklayout = layout->getVkLayout();
auto set = fvkmemory::resource_ptr<VulkanDescriptorSet>::make(mResourceManager, handle,
layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
[vkSet, count, vklayout, this](VulkanDescriptorSet*) {
// Note that mDescriptorPool could be gone due to terminate (when the backend shuts
// down).
if (mDescriptorPool) {
mDescriptorPool->recycle(count, vklayout, vkSet);
}
});
set->setVkSet(vkSet);
return set;
}
VkDescriptorSet VulkanDescriptorSetCache::getVkSet(
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
return mDescriptorPool->obtainSet(layout);
}
void VulkanDescriptorSetCache::manualRecyle(VulkanDescriptorSetLayout::Count const& count,
VkDescriptorSetLayout vklayout, VkDescriptorSet vkSet) {
mDescriptorPool->recycle(count, vklayout, vkSet);
}
void VulkanDescriptorSetCache::gc() { mStashedSets = {}; }
} // namespace filament::backend
@@ -1,103 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETCACHE_H
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETCACHE_H
#include "VulkanHandles.h"
#include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Definitions.h" // For DescriptorSetMask
#include <backend/DriverEnums.h>
#include <backend/Program.h>
#include <backend/TargetBufferInfo.h>
#include <utils/bitset.h>
#include <bluevk/BlueVK.h>
#include <tsl/robin_map.h>
#include <memory>
namespace filament::backend {
// Abstraction over the descriptor set pool.
class VulkanDescriptorSetCache {
public:
static constexpr uint8_t UNIQUE_DESCRIPTOR_SET_COUNT =
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT;
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
using DescriptorSetArray =
std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>, UNIQUE_DESCRIPTOR_SET_COUNT>;
VulkanDescriptorSetCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
~VulkanDescriptorSetCache();
void terminate() noexcept;
void updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
fvkmemory::resource_ptr<VulkanBufferObject> bufferObject, VkDeviceSize offset,
VkDeviceSize size) noexcept;
void updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept;
void updateInputAttachment(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
VulkanAttachment const& attachment) noexcept;
void bind(uint8_t setIndex, fvkmemory::resource_ptr<VulkanDescriptorSet> set,
backend::DescriptorSetOffsetArray&& offsets);
void unbind(uint8_t setIndex);
void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout,
fvkutils::DescriptorSetMask const& setMask);
fvkmemory::resource_ptr<VulkanDescriptorSet> createSet(Handle<HwDescriptorSet> handle,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
// This method is only meant to be used with external samplers (or internally within this
// class).
VkDescriptorSet getVkSet(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
// This method is only meant to be used with external samplers.
void manualRecyle(VulkanDescriptorSetLayout::Count const& count, VkDescriptorSetLayout vklayout,
VkDescriptorSet vkSet);
DescriptorSetArray const& getBoundSets() const { return mStashedSets; }
void gc();
private:
class DescriptorInfinitePool;
VkDevice mDevice;
fvkmemory::ResourceManager* mResourceManager;
std::unique_ptr<DescriptorInfinitePool> mDescriptorPool;
std::pair<VulkanAttachment, VkDescriptorImageInfo> mInputAttachment;
DescriptorSetArray mStashedSets = {};
struct {
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
fvkutils::DescriptorSetMask setMask;
DescriptorSetArray boundSets = {};
} mLastBoundInfo;
};
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETCACHE_H
@@ -1,169 +0,0 @@
/*
* 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 "VulkanDescriptorSetLayoutCache.h"
#include "VulkanHandles.h"
#include <utils/Hash.h>
namespace filament::backend {
namespace {
using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask;
template<typename Bitmask>
uint32_t appendBindings(VkDescriptorSetLayoutBinding* toBind, VkDescriptorType type,
Bitmask const& mask) {
uint32_t count = 0;
Bitmask alreadySeen;
mask.forEachSetBit([&](size_t index) {
VkShaderStageFlags stages = 0;
uint32_t binding = 0;
if (index < fvkutils::getFragmentStageShift<Bitmask>()) {
binding = (uint32_t) index;
stages |= VK_SHADER_STAGE_VERTEX_BIT;
auto fragIndex = index + fvkutils::getFragmentStageShift<Bitmask>();
if (mask.test(fragIndex)) {
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
alreadySeen.set(fragIndex);
}
} else if (!alreadySeen.test(index)) {
// We are in fragment stage bits
binding = (uint32_t) (index - fvkutils::getFragmentStageShift<Bitmask>());
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
}
if (stages) {
toBind[count++] = {
.binding = binding,
.descriptorType = type,
.descriptorCount = 1,
.stageFlags = stages,
};
}
});
return count;
}
uint32_t appendSamplerBindings(VkDescriptorSetLayoutBinding* toBind,
fvkutils::SamplerBitmask const& mask, fvkutils::SamplerBitmask const& external,
utils::FixedCapacityVector<VkSampler> const& immutableSamplers) {
using Bitmask = fvkutils::SamplerBitmask;
uint32_t count = 0;
Bitmask alreadySeen;
uint8_t immutableIndex = 0;
size_t const immutableSamplerCount = immutableSamplers.size();
mask.forEachSetBit([&](size_t index) {
VkShaderStageFlags stages = 0;
uint32_t binding = 0;
if (index < fvkutils::getFragmentStageShift<Bitmask>()) {
binding = (uint32_t) index;
stages |= VK_SHADER_STAGE_VERTEX_BIT;
auto fragIndex = index + fvkutils::getFragmentStageShift<Bitmask>();
if (mask.test(fragIndex)) {
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
alreadySeen.set(fragIndex);
}
} else if (!alreadySeen.test(index)) {
// We are in fragment stage bits
binding = (uint32_t) (index - fvkutils::getFragmentStageShift<Bitmask>());
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
}
if (stages) {
toBind[count++] = {
.binding = binding,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = 1,
.stageFlags = stages,
.pImmutableSamplers = external[index] && immutableSamplerCount > immutableIndex
? &immutableSamplers[immutableIndex++]
: nullptr,
};
}
});
return count;
}
uint64_t computeImmutableSamplerHash(utils::FixedCapacityVector<VkSampler> const& samplers) {
size_t const size = samplers.size();
if (size == 0) {
return 0;
} else if (size == 1) {
return (uint64_t) samplers[0];
}
return utils::hash::murmur3((uint32_t*) samplers.data(), samplers.size() * 2, 0);
}
} // anonymous namespace
VulkanDescriptorSetLayoutCache::VulkanDescriptorSetLayoutCache(VkDevice device,
fvkmemory::ResourceManager* resourceManager)
: mDevice(device),
mResourceManager(resourceManager) {}
VulkanDescriptorSetLayoutCache::~VulkanDescriptorSetLayoutCache() = default;
void VulkanDescriptorSetLayoutCache::terminate() noexcept {
for (auto& itr: mVkLayouts) {
vkDestroyDescriptorSetLayout(mDevice, itr.second, VKALLOC);
}
}
VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
VulkanDescriptorSetLayout::Bitmask const& bitmasks,
utils::FixedCapacityVector<VkSampler> immutableSamplers) {
LayoutKey key = {
.bitmask = bitmasks,
.immutableSamplerHash = computeImmutableSamplerHash(immutableSamplers),
};
if (auto itr = mVkLayouts.find(key); itr != mVkLayouts.end()) {
return itr->second;
}
VkDescriptorSetLayoutBinding toBind[VulkanDescriptorSetLayout::MAX_BINDINGS];
uint32_t count = 0;
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
bitmasks.dynamicUbo);
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bitmasks.ubo);
count += appendSamplerBindings(&toBind[count], bitmasks.sampler, bitmasks.externalSampler,
immutableSamplers);
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
bitmasks.inputAttachment);
assert_invariant(count != 0 && "Need at least one binding for descriptor set layout.");
VkDescriptorSetLayoutCreateInfo dlinfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.bindingCount = count,
.pBindings = toBind,
};
VkDescriptorSetLayout vklayout;
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout);
mVkLayouts[key] = vklayout;
return vklayout;
}
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> VulkanDescriptorSetLayoutCache::createLayout(
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info) {
auto layout = fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::make(mResourceManager, handle,
info);
layout->setVkLayout(getVkLayout(layout->bitmask));
return layout;
}
} // namespace filament::backend
@@ -1,75 +0,0 @@
/*
* 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.
*/
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETLAYOUTCACHE_H
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETLAYOUTCACHE_H
#include "VulkanHandles.h"
#include "vulkan/memory/ResourcePointer.h"
#include <backend/DriverEnums.h>
#include <backend/Program.h>
#include <backend/TargetBufferInfo.h>
#include <utils/bitset.h>
#include <utils/FixedCapacityVector.h>
#include <bluevk/BlueVK.h>
#include <tsl/robin_map.h>
namespace filament::backend {
class VulkanDescriptorSetLayoutCache {
public:
VulkanDescriptorSetLayoutCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
~VulkanDescriptorSetLayoutCache();
void terminate() noexcept;
// Just a wrapper around getVkLayout()
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> createLayout(
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info);
// This method is meant to be used with external samplers
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks,
utils::FixedCapacityVector<VkSampler> immutableSamplers = {});
private:
VkDevice mDevice;
fvkmemory::ResourceManager* mResourceManager;
struct LayoutKey {
// this describes the layout using bitset.
VulkanDescriptorSetLayout::Bitmask bitmask = {};
// number of immutable samplers can be arbitrary; so we hash them into 64-bit.
uint64_t immutableSamplerHash = 0;
};
static_assert(sizeof(LayoutKey) == 48);
using LayoutKeyHashFn = utils::hash::MurmurHashFn<LayoutKey>;
struct LayoutKeyEqual {
bool operator()(LayoutKey const& k1, LayoutKey const& k2) const {
return k1.bitmask == k2.bitmask && k1.immutableSamplerHash == k2.immutableSamplerHash;
}
};
tsl::robin_map<LayoutKey, VkDescriptorSetLayout, LayoutKeyHashFn, LayoutKeyEqual> mVkLayouts;
};
} // namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETLAYOUTCACHE_H
+35 -74
View File
@@ -17,6 +17,7 @@
#include "VulkanDriver.h" #include "VulkanDriver.h"
#include "CommandStreamDispatcher.h" #include "CommandStreamDispatcher.h"
#include "DataReshaper.h"
#include "SystraceProfile.h" #include "SystraceProfile.h"
#include "VulkanAsyncHandles.h" #include "VulkanAsyncHandles.h"
#include "VulkanBuffer.h" #include "VulkanBuffer.h"
@@ -25,17 +26,16 @@
#include "VulkanHandles.h" #include "VulkanHandles.h"
#include "VulkanMemory.h" #include "VulkanMemory.h"
#include "VulkanTexture.h" #include "VulkanTexture.h"
#include "vulkan/VulkanSamplerCache.h"
#include "vulkan/memory/ResourceManager.h" #include "vulkan/memory/ResourceManager.h"
#include "vulkan/memory/ResourcePointer.h" #include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Conversion.h" #include "vulkan/utils/Conversion.h"
#include "vulkan/utils/Definitions.h" #include "vulkan/utils/Definitions.h"
#include "vulkan/vulkan_core.h"
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <backend/platforms/VulkanPlatform.h> #include <backend/platforms/VulkanPlatform.h>
#include <utils/CString.h> #include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Panic.h> #include <utils/Panic.h>
#ifndef NDEBUG #ifndef NDEBUG
@@ -44,6 +44,8 @@
using namespace bluevk; using namespace bluevk;
using utils::FixedCapacityVector;
#if defined(__clang__) #if defined(__clang__)
// Vulkan functions often immediately dereference pointers, so it's fine to pass in a pointer // Vulkan functions often immediately dereference pointers, so it's fine to pass in a pointer
// to a stack-allocated variable. // to a stack-allocated variable.
@@ -214,12 +216,10 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mPipelineCache(mPlatform->getDevice()), mPipelineCache(mPlatform->getDevice()),
mStagePool(mAllocator, &mCommands), mStagePool(mAllocator, &mCommands),
mFramebufferCache(mPlatform->getDevice()), mFramebufferCache(mPlatform->getDevice()),
mYcbcrConversionCache(mPlatform->getDevice()),
mSamplerCache(mPlatform->getDevice()), mSamplerCache(mPlatform->getDevice()),
mBlitter(mPlatform->getPhysicalDevice(), &mCommands), mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
mReadPixels(mPlatform->getDevice()), mReadPixels(mPlatform->getDevice()),
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager), mDescriptorSetManager(mPlatform->getDevice(), &mResourceManager),
mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager),
mQueryManager(mPlatform->getDevice()), mQueryManager(mPlatform->getDevice()),
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported), mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
mStereoscopicType(driverConfig.stereoscopicType) { mStereoscopicType(driverConfig.stereoscopicType) {
@@ -327,10 +327,9 @@ void VulkanDriver::terminate() {
mStagePool.terminate(); mStagePool.terminate();
mPipelineCache.terminate(); mPipelineCache.terminate();
mFramebufferCache.terminate(); mFramebufferCache.reset();
mSamplerCache.terminate(); mSamplerCache.terminate();
mDescriptorSetLayoutCache.terminate(); mDescriptorSetManager.terminate();
mDescriptorSetCache.terminate();
mPipelineLayoutCache.terminate(); mPipelineLayoutCache.terminate();
// Before terminating ResourceManager, we must make sure all of the resource_ptrs have been unset. // Before terminating ResourceManager, we must make sure all of the resource_ptrs have been unset.
@@ -366,7 +365,7 @@ void VulkanDriver::collectGarbage() {
FVK_SYSTRACE_SCOPE(); FVK_SYSTRACE_SCOPE();
// Command buffers need to be submitted and completed before other resources can be gc'd. // Command buffers need to be submitted and completed before other resources can be gc'd.
mCommands.gc(); mCommands.gc();
mDescriptorSetCache.gc(); mDescriptorSetManager.clearHistory();
mStagePool.gc(); mStagePool.gc();
mFramebufferCache.gc(); mFramebufferCache.gc();
mPipelineCache.gc(); mPipelineCache.gc();
@@ -409,7 +408,7 @@ void VulkanDriver::updateDescriptorSetBuffer(
FVK_SYSTRACE_SCOPE(); FVK_SYSTRACE_SCOPE();
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh); auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
auto buffer = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh); auto buffer = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh);
mDescriptorSetCache.updateBuffer(set, binding, buffer, offset, size); mDescriptorSetManager.updateBuffer(set, binding, buffer, offset, size);
} }
void VulkanDriver::updateDescriptorSetTexture( void VulkanDriver::updateDescriptorSetTexture(
@@ -421,12 +420,8 @@ void VulkanDriver::updateDescriptorSetTexture(
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh); auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th); auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
// TODO: YcbcrConversion? VkSampler const vksampler = mSamplerCache.getSampler(params);
VulkanSamplerCache::Params cacheParams = { mDescriptorSetManager.updateSampler(set, binding, texture, vksampler);
.sampler = params,
};
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
} }
void VulkanDriver::flush(int) { void VulkanDriver::flush(int) {
@@ -559,45 +554,26 @@ void VulkanDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwText
texture.inc(); texture.inc();
} }
void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target, void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, backend::SamplerType target, backend::TextureFormat format,
uint32_t width, uint32_t height, backend::TextureUsage usage,
Platform::ExternalImageHandleRef externalImage) { Platform::ExternalImageHandleRef externalImage) {
FVK_SYSTRACE_SCOPE(); FVK_SYSTRACE_SCOPE();
auto const& metadata = mPlatform->getExternalImageMetadata(externalImage);
const auto& metadata = mPlatform->getExternalImageMetadata(externalImage);
if (metadata.isProtected) { if (metadata.isProtected) {
usage |= backend::TextureUsage::PROTECTED; usage |= backend::TextureUsage::PROTECTED;
} }
VkImageUsageFlags vkUsage = metadata.usage;
if (any(usage & TextureUsage::BLIT_SRC)) {
vkUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
if (any(usage & (TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE))) {
vkUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
assert_invariant(width == metadata.width); assert_invariant(width == metadata.width);
assert_invariant(height == metadata.height); assert_invariant(height == metadata.height);
assert_invariant(fvkutils::getVkFormat(format) == metadata.format); assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
VkMemoryPropertyFlags const requiredMemoryFlags = any(usage & TextureUsage::UPLOADABLE) const auto& data = mPlatform->createExternalImageData(externalImage, metadata);
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
uint32_t const memoryTypeIndex =
mContext.selectMemoryType(metadata.memoryTypeBits, requiredMemoryFlags);
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex != VK_MAX_MEMORY_TYPES)
<< "failed to find a valid memory type for external image memory.";
VkImage vkimg; auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th,
VkDeviceMemory deviceMemory; mPlatform->getDevice(), mAllocator, &mResourceManager, &mCommands, data.first, data.second, metadata.format,
std::tie(vkimg, deviceMemory) = 1, metadata.width, metadata.height, /*depth=*/1, usage, mStagePool);
mPlatform->createExternalImageData(externalImage, metadata, memoryTypeIndex, vkUsage);
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mContext,
mPlatform->getDevice(), mAllocator, &mResourceManager, &mCommands, vkimg, deviceMemory,
metadata.format, VK_NULL_HANDLE, metadata.samples, metadata.width, metadata.height,
metadata.layerCount, usage, mStagePool);
texture.inc(); texture.inc();
} }
@@ -606,14 +582,13 @@ void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::Sa
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* externalImage) { void* externalImage) {
FVK_SYSTRACE_SCOPE(); FVK_SYSTRACE_SCOPE();
assert_invariant(false && "Not supported in Vulkan backend");
// not supported in this backend // not supported in this backend
} }
void VulkanDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th, void VulkanDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, uint32_t plane) { void* image, uint32_t plane) {
assert_invariant(false && "Not supported in Vulkan backend");
} }
void VulkanDriver::importTextureR(Handle<HwTexture> th, intptr_t id, void VulkanDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
@@ -621,7 +596,6 @@ void VulkanDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage usage) { TextureUsage usage) {
// not supported in this backend // not supported in this backend
assert_invariant(false && "Not supported in Vulkan backend");
} }
void VulkanDriver::destroyTexture(Handle<HwTexture> th) { void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
@@ -787,7 +761,8 @@ void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
void VulkanDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh, void VulkanDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
backend::DescriptorSetLayout&& info) { backend::DescriptorSetLayout&& info) {
auto layout = mDescriptorSetLayoutCache.createLayout(dslh, std::move(info)); auto layout = resource_ptr<VulkanDescriptorSetLayout>::make(&mResourceManager, dslh, info);
mDescriptorSetManager.initVkLayout(layout);
layout.inc(); layout.inc();
} }
@@ -796,7 +771,7 @@ void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
FVK_SYSTRACE_SCOPE(); FVK_SYSTRACE_SCOPE();
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout = fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout =
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh); fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
auto set = mDescriptorSetCache.createSet(dsh, layout); auto set = mDescriptorSetManager.createSet(dsh, layout);
set.inc(); set.inc();
} }
@@ -1068,7 +1043,7 @@ bool VulkanDriver::isDepthStencilBlitSupported(TextureFormat format) {
formats.end(); formats.end();
} }
bool VulkanDriver::isProtectedTexturesSupported() { return isProtectedContentSupported(); } bool VulkanDriver::isProtectedTexturesSupported() { return false; }
bool VulkanDriver::isDepthClampSupported() { bool VulkanDriver::isDepthClampSupported() {
return mContext.isDepthClampSupported(); return mContext.isDepthClampSupported();
@@ -1145,28 +1120,14 @@ math::float2 VulkanDriver::getClipSpaceParams() {
} }
uint8_t VulkanDriver::getMaxDrawBuffers() { uint8_t VulkanDriver::getMaxDrawBuffers() {
return mContext.getPhysicalDeviceLimits().maxColorAttachments; return MRT::MIN_SUPPORTED_RENDER_TARGET_COUNT; // TODO: query real value
} }
size_t VulkanDriver::getMaxUniformBufferSize() { size_t VulkanDriver::getMaxUniformBufferSize() {
return mContext.getPhysicalDeviceLimits().maxUniformBufferRange; // TODO: return the actual size instead of hardcoded value
} // TODO: devices that return less than 32768 should be rejected. This represents only 3%
// of android devices.
size_t VulkanDriver::getMaxTextureSize(SamplerType type) { return 32768;
switch (type) {
case SamplerType::SAMPLER_2D:
return mContext.getPhysicalDeviceLimits().maxImageDimension2D;
case SamplerType::SAMPLER_3D:
return mContext.getPhysicalDeviceLimits().maxImageDimension3D;
case SamplerType::SAMPLER_CUBEMAP:
return mContext.getPhysicalDeviceLimits().maxImageDimensionCube;
default:
return mContext.getPhysicalDeviceLimits().maxImageDimension1D;
}
}
size_t VulkanDriver::getMaxArrayTextureLayers() {
return mContext.getPhysicalDeviceLimits().maxImageArrayLayers;
} }
void VulkanDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index, void VulkanDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index,
@@ -1505,7 +1466,7 @@ void VulkanDriver::nextSubpass(int) {
if (mCurrentRenderPass.params.subpassMask & 0x1) { if (mCurrentRenderPass.params.subpassMask & 0x1) {
VulkanAttachment& subpassInput = renderTarget->getColor0(); VulkanAttachment& subpassInput = renderTarget->getColor0();
mDescriptorSetCache.updateInputAttachment({}, subpassInput); mDescriptorSetManager.updateInputAttachment({}, subpassInput);
} }
} }
@@ -1523,7 +1484,7 @@ void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
swapChain->acquire(resized); swapChain->acquire(resized);
if (resized) { if (resized) {
mFramebufferCache.resetFramebuffers(); mFramebufferCache.reset();
} }
if (UTILS_LIKELY(mDefaultRenderTarget)) { if (UTILS_LIKELY(mDefaultRenderTarget)) {
@@ -1832,9 +1793,9 @@ void VulkanDriver::bindDescriptorSet(
backend::DescriptorSetOffsetArray&& offsets) { backend::DescriptorSetOffsetArray&& offsets) {
if (dsh) { if (dsh) {
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh); auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
mDescriptorSetCache.bind(setIndex, set, std::move(offsets)); mDescriptorSetManager.bind(setIndex, set, std::move(offsets));
} else { } else {
mDescriptorSetCache.unbind(setIndex); mDescriptorSetManager.unbind(setIndex);
} }
} }
@@ -1842,7 +1803,7 @@ void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
FVK_SYSTRACE_SCOPE(); FVK_SYSTRACE_SCOPE();
VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer(); VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer();
mDescriptorSetCache.commit(mCurrentRenderPass.commandBuffer, mDescriptorSetManager.commit(mCurrentRenderPass.commandBuffer,
mBoundPipeline.pipelineLayout, mBoundPipeline.pipelineLayout,
mBoundPipeline.descriptorSetMask); mBoundPipeline.descriptorSetMask);
+3 -7
View File
@@ -27,10 +27,8 @@
#include "VulkanSamplerCache.h" #include "VulkanSamplerCache.h"
#include "VulkanStagePool.h" #include "VulkanStagePool.h"
#include "VulkanQueryManager.h" #include "VulkanQueryManager.h"
#include "VulkanYcbcrConversionCache.h" #include "vulkan/caching/VulkanDescriptorSetManager.h"
#include "vulkan/VulkanDescriptorSetCache.h" #include "vulkan/caching/VulkanPipelineLayoutCache.h"
#include "vulkan/VulkanDescriptorSetLayoutCache.h"
#include "vulkan/VulkanPipelineLayoutCache.h"
#include "vulkan/memory/ResourceManager.h" #include "vulkan/memory/ResourceManager.h"
#include "vulkan/memory/ResourcePointer.h" #include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Definitions.h" #include "vulkan/utils/Definitions.h"
@@ -136,12 +134,10 @@ private:
VulkanPipelineCache mPipelineCache; VulkanPipelineCache mPipelineCache;
VulkanStagePool mStagePool; VulkanStagePool mStagePool;
VulkanFboCache mFramebufferCache; VulkanFboCache mFramebufferCache;
VulkanYcbcrConversionCache mYcbcrConversionCache;
VulkanSamplerCache mSamplerCache; VulkanSamplerCache mSamplerCache;
VulkanBlitter mBlitter; VulkanBlitter mBlitter;
VulkanReadPixels mReadPixels; VulkanReadPixels mReadPixels;
VulkanDescriptorSetLayoutCache mDescriptorSetLayoutCache; VulkanDescriptorSetManager mDescriptorSetManager;
VulkanDescriptorSetCache mDescriptorSetCache;
VulkanQueryManager mQueryManager; VulkanQueryManager mQueryManager;
// This is necessary for us to write to push constants after binding a pipeline. // This is necessary for us to write to push constants after binding a pipeline.
@@ -340,21 +340,15 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
return renderPass; return renderPass;
} }
void VulkanFboCache::resetFramebuffers() noexcept { void VulkanFboCache::reset() noexcept {
for (const auto& pair: mFramebufferCache) { for (auto pair : mFramebufferCache) {
mRenderPassRefCount[pair.first.renderPass]--; mRenderPassRefCount[pair.first.renderPass]--;
vkDestroyFramebuffer(mDevice, pair.second.handle, VKALLOC); vkDestroyFramebuffer(mDevice, pair.second.handle, VKALLOC);
} }
mFramebufferCache.clear(); mFramebufferCache.clear();
} for (auto pair : mRenderPassCache) {
void VulkanFboCache::terminate() noexcept {
resetFramebuffers();
for (const auto& pair: mRenderPassCache) {
vkDestroyRenderPass(mDevice, pair.second.handle, VKALLOC); vkDestroyRenderPass(mDevice, pair.second.handle, VKALLOC);
} }
mRenderPassRefCount.clear();
mRenderPassCache.clear(); mRenderPassCache.clear();
} }
+1 -4
View File
@@ -106,11 +106,8 @@ public:
// Evicts old unused Vulkan objects. Call this once per frame. // Evicts old unused Vulkan objects. Call this once per frame.
void gc() noexcept; void gc() noexcept;
// Frees all Framebuffer objects. Call this every time a the swapchain is resized
void resetFramebuffers() noexcept;
// Frees all Vulkan objects. Call this during shutdown before the device is destroyed. // Frees all Vulkan objects. Call this during shutdown before the device is destroyed.
void terminate() noexcept; void reset() noexcept;
private: private:
VkDevice mDevice; VkDevice mDevice;
@@ -30,7 +30,6 @@
#include <backend/platforms/VulkanPlatform.h> #include <backend/platforms/VulkanPlatform.h>
#include <utils/compiler.h> // UTILS_FALLTHROUGH
#include <utils/Panic.h> // ASSERT_POSTCONDITION #include <utils/Panic.h> // ASSERT_POSTCONDITION
using namespace bluevk; using namespace bluevk;
@@ -90,9 +89,8 @@ BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) {
} }
break; break;
} }
// TODO: properly handle external sampler
case DescriptorType::SAMPLER_EXTERNAL: case DescriptorType::SAMPLER_EXTERNAL:
fromStageFlags(binding.stageFlags, binding.binding, mask.externalSampler);
UTILS_FALLTHROUGH;
case DescriptorType::SAMPLER: { case DescriptorType::SAMPLER: {
fromStageFlags(binding.stageFlags, binding.binding, mask.sampler); fromStageFlags(binding.stageFlags, binding.binding, mask.sampler);
break; break;
+9 -24
View File
@@ -66,21 +66,18 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
// The bitmask representation of a set layout. // The bitmask representation of a set layout.
struct Bitmask { struct Bitmask {
// TODO: better utiltize the space below and use bitset instead.
fvkutils::UniformBufferBitmask ubo; // 8 bytes fvkutils::UniformBufferBitmask ubo; // 8 bytes
fvkutils::UniformBufferBitmask dynamicUbo; // 8 bytes fvkutils::UniformBufferBitmask dynamicUbo; // 8 bytes
fvkutils::SamplerBitmask sampler; // 8 bytes fvkutils::SamplerBitmask sampler; // 8 bytes
fvkutils::InputAttachmentBitmask inputAttachment; // 8 bytes fvkutils::InputAttachmentBitmask inputAttachment; // 8 bytes
// This is a subset of the bitmask.sampler field.
fvkutils::SamplerBitmask externalSampler; // 8 bytes
bool operator==(Bitmask const& right) const { bool operator==(Bitmask const& right) const {
return ubo == right.ubo && dynamicUbo == right.dynamicUbo && sampler == right.sampler && return ubo == right.ubo && dynamicUbo == right.dynamicUbo && sampler == right.sampler &&
inputAttachment == right.inputAttachment && inputAttachment == right.inputAttachment;
externalSampler == right.externalSampler;
} }
}; };
static_assert(sizeof(Bitmask) == 40); static_assert(sizeof(Bitmask) == 32);
// This is a convenience struct to quickly check layout compatibility in terms of descriptor set // This is a convenience struct to quickly check layout compatibility in terms of descriptor set
// pools. // pools.
@@ -122,16 +119,10 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
VulkanDescriptorSetLayout(DescriptorSetLayout const& layout); VulkanDescriptorSetLayout(DescriptorSetLayout const& layout);
// Note that we don't destroy the vklayout. This is done by the layout cache.
~VulkanDescriptorSetLayout() = default; ~VulkanDescriptorSetLayout() = default;
VkDescriptorSetLayout const& getVkLayout() const noexcept { return mVkLayout; } VkDescriptorSetLayout getVkLayout() const { return mVkLayout; }
void setVkLayout(VkDescriptorSetLayout vklayout) { mVkLayout = vklayout; }
// It is possible to have the layout switch out due to AHardwarebuffer (external image) format
// changes.
void setVkLayout(VkDescriptorSetLayout vklayout) noexcept { mVkLayout = vklayout; }
bool hasExternalSamplers() const noexcept { return bitmask.externalSampler.count() > 0; }
Bitmask const bitmask; Bitmask const bitmask;
Count const count; Count const count;
@@ -146,11 +137,12 @@ public:
// can use to repackage the vk handle. // can use to repackage the vk handle.
using OnRecycle = std::function<void(VulkanDescriptorSet*)>; using OnRecycle = std::function<void(VulkanDescriptorSet*)>;
VulkanDescriptorSet( VulkanDescriptorSet(VkDescriptorSet rawSet,
fvkutils::UniformBufferBitmask const& dynamicUboMask, fvkutils::UniformBufferBitmask const& dynamicUboMask,
uint8_t uniqueDynamicUboCount, uint8_t uniqueDynamicUboCount,
OnRecycle&& onRecycleFn) OnRecycle&& onRecycleFn)
: dynamicUboMask(dynamicUboMask), : vkSet(rawSet),
dynamicUboMask(dynamicUboMask),
uniqueDynamicUboCount(uniqueDynamicUboCount), uniqueDynamicUboCount(uniqueDynamicUboCount),
mOnRecycleFn(std::move(onRecycleFn)) {} mOnRecycleFn(std::move(onRecycleFn)) {}
@@ -160,13 +152,6 @@ public:
} }
} }
VkDescriptorSet const& getVkSet() const noexcept {
return mVkSet;
}
// Note that the only case where you'd set it more than once is with external images/samplers.
void setVkSet(VkDescriptorSet vkset) noexcept { mVkSet = vkset; }
void setOffsets(backend::DescriptorSetOffsetArray&& offsets) noexcept { void setOffsets(backend::DescriptorSetOffsetArray&& offsets) noexcept {
mOffsets = std::move(offsets); mOffsets = std::move(offsets);
} }
@@ -178,11 +163,11 @@ public:
void acquire(fvkmemory::resource_ptr<VulkanTexture> texture); void acquire(fvkmemory::resource_ptr<VulkanTexture> texture);
void acquire(fvkmemory::resource_ptr<VulkanBufferObject> buffer); void acquire(fvkmemory::resource_ptr<VulkanBufferObject> buffer);
VkDescriptorSet const vkSet;
fvkutils::UniformBufferBitmask const dynamicUboMask; fvkutils::UniformBufferBitmask const dynamicUboMask;
uint8_t const uniqueDynamicUboCount; uint8_t const uniqueDynamicUboCount;
private: private:
VkDescriptorSet mVkSet = VK_NULL_HANDLE;
backend::DescriptorSetOffsetArray mOffsets; backend::DescriptorSetOffsetArray mOffsets;
std::vector<fvkmemory::resource_ptr<fvkmemory::Resource>> mResources; std::vector<fvkmemory::resource_ptr<fvkmemory::Resource>> mResources;
OnRecycle mOnRecycleFn; OnRecycle mOnRecycleFn;
@@ -15,12 +15,15 @@
*/ */
#include "VulkanPipelineCache.h" #include "VulkanPipelineCache.h"
#include "VulkanMemory.h"
#include "caching/VulkanDescriptorSetManager.h"
#include <utils/Log.h> #include <utils/Log.h>
#include <utils/Panic.h> #include <utils/Panic.h>
#include "VulkanConstants.h" #include "VulkanConstants.h"
#include "VulkanHandles.h" #include "VulkanHandles.h"
#include "VulkanTexture.h"
#include "vulkan/utils/Conversion.h" #include "vulkan/utils/Conversion.h"
#if defined(__clang__) #if defined(__clang__)
@@ -35,12 +38,7 @@ using namespace bluevk;
namespace filament::backend { namespace filament::backend {
VulkanPipelineCache::VulkanPipelineCache(VkDevice device) VulkanPipelineCache::VulkanPipelineCache(VkDevice device)
: mDevice(device) { : mDevice(device) {}
VkPipelineCacheCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
};
bluevk::vkCreatePipelineCache(mDevice, &createInfo, VKALLOC, &mPipelineCache);
}
void VulkanPipelineCache::bindLayout(VkPipelineLayout layout) noexcept { void VulkanPipelineCache::bindLayout(VkPipelineLayout layout) noexcept {
mPipelineRequirements.layout = layout; mPipelineRequirements.layout = layout;
@@ -220,7 +218,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
PipelineCacheEntry cacheEntry = { PipelineCacheEntry cacheEntry = {
.lastUsed = mCurrentTime, .lastUsed = mCurrentTime,
}; };
VkResult error = vkCreateGraphicsPipelines(mDevice, mPipelineCache, 1, &pipelineCreateInfo, VkResult error = vkCreateGraphicsPipelines(mDevice, VK_NULL_HANDLE, 1, &pipelineCreateInfo,
VKALLOC, &cacheEntry.handle); VKALLOC, &cacheEntry.handle);
assert_invariant(error == VK_SUCCESS); assert_invariant(error == VK_SUCCESS);
if (error != VK_SUCCESS) { if (error != VK_SUCCESS) {
@@ -276,8 +274,6 @@ void VulkanPipelineCache::terminate() noexcept {
} }
mPipelines.clear(); mPipelines.clear();
mBoundPipeline = {}; mBoundPipeline = {};
vkDestroyPipelineCache(mDevice, mPipelineCache, VKALLOC);
} }
void VulkanPipelineCache::gc() noexcept { void VulkanPipelineCache::gc() noexcept {
@@ -18,18 +18,24 @@
#define TNT_FILAMENT_BACKEND_VULKANPIPELINECACHE_H #define TNT_FILAMENT_BACKEND_VULKANPIPELINECACHE_H
#include "VulkanCommands.h" #include "VulkanCommands.h"
#include "VulkanMemory.h"
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <backend/TargetBufferInfo.h> #include <backend/TargetBufferInfo.h>
#include "backend/Program.h"
#include <bluevk/BlueVK.h> #include <bluevk/BlueVK.h>
#include <utils/bitset.h> #include <utils/bitset.h>
#include <utils/compiler.h> #include <utils/compiler.h>
#include <utils/Hash.h> #include <utils/Hash.h>
#include <list>
#include <tsl/robin_map.h> #include <tsl/robin_map.h>
#include <type_traits> #include <type_traits>
#include <vector>
#include <unordered_map>
namespace filament::backend { namespace filament::backend {
@@ -198,10 +204,6 @@ private:
// Immutable state. // Immutable state.
VkDevice mDevice = VK_NULL_HANDLE; VkDevice mDevice = VK_NULL_HANDLE;
// Vuklan Driver pipeline cache handle. In the cases a pipeline has been evicted by the `gc`,
// recreating the same pipeline is cheaper, helping with frame stalling.
VkPipelineCache mPipelineCache = VK_NULL_HANDLE;
// Current requirements for the pipeline layout, pipeline, and descriptor sets. // Current requirements for the pipeline layout, pipeline, and descriptor sets.
PipelineKey mPipelineRequirements = {}; PipelineKey mPipelineRequirements = {};
@@ -14,9 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
#include "VulkanSamplerCache.h" #include "vulkan/VulkanSamplerCache.h"
#include "VulkanConstants.h"
#include "vulkan/utils/Conversion.h" #include "vulkan/utils/Conversion.h"
#include <utils/Panic.h> #include <utils/Panic.h>
@@ -25,37 +23,103 @@ using namespace bluevk;
namespace filament::backend { namespace filament::backend {
constexpr inline VkSamplerAddressMode getWrapMode(SamplerWrapMode mode) noexcept {
switch (mode) {
case SamplerWrapMode::REPEAT:
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case SamplerWrapMode::CLAMP_TO_EDGE:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case SamplerWrapMode::MIRRORED_REPEAT:
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
}
}
constexpr inline VkFilter getFilter(SamplerMinFilter filter) noexcept {
switch (filter) {
case SamplerMinFilter::NEAREST:
return VK_FILTER_NEAREST;
case SamplerMinFilter::LINEAR:
return VK_FILTER_LINEAR;
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
return VK_FILTER_NEAREST;
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
return VK_FILTER_LINEAR;
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
return VK_FILTER_NEAREST;
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
return VK_FILTER_LINEAR;
}
}
constexpr inline VkFilter getFilter(SamplerMagFilter filter) noexcept {
switch (filter) {
case SamplerMagFilter::NEAREST:
return VK_FILTER_NEAREST;
case SamplerMagFilter::LINEAR:
return VK_FILTER_LINEAR;
}
}
constexpr inline VkSamplerMipmapMode getMipmapMode(SamplerMinFilter filter) noexcept {
switch (filter) {
case SamplerMinFilter::NEAREST:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerMinFilter::LINEAR:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
}
}
constexpr inline float getMaxLod(SamplerMinFilter filter) noexcept {
switch (filter) {
case SamplerMinFilter::NEAREST:
case SamplerMinFilter::LINEAR:
// The Vulkan spec recommends a max LOD of 0.25 to "disable" mipmapping.
// See "Mapping of OpenGL to Vulkan filter modes" in the VK Spec.
return 0.25f;
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
return VK_LOD_CLAMP_NONE;
}
}
constexpr inline VkBool32 getCompareEnable(SamplerCompareMode mode) noexcept {
return mode == SamplerCompareMode::NONE ? VK_FALSE : VK_TRUE;
}
VulkanSamplerCache::VulkanSamplerCache(VkDevice device) VulkanSamplerCache::VulkanSamplerCache(VkDevice device)
: mDevice(device) {} : mDevice(device) {}
VkSampler VulkanSamplerCache::getSampler(Params params) noexcept { VkSampler VulkanSamplerCache::getSampler(SamplerParams params) noexcept {
auto iter = mCache.find(params); auto iter = mCache.find(params);
if (UTILS_LIKELY(iter != mCache.end())) { if (UTILS_LIKELY(iter != mCache.end())) {
return iter->second; return iter->second;
} }
VkSamplerYcbcrConversionInfo ycbcrConversion = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.conversion = params.conversion,
};
auto const& samplerParams = params.sampler;
VkSamplerCreateInfo samplerInfo { VkSamplerCreateInfo samplerInfo {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = params.conversion != VK_NULL_HANDLE ? &ycbcrConversion : VK_NULL_HANDLE, .magFilter = getFilter(params.filterMag),
.magFilter = fvkutils::getFilter(samplerParams.filterMag), .minFilter = getFilter(params.filterMin),
.minFilter = fvkutils::getFilter(samplerParams.filterMin), .mipmapMode = getMipmapMode(params.filterMin),
.mipmapMode = fvkutils::getMipmapMode(samplerParams.filterMin), .addressModeU = getWrapMode(params.wrapS),
.addressModeU = fvkutils::getWrapMode(samplerParams.wrapS), .addressModeV = getWrapMode(params.wrapT),
.addressModeV = fvkutils::getWrapMode(samplerParams.wrapT), .addressModeW = getWrapMode(params.wrapR),
.addressModeW = fvkutils::getWrapMode(samplerParams.wrapR), .anisotropyEnable = params.anisotropyLog2 == 0 ? 0u : 1u,
.anisotropyEnable = samplerParams.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE, .maxAnisotropy = (float)(1u << params.anisotropyLog2),
.maxAnisotropy = (float) (1u << samplerParams.anisotropyLog2), .compareEnable = getCompareEnable(params.compareMode),
.compareEnable = fvkutils::getCompareEnable(samplerParams.compareMode), .compareOp = fvkutils::getCompareOp(params.compareFunc),
.compareOp = fvkutils::getCompareOp(samplerParams.compareFunc),
.minLod = 0.0f, .minLod = 0.0f,
.maxLod = fvkutils::getMaxLod(samplerParams.filterMin), .maxLod = getMaxLod(params.filterMin),
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK, .borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE, .unnormalizedCoordinates = VK_FALSE
}; };
VkSampler sampler; VkSampler sampler;
VkResult result = vkCreateSampler(mDevice, &samplerInfo, VKALLOC, &sampler); VkResult result = vkCreateSampler(mDevice, &samplerInfo, VKALLOC, &sampler);
@@ -17,11 +17,8 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H #ifndef TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H
#define TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H #define TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H
#include <backend/DriverEnums.h> #include "VulkanContext.h"
#include <utils/Hash.h>
#include <bluevk/BlueVK.h>
#include <tsl/robin_map.h> #include <tsl/robin_map.h>
namespace filament::backend { namespace filament::backend {
@@ -29,28 +26,12 @@ namespace filament::backend {
// Simple manager for VkSampler objects. // Simple manager for VkSampler objects.
class VulkanSamplerCache { class VulkanSamplerCache {
public: public:
struct Params {
SamplerParams sampler = {};
uint32_t padding = 0;
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
};
static_assert(sizeof(Params) == 16);
explicit VulkanSamplerCache(VkDevice device); explicit VulkanSamplerCache(VkDevice device);
VkSampler getSampler(Params params) noexcept; VkSampler getSampler(SamplerParams params) noexcept;
void terminate() noexcept; void terminate() noexcept;
private: private:
VkDevice mDevice; VkDevice mDevice;
tsl::robin_map<SamplerParams, VkSampler, SamplerParams::Hasher, SamplerParams::EqualTo> mCache;
struct SamplerEqualTo {
bool operator()(Params lhs, Params rhs) const noexcept {
SamplerParams::EqualTo equal;
return equal(lhs.sampler, rhs.sampler) && lhs.conversion == rhs.conversion;
}
};
using SamplerHashFn = utils::hash::MurmurHashFn<Params>;
tsl::robin_map<Params, VkSampler, SamplerHashFn, SamplerEqualTo> mCache;
}; };
} // namespace filament::backend } // namespace filament::backend
@@ -32,7 +32,6 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
VulkanCommands* commands, VulkanStagePool& stagePool, void* nativeWindow, uint64_t flags, VulkanCommands* commands, VulkanStagePool& stagePool, void* nativeWindow, uint64_t flags,
VkExtent2D extent) VkExtent2D extent)
: mPlatform(platform), : mPlatform(platform),
mContext(context),
mResourceManager(resourceManager), mResourceManager(resourceManager),
mCommands(commands), mCommands(commands),
mAllocator(allocator), mAllocator(allocator),
@@ -78,16 +77,16 @@ void VulkanSwapChain::update() {
} }
for (auto const color: bundle.colors) { for (auto const color: bundle.colors) {
auto colorTexture = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager, auto colorTexture = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager,
mContext, device, mAllocator, mResourceManager, mCommands, color, VK_NULL_HANDLE, device, mAllocator, mResourceManager, mCommands, color, VK_NULL_HANDLE,
bundle.colorFormat, VK_NULL_HANDLE /*ycrcb */, 1, bundle.extent.width, bundle.colorFormat, 1, bundle.extent.width, bundle.extent.height, bundle.layerCount, colorUsage,
bundle.extent.height, bundle.layerCount, colorUsage, mStagePool); mStagePool);
mColors.push_back(colorTexture); mColors.push_back(colorTexture);
} }
mDepth = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager, mContext, device, mDepth = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager, device,
mAllocator, mResourceManager, mCommands, bundle.depth, VK_NULL_HANDLE, mAllocator, mResourceManager, mCommands, bundle.depth, VK_NULL_HANDLE,
bundle.depthFormat, VK_NULL_HANDLE /*ycrcb */, 1, bundle.extent.width, bundle.depthFormat, 1, bundle.extent.width, bundle.extent.height, bundle.layerCount, depthUsage,
bundle.extent.height, bundle.layerCount, depthUsage, mStagePool); mStagePool);
mExtent = bundle.extent; mExtent = bundle.extent;
mLayerCount = bundle.layerCount; mLayerCount = bundle.layerCount;
@@ -108,6 +107,11 @@ void VulkanSwapChain::present() {
mCommands->flush(); mCommands->flush();
// call the image ready wait function
if (mExplicitImageReadyWait != nullptr) {
mExplicitImageReadyWait(swapChain);
}
// We only present if it is not headless. No-op for headless. // We only present if it is not headless. No-op for headless.
if (!mHeadless) { if (!mHeadless) {
VkSemaphore const finishedDrawing = mCommands->acquireFinishedSignal(); VkSemaphore const finishedDrawing = mCommands->acquireFinishedSignal();
@@ -141,6 +145,7 @@ void VulkanSwapChain::acquire(bool& resized) {
VulkanPlatform::ImageSyncData imageSyncData; VulkanPlatform::ImageSyncData imageSyncData;
VkResult const result = mPlatform->acquire(swapChain, &imageSyncData); VkResult const result = mPlatform->acquire(swapChain, &imageSyncData);
mCurrentSwapIndex = imageSyncData.imageIndex; mCurrentSwapIndex = imageSyncData.imageIndex;
mExplicitImageReadyWait = imageSyncData.explicitImageReadyWait;
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
<< "Cannot acquire in swapchain. error=" << static_cast<int32_t>(result); << "Cannot acquire in swapchain. error=" << static_cast<int32_t>(result);
if (imageSyncData.imageReadySemaphore != VK_NULL_HANDLE) { if (imageSyncData.imageReadySemaphore != VK_NULL_HANDLE) {
@@ -19,7 +19,6 @@
#include "DriverBase.h" #include "DriverBase.h"
#include "VulkanConstants.h"
#include "VulkanContext.h" #include "VulkanContext.h"
#include "VulkanTexture.h" #include "VulkanTexture.h"
#include "vulkan/memory/Resource.h" #include "vulkan/memory/Resource.h"
@@ -29,6 +28,7 @@
#include <bluevk/BlueVK.h> #include <bluevk/BlueVK.h>
#include <utils/FixedCapacityVector.h> #include <utils/FixedCapacityVector.h>
#include <memory>
using namespace bluevk; using namespace bluevk;
@@ -49,9 +49,7 @@ struct VulkanSwapChain : public HwSwapChain, fvkmemory::Resource {
void present(); void present();
// Acquire a new image from the swapchain. If the image is not available it would wait until it void acquire(bool& reized);
// is.
void acquire(bool& resized);
fvkmemory::resource_ptr<VulkanTexture> getCurrentColor() const noexcept { fvkmemory::resource_ptr<VulkanTexture> getCurrentColor() const noexcept {
uint32_t const imageIndex = mCurrentSwapIndex; uint32_t const imageIndex = mCurrentSwapIndex;
@@ -85,7 +83,6 @@ private:
void update(); void update();
VulkanPlatform* mPlatform; VulkanPlatform* mPlatform;
VulkanContext const& mContext;
fvkmemory::ResourceManager* mResourceManager; fvkmemory::ResourceManager* mResourceManager;
VulkanCommands* mCommands; VulkanCommands* mCommands;
VmaAllocator mAllocator; VmaAllocator mAllocator;
@@ -101,6 +98,7 @@ private:
VkExtent2D mExtent; VkExtent2D mExtent;
uint32_t mLayerCount; uint32_t mLayerCount;
uint32_t mCurrentSwapIndex; uint32_t mCurrentSwapIndex;
std::function<void(Platform::SwapChain* handle)> mExplicitImageReadyWait = nullptr;
bool mAcquired; bool mAcquired;
bool mIsFirstRenderPass; bool mIsFirstRenderPass;
}; };
+189 -234
View File
@@ -15,7 +15,6 @@
*/ */
#include "VulkanCommands.h" #include "VulkanCommands.h"
#include "VulkanContext.h"
#include "VulkanMemory.h" #include "VulkanMemory.h"
#include "VulkanTexture.h" #include "VulkanTexture.h"
#include "vulkan/memory/ResourcePointer.h" #include "vulkan/memory/ResourcePointer.h"
@@ -55,10 +54,13 @@ VkComponentMapping composeSwizzle(VkComponentMapping const& prev, VkComponentMap
VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_B,
VK_COMPONENT_SWIZZLE_A, VK_COMPONENT_SWIZZLE_A,
}; };
auto const compose = [](VkComponentMapping const& prev,
VkComponentMapping const& next) -> VkComponentMapping { auto const compose = [](VkComponentSwizzle out, VkComponentMapping const& prev,
VkComponentSwizzle vals[4] = { next.r, next.g, next.b, next.a }; uint8_t channelIndex) {
for (auto& out: vals) { // We need to first change all identities to its equivalent channel.
if (out == VK_COMPONENT_SWIZZLE_IDENTITY) {
out = IDENTITY[channelIndex];
}
switch (out) { switch (out) {
case VK_COMPONENT_SWIZZLE_R: case VK_COMPONENT_SWIZZLE_R:
out = prev.r; out = prev.r;
@@ -72,44 +74,44 @@ VkComponentMapping composeSwizzle(VkComponentMapping const& prev, VkComponentMap
case VK_COMPONENT_SWIZZLE_A: case VK_COMPONENT_SWIZZLE_A:
out = prev.a; out = prev.a;
break; break;
// Do not modify the value
case VK_COMPONENT_SWIZZLE_IDENTITY: case VK_COMPONENT_SWIZZLE_IDENTITY:
case VK_COMPONENT_SWIZZLE_ZERO: case VK_COMPONENT_SWIZZLE_ZERO:
case VK_COMPONENT_SWIZZLE_ONE: case VK_COMPONENT_SWIZZLE_ONE:
return out;
// Below is not exposed in Vulkan's API, but needs to be there for compilation. // Below is not exposed in Vulkan's API, but needs to be there for compilation.
case VK_COMPONENT_SWIZZLE_MAX_ENUM: case VK_COMPONENT_SWIZZLE_MAX_ENUM:
break; break;
} }
// If the result correctly corresponds to the identity, just return identity.
if (IDENTITY[channelIndex] == out) {
return VK_COMPONENT_SWIZZLE_IDENTITY;
} }
return { vals[0], vals[1], vals[2], vals[3] }; return out;
}; };
auto const identityToChannel = [](VkComponentMapping const& mapping) -> VkComponentMapping { auto const identityToChannel = [](VkComponentSwizzle val, uint8_t channelIndex) {
VkComponentSwizzle vals[4] = { mapping.r, mapping.g, mapping.b, mapping.a }; if (val != VK_COMPONENT_SWIZZLE_IDENTITY) {
for (uint8_t i = 0; i < 4; i++) { return val;
if (vals[i] != VK_COMPONENT_SWIZZLE_IDENTITY) {
continue;
} }
vals[i] = IDENTITY[i]; return IDENTITY[channelIndex];
}
return { vals[0], vals[1], vals[2], vals[3] };
};
auto const channelToIdentity = [](VkComponentMapping const& mapping) -> VkComponentMapping {
VkComponentSwizzle vals[4] = { mapping.r, mapping.g, mapping.b, mapping.a };
for (uint8_t i = 0; i < 4; i++) {
if (IDENTITY[i] != vals[i]) {
continue;
}
vals[i] = VK_COMPONENT_SWIZZLE_IDENTITY;
}
return { vals[0], vals[1], vals[2], vals[3] };
}; };
// We make sure all all identities are mapped into respective channels so that actual channel // We make sure all all identities are mapped into respective channels so that actual channel
// mapping will be passed onto the output. // mapping will be passed onto the output.
VkComponentMapping const prevExplicit = identityToChannel(prev); VkComponentMapping const prevExplicit = {
VkComponentMapping const nextExplicit = identityToChannel(next); identityToChannel(prev.r, 0),
return channelToIdentity(compose(prevExplicit, nextExplicit)); identityToChannel(prev.g, 1),
identityToChannel(prev.b, 2),
identityToChannel(prev.a, 3),
};
// Note that the channel index corresponds to the VkComponentMapping struct layout.
return {
compose(next.r, prevExplicit, 0),
compose(next.g, prevExplicit, 1),
compose(next.b, prevExplicit, 2),
compose(next.a, prevExplicit, 3),
};
} }
inline VulkanLayout getDefaultLayoutImpl(TextureUsage usage) { inline VulkanLayout getDefaultLayoutImpl(TextureUsage usage) {
@@ -151,152 +153,36 @@ uint8_t getLayerCountFromDepth(uint32_t const depth) {
return getLayerCount(getSamplerTypeFromDepth(depth), depth); return getLayerCount(getSamplerTypeFromDepth(depth), depth);
} }
VkImageUsageFlags getUsage(VulkanContext const& context, uint8_t samples,
VkPhysicalDevice physicalDevice, VkFormat vkFormat, TextureUsage tusage) {
VkImageUsageFlags usage = {};
if (any(tusage & TextureUsage::BLIT_SRC)) {
usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
if (any(tusage & TextureUsage::BLIT_DST)) {
usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
// Determine if we can use the transient usage flag combined with lazily allocated memory.
const bool useTransientAttachment =
// Lazily allocated memory is available.
context.isLazilyAllocatedMemorySupported() &&
// Usage consists of attachment flags only.
none(tusage & ~TextureUsage::ALL_ATTACHMENTS) &&
// Usage contains at least one attachment flag.
any(tusage & TextureUsage::ALL_ATTACHMENTS) &&
// Depth resolve cannot use transient attachment because it uses a custom shader.
// TODO: see VulkanDriver::isDepthStencilResolveSupported() to know when to remove this
// restriction.
// Note that the custom shader does not resolve stencil. We do need to move to vk 1.2
// and above to be able to support stencil resolve (along with depth).
!(any(tusage & TextureUsage::DEPTH_ATTACHMENT) && samples > 1);
const VkImageUsageFlags transientFlag =
useTransientAttachment ? VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT : 0U;
if (any(tusage & TextureUsage::SAMPLEABLE)) {
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
if (physicalDevice != VK_NULL_HANDLE) {
// Validate that the format is actually sampleable.
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(physicalDevice, vkFormat, &props);
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << mState->mVkFormat << " is not "
"sampleable with optimal tiling." << utils::io::endl;
}
}
#endif
usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
}
if (any(tusage & TextureUsage::COLOR_ATTACHMENT)) {
usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | transientFlag;
if (any(tusage & TextureUsage::SUBPASS_INPUT)) {
usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
}
}
if (any(tusage & TextureUsage::STENCIL_ATTACHMENT)) {
usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transientFlag;
}
if (any(tusage & TextureUsage::UPLOADABLE)) {
usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
if (any(tusage & TextureUsage::DEPTH_ATTACHMENT)) {
usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transientFlag;
// Depth resolves uses a custom shader and therefore needs to be sampleable.
if (samples > 1) {
usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
}
}
return usage;
}
} // anonymous namespace } // anonymous namespace
VulkanTextureState::VulkanTextureState(VulkanStagePool& stagePool, VulkanCommands* commands, VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator,
VmaAllocator allocator, VkDevice device, VkImage image, VkDeviceMemory deviceMemory, VulkanCommands* commands, VulkanStagePool& stagePool, VkFormat format,
VkFormat format, VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VulkanLayout defaultLayout,
VkSamplerYcbcrConversion ycbcrConversion, bool isExternalFormat, VkImageUsageFlags usage,
bool isProtected) bool isProtected)
: mStagePool(stagePool), : mVkFormat(format),
mCommands(commands),
mAllocator(allocator),
mDevice(device),
mTextureImage(image),
mTextureImageMemory(deviceMemory),
mVkFormat(format),
mViewType(viewType), mViewType(viewType),
mFullViewRange{fvkutils::getImageAspect(format), 0, levels, 0, layerCount}, mFullViewRange{fvkutils::getImageAspect(format), 0, levels, 0, layerCount},
mYcbcr{ ycbcrConversion, isExternalFormat }, mDefaultLayout(defaultLayout),
mDefaultLayout(getDefaultLayoutImpl(usage)), mIsProtected(isProtected),
mUsage(usage), mStagePool(stagePool),
mIsProtected(isProtected) {} mDevice(device),
mAllocator(allocator),
mCommands(commands),
mIsTransientAttachment(false) {}
VulkanTextureState::~VulkanTextureState() { // Constructor for internally passed VkImage
if (mTextureImageMemory != VK_NULL_HANDLE) { VulkanTexture::VulkanTexture(VkDevice device, VmaAllocator allocator,
vkDestroyImage(mDevice, mTextureImage, VKALLOC);
vkFreeMemory(mDevice, mTextureImageMemory, VKALLOC);
}
clearCachedImageViews();
}
void VulkanTextureState::clearCachedImageViews() noexcept {
for (auto entry: mCachedImageViews) {
vkDestroyImageView(mDevice, entry.second, VKALLOC);
}
mCachedImageViews.clear();
}
VkImageView VulkanTextureState::getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
VkComponentMapping swizzle) {
ImageViewKey const key{ range, viewType, swizzle };
if (auto iter = mCachedImageViews.find(key); iter != mCachedImageViews.end()) {
return iter->second;
}
VkSamplerYcbcrConversionInfo conversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.conversion = mYcbcr.conversion,
};
VkImageViewCreateInfo viewInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = mYcbcr.conversion != VK_NULL_HANDLE ? &conversionInfo : nullptr,
.flags = 0,
.image = mTextureImage,
.viewType = viewType,
.format = mYcbcr.isExternalFormat ? VK_FORMAT_UNDEFINED : mVkFormat,
.components = swizzle,
.subresourceRange = range,
};
VkImageView imageView;
vkCreateImageView(mDevice, &viewInfo, VKALLOC, &imageView);
mCachedImageViews.emplace(key, imageView);
return imageView;
}
// Constructor for internally passed VkImage - including swapchain images and external images.
VulkanTexture::VulkanTexture(VulkanContext const& context, VkDevice device, VmaAllocator allocator,
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image, fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image,
VkDeviceMemory memory, VkFormat format, VkSamplerYcbcrConversion conversion, VkDeviceMemory memory, VkFormat format, uint8_t samples, uint32_t width,
uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage tusage, uint32_t height, uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool)
VulkanStagePool& stagePool) : HwTexture(getSamplerTypeFromDepth(depth), 1, samples, width, height, depth, TextureFormat::UNUSED,
: HwTexture(getSamplerTypeFromDepth(depth), 1, samples, width, height, depth, tusage),
TextureFormat::UNUSED, tusage), mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, device,
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, stagePool, allocator, commands, stagePool, format, fvkutils::getViewType(SamplerType::SAMPLER_2D),
commands, allocator, device, image, memory, format, /*mipLevels=*/1, getLayerCountFromDepth(depth), getDefaultLayoutImpl(tusage), any(usage & TextureUsage::PROTECTED))) {
fvkutils::getViewType(SamplerType::SAMPLER_2D), mState->mTextureImage = image;
/*mipLevels=*/1, getLayerCountFromDepth(depth), conversion, mState->mTextureImageMemory = memory;
/*isExternalFormat=*/false,
getUsage(context, samples, VK_NULL_HANDLE, format, tusage),
any(usage & TextureUsage::PROTECTED))) {
mPrimaryViewRange = mState->mFullViewRange; mPrimaryViewRange = mState->mFullViewRange;
} }
@@ -306,19 +192,21 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, SamplerType target, fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, SamplerType target,
uint8_t levels, TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h, uint8_t levels, TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h,
uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool) 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),
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, device,
allocator, commands, stagePool, fvkutils::getVkFormat(tformat),
fvkutils::getViewType(target), levels, getLayerCount(target, depth),
VulkanLayout::UNDEFINED, any(usage & TextureUsage::PROTECTED))) {
// Create an appropriately-sized device-only VkImage, but do not fill it yet. // 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);
VkImageCreateInfo imageInfo{ VkImageCreateInfo imageInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = target == SamplerType::SAMPLER_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D, .imageType = target == SamplerType::SAMPLER_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D,
.format = vkFormat, .format = mState->mVkFormat,
.extent = {w, h, depth}, .extent = {w, h, depth},
.mipLevels = levels, .mipLevels = levels,
.arrayLayers = 1, .arrayLayers = 1,
.tiling = VK_IMAGE_TILING_OPTIMAL, .tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = getUsage(context, samples, physicalDevice, vkFormat, tusage), .usage = 0,
}; };
if (target == SamplerType::SAMPLER_3D && any(tusage & TextureUsage::ALL_ATTACHMENTS)) { if (target == SamplerType::SAMPLER_3D && any(tusage & TextureUsage::ALL_ATTACHMENTS)) {
if (context.isImageView2DOn3DImageSupported()) { if (context.isImageView2DOn3DImageSupported()) {
@@ -348,17 +236,72 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.arrayLayers = depth * 6; imageInfo.arrayLayers = depth * 6;
imageInfo.extent.depth = 1; imageInfo.extent.depth = 1;
} }
if (isProtected) { if (any(usage & TextureUsage::PROTECTED)) {
imageInfo.flags |= VK_IMAGE_CREATE_PROTECTED_BIT; imageInfo.flags |= VK_IMAGE_CREATE_PROTECTED_BIT;
} }
if (any(usage & TextureUsage::BLIT_SRC)) {
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
if (any(usage & TextureUsage::BLIT_DST)) {
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
// Determine if we can use the transient usage flag combined with lazily allocated memory.
const bool useTransientAttachment =
// Lazily allocated memory is available.
context.isLazilyAllocatedMemorySupported() &&
// Usage consists of attachment flags only.
none(tusage & ~TextureUsage::ALL_ATTACHMENTS) &&
// Usage contains at least one attachment flag.
any(tusage & TextureUsage::ALL_ATTACHMENTS);
mState->mIsTransientAttachment = useTransientAttachment;
const VkImageUsageFlags transientFlag =
useTransientAttachment ? VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT : 0U;
if (any(usage & TextureUsage::SAMPLEABLE)) {
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
// Validate that the format is actually sampleable.
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(physicalDevice, mState->mVkFormat, &props);
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << mState->mVkFormat << " is not "
"sampleable with optimal tiling." << utils::io::endl;
}
#endif
imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
}
if (any(usage & TextureUsage::COLOR_ATTACHMENT)) {
imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | transientFlag;
if (any(usage & TextureUsage::SUBPASS_INPUT)) {
imageInfo.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
}
}
if (any(usage & TextureUsage::STENCIL_ATTACHMENT)) {
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transientFlag;
}
if (any(usage & TextureUsage::UPLOADABLE)) {
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
if (any(usage & TextureUsage::DEPTH_ATTACHMENT)) {
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transientFlag;
// Depth resolves uses a custom shader and therefore needs to be sampleable.
if (samples > 1) {
imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
}
}
// Constrain the sample count according to the sample count masks in VkPhysicalDeviceProperties. // Constrain the sample count according to the sample count masks in VkPhysicalDeviceProperties.
// Note that VulkanRenderTarget holds a single MSAA count, so we play it safe if this is used as // Note that VulkanRenderTarget holds a single MSAA count, so we play it safe if this is used as
// any kind of attachment (color or depth). // any kind of attachment (color or depth).
auto const& limits = context.getPhysicalDeviceLimits(); const auto& limits = context.getPhysicalDeviceLimits();
if (imageInfo.usage & VK_IMAGE_USAGE_SAMPLED_BIT) { if (imageInfo.usage & VK_IMAGE_USAGE_SAMPLED_BIT) {
samples = fvkutils::reduceSampleCount(samples, samples = fvkutils::reduceSampleCount(samples,
fvkutils::isVkDepthFormat(vkFormat) fvkutils::isVkDepthFormat(mState->mVkFormat)
? limits.sampledImageDepthSampleCounts ? limits.sampledImageDepthSampleCounts
: limits.sampledImageColorSampleCounts); : limits.sampledImageColorSampleCounts);
} }
@@ -372,36 +315,33 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
this->samples = samples; this->samples = samples;
imageInfo.samples = (VkSampleCountFlagBits) samples; imageInfo.samples = (VkSampleCountFlagBits) samples;
VkImage textureImage; VkResult result = vkCreateImage(mState->mDevice, &imageInfo, VKALLOC, &mState->mTextureImage);
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &textureImage);
if (result != VK_SUCCESS || FVK_ENABLED(FVK_DEBUG_TEXTURE)) { if (result != VK_SUCCESS || FVK_ENABLED(FVK_DEBUG_TEXTURE)) {
FVK_LOGD << "vkCreateImage: " FVK_LOGD << "vkCreateImage: "
<< "image = " << textureImage << ", " << "image = " << mState->mTextureImage << ", "
<< "result = " << result << ", " << "result = " << result << ", "
<< "handle = " << utils::io::hex << textureImage << utils::io::dec << ", " << "handle = " << utils::io::hex << mState->mTextureImage << utils::io::dec << ", "
<< "extent = " << w << "x" << h << "x"<< depth << ", " << "extent = " << w << "x" << h << "x"<< depth << ", "
<< "mipLevels = " << int(levels) << ", " << "mipLevels = " << int(levels) << ", "
<< "TextureUsage = " << static_cast<int>(tusage) << ", " << "TextureUsage = " << static_cast<int>(usage) << ", "
<< "usage = " << imageInfo.usage << ", " << "usage = " << imageInfo.usage << ", "
<< "samples = " << imageInfo.samples << ", " << "samples = " << imageInfo.samples << ", "
<< "type = " << imageInfo.imageType << ", " << "type = " << imageInfo.imageType << ", "
<< "flags = " << imageInfo.flags << ", " << "flags = " << imageInfo.flags << ", "
<< "target = " << static_cast<int>(target) <<", " << "target = " << static_cast<int>(target) <<", "
<< "format = " << vkFormat << utils::io::endl; << "format = " << mState->mVkFormat << utils::io::endl;
} }
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create image." FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create image."
<< " error=" << static_cast<int32_t>(result); << " error=" << static_cast<int32_t>(result);
// Allocate memory for the VkImage and bind it. // Allocate memory for the VkImage and bind it.
VkMemoryRequirements memReqs; VkMemoryRequirements memReqs = {};
vkGetImageMemoryRequirements(device, textureImage, &memReqs); vkGetImageMemoryRequirements(mState->mDevice, mState->mTextureImage, &memReqs);
bool const useTransientAttachment = imageInfo.usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; const VkFlags requiredMemoryFlags =
VkFlags const requiredMemoryFlags =
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
(useTransientAttachment ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0U) | (useTransientAttachment ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0U) |
(isProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U); (mState->mIsProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
uint32_t memoryTypeIndex uint32_t memoryTypeIndex
= context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags); = context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags);
@@ -413,22 +353,21 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
.allocationSize = memReqs.size, .allocationSize = memReqs.size,
.memoryTypeIndex = memoryTypeIndex, .memoryTypeIndex = memoryTypeIndex,
}; };
VkDeviceMemory textureImageMemory; result = vkAllocateMemory(mState->mDevice, &allocInfo, nullptr, &mState->mTextureImageMemory);
result = vkAllocateMemory(device, &allocInfo, nullptr, &textureImageMemory);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to allocate image memory." FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to allocate image memory."
<< " error=" << static_cast<int32_t>(result); << " error=" << static_cast<int32_t>(result);
result = vkBindImageMemory(device, textureImage, textureImageMemory, 0); result = vkBindImageMemory(mState->mDevice, mState->mTextureImage, mState->mTextureImageMemory,
0);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to bind image." FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to bind image."
<< " error=" << static_cast<int32_t>(result); << " error=" << static_cast<int32_t>(result);
mState = fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, stagePool,
commands, allocator, device, textureImage, textureImageMemory, vkFormat,
fvkutils::getViewType(target), levels, getLayerCount(target, depth),
VK_NULL_HANDLE /* ycbcrConversion */, false /*isExternalFormat*/, imageInfo.usage,
isProtected);
// Spec out the "primary" VkImageView that shaders use to sample from the image. // Spec out the "primary" VkImageView that shaders use to sample from the image.
mPrimaryViewRange = mState->mFullViewRange; mPrimaryViewRange = mState->mFullViewRange;
// Go ahead and create the primary image view.
getImageView(mPrimaryViewRange, mState->mViewType, mSwizzle);
mState->mDefaultLayout = getDefaultLayoutImpl(imageInfo.usage);
} }
// Constructor for creating a texture view // Constructor for creating a texture view
@@ -437,8 +376,8 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
fvkmemory::resource_ptr<VulkanTexture> src, uint8_t baseLevel, fvkmemory::resource_ptr<VulkanTexture> src, uint8_t baseLevel,
uint8_t levelCount) uint8_t levelCount)
: HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth, : HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth,
src->format, src->usage), src->format, src->usage) {
mState(src->mState) { mState = src->mState;
mPrimaryViewRange = src->mPrimaryViewRange; mPrimaryViewRange = src->mPrimaryViewRange;
mPrimaryViewRange.baseMipLevel = src->mPrimaryViewRange.baseMipLevel + baseLevel; mPrimaryViewRange.baseMipLevel = src->mPrimaryViewRange.baseMipLevel + baseLevel;
mPrimaryViewRange.levelCount = levelCount; mPrimaryViewRange.levelCount = levelCount;
@@ -449,10 +388,21 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands, VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
fvkmemory::resource_ptr<VulkanTexture> src, VkComponentMapping swizzle) fvkmemory::resource_ptr<VulkanTexture> src, VkComponentMapping swizzle)
: HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth, : HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth,
src->format, src->usage), src->format, src->usage) {
mState(src->mState), mState = src->mState;
mPrimaryViewRange(src->mPrimaryViewRange), mPrimaryViewRange = src->mPrimaryViewRange;
mSwizzle(composeSwizzle(src->mSwizzle, swizzle)) {} mSwizzle = composeSwizzle(src->mSwizzle, swizzle);
}
VulkanTextureState::~VulkanTextureState() {
if (mTextureImageMemory != VK_NULL_HANDLE) {
vkDestroyImage(mDevice, mTextureImage, VKALLOC);
vkFreeMemory(mDevice, mTextureImageMemory, VKALLOC);
}
for (auto entry: mCachedImageViews) {
vkDestroyImageView(mDevice, entry.second, VKALLOC);
}
}
void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height, void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height,
uint32_t depth, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t miplevel) { uint32_t depth, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t miplevel) {
@@ -559,20 +509,20 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, u
commands.acquire(fvkmemory::resource_ptr<VulkanTexture>::cast(this)); commands.acquire(fvkmemory::resource_ptr<VulkanTexture>::cast(this));
// TODO: support blit-based format conversion for 3D images and cubemaps. // TODO: support blit-based format conversion for 3D images and cubemaps.
constexpr int layer = 0; const int layer = 0;
VkOffset3D const rect[2] { {0, 0, 0}, {int32_t(width), int32_t(height), 1} }; const VkOffset3D rect[2] { {0, 0, 0}, {int32_t(width), int32_t(height), 1} };
VkImageAspectFlags const aspect = getImageAspect(); const VkImageAspectFlags aspect = getImageAspect();
VkImageBlit const blitRegions[1] = {{ const VkImageBlit blitRegions[1] = {{
.srcSubresource = { aspect, 0, 0, 1 }, .srcSubresource = { aspect, 0, 0, 1 },
.srcOffsets = { rect[0], rect[1] }, .srcOffsets = { rect[0], rect[1] },
.dstSubresource = { aspect, uint32_t(miplevel), layer, 1 }, .dstSubresource = { aspect, uint32_t(miplevel), layer, 1 },
.dstOffsets = { rect[0], rect[1] } .dstOffsets = { rect[0], rect[1] }
}}; }};
VkImageSubresourceRange const range = { aspect, miplevel, 1, layer, 1 }; const VkImageSubresourceRange range = { aspect, miplevel, 1, layer, 1 };
VulkanLayout const newLayout = VulkanLayout::TRANSFER_DST; VulkanLayout const newLayout = VulkanLayout::TRANSFER_DST;
VulkanLayout const oldLayout = getLayout(layer, miplevel); VulkanLayout const oldLayout = getLayout(layer, miplevel);
@@ -588,21 +538,41 @@ VulkanLayout VulkanTexture::getDefaultLayout() const {
return mState->mDefaultLayout; return mState->mDefaultLayout;
} }
VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange const& range, VkImageViewType type) { VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange range) {
assert_invariant(mState->mYcbcr.conversion == VK_NULL_HANDLE && range.levelCount = 1;
"We are not yet supporting external image as attachments."); range.layerCount = 1;
if (type == VK_IMAGE_VIEW_TYPE_2D) { return getImageView(range, VK_IMAGE_VIEW_TYPE_2D, {});
VkImageSubresourceRange copy = range;
copy.levelCount = 1;
copy.layerCount = 1;
return mState->getImageView(copy, type, {});
} else {
return mState->getImageView(range, type, {});
}
} }
VkImageView VulkanTexture::getView(VkImageSubresourceRange const& range) { VkImageView VulkanTexture::getMultiviewAttachmentView(VkImageSubresourceRange range) {
return mState->getImageView(range, mState->mViewType, mSwizzle); return getImageView(range, VK_IMAGE_VIEW_TYPE_2D_ARRAY, {});
}
VkImageView VulkanTexture::getViewForType(VkImageSubresourceRange const& range, VkImageViewType type) {
return getImageView(range, type, mSwizzle);
}
VkImageView VulkanTexture::getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
VkComponentMapping swizzle) {
VulkanTextureState::ImageViewKey const key{ range, viewType, swizzle };
auto iter = mState->mCachedImageViews.find(key);
if (iter != mState->mCachedImageViews.end()) {
return iter->second;
}
VkImageViewCreateInfo viewInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.image = mState->mTextureImage,
.viewType = viewType,
.format = mState->mVkFormat,
.components = swizzle,
.subresourceRange = range,
};
VkImageView imageView;
vkCreateImageView(mState->mDevice, &viewInfo, VKALLOC, &imageView);
mState->mCachedImageViews.emplace(key, imageView);
return imageView;
} }
VkImageAspectFlags VulkanTexture::getImageAspect() const { VkImageAspectFlags VulkanTexture::getImageAspect() const {
@@ -764,21 +734,6 @@ void VulkanTexture::setLayout(VkImageSubresourceRange const& range, VulkanLayout
} }
} }
void VulkanTexture::setYcbcrConversion(VkSamplerYcbcrConversion conversion, bool isExternalFormat) {
// Note that this comparison is valid because we only ever create VkSamplerYcbcrConversion from
// a cache. So for each set of parameters, there is exactly one conversion (similar to
// samplers).
VulkanTextureState::Ycbcr ycbcr = {
.conversion = conversion,
.isExternalFormat = isExternalFormat,
};
if (mState->mYcbcr != ycbcr) {
mState->mYcbcr = ycbcr;
mState->clearCachedImageViews();
}
}
VulkanLayout VulkanTexture::getLayout(uint32_t layer, uint32_t level) const { VulkanLayout VulkanTexture::getLayout(uint32_t layer, uint32_t level) const {
assert_invariant(level <= 0xffff && layer <= 0xffff); assert_invariant(level <= 0xffff && layer <= 0xffff);
const uint32_t key = (layer << 16) | level; const uint32_t key = (layer << 16) | level;
+37 -56
View File
@@ -19,10 +19,7 @@
#include "DriverBase.h" #include "DriverBase.h"
#include "VulkanCommands.h" #include "VulkanBuffer.h"
#include "VulkanConstants.h"
#include "VulkanMemory.h"
#include "VulkanStagePool.h"
#include "vulkan/memory/Resource.h" #include "vulkan/memory/Resource.h"
#include "vulkan/memory/ResourcePointer.h" #include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Image.h" #include "vulkan/utils/Image.h"
@@ -34,14 +31,10 @@
namespace filament::backend { namespace filament::backend {
struct VulkanTexture;
struct VulkanTextureState : public fvkmemory::Resource { struct VulkanTextureState : public fvkmemory::Resource {
VulkanTextureState(VulkanStagePool& stagePool, VulkanCommands* commands, VmaAllocator allocator, VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
VkDevice device, VkImage image, VkDeviceMemory deviceMemory, VkFormat format, VulkanStagePool& stagePool, VkFormat format, VkImageViewType viewType, uint8_t levels,
VkImageViewType viewType, uint8_t levels, uint8_t layerCount, uint8_t layerCount, VulkanLayout defaultLayout, bool isProtected);
VkSamplerYcbcrConversion ycbcrConversion, bool isExternalFormat,
VkImageUsageFlags usage, bool isProtected);
~VulkanTextureState(); ~VulkanTextureState();
@@ -64,50 +57,29 @@ struct VulkanTextureState : public fvkmemory::Resource {
// No implicit padding allowed due to it being a hash key. // No implicit padding allowed due to it being a hash key.
static_assert(sizeof(ImageViewKey) == 40); static_assert(sizeof(ImageViewKey) == 40);
VkImageView getImageView(VkImageSubresourceRange range, VkImageViewType viewType, using ImageViewHash = utils::hash::MurmurHashFn<ImageViewKey>;
VkComponentMapping swizzle);
private:
void clearCachedImageViews() noexcept;
VulkanStagePool& mStagePool;
VulkanCommands* const mCommands;
VmaAllocator const mAllocator;
VkDevice const mDevice;
// The texture with the sidecar owns the sidecar. // The texture with the sidecar owns the sidecar.
fvkmemory::resource_ptr<VulkanTexture> mSidecarMSAA; fvkmemory::resource_ptr<VulkanTexture> mSidecarMSAA;
VkDeviceMemory mTextureImageMemory = VK_NULL_HANDLE;
VkImage const mTextureImage;
VkDeviceMemory const mTextureImageMemory;
VkFormat const mVkFormat; VkFormat const mVkFormat;
VkImageViewType const mViewType; VkImageViewType const mViewType;
VkImageSubresourceRange const mFullViewRange; VkImageSubresourceRange const mFullViewRange;
VkImage mTextureImage = VK_NULL_HANDLE;
VulkanLayout mDefaultLayout;
// Note that this parameter is not constant due to the fact that AHB can force a change in the bool mIsProtected = false;
// conversion matrix per-frame.
struct Ycbcr {
VkSamplerYcbcrConversion conversion;
bool isExternalFormat;
bool operator==(Ycbcr const& other) const {
return conversion == other.conversion && isExternalFormat == other.isExternalFormat;
}
bool operator!=(Ycbcr const& other) const {
return !((*this) == other);
}
} mYcbcr;
VulkanLayout const mDefaultLayout;
VkImageUsageFlags const mUsage;
bool const mIsProtected;
// Track the image layout of each subresource using a sparse range map. // Track the image layout of each subresource using a sparse range map.
utils::RangeMap<uint32_t, VulkanLayout> mSubresourceLayouts; utils::RangeMap<uint32_t, VulkanLayout> mSubresourceLayouts;
using ImageViewHash = utils::hash::MurmurHashFn<ImageViewKey>;
std::unordered_map<ImageViewKey, VkImageView, ImageViewHash> mCachedImageViews;
friend struct VulkanTexture; std::unordered_map<ImageViewKey, VkImageView, ImageViewHash> mCachedImageViews;
VulkanStagePool& mStagePool;
VkDevice mDevice;
VmaAllocator mAllocator;
VulkanCommands* mCommands;
bool mIsTransientAttachment;
}; };
struct VulkanTexture : public HwTexture, fvkmemory::Resource { struct VulkanTexture : public HwTexture, fvkmemory::Resource {
@@ -119,10 +91,10 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
VulkanStagePool& stagePool); VulkanStagePool& stagePool);
// Specialized constructor for internally created textures (e.g. from a swap chain) // Specialized constructor for internally created textures (e.g. from a swap chain)
VulkanTexture(VulkanContext const& context, VkDevice device, VmaAllocator allocator, // The texture will never destroy the given VkImage, but it does manages its subresources.
VulkanTexture(VkDevice device, VmaAllocator allocator,
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image, fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image,
VkDeviceMemory memory, VkFormat format, VkSamplerYcbcrConversion conversion, VkDeviceMemory memory, VkFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth,
uint8_t samples, uint32_t width, uint32_t height, uint32_t depth,
TextureUsage tusage, VulkanStagePool& stagePool); TextureUsage tusage, VulkanStagePool& stagePool);
// Constructor for creating a texture view for wrt specific mip range // Constructor for creating a texture view for wrt specific mip range
@@ -141,6 +113,11 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
void updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height, void updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height,
uint32_t depth, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t miplevel); uint32_t depth, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t miplevel);
// Returns the primary image view, which is used for shader sampling.
VkImageView getPrimaryImageView() {
return getImageView(mPrimaryViewRange, mState->mViewType, mSwizzle);
}
VkImageViewType getViewType() const { VkImageViewType getViewType() const {
return mState->mViewType; return mState->mViewType;
} }
@@ -155,11 +132,20 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
// time of the call. // time of the call.
VulkanLayout getDefaultLayout() const; VulkanLayout getDefaultLayout() const;
// Gets or creates a cached VkImageView for a subresource that can be used as a render // Gets or creates a cached VkImageView for a single subresource that can be used as a render
// target attachment. Unlike the primary image view, this always the identity swizzle. // target attachment. Unlike the primary image view, this always has type VK_IMAGE_VIEW_TYPE_2D
VkImageView getAttachmentView(VkImageSubresourceRange const& range, VkImageViewType type); // and the identity swizzle.
VkImageView getAttachmentView(VkImageSubresourceRange range);
VkImageView getView(VkImageSubresourceRange const& range); // Gets or creates a cached VkImageView for a single subresource that can be used as a render
// target attachment when rendering with multiview.
VkImageView getMultiviewAttachmentView(VkImageSubresourceRange range);
// This is a workaround for the first few frames where we're waiting for the texture to actually
// be uploaded. In that case, we bind the sampler to an empty texture, but the corresponding
// imageView needs to be of the right type. Hence, we provide an option to indicate the
// view type. Swizzle option does not matter in this case.
VkImageView getViewForType(VkImageSubresourceRange const& range, VkImageViewType type);
VkFormat getVkFormat() const { VkFormat getVkFormat() const {
return mState->mVkFormat; return mState->mVkFormat;
@@ -179,7 +165,7 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
} }
bool isTransientAttachment() const { bool isTransientAttachment() const {
return mState->mUsage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; return mState->mIsTransientAttachment;
} }
bool getIsProtected() const { bool getIsProtected() const {
@@ -206,11 +192,6 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
// manually (outside of calls to transitionLayout). // manually (outside of calls to transitionLayout).
void setLayout(VkImageSubresourceRange const& range, VulkanLayout newLayout); void setLayout(VkImageSubresourceRange const& range, VulkanLayout newLayout);
// This is used in the case of external images and external samplers. AHB might update the
// conversion per-frame. This implies that we need to invalidate the view cache when that
// happens.
void setYcbcrConversion(VkSamplerYcbcrConversion conversion, bool isExternal);
#if FVK_ENABLED(FVK_DEBUG_TEXTURE) #if FVK_ENABLED(FVK_DEBUG_TEXTURE)
void print() const; void print() const;
#endif #endif
@@ -1,84 +0,0 @@
/*
* 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 "VulkanYcbcrConversionCache.h"
#include "vulkan/VulkanConstants.h"
#include "vulkan/utils/Conversion.h"
#include <utils/Panic.h>
#if defined(__ANDROID__)
#include <vulkan/vulkan_android.h> // for VkExternalFormatANDROID
#endif
using namespace bluevk;
namespace filament::backend {
VulkanYcbcrConversionCache::VulkanYcbcrConversionCache(VkDevice device)
: mDevice(device) {}
VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
VulkanYcbcrConversionCache::Params params) noexcept {
auto iter = mCache.find(params);
if (UTILS_LIKELY(iter != mCache.end())) {
return iter->second;
}
auto const& chroma = params.conversion;
TextureSwizzle const swizzleArray[] = { chroma.r, chroma.g, chroma.b, chroma.a };
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
.format = fvkutils::getVkFormat(params.format),
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
.components = fvkutils::getSwizzleMap(swizzleArray),
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
};
// We could put this in the platform class, but that seems like a bit of an overkill
#if defined(__ANDROID__)
VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.externalFormat = params.externalFormat,
};
if (params.externalFormat) {
conversionInfo.pNext = &externalFormat;
conversionInfo.format = VK_FORMAT_UNDEFINED;
}
#endif
VkSamplerYcbcrConversion conversion;
VkResult result =
vkCreateSamplerYcbcrConversion(mDevice, &conversionInfo, nullptr, &conversion);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create Ycbcr Conversion."
<< " error=" << static_cast<int32_t>(result);
mCache.insert({ params, conversion });
return conversion;
}
void VulkanYcbcrConversionCache::terminate() noexcept {
for (auto& [param, conv]: mCache) {
vkDestroySamplerYcbcrConversion(mDevice, conv, VKALLOC);
}
mCache.clear();
}
} // namespace filament::backend
@@ -1,60 +0,0 @@
/*
* 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.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
#define TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
#include <backend/DriverEnums.h>
#include <utils/Hash.h>
#include <bluevk/BlueVK.h>
#include <tsl/robin_map.h>
namespace filament::backend {
// Simple manager for VkSamplerYcbcrConversion objects.
class VulkanYcbcrConversionCache {
public:
struct Params {
SamplerYcbcrConversion conversion = {};
TextureFormat format = {};
uint16_t padding = 0;
uint64_t externalFormat = 0;
};
static_assert(sizeof(Params) == 16);
explicit VulkanYcbcrConversionCache(VkDevice device);
VkSamplerYcbcrConversion getConversion(Params params) noexcept;
void terminate() noexcept;
private:
VkDevice mDevice;
struct ConversionEqualTo {
bool operator()(Params lhs, Params rhs) const noexcept {
SamplerYcbcrConversion::EqualTo equal;
return equal(lhs.conversion, rhs.conversion) &&
lhs.externalFormat == rhs.externalFormat;
}
};
using ConversionHashFn = utils::hash::MurmurHashFn<Params>;
tsl::robin_map<Params, VkSamplerYcbcrConversion, ConversionHashFn, ConversionEqualTo> mCache;
};
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
@@ -0,0 +1,504 @@
/*
* Copyright (C) 2024 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 "VulkanDescriptorSetManager.h"
#include "vulkan/VulkanCommands.h"
#include "vulkan/VulkanHandles.h"
#include "vulkan/VulkanConstants.h"
#include <utils/FixedCapacityVector.h>
#include <utils/Panic.h>
#include <math.h>
#include <algorithm>
#include <memory>
#include <type_traits>
#include <vector>
namespace filament::backend {
namespace {
using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask;
using DescriptorCount = VulkanDescriptorSetLayout::Count;
using DescriptorSetLayoutArray = VulkanDescriptorSetManager::DescriptorSetLayoutArray;
using BitmaskGroupHashFn = utils::hash::MurmurHashFn<BitmaskGroup>;
struct BitmaskGroupEqual {
bool operator()(BitmaskGroup const& k1, BitmaskGroup const& k2) const {
return k1 == k2;
}
};
// We create a pool for each layout as defined by the number of descriptors of each type. For
// example, a layout of
// 'A' =>
// layout(binding = 0, set = 1) uniform {};
// layout(binding = 1, set = 1) sampler1;
// layout(binding = 2, set = 1) sampler2;
//
// would be equivalent to
// 'B' =>
// layout(binding = 1, set = 2) uniform {};
// layout(binding = 2, set = 2) sampler2;
// layout(binding = 3, set = 2) sampler3;
//
// TODO: we might do better if we understand the types of unique layouts and can combine them in a
// single pool without too much waste.
class DescriptorPool {
public:
DescriptorPool(VkDevice device, DescriptorCount const& count, uint16_t capacity)
: mDevice(device),
mCount(count),
mCapacity(capacity),
mSize(0),
mUnusedCount(0) {
DescriptorCount const actual = mCount * capacity;
VkDescriptorPoolSize sizes[4];
uint8_t npools = 0;
if (actual.ubo) {
sizes[npools++] = {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
.descriptorCount = actual.ubo,
};
}
if (actual.dynamicUbo) {
sizes[npools++] = {
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
.descriptorCount = actual.dynamicUbo,
};
}
if (actual.sampler) {
sizes[npools++] = {
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.descriptorCount = actual.sampler,
};
}
if (actual.inputAttachment) {
sizes[npools++] = {
.type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
.descriptorCount = actual.inputAttachment,
};
}
VkDescriptorPoolCreateInfo info{
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.maxSets = capacity,
.poolSizeCount = npools,
.pPoolSizes = sizes,
};
vkCreateDescriptorPool(mDevice, &info, VKALLOC, &mPool);
}
DescriptorPool(DescriptorPool const&) = delete;
DescriptorPool& operator=(DescriptorPool const&) = delete;
~DescriptorPool() {
vkDestroyDescriptorPool(mDevice, mPool, VKALLOC);
}
uint16_t const& capacity() {
return mCapacity;
}
// A convenience method for checking if this pool can allocate sets for a given layout.
inline bool canAllocate(DescriptorCount const& count) {
return count == mCount;
}
VkDescriptorSet obtainSet(VkDescriptorSetLayout vklayout) {
auto itr = findSets(vklayout);
if (itr != mUnused.end()) {
// If we don't have any unused, then just return an empty handle.
if (itr->second.empty()) {
return VK_NULL_HANDLE;
}
std::vector<VkDescriptorSet>& sets = itr->second;
auto set = sets.back();
sets.pop_back();
mUnusedCount--;
return set;
}
if (mSize + 1 > mCapacity) {
return VK_NULL_HANDLE;
}
// Creating a new set
VkDescriptorSetLayout layouts[1] = {vklayout};
VkDescriptorSetAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
.pNext = nullptr,
.descriptorPool = mPool,
.descriptorSetCount = 1,
.pSetLayouts = layouts,
};
VkDescriptorSet vkSet;
UTILS_UNUSED VkResult result = vkAllocateDescriptorSets(mDevice, &allocInfo, &vkSet);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Failed to allocate descriptor set code=" << result << " size=" << mSize
<< " capacity=" << mCapacity << " count=" << mUnusedCount;
mSize++;
return vkSet;
}
void recycle(VkDescriptorSetLayout vklayout, VkDescriptorSet vkSet) {
// We are recycling - release the set back into the pool. Note that the
// vk handle has not changed, but we need to change the backend handle to allow
// for proper refcounting of resources referenced in this set.
auto itr = findSets(vklayout);
if (itr != mUnused.end()) {
itr->second.push_back(vkSet);
} else {
mUnused.push_back(std::make_pair(vklayout, std::vector<VkDescriptorSet> {vkSet}));
}
mUnusedCount++;
}
private:
using UnusedSets = std::pair<VkDescriptorSetLayout, std::vector<VkDescriptorSet>>;
using UnusedSetMap = std::vector<UnusedSets>;
inline UnusedSetMap::iterator findSets(VkDescriptorSetLayout vklayout) {
return std::find_if(mUnused.begin(), mUnused.end(), [vklayout](auto const& value) {
return value.first == vklayout;
});
}
VkDevice mDevice;
VkDescriptorPool mPool;
DescriptorCount const mCount;
uint16_t const mCapacity;
// Tracks the number of allocated descriptor sets.
uint16_t mSize;
// Tracks the number of in-use descriptor sets.
uint16_t mUnusedCount;
// This maps a layout to a list of descriptor sets allocated for that layout.
UnusedSetMap mUnused;
};
template<typename Key>
struct Equal {
bool operator()(Key const& k1, Key const& k2) const {
return 0 == memcmp((const void*) &k1, (const void*) &k2, sizeof(Key));
}
};
template<typename Bitmask>
uint32_t createBindings(VkDescriptorSetLayoutBinding* toBind, uint32_t count, VkDescriptorType type,
Bitmask const& mask) {
Bitmask alreadySeen;
mask.forEachSetBit([&](size_t index) {
VkShaderStageFlags stages = 0;
uint32_t binding = 0;
if (index < fvkutils::getFragmentStageShift<Bitmask>()) {
binding = (uint32_t) index;
stages |= VK_SHADER_STAGE_VERTEX_BIT;
auto fragIndex = index + fvkutils::getFragmentStageShift<Bitmask>();
if (mask.test(fragIndex)) {
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
alreadySeen.set(fragIndex);
}
} else if (!alreadySeen.test(index)) {
// We are in fragment stage bits
binding = (uint32_t) (index - fvkutils::getFragmentStageShift<Bitmask>());
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
}
if (stages) {
toBind[count++] = {
.binding = binding,
.descriptorType = type,
.descriptorCount = 1,
.stageFlags = stages,
};
}
});
return count;
}
inline VkDescriptorSetLayout createLayout(VkDevice device, BitmaskGroup const& bitmaskGroup) {
// Note that the following *needs* to be static so that VkDescriptorSetLayoutCreateInfo will not
// refer to stack memory.
VkDescriptorSetLayoutBinding toBind[VulkanDescriptorSetLayout::MAX_BINDINGS];
uint32_t count = 0;
count = createBindings(toBind, count, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
bitmaskGroup.dynamicUbo);
count = createBindings(toBind, count, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bitmaskGroup.ubo);
count = createBindings(toBind, count, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
bitmaskGroup.sampler);
count = createBindings(toBind, count, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
bitmaskGroup.inputAttachment);
assert_invariant(count != 0 && "Need at least one binding for descriptor set layout.");
VkDescriptorSetLayoutCreateInfo dlinfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.bindingCount = count,
.pBindings = toBind,
};
VkDescriptorSetLayout layout;
vkCreateDescriptorSetLayout(device, &dlinfo, VKALLOC, &layout);
return layout;
}
} // anonymous namespace
// This is an ever-expanding pool of sets where it
// 1. Keeps a list of smaller pools of different layout-dimensions.
// 2. Will add a pool if existing pool are not compatible with the requested layout o runs out.
class VulkanDescriptorSetManager::DescriptorInfinitePool {
private:
static constexpr uint16_t EXPECTED_SET_COUNT = 10;
static constexpr float SET_COUNT_GROWTH_FACTOR = 1.5;
public:
DescriptorInfinitePool(VkDevice device)
: mDevice(device) {}
VkDescriptorSet obtainSet(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
auto const vklayout = layout->getVkLayout();
DescriptorPool* sameTypePool = nullptr;
for (auto& pool: mPools) {
if (!pool->canAllocate(layout->count)) {
continue;
}
if (auto set = pool->obtainSet(vklayout); set != VK_NULL_HANDLE) {
return set;
}
if (!sameTypePool || sameTypePool->capacity() < pool->capacity()) {
sameTypePool = pool.get();
}
}
uint16_t capacity = EXPECTED_SET_COUNT;
if (sameTypePool) {
// Exponentially increase the size of the pool to ensure we don't hit this too often.
capacity = std::ceil(sameTypePool->capacity() * SET_COUNT_GROWTH_FACTOR);
}
// We need to increase the set of pools by one.
mPools.push_back(std::make_unique<DescriptorPool>(mDevice,
DescriptorCount::fromLayoutBitmask(layout->bitmask), capacity));
auto& pool = mPools.back();
auto ret = pool->obtainSet(vklayout);
assert_invariant(ret != VK_NULL_HANDLE && "failed to obtain a set?");
return ret;
}
void recycle(DescriptorCount const& count, VkDescriptorSetLayout vklayout,
VkDescriptorSet vkSet) {
for (auto& pool: mPools) {
if (!pool->canAllocate(count)) {
continue;
}
pool->recycle(vklayout, vkSet);
break;
}
}
private:
VkDevice mDevice;
std::vector<std::unique_ptr<DescriptorPool>> mPools;
};
class VulkanDescriptorSetManager::DescriptorSetLayoutManager {
public:
DescriptorSetLayoutManager(VkDevice device)
: mDevice(device) {}
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks) {
if (auto itr = mVkLayouts.find(bitmasks); itr != mVkLayouts.end()) {
return itr->second;
}
auto vklayout = createLayout(mDevice, bitmasks);
mVkLayouts[bitmasks] = vklayout;
return vklayout;
}
~DescriptorSetLayoutManager() {
for (auto& itr: mVkLayouts) {
vkDestroyDescriptorSetLayout(mDevice, itr.second, VKALLOC);
}
}
private:
VkDevice mDevice;
tsl::robin_map<BitmaskGroup, VkDescriptorSetLayout, BitmaskGroupHashFn, BitmaskGroupEqual>
mVkLayouts;
};
VulkanDescriptorSetManager::VulkanDescriptorSetManager(VkDevice device,
fvkmemory::ResourceManager* resourceManager)
: mDevice(device),
mResourceManager(resourceManager),
mLayoutManager(std::make_unique<DescriptorSetLayoutManager>(device)),
mDescriptorPool(std::make_unique<DescriptorInfinitePool>(device)) {}
VulkanDescriptorSetManager::~VulkanDescriptorSetManager() = default;
void VulkanDescriptorSetManager::terminate() noexcept{
mLayoutManager.reset();
mDescriptorPool.reset();
clearHistory();
}
// bind() is not really binding the set but just stashing until we have all the info
// (pipelinelayout).
void VulkanDescriptorSetManager::bind(uint8_t setIndex,
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
backend::DescriptorSetOffsetArray&& offsets) {
set->setOffsets(std::move(offsets));
mStashedSets[setIndex] = set;
}
void VulkanDescriptorSetManager::unbind(uint8_t setIndex) {
mStashedSets[setIndex] = {};
}
void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands,
VkPipelineLayout pipelineLayout, fvkutils::DescriptorSetMask const& setMask) {
// setMask indicates the set of descriptor sets the driver wants to bind, curMask is the
// actual set of sets that *needs* to be bound.
fvkutils::DescriptorSetMask curMask = setMask;
auto& updateSets = mStashedSets;
auto& lastBoundSets = mLastBoundInfo.boundSets;
setMask.forEachSetBit([&](size_t index) {
if (!updateSets[index] || updateSets[index] == lastBoundSets[index]) {
curMask.unset(index);
}
});
if (curMask.none() &&
(mLastBoundInfo.pipelineLayout == pipelineLayout && mLastBoundInfo.setMask == setMask &&
mLastBoundInfo.boundSets == updateSets)) {
return;
}
curMask.forEachSetBit([&updateSets, commands, pipelineLayout](size_t index) {
// This code actually binds the descriptor sets.
auto set = updateSets[index];
VkCommandBuffer const cmdbuffer = commands->buffer();
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index,
1, &set->vkSet, set->uniqueDynamicUboCount, set->getOffsets()->data());
commands->acquire(set);
});
mStashedSets = {};
mLastBoundInfo = {
pipelineLayout,
setMask,
updateSets,
};
}
void VulkanDescriptorSetManager::updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanBufferObject> bufferObject,
VkDeviceSize offset, VkDeviceSize size) noexcept {
VkDescriptorBufferInfo const info = {
.buffer = bufferObject->buffer.getGpuBuffer(),
.offset = offset,
.range = size,
};
VkDescriptorType type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
if (set->dynamicUboMask.test(binding)) {
type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
}
VkWriteDescriptorSet const descriptorWrite = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = nullptr,
.dstSet = set->vkSet,
.dstBinding = binding,
.descriptorCount = 1,
.descriptorType = type,
.pBufferInfo = &info,
};
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
set->acquire(bufferObject);
}
void VulkanDescriptorSetManager::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
VkSampler sampler) noexcept {
VkDescriptorImageInfo info{
.sampler = sampler,
};
VkImageSubresourceRange const range = texture->getPrimaryViewRange();
VkImageViewType const expectedType = texture->getViewType();
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
expectedType == VK_IMAGE_VIEW_TYPE_2D) {
// If the sampler is part of a mipmapped depth texture, where one of the level *can* be
// an attachment, then the sampler for this texture has the same view properties as a
// view for an attachment. Therefore, we can use getAttachmentView to get a
// corresponding VkImageView.
info.imageView = texture->getAttachmentView(range);
} else {
info.imageView = texture->getViewForType(range, expectedType);
}
info.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout());
VkWriteDescriptorSet const descriptorWrite = {
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
.pNext = nullptr,
.dstSet = set->vkSet,
.dstBinding = binding,
.descriptorCount = 1,
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
.pImageInfo = &info,
};
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
set->acquire(texture);
}
void VulkanDescriptorSetManager::updateInputAttachment(
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
VulkanAttachment const& attachment) noexcept {
// TOOD: fill-in this region
}
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetManager::createSet(
Handle<HwDescriptorSet> handle, fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
auto const vkSet = mDescriptorPool->obtainSet(layout);
auto const& count = layout->count;
auto const vklayout = layout->getVkLayout();
return fvkmemory::resource_ptr<VulkanDescriptorSet>::make(mResourceManager, handle, vkSet,
layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
[vkSet, count, vklayout, this](VulkanDescriptorSet*) {
// Note that mDescriptorPool could be gone due to terminate (when the backend shuts
// down).
if (mDescriptorPool) {
mDescriptorPool->recycle(count, vklayout, vkSet);
}
});
}
void VulkanDescriptorSetManager::initVkLayout(
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
layout->setVkLayout(mLayoutManager->getVkLayout(layout->bitmask));
}
void VulkanDescriptorSetManager::clearHistory() {
mStashedSets = {};
}
} // namespace filament::backend
@@ -0,0 +1,102 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
#include "vulkan/VulkanHandles.h"
#include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Definitions.h" // For DescriptorSetMask
#include <backend/DriverEnums.h>
#include <backend/Program.h>
#include <backend/TargetBufferInfo.h>
#include <utils/bitset.h>
#include <bluevk/BlueVK.h>
#include <tsl/robin_map.h>
#include <memory>
namespace filament::backend {
// [GDSR]: Great-Descriptor-Set-Refactor: As of 03/20/24, the Filament frontend is planning to
// introduce descriptor set. This PR will arrive before that change is complete. As such, some of
// the methods introduced here will be obsolete, and certain logic will be generalized.
// Abstraction over the pool and the layout cache.
class VulkanDescriptorSetManager {
public:
static constexpr uint8_t UNIQUE_DESCRIPTOR_SET_COUNT =
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT;
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
VulkanDescriptorSetManager(VkDevice device, fvkmemory::ResourceManager* resourceManager);
~VulkanDescriptorSetManager();
void terminate() noexcept;
void updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
fvkmemory::resource_ptr<VulkanBufferObject> bufferObject, VkDeviceSize offset,
VkDeviceSize size) noexcept;
void updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept;
void updateInputAttachment(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
VulkanAttachment const& attachment) noexcept;
void bind(uint8_t setIndex, fvkmemory::resource_ptr<VulkanDescriptorSet> set,
backend::DescriptorSetOffsetArray&& offsets);
void unbind(uint8_t setIndex);
void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout,
fvkutils::DescriptorSetMask const& setMask);
fvkmemory::resource_ptr<VulkanDescriptorSet> createSet(Handle<HwDescriptorSet> handle,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
void initVkLayout(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
void clearHistory();
private:
class DescriptorSetLayoutManager;
class DescriptorInfinitePool;
using DescriptorSetArray =
std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>, UNIQUE_DESCRIPTOR_SET_COUNT>;
VkDevice mDevice;
fvkmemory::ResourceManager* mResourceManager;
std::unique_ptr<DescriptorSetLayoutManager> mLayoutManager;
std::unique_ptr<DescriptorInfinitePool> mDescriptorPool;
std::pair<VulkanAttachment, VkDescriptorImageInfo> mInputAttachment;
DescriptorSetArray mStashedSets = {};
struct {
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
fvkutils::DescriptorSetMask setMask;
DescriptorSetArray boundSets = {};
} mLastBoundInfo;
};
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
@@ -17,8 +17,7 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H #ifndef TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H
#define TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H #define TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H
#include "VulkanHandles.h" #include <vulkan/VulkanHandles.h>
#include <bluevk/BlueVK.h> #include <bluevk/BlueVK.h>
#include <utils/Hash.h> #include <utils/Hash.h>
@@ -211,7 +211,6 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME, VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME, VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME, VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME,
#endif #endif
// MoltenVk is the only non-conformant implementation we're interested in. // MoltenVk is the only non-conformant implementation we're interested in.
#if defined(__APPLE__) #if defined(__APPLE__)
@@ -327,9 +326,7 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
} }
VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice, VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceFeatures2 const& features, VkPhysicalDeviceFeatures2 const& features, uint32_t graphicsQueueFamilyIndex,
VkPhysicalDeviceVulkan11Features const& vk11Features,
uint32_t graphicsQueueFamilyIndex,
uint32_t protectedGraphicsQueueFamilyIndex, ExtensionSet const& deviceExtensions, uint32_t protectedGraphicsQueueFamilyIndex, ExtensionSet const& deviceExtensions,
bool requestImageView2DOn3DImage) { bool requestImageView2DOn3DImage) {
VkDevice device; VkDevice device;
@@ -362,28 +359,14 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
// We could simply enable all supported features, but since that may have performance // We could simply enable all supported features, but since that may have performance
// consequences let's just enable the features we need. // consequences let's just enable the features we need.
VkPhysicalDeviceFeatures enabledFeatures = { VkPhysicalDeviceFeatures enabledFeatures{
.depthClamp = features.features.depthClamp, .depthClamp = features.features.depthClamp,
.samplerAnisotropy = features.features.samplerAnisotropy, .samplerAnisotropy = features.features.samplerAnisotropy,
.textureCompressionETC2 = features.features.textureCompressionETC2, .textureCompressionETC2 = features.features.textureCompressionETC2,
.textureCompressionBC = features.features.textureCompressionBC, .textureCompressionBC = features.features.textureCompressionBC,
.shaderClipDistance = features.features.shaderClipDistance, .shaderClipDistance = features.features.shaderClipDistance,
}; };
deviceCreateInfo.pEnabledFeatures = &enabledFeatures;
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.enabledExtensionCount = (uint32_t) requestExtensions.size();
deviceCreateInfo.ppEnabledExtensionNames = requestExtensions.data(); deviceCreateInfo.ppEnabledExtensionNames = requestExtensions.data();
@@ -400,7 +383,7 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceMultiviewFeaturesKHR multiview = { VkPhysicalDeviceMultiviewFeaturesKHR multiview = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR, .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR,
.multiview = vk11Features.multiview, .multiview = VK_TRUE,
.multiviewGeometryShader = VK_FALSE, .multiviewGeometryShader = VK_FALSE,
.multiviewTessellationShader = VK_FALSE, .multiviewTessellationShader = VK_FALSE,
}; };
@@ -751,9 +734,12 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES, .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES,
}; };
chainStruct(&context.mPhysicalDeviceFeatures, &queryProtectedMemoryFeatures); chainStruct(&context.mPhysicalDeviceFeatures, &queryProtectedMemoryFeatures);
chainStruct(&context.mPhysicalDeviceFeatures, &context.mPhysicalDeviceVk11Features);
chainStruct(&context.mPhysicalDeviceProperties, &protectedMemoryProperties); chainStruct(&context.mPhysicalDeviceProperties, &protectedMemoryProperties);
// We know we need to allocate the protected version of the VK objects
context.mProtectedMemorySupported =
static_cast<bool>(queryProtectedMemoryFeatures.protectedMemory);
// Initialize the following fields: physicalDeviceProperties, memoryProperties, // Initialize the following fields: physicalDeviceProperties, memoryProperties,
// physicalDeviceFeatures, graphicsQueueFamilyIndex. // physicalDeviceFeatures, graphicsQueueFamilyIndex.
vkGetPhysicalDeviceProperties2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceProperties); vkGetPhysicalDeviceProperties2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceProperties);
@@ -773,10 +759,6 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
mImpl->mGraphicsQueueIndex = 0; 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) { if (context.mProtectedMemorySupported) {
mImpl->mProtectedGraphicsQueueFamilyIndex = identifyGraphicsQueueFamilyIndex( mImpl->mProtectedGraphicsQueueFamilyIndex = identifyGraphicsQueueFamilyIndex(
mImpl->mPhysicalDevice, (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_PROTECTED_BIT)); mImpl->mPhysicalDevice, (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_PROTECTED_BIT));
@@ -813,8 +795,8 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
} }
if (mImpl->mDevice == VK_NULL_HANDLE) { if (mImpl->mDevice == VK_NULL_HANDLE) {
mImpl->mDevice = createLogicalDevice(mImpl->mPhysicalDevice, mImpl->mDevice =
context.mPhysicalDeviceFeatures, context.mPhysicalDeviceVk11Features, createLogicalDevice(mImpl->mPhysicalDevice, context.mPhysicalDeviceFeatures,
mImpl->mGraphicsQueueFamilyIndex, mImpl->mProtectedGraphicsQueueFamilyIndex, mImpl->mGraphicsQueueFamilyIndex, mImpl->mProtectedGraphicsQueueFamilyIndex,
deviceExts, requestPortabilitySubsetImageView2DOn3DImage); deviceExts, requestPortabilitySubsetImageView2DOn3DImage);
} }
@@ -844,10 +826,12 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
if (!mImpl->mSharedContext) { if (!mImpl->mSharedContext) {
context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME); context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME); context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
context.mMultiviewEnabled = setContains(deviceExts, VK_KHR_MULTIVIEW_EXTENSION_NAME);
} else { } else {
VulkanSharedContext const* scontext = (VulkanSharedContext const*) sharedContext; VulkanSharedContext const* scontext = (VulkanSharedContext const*) sharedContext;
context.mDebugUtilsSupported = scontext->debugUtilsSupported; context.mDebugUtilsSupported = scontext->debugUtilsSupported;
context.mDebugMarkersSupported = scontext->debugMarkersSupported; context.mDebugMarkersSupported = scontext->debugMarkersSupported;
context.mMultiviewEnabled = scontext->multiviewSupported;
} }
// Check the availability of lazily allocated memory // Check the availability of lazily allocated memory
@@ -999,32 +983,10 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadata(
} }
VulkanPlatform::ImageData VulkanPlatform::createExternalImageData( VulkanPlatform::ImageData VulkanPlatform::createExternalImageData(
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata, ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata) {
uint32_t memoryTypeIndex, VkImageUsageFlags usage) { return createExternalImageDataImpl(externalImage, mImpl->mDevice, metadata);
return createExternalImageDataImpl(externalImage, mImpl->mDevice, metadata, memoryTypeIndex,
usage);
} }
VkSampler VulkanPlatform::createExternalSampler(SamplerYcbcrConversion chroma,
SamplerParams sampler, uint32_t internalFormat) {
return createExternalSamplerImpl(mImpl->mDevice, chroma, sampler, internalFormat);
}
VkImageView VulkanPlatform::createExternalImageView(SamplerYcbcrConversion chroma,
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
VkImageViewType viewType, VkComponentMapping swizzle) {
return createExternalImageViewImpl(mImpl->mDevice, chroma, internalFormat, image, range,
viewType, swizzle);
}
ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() const {
return getSwapchainInstanceExtensionsImpl();
}
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWindow,
VkInstance instance, uint64_t flags) const noexcept {
return createVkSurfaceKHRImpl(nativeWindow, instance, flags);
}
#undef SWAPCHAIN_RET_FUNC #undef SWAPCHAIN_RET_FUNC
}// namespace filament::backend }// namespace filament::backend
@@ -13,16 +13,15 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
#include <backend/platforms/VulkanPlatformAndroid.h> #include <backend/platforms/VulkanPlatform.h>
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <backend/platforms/VulkanPlatformAndroid.h>
#include <private/backend/BackendUtilsAndroid.h> #include <private/backend/BackendUtilsAndroid.h>
#include "vulkan/VulkanConstants.h" #include "vulkan/VulkanConstants.h"
#include <utils/Panic.h> #include <utils/Panic.h>
#include "vulkan/utils/Image.h"
#include "vulkan/utils/Conversion.h"
#include <bluevk/BlueVK.h> #include <bluevk/BlueVK.h>
@@ -137,8 +136,7 @@ std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer
} }
VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevice device, VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevice device,
VulkanPlatform::ExternalImageMetadata const& metadata, uint32_t memoryTypeIndex, VulkanPlatform::ExternalImageMetadata const& metadata) {
VkImageUsageFlags usage) {
VulkanPlatform::ImageData data; VulkanPlatform::ImageData data;
// if external format we need to specifiy it in the allocation // if external format we need to specifiy it in the allocation
@@ -147,8 +145,8 @@ VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevic
const VkExternalFormatANDROID externalFormat = { const VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID, .sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.pNext = nullptr, .pNext = nullptr,
// pass down the format (external means we don't have it VK defined) .externalFormat = metadata
.externalFormat = metadata.externalFormat, .externalFormat,// pass down the format (external means we don't have it VK defined)
}; };
const VkExternalMemoryImageCreateInfo externalCreateInfo = { const VkExternalMemoryImageCreateInfo externalCreateInfo = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
@@ -167,8 +165,10 @@ VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevic
}; };
imageInfo.mipLevels = 1; imageInfo.mipLevels = 1;
imageInfo.arrayLayers = metadata.layers; imageInfo.arrayLayers = metadata.layers;
imageInfo.samples = metadata.samples; imageInfo.usage = metadata.usage;
imageInfo.usage = usage; // In the unprotected case add R/W capabilities
if (metadata.isProtected == false)
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &data.first); VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &data.first);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
@@ -186,12 +186,10 @@ VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevic
.image = data.first, .image = data.first,
.buffer = VK_NULL_HANDLE, .buffer = VK_NULL_HANDLE,
}; };
VkMemoryAllocateInfo allocInfo = { VkMemoryAllocateInfo allocInfo = {.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = &memoryDedicatedAllocateInfo, .pNext = &memoryDedicatedAllocateInfo,
.allocationSize = metadata.allocationSize, .allocationSize = metadata.allocationSize,
.memoryTypeIndex = memoryTypeIndex, .memoryTypeIndex = metadata.memoryTypeBits};
};
result = vkAllocateMemory(device, &allocInfo, VKALLOC, &data.second); result = vkAllocateMemory(device, &allocInfo, VKALLOC, &data.second);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkAllocateMemory failed with error=" << static_cast<int32_t>(result); << "vkAllocateMemory failed with error=" << static_cast<int32_t>(result);
@@ -201,10 +199,12 @@ VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevic
} // namespace } // namespace
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() = default; namespace fvkandroid {
Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage( ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() = default;
AHardwareBuffer const* buffer, bool sRGB) noexcept {
Platform::ExternalImageHandle createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept {
if (__builtin_available(android 26, *)) { if (__builtin_available(android 26, *)) {
AHardwareBuffer_Desc hardwareBufferDescription = {}; AHardwareBuffer_Desc hardwareBufferDescription = {};
AHardwareBuffer_describe(buffer, &hardwareBufferDescription); AHardwareBuffer_describe(buffer, &hardwareBufferDescription);
@@ -223,23 +223,12 @@ Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
return Platform::ExternalImageHandle{}; return Platform::ExternalImageHandle{};
} }
VulkanPlatformAndroid::ExternalImageDescAndroid VulkanPlatformAndroid::getExternalImageDesc( } // namespace fvkandroid
ExternalImageHandleRef externalImage) const noexcept {
auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
return { VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
.width = fvkExternalImage->width, ExternalImageHandleRef externalImage, VkDevice device) {
.height = fvkExternalImage->height,
.format = fvkExternalImage->format,
.usage = fvkExternalImage->usage,
};
}
VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMetadata(
ExternalImageHandleRef externalImage) {
auto const* fvkExternalImage = auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get()); static_cast<fvkandroid::ExternalImageVulkanAndroid const*>(externalImage.get());
ExternalImageMetadata metadata; ExternalImageMetadata metadata;
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer; AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
@@ -253,8 +242,10 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMet
std::tie(metadata.format, metadata.usage) = std::tie(metadata.format, metadata.usage) =
getVKFormatAndUsage(bufferDesc, fvkExternalImage->sRGB); getVKFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
} }
// In the unprotected case add R/W capabilities
metadata.samples = VK_SAMPLE_COUNT_1_BIT; if (metadata.isProtected == false) {
metadata.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
VkAndroidHardwareBufferFormatPropertiesANDROID formatInfo = { VkAndroidHardwareBufferFormatPropertiesANDROID formatInfo = {
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID, .sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID,
@@ -264,7 +255,7 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMet
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID, .sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID,
.pNext = &formatInfo, .pNext = &formatInfo,
}; };
VkResult result = vkGetAndroidHardwareBufferPropertiesANDROID(getDevice(), buffer, &properties); VkResult result = vkGetAndroidHardwareBufferPropertiesANDROID(device, buffer, &properties);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkGetAndroidHardwareBufferProperties failed with error=" << "vkGetAndroidHardwareBufferProperties failed with error="
<< static_cast<int32_t>(result); << static_cast<int32_t>(result);
@@ -279,137 +270,26 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMet
return metadata; return metadata;
} }
VulkanPlatformAndroid::ImageData VulkanPlatformAndroid::createExternalImageData( VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata, ExternalImageHandleRef externalImage, VkDevice device,
uint32_t memoryTypeIndex, VkImageUsageFlags usage) { const ExternalImageMetadata& metadata) {
auto const* fvkExternalImage = auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get()); static_cast<fvkandroid::ExternalImageVulkanAndroid const*>(externalImage.get());
ImageData data = allocateExternalImage(fvkExternalImage->aHardwareBuffer, getDevice(), metadata, ImageData data = allocateExternalImage(fvkExternalImage->aHardwareBuffer, device, metadata);
memoryTypeIndex, usage); VkResult result = vkBindImageMemory(device, data.first, data.second, 0);
VkResult result = vkBindImageMemory(getDevice(), data.first, data.second, 0);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkBindImageMemory error=" << static_cast<int32_t>(result); << "vkBindImageMemory error=" << static_cast<int32_t>(result);
return data; return data;
} }
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device, SamplerYcbcrConversion chroma, VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
VkImageViewType viewType, VkComponentMapping swizzle){
VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.externalFormat = internalFormat,
};
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
.pNext = &externalFormat,
.format = VK_FORMAT_UNDEFINED,
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
.components = fvkutils::getSwizzleMap(swizzleArray),
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
};
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
nullptr, &conversion);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create Ycbcr Conversion."
<< " error=" << static_cast<int32_t>(result);
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.conversion = conversion,
};
VkImageViewCreateInfo viewInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = &samplerYcbcrConversionInfo,
.flags = 0,
.image = image,
.viewType = viewType,
.format = VK_FORMAT_UNDEFINED,
.components = swizzle,
.subresourceRange = range,
};
VkImageView imageView;
result = vkCreateImageView(device, &viewInfo, VKALLOC, &imageView);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create VkImageView."
<< " error=" << static_cast<int32_t>(result);
return imageView;
}
VkSampler VulkanPlatform::createExternalSamplerImpl(
VkDevice device, SamplerYcbcrConversion chroma, SamplerParams params,
uint32_t internalFormat) {
VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.externalFormat = internalFormat,
};
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
.pNext = &externalFormat,
.format = VK_FORMAT_UNDEFINED,
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
.components = fvkutils::getSwizzleMap(swizzleArray),
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
};
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
nullptr, &conversion);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create Ycbcr Conversion."
<< " error=" << static_cast<int32_t>(result);
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.pNext = nullptr,
.conversion = conversion,
};
VkSamplerCreateInfo samplerInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = &samplerYcbcrConversionInfo,
.magFilter = fvkutils::getFilter(params.filterMag),
.minFilter = fvkutils::getFilter(params.filterMin),
.mipmapMode = fvkutils::getMipmapMode(params.filterMin),
.addressModeU = fvkutils::getWrapMode(params.wrapS),
.addressModeV = fvkutils::getWrapMode(params.wrapT),
.addressModeW = fvkutils::getWrapMode(params.wrapR),
.anisotropyEnable = params.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
.maxAnisotropy = (float)(1u << params.anisotropyLog2),
.compareEnable = fvkutils::getCompareEnable(params.compareMode),
.compareOp = fvkutils::getCompareOp(params.compareFunc),
.minLod = 0.0f,
.maxLod = fvkutils::getMaxLod(params.filterMin),
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE,
};
VkSampler sampler;
result = vkCreateSampler(device, &samplerInfo, VKALLOC, &sampler);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create sampler."
<< " error=" << static_cast<int32_t>(result);
return sampler;
}
VulkanPlatform::ExtensionSet VulkanPlatformAndroid::getSwapchainInstanceExtensions() const {
return { return {
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME, VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
}; };
} }
VulkanPlatform::SurfaceBundle VulkanPlatformAndroid::createVkSurfaceKHR(void* nativeWindow, VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWindow,
VkInstance instance, uint64_t flags) const noexcept { VkInstance instance, uint64_t flags) noexcept {
VkSurfaceKHR surface; VkSurfaceKHR surface;
VkExtent2D extent; VkExtent2D extent;
@@ -423,24 +303,4 @@ VulkanPlatform::SurfaceBundle VulkanPlatformAndroid::createVkSurfaceKHR(void* na
<< "vkCreateAndroidSurfaceKHR with error=" << static_cast<int32_t>(result); << "vkCreateAndroidSurfaceKHR with error=" << static_cast<int32_t>(result);
return {surface, extent}; return {surface, extent};
} }
// Deprecated platform dependent helper methods
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() { return {}; }
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
ExternalImageHandleRef externalImage, VkDevice device) {
return ExternalImageMetadata{};
} }
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return ImageData{};
}
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept {
return SurfaceBundle{};
}
}// namespace filament::backend
@@ -24,6 +24,7 @@
#include <bluevk/BlueVK.h> #include <bluevk/BlueVK.h>
// Platform specific includes and defines // Platform specific includes and defines
#if defined(__APPLE__)
#include <Cocoa/Cocoa.h> #include <Cocoa/Cocoa.h>
#import <Metal/Metal.h> #import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h> #import <QuartzCore/CAMetalLayer.h>
@@ -31,14 +32,33 @@
#ifndef VK_MVK_macos_surface #ifndef VK_MVK_macos_surface
#error VK_MVK_macos_surface is not defined #error VK_MVK_macos_surface is not defined
#endif #endif
#elif defined(FILAMENT_IOS)
// Metal is not available when building for the iOS simulator on Desktop.
#define METAL_AVAILABLE __has_include(<QuartzCore/CAMetalLayer.h>)
#if METAL_AVAILABLE
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
#endif
#ifndef VK_MVK_ios_surface
#error VK_MVK_ios_surface is not defined
#endif
#define METALVIEW_TAG 255
#else
#error Not a supported Apple + Vulkan platform
#endif
using namespace bluevk; using namespace bluevk;
namespace filament::backend { namespace filament::backend {
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() { VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
ExtensionSet const ret = { ExtensionSet const ret = {
#if defined(__APPLE__)
VK_MVK_MACOS_SURFACE_EXTENSION_NAME, // TODO: replace with VK_EXT_metal_surface VK_MVK_MACOS_SURFACE_EXTENSION_NAME, // TODO: replace with VK_EXT_metal_surface
#elif defined(FILAMENT_IOS) && defined(METAL_AVAILABLE)
VK_MVK_IOS_SURFACE_EXTENSION_NAME,
#endif
}; };
return ret; return ret;
} }
@@ -50,26 +70,14 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataIm
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl( VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device, ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) { const ExternalImageMetadata& metadata) {
return {}; return {};
} }
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device, VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWindow,
SamplerYcbcrConversion chroma,
SamplerParams sampler,
uint32_t internalFormat) {
return VK_NULL_HANDLE;
}
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
return VK_NULL_HANDLE;
}
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept { VkInstance instance, uint64_t flags) noexcept {
VkSurfaceKHR surface; VkSurfaceKHR surface;
#if defined(__APPLE__)
NSView* nsview = (__bridge NSView*) nativeWindow; NSView* nsview = (__bridge NSView*) nativeWindow;
FILAMENT_CHECK_POSTCONDITION(nsview) << "Unable to obtain Metal-backed NSView."; FILAMENT_CHECK_POSTCONDITION(nsview) << "Unable to obtain Metal-backed NSView.";
@@ -83,6 +91,21 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativ
(VkSurfaceKHR*) &surface); (VkSurfaceKHR*) &surface);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkCreateMacOSSurfaceMVK. error=" << static_cast<int32_t>(result); << "vkCreateMacOSSurfaceMVK. error=" << static_cast<int32_t>(result);
#elif defined(FILAMENT_IOS) && defined(METAL_AVAILABLE)
CAMetalLayer* metalLayer = (CAMetalLayer*) nativeWindow;
// Create the VkSurface.
FILAMENT_CHECK_POSTCONDITION(vkCreateIOSSurfaceMVK)
<< "Unable to load vkCreateIOSSurfaceMVK function.";
VkIOSSurfaceCreateInfoMVK createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
createInfo.pNext = NULL;
createInfo.flags = 0;
createInfo.pView = metalLayer;
VkResult result = vkCreateIOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
(VkSurfaceKHR*) &surface);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkCreateIOSSurfaceMVK failed. error=" << static_cast<int32_t>(result);
#endif
return std::make_tuple(surface, VkExtent2D{}); return std::make_tuple(surface, VkExtent2D{});
} }
@@ -91,24 +91,11 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataIm
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl( VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device, ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) { const ExternalImageMetadata& metadata) {
return {}; return {};
} }
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device, VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
SamplerYcbcrConversion chroma,
SamplerParams sampler,
uint32_t internalFormat) {
return VK_NULL_HANDLE;
}
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
return VK_NULL_HANDLE;
}
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
VulkanPlatform::ExtensionSet const ret = { VulkanPlatform::ExtensionSet const ret = {
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND) #if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME, VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME,
@@ -126,7 +113,7 @@ VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl(
return ret; return ret;
} }
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow, VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept { VkInstance instance, uint64_t flags) noexcept {
VkSurfaceKHR surface; VkSurfaceKHR surface;
@@ -18,12 +18,12 @@
#define TNT_FILAMENT_BACKEND_VULKANSWAPCHAIN_IMPL_H #define TNT_FILAMENT_BACKEND_VULKANSWAPCHAIN_IMPL_H
#include "vulkan/VulkanContext.h" #include "vulkan/VulkanContext.h"
#include "vulkan/VulkanConstants.h"
#include <backend/platforms/VulkanPlatform.h> #include <backend/platforms/VulkanPlatform.h>
#include <bluevk/BlueVK.h> #include <bluevk/BlueVK.h>
#include <tuple>
#include <unordered_map> #include <unordered_map>
using namespace bluevk; using namespace bluevk;
@@ -588,79 +588,6 @@ VkComponentMapping getSwizzleMap(TextureSwizzle const swizzle[4]) {
return map; return map;
} }
VkFilter getFilter(SamplerMinFilter filter) {
switch (filter) {
case SamplerMinFilter::NEAREST:
return VK_FILTER_NEAREST;
case SamplerMinFilter::LINEAR:
return VK_FILTER_LINEAR;
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
return VK_FILTER_NEAREST;
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
return VK_FILTER_LINEAR;
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
return VK_FILTER_NEAREST;
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
return VK_FILTER_LINEAR;
}
}
VkFilter getFilter(SamplerMagFilter filter) {
switch (filter) {
case SamplerMagFilter::NEAREST:
return VK_FILTER_NEAREST;
case SamplerMagFilter::LINEAR:
return VK_FILTER_LINEAR;
}
}
VkSamplerMipmapMode getMipmapMode(SamplerMinFilter filter) {
switch (filter) {
case SamplerMinFilter::NEAREST:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerMinFilter::LINEAR:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
}
}
VkSamplerAddressMode getWrapMode(SamplerWrapMode mode) {
switch (mode) {
case SamplerWrapMode::REPEAT:
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case SamplerWrapMode::CLAMP_TO_EDGE:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case SamplerWrapMode::MIRRORED_REPEAT:
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
}
}
VkBool32 getCompareEnable(SamplerCompareMode mode) {
return mode == SamplerCompareMode::NONE ? VK_FALSE : VK_TRUE;
}
float getMaxLod(SamplerMinFilter filter) {
switch (filter) {
case SamplerMinFilter::NEAREST:
case SamplerMinFilter::LINEAR:
// The Vulkan spec recommends a max LOD of 0.25 to "disable" mipmapping.
// See "Mapping of OpenGL to Vulkan filter modes" in the VK Spec.
return 0.25f;
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
return VK_LOD_CLAMP_NONE;
}
}
VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags) { VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags) {
VkShaderStageFlags flags = 0x0; VkShaderStageFlags flags = 0x0;
if (any(stageFlags & ShaderStageFlags::VERTEX)) flags |= VK_SHADER_STAGE_VERTEX_BIT; if (any(stageFlags & ShaderStageFlags::VERTEX)) flags |= VK_SHADER_STAGE_VERTEX_BIT;
@@ -668,111 +595,4 @@ VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags) {
return flags; return flags;
} }
VkSamplerYcbcrModelConversion getYcbcrModelConversion(
SamplerYcbcrModelConversion model) {
switch (model) {
case SamplerYcbcrModelConversion::RGB_IDENTITY:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
case SamplerYcbcrModelConversion::YCBCR_IDENTITY:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY;
case SamplerYcbcrModelConversion::YCBCR_709:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709;
case SamplerYcbcrModelConversion::YCBCR_601:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601;
case SamplerYcbcrModelConversion::YCBCR_2020:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020;
default:
assert_invariant(false &&
"Unknown data type, conversion is not supported.");
}
}
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range) {
switch (range) {
case SamplerYcbcrRange::ITU_FULL:
return VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
case SamplerYcbcrRange::ITU_NARROW:
return VK_SAMPLER_YCBCR_RANGE_ITU_NARROW;
default:
assert_invariant(false &&
"Unknown data type, conversion is not supported.");
}
}
VkChromaLocation getChromaLocation(ChromaLocation loc) {
switch (loc) {
case ChromaLocation::COSITED_EVEN:
return VK_CHROMA_LOCATION_COSITED_EVEN;
case ChromaLocation::MIDPOINT:
return VK_CHROMA_LOCATION_MIDPOINT;
default:
assert_invariant(false &&
"Unknown data type, conversion is not supported.");
}
}
SamplerYcbcrModelConversion getYcbcrModelConversionFilament(VkSamplerYcbcrModelConversion model) {
switch (model) {
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY:
return SamplerYcbcrModelConversion::RGB_IDENTITY;
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY:
return SamplerYcbcrModelConversion::YCBCR_IDENTITY;
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709:
return SamplerYcbcrModelConversion::YCBCR_709;
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601:
return SamplerYcbcrModelConversion::YCBCR_601;
case VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020:
return SamplerYcbcrModelConversion::YCBCR_2020;
default:
assert_invariant(false && "Unknown data type, conversion is not supported.");
return {};
}
}
SamplerYcbcrRange getYcbcrRangeFilament(VkSamplerYcbcrRange range) {
switch (range) {
case VK_SAMPLER_YCBCR_RANGE_ITU_FULL:
return SamplerYcbcrRange::ITU_FULL;
case VK_SAMPLER_YCBCR_RANGE_ITU_NARROW:
return SamplerYcbcrRange::ITU_NARROW;
default:
assert_invariant(false && "Unknown data type, conversion is not supported.");
return {};
}
}
ChromaLocation getChromaLocationFilament(VkChromaLocation loc) {
switch (loc) {
case VK_CHROMA_LOCATION_COSITED_EVEN:
return ChromaLocation::COSITED_EVEN;
case VK_CHROMA_LOCATION_MIDPOINT:
return ChromaLocation::MIDPOINT;
default:
assert_invariant(false && "Unknown data type, conversion is not supported.");
return {};
}
}
TextureSwizzle getSwizzleFilament(VkComponentSwizzle c, uint8_t rgbaIndex) {
switch (c) {
case VK_COMPONENT_SWIZZLE_ZERO:
return TextureSwizzle::SUBSTITUTE_ZERO;
case VK_COMPONENT_SWIZZLE_ONE:
return TextureSwizzle::SUBSTITUTE_ONE;
case VK_COMPONENT_SWIZZLE_IDENTITY:
return (TextureSwizzle) (((uint8_t) TextureSwizzle::CHANNEL_0) + rgbaIndex);
case VK_COMPONENT_SWIZZLE_R:
return TextureSwizzle::CHANNEL_0;
case VK_COMPONENT_SWIZZLE_G:
return TextureSwizzle::CHANNEL_1;
case VK_COMPONENT_SWIZZLE_B:
return TextureSwizzle::CHANNEL_2;
case VK_COMPONENT_SWIZZLE_A:
return TextureSwizzle::CHANNEL_3;
default:
assert_invariant(false && "Unknown data type, conversion is not supported.");
return {};
}
}
} // namespace filament::backend::fvkutils } // namespace filament::backend::fvkutils
@@ -56,25 +56,6 @@ uint32_t getComponentCount(VkFormat format);
VkComponentMapping getSwizzleMap(TextureSwizzle const swizzle[4]); VkComponentMapping getSwizzleMap(TextureSwizzle const swizzle[4]);
VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags); VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags);
// Needed by the Platform for external sampler creation
VkFilter getFilter(SamplerMinFilter filter);
VkFilter getFilter(SamplerMagFilter filter);
VkSamplerMipmapMode getMipmapMode(SamplerMinFilter filter);
VkSamplerAddressMode getWrapMode(SamplerWrapMode mode);
VkBool32 getCompareEnable(SamplerCompareMode mode);
float getMaxLod(SamplerMinFilter filter);
// Ycbcr related functions
VkSamplerYcbcrModelConversion getYcbcrModelConversion(SamplerYcbcrModelConversion model);
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range);
VkChromaLocation getChromaLocation(ChromaLocation loc);
// Ycbcr related functions
SamplerYcbcrModelConversion getYcbcrModelConversionFilament(VkSamplerYcbcrModelConversion model);
SamplerYcbcrRange getYcbcrRangeFilament(VkSamplerYcbcrRange range);
ChromaLocation getChromaLocationFilament(VkChromaLocation loc);
TextureSwizzle getSwizzleFilament(VkComponentSwizzle c, uint8_t rgbaIndex);
inline VkImageViewType getViewType(SamplerType target) { inline VkImageViewType getViewType(SamplerType target) {
switch (target) { switch (target) {
case SamplerType::SAMPLER_CUBEMAP: case SamplerType::SAMPLER_CUBEMAP:
@@ -348,10 +348,6 @@ using SamplerBitmask = utils::bitset64;
// general. // general.
using InputAttachmentBitmask = utils::bitset64; using InputAttachmentBitmask = utils::bitset64;
constexpr uint8_t MAX_DESCRIPTOR_SET_BITMASK_BITS =
std::max(std::max(sizeof(UniformBufferBitmask), sizeof(SamplerBitmask)),
sizeof(InputAttachmentBitmask)) * 8;
template<typename Bitmask> template<typename Bitmask>
static constexpr uint8_t getVertexStageShift() noexcept { static constexpr uint8_t getVertexStageShift() noexcept {
// We assume the bottom half of bits are for vertex stages. // We assume the bottom half of bits are for vertex stages.
-150
View File
@@ -1,150 +0,0 @@
/*
* 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 "WebGPUHandles.h"
#include "WebGPUConstants.h"
#include "DriverBase.h"
#include <backend/DriverEnums.h>
#include <backend/Program.h>
#include <utils/Panic.h>
#include <utils/ostream.h>
#include <webgpu/webgpu_cpp.h>
#include <sstream>
#include <string_view>
#include <vector>
namespace filament::backend {
namespace {
[[nodiscard]] constexpr std::string_view toString(ShaderStage stage) {
switch (stage) {
case ShaderStage::VERTEX:
return "vertex";
case ShaderStage::FRAGMENT:
return "fragment";
case ShaderStage::COMPUTE:
return "compute";
}
}
[[nodiscard]] wgpu::ShaderModule createShaderModule(wgpu::Device& device, const char* programName,
std::array<utils::FixedCapacityVector<uint8_t>, Program::SHADER_TYPE_COUNT> const&
shaderSource,
ShaderStage stage) {
utils::FixedCapacityVector<uint8_t> const& sourceBytes =
shaderSource[static_cast<size_t>(stage)];
if (sourceBytes.empty()) {
return nullptr;// nothing to compile, the shader was not provided
}
wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{};
wgslDescriptor.code = wgpu::StringView(reinterpret_cast<const char*>(sourceBytes.data()));
std::stringstream labelStream;
labelStream << programName << " " << toString(stage) << " shader";
auto label = labelStream.str();
wgpu::ShaderModuleDescriptor descriptor{
.nextInChain = &wgslDescriptor,
.label = label.data()
};
wgpu::ShaderModule module = device.CreateShaderModule(&descriptor);
FILAMENT_CHECK_POSTCONDITION(module != nullptr) << "Failed to create " << descriptor.label;
module.GetCompilationInfo(wgpu::CallbackMode::AllowSpontaneous,
[&descriptor](auto const& status, wgpu::CompilationInfo const* info) {
switch (status) {
case wgpu::CompilationInfoRequestStatus::CallbackCancelled:
FWGPU_LOGW << "Shader compilation info callback cancelled for "
<< descriptor.label << "?" << utils::io::endl;
return;
case wgpu::CompilationInfoRequestStatus::Success:
break;
}
if (info != nullptr) {
std::stringstream errorStream;
int errorCount = 0;
for (size_t msgIndex = 0; msgIndex < info->messageCount; msgIndex++) {
wgpu::CompilationMessage const& message = info->messages[msgIndex];
switch (message.type) {
case wgpu::CompilationMessageType::Info:
FWGPU_LOGI << descriptor.label << ": " << message.message
<< " line#:" << message.lineNum
<< " linePos:" << message.linePos
<< " offset:" << message.offset
<< " length:" << message.length << utils::io::endl;
break;
case wgpu::CompilationMessageType::Warning:
FWGPU_LOGW << "Warning compiling " << descriptor.label << ": "
<< message.message << " line#:" << message.lineNum
<< " linePos:" << message.linePos
<< " offset:" << message.offset
<< " length:" << message.length << utils::io::endl;
break;
case wgpu::CompilationMessageType::Error:
errorCount++;
errorStream << "Error " << errorCount << " : "
<< std::string_view(message.message)
<< " line#:" << message.lineNum
<< " linePos:" << message.linePos
<< " offset:" << message.offset
<< " length:" << message.length << "\n";
break;
}
}
FILAMENT_CHECK_POSTCONDITION(errorCount < 1)
<< errorCount << " error(s) compiling " << descriptor.label << ":\n"
<< errorStream.str();
}
FWGPU_LOGD << descriptor.label << " compiled successfully" << utils::io::endl;
});
return module;
}
std::vector<wgpu::ConstantEntry> convertConstants(
utils::FixedCapacityVector<filament::backend::Program::SpecializationConstant> const&
constantsInfo) {
std::vector<wgpu::ConstantEntry> constants(constantsInfo.size());
for (size_t i = 0; i < constantsInfo.size(); i++) {
filament::backend::Program::SpecializationConstant const& specConstant = constantsInfo[i];
wgpu::ConstantEntry& constantEntry = constants[i];
constantEntry.key = wgpu::StringView(std::to_string(specConstant.id));
if (auto* v = std::get_if<int32_t>(&specConstant.value)) {
constantEntry.value = static_cast<double>(*v);
} else if (auto* f = std::get_if<float>(&specConstant.value)) {
constantEntry.value = static_cast<double>(*f);
} else if (auto* b = std::get_if<bool>(&specConstant.value)) {
constantEntry.value = *b ? 0.0 : 1.0;
}
}
return constants;
}
}// namespace
WGPUProgram::WGPUProgram(wgpu::Device& device, Program& program)
: HwProgram(program.getName()),
vertexShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
ShaderStage::VERTEX)),
fragmentShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
ShaderStage::FRAGMENT)),
computeShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
ShaderStage::COMPUTE)),
constants(convertConstants(program.getSpecializationConstants())) {}
}// namespace filament::backend
@@ -1,66 +0,0 @@
/*
* 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.
*/
#ifndef TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
#define TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
#include <utils/Log.h>
#include <cstdint>
// FWGPU is short for Filament WebGPU
// turn on runtime validation, namely for debugging, that would normally not run (for release)
#define FWGPU_DEBUG_VALIDATION 0x00000001
// print/log system component details to the console, e.g. about the
// instance, surface, adapter, device, etc.
#define FWGPU_PRINT_SYSTEM 0x00000002
// Set this to enable logging "only" to one output stream. This is useful in the case where we want
// to debug with print statements and want ordered logging (e.g slog.i and slog.e will not appear in
// order of calls).
#define FWGPU_DEBUG_FORCE_LOG_TO_I 0x00000004
// Useful default combinations
#define FWGPU_DEBUG_EVERYTHING 0xFFFFFFFF
#if defined(FILAMENT_BACKEND_DEBUG_FLAG)
#define FWGPU_DEBUG_FORWARDED_FLAG (FILAMENT_BACKEND_DEBUG_FLAG & FWGPU_DEBUG_EVERYTHING)
#else
#define FWGPU_DEBUG_FORWARDED_FLAG 0
#endif
#ifndef NDEBUG
#define FWGPU_DEBUG_FLAGS FWGPU_DEBUG_FORWARDED_FLAG
#else
#define FWGPU_DEBUG_FLAGS 0
#endif
#define FWGPU_ENABLED(flags) (((FWGPU_DEBUG_FLAGS) & (flags)) == (flags))
#if FWGPU_ENABLED(FWGPU_DEBUG_FORCE_LOG_TO_I)
#define FWGPU_LOGI (utils::slog.i)
#define FWGPU_LOGD FWGPU_LOGI
#define FWGPU_LOGE FWGPU_LOGI
#define FWGPU_LOGW FWGPU_LOGI
#else
#define FWGPU_LOGE (utils::slog.e)
#define FWGPU_LOGW (utils::slog.w)
#define FWGPU_LOGD (utils::slog.d)
#define FWGPU_LOGI (utils::slog.i)
#endif
#endif// TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
+12 -488
View File
@@ -14,230 +14,21 @@
* limitations under the License. * limitations under the License.
*/ */
#include "webgpu/WebGPUDriver.h"
#include "WebGPUSwapChain.h"
#include "webgpu/WebGPUConstants.h"
#include <backend/platforms/WebGPUPlatform.h>
#include "CommandStreamDispatcher.h"
#include "DriverBase.h"
#include "private/backend/Dispatcher.h"
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <backend/Handle.h> #include <backend/Handle.h>
#include <math/mat3.h> #include "webgpu/WebGPUDriver.h"
#include <utils/CString.h> #include "CommandStreamDispatcher.h"
#include <utils/ostream.h>
#include <dawn/webgpu_cpp_print.h> #include <stdint.h>
#include <webgpu/webgpu_cpp.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <sstream>
#include <string_view>
#include <utility>
#include <variant>
namespace filament::backend { namespace filament::backend {
namespace { Driver* WebGPUDriver::create() {
return new WebGPUDriver();
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printInstanceDetails(wgpu::Instance const& instance) {
wgpu::SupportedWGSLLanguageFeatures supportedWGSLLanguageFeatures{};
if (!instance.GetWGSLLanguageFeatures(&supportedWGSLLanguageFeatures)) {
FWGPU_LOGW << "Failed to get WebGPU instance supported WGSL language features"
<< utils::io::endl;
} else {
FWGPU_LOGI << "WebGPU instance supported WGSL language features ("
<< supportedWGSLLanguageFeatures.featureCount << "):" << utils::io::endl;
if (supportedWGSLLanguageFeatures.featureCount > 0 &&
supportedWGSLLanguageFeatures.features != nullptr) {
std::for_each(supportedWGSLLanguageFeatures.features,
supportedWGSLLanguageFeatures.features +
supportedWGSLLanguageFeatures.featureCount,
[](wgpu::WGSLLanguageFeatureName const featureName) {
std::stringstream nameStream{};
nameStream << featureName;
FWGPU_LOGI << " " << nameStream.str() << utils::io::endl;
});
}
}
}
#endif
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printLimit(std::string_view name, const std::variant<uint32_t, uint64_t> value) {
FWGPU_LOGI << " " << name.data() << ": ";
bool undefined = true;
if (std::holds_alternative<uint32_t>(value)) {
if (std::get<uint32_t>(value) != WGPU_LIMIT_U32_UNDEFINED) {
undefined = false;
FWGPU_LOGI << std::get<uint32_t>(value);
}
} else if (std::holds_alternative<uint64_t>(value)) {
if (std::get<uint64_t>(value) != WGPU_LIMIT_U64_UNDEFINED) {
undefined = false;
FWGPU_LOGI << std::get<uint64_t>(value);
}
}
if (undefined) {
FWGPU_LOGI << "UNDEFINED";
}
FWGPU_LOGI << utils::io::endl;
}
#endif
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printLimits(wgpu::Limits const& limits) {
printLimit("maxTextureDimension1D", limits.maxTextureDimension1D);
printLimit("maxTextureDimension2D", limits.maxTextureDimension2D);
printLimit("maxTextureDimension3D", limits.maxTextureDimension3D);
printLimit("maxTextureArrayLayers", limits.maxTextureArrayLayers);
printLimit("maxBindGroups", limits.maxBindGroups);
printLimit("maxBindGroupsPlusVertexBuffers", limits.maxBindGroupsPlusVertexBuffers);
printLimit("maxBindingsPerBindGroup", limits.maxBindingsPerBindGroup);
printLimit("maxDynamicUniformBuffersPerPipelineLayout",
limits.maxDynamicUniformBuffersPerPipelineLayout);
printLimit("maxDynamicStorageBuffersPerPipelineLayout",
limits.maxDynamicStorageBuffersPerPipelineLayout);
printLimit("maxSampledTexturesPerShaderStage", limits.maxSampledTexturesPerShaderStage);
printLimit("maxSamplersPerShaderStage", limits.maxSamplersPerShaderStage);
printLimit("maxStorageBuffersPerShaderStage", limits.maxStorageBuffersPerShaderStage);
printLimit("maxStorageTexturesPerShaderStage", limits.maxStorageTexturesPerShaderStage);
printLimit("maxUniformBuffersPerShaderStage", limits.maxUniformBuffersPerShaderStage);
printLimit("maxUniformBufferBindingSize", limits.maxUniformBufferBindingSize);
printLimit("maxStorageBufferBindingSize", limits.maxStorageBufferBindingSize);
printLimit("minUniformBufferOffsetAlignment", limits.minUniformBufferOffsetAlignment);
printLimit("minStorageBufferOffsetAlignment", limits.minStorageBufferOffsetAlignment);
printLimit("maxVertexBuffers", limits.maxVertexBuffers);
printLimit("maxBufferSize", limits.maxBufferSize);
printLimit("maxVertexAttributes", limits.maxVertexAttributes);
printLimit("maxVertexBufferArrayStride", limits.maxVertexBufferArrayStride);
printLimit("maxInterStageShaderVariables", limits.maxInterStageShaderVariables);
printLimit("maxColorAttachments", limits.maxColorAttachments);
printLimit("maxColorAttachmentBytesPerSample", limits.maxColorAttachmentBytesPerSample);
printLimit("maxComputeWorkgroupStorageSize", limits.maxComputeWorkgroupStorageSize);
printLimit("maxComputeInvocationsPerWorkgroup", limits.maxComputeInvocationsPerWorkgroup);
printLimit("maxComputeWorkgroupSizeX", limits.maxComputeWorkgroupSizeX);
printLimit("maxComputeWorkgroupSizeY", limits.maxComputeWorkgroupSizeY);
printLimit("maxComputeWorkgroupSizeZ", limits.maxComputeWorkgroupSizeZ);
printLimit("maxComputeWorkgroupsPerDimension", limits.maxComputeWorkgroupsPerDimension);
printLimit("maxStorageBuffersInVertexStage", limits.maxStorageBuffersInVertexStage);
printLimit("maxStorageTexturesInVertexStage", limits.maxStorageTexturesInVertexStage);
printLimit("maxStorageBuffersInFragmentStage", limits.maxStorageBuffersInFragmentStage);
printLimit("maxStorageTexturesInFragmentStage", limits.maxStorageTexturesInFragmentStage);
}
#endif
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printAdapterDetails(wgpu::Adapter const& adapter) {
wgpu::DawnAdapterPropertiesPowerPreference powerPreferenceProperties{};
wgpu::AdapterInfo adapterInfo{};
adapterInfo.nextInChain = &powerPreferenceProperties;
if (!adapter.GetInfo(&adapterInfo)) {
FWGPU_LOGW << "Failed to get WebGPU adapter info" << utils::io::endl;
} else {
std::stringstream backendTypeStream{};
backendTypeStream << adapterInfo.backendType;
std::stringstream adapterTypeStream{};
adapterTypeStream << adapterInfo.adapterType;
std::stringstream powerPreferenceStream{};
powerPreferenceStream << powerPreferenceProperties.powerPreference;
FWGPU_LOGI << "WebGPU adapter info:" << utils::io::endl;
FWGPU_LOGI << " vendor: " << adapterInfo.vendor.data << utils::io::endl;
FWGPU_LOGI << " architecture: " << adapterInfo.architecture.data << utils::io::endl;
FWGPU_LOGI << " device: " << adapterInfo.device.data << utils::io::endl;
FWGPU_LOGI << " description: " << adapterInfo.description.data << utils::io::endl;
FWGPU_LOGI << " backend type: " << backendTypeStream.str().data() << utils::io::endl;
FWGPU_LOGI << " adapter type: " << adapterTypeStream.str().data() << utils::io::endl;
FWGPU_LOGI << " device ID: " << adapterInfo.deviceID << utils::io::endl;
FWGPU_LOGI << " vendor ID: " << adapterInfo.vendorID << utils::io::endl;
FWGPU_LOGI << " subgroup min size: " << adapterInfo.subgroupMinSize << utils::io::endl;
FWGPU_LOGI << " subgroup max size: " << adapterInfo.subgroupMaxSize << utils::io::endl;
FWGPU_LOGI << " power preference: " << powerPreferenceStream.str() << utils::io::endl;
}
wgpu::SupportedFeatures supportedFeatures{};
adapter.GetFeatures(&supportedFeatures);
FWGPU_LOGI << "WebGPU adapter supported features (" << supportedFeatures.featureCount
<< "):" << utils::io::endl;
if (supportedFeatures.featureCount > 0 && supportedFeatures.features != nullptr) {
std::for_each(supportedFeatures.features,
supportedFeatures.features + supportedFeatures.featureCount,
[](wgpu::FeatureName const name) {
std::stringstream nameStream{};
nameStream << name;
FWGPU_LOGI << " " << nameStream.str().data() << utils::io::endl;
});
}
wgpu::Limits supportedLimits{};
if (!adapter.GetLimits(&supportedLimits)) {
FWGPU_LOGW << "Failed to get WebGPU adapter supported limits" << utils::io::endl;
} else {
FWGPU_LOGI << "WebGPU adapter supported limits:" << utils::io::endl;
printLimits(supportedLimits);
}
}
#endif
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printDeviceDetails(wgpu::Device const& device) {
wgpu::SupportedFeatures supportedFeatures{};
device.GetFeatures(&supportedFeatures);
FWGPU_LOGI << "WebGPU device supported features (" << supportedFeatures.featureCount
<< "):" << utils::io::endl;
if (supportedFeatures.featureCount > 0 && supportedFeatures.features != nullptr) {
std::for_each(supportedFeatures.features,
supportedFeatures.features + supportedFeatures.featureCount,
[](wgpu::FeatureName const name) {
std::stringstream nameStream{};
nameStream << name;
FWGPU_LOGI << " " << nameStream.str().data() << utils::io::endl;
});
}
wgpu::Limits supportedLimits{};
if (!device.GetLimits(&supportedLimits)) {
FWGPU_LOGW << "Failed to get WebGPU supported device limits" << utils::io::endl;
} else {
FWGPU_LOGI << "WebGPU device supported limits:" << utils::io::endl;
printLimits(supportedLimits);
}
}
#endif
}// namespace
Driver* WebGPUDriver::create(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept {
constexpr size_t defaultSize = FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
Platform::DriverConfig validConfig {driverConfig};
validConfig.handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize);
return new WebGPUDriver(platform, validConfig);
} }
WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept WebGPUDriver::WebGPUDriver() noexcept = default;
: mPlatform(platform),
mHandleAllocator("Handles",
driverConfig.handleArenaSize,
driverConfig.disableHandleUseAfterFreeCheck,
driverConfig.disableHeapHandleTags) {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printInstanceDetails(mPlatform.getInstance());
#endif
mAdapter = mPlatform.requestAdapter(nullptr);
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printAdapterDetails(mAdapter);
#endif
mDevice = mPlatform.requestDevice(mAdapter);
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printDeviceDetails(mDevice);
#endif
}
WebGPUDriver::~WebGPUDriver() noexcept = default; WebGPUDriver::~WebGPUDriver() noexcept = default;
@@ -265,7 +56,6 @@ void WebGPUDriver::terminate() {
} }
void WebGPUDriver::tick(int) { void WebGPUDriver::tick(int) {
mDevice.Tick();
} }
void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns, void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns,
@@ -295,27 +85,15 @@ void WebGPUDriver::finish(int) {
} }
void WebGPUDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) { void WebGPUDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
if (rph) {
destructHandle<WGPURenderPrimitive>(rph);
}
} }
void WebGPUDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) { void WebGPUDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
if (vbih) {
destructHandle<WGPUVertexBufferInfo>(vbih);
}
} }
void WebGPUDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) { void WebGPUDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
if (vbh) {
destructHandle<WGPUVertexBuffer>(vbh);
}
} }
void WebGPUDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) { void WebGPUDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
if (ibh) {
destructHandle<WGPUIndexBuffer>(ibh);
}
} }
void WebGPUDriver::destroyBufferObject(Handle<HwBufferObject> boh) { void WebGPUDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
@@ -325,19 +103,12 @@ void WebGPUDriver::destroyTexture(Handle<HwTexture> th) {
} }
void WebGPUDriver::destroyProgram(Handle<HwProgram> ph) { void WebGPUDriver::destroyProgram(Handle<HwProgram> ph) {
if (ph) {
destructHandle<WGPUProgram>(ph);
}
} }
void WebGPUDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) { void WebGPUDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
} }
void WebGPUDriver::destroySwapChain(Handle<HwSwapChain> sch) { void WebGPUDriver::destroySwapChain(Handle<HwSwapChain> sch) {
if (sch) {
destructHandle<WebGPUSwapChain>(sch);
}
mSwapChain = nullptr;
} }
void WebGPUDriver::destroyStream(Handle<HwStream> sh) { void WebGPUDriver::destroyStream(Handle<HwStream> sh) {
@@ -347,193 +118,11 @@ void WebGPUDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
} }
void WebGPUDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh) { void WebGPUDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh) {
if (tqh) {
destructHandle<WebGPUDescriptorSetLayout>(tqh);
}
} }
void WebGPUDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) { void WebGPUDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) {
} }
Handle<HwSwapChain> WebGPUDriver::createSwapChainS() noexcept {
return allocHandle<WebGPUSwapChain>();
}
Handle<HwSwapChain> WebGPUDriver::createSwapChainHeadlessS() noexcept {
return Handle<HwSwapChain>((Handle<HwSwapChain>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::createTextureS() noexcept {
return allocHandle<WGPUTexture>();
}
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwProgram> WebGPUDriver::createProgramS() noexcept {
return allocHandle<WGPUProgram>();
}
Handle<HwFence> WebGPUDriver::createFenceS() noexcept {
return Handle<HwFence>((Handle<HwFence>::HandleId) mNextFakeHandle++);
}
Handle<HwTimerQuery> WebGPUDriver::createTimerQueryS() noexcept {
return Handle<HwTimerQuery>((Handle<HwTimerQuery>::HandleId) mNextFakeHandle++);
}
Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
return allocHandle<HwIndexBuffer>();
}
Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
return allocHandle<WGPUBufferObject>();
}
Handle<HwRenderTarget> WebGPUDriver::createRenderTargetS() noexcept {
return allocHandle<WGPURenderTarget>();
}
Handle<HwVertexBuffer> WebGPUDriver::createVertexBufferS() noexcept {
return allocHandle<WGPUVertexBuffer>();
}
Handle<HwDescriptorSet> WebGPUDriver::createDescriptorSetS() noexcept {
return Handle<HwDescriptorSet>((Handle<HwDescriptorSet>::HandleId) mNextFakeHandle++);
}
Handle<HwRenderPrimitive> WebGPUDriver::createRenderPrimitiveS() noexcept {
return allocHandle<WGPURenderPrimitive>();
}
Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
return allocHandle<WGPUVertexBufferInfo>();
}
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
return allocHandle<WGPURenderTarget>();
}
Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcept {
return allocHandle<WebGPUDescriptorSetLayout>();
}
Handle<HwTexture> WebGPUDriver::createTextureExternalImageS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::createTextureExternalImage2S() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
mNativeWindow = nativeWindow;
assert_invariant(!mSwapChain);
wgpu::Surface surface = mPlatform.createSurface(nativeWindow, flags);
mQueue = mDevice.GetQueue();
wgpu::Extent2D surfaceSize = mPlatform.getSurfaceExtent(mNativeWindow);
mSwapChain = constructHandle<WebGPUSwapChain>(sch, std::move(surface), surfaceSize, mAdapter,
mDevice, flags);
assert_invariant(mSwapChain);
FWGPU_LOGW << "WebGPU support is still essentially a no-op at this point in development (only "
"background components have been instantiated/selected, such as surface/screen, "
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
<< utils::io::endl;
#if !FWGPU_ENABLED(FWGPU_PRINT_SYSTEM) && !defined(NDEBUG)
FWGPU_LOGI << "If the FILAMENT_BACKEND_DEBUG_FLAG variable were set with the " << utils::io::hex
<< FWGPU_PRINT_SYSTEM << utils::io::dec
<< " bit flag on during build time the application would print system details "
"about the selected graphics device, surface, etc. To see this try "
"rebuilding Filament with that flag, e.g. ./build.sh -x "
<< FWGPU_PRINT_SYSTEM << " ..." << utils::io::endl;
#endif
}
void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
uint32_t height, uint64_t flags) {}
void WebGPUDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes) {}
void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint32_t vertexCount,
Handle<HwVertexBufferInfo> vbih) {}
void WebGPUDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) {}
void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {}
void WebGPUDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage usage) {}
void WebGPUDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
uint8_t baseLevel, uint8_t levelCount) {}
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
backend::TextureSwizzle a) {}
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
Platform::ExternalImageHandleRef externalImage) {}
void WebGPUDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* externalImage) {}
void WebGPUDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, uint32_t plane) {}
void WebGPUDriver::importTextureR(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) {}
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {}
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
constructHandle<WGPUProgram>(ph, mDevice, program);
}
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
assert_invariant(!mDefaultRenderTarget);
mDefaultRenderTarget = constructHandle<WGPURenderTarget>(rth);
assert_invariant(mDefaultRenderTarget);
}
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets,
uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color,
TargetBufferInfo depth, TargetBufferInfo stencil) {}
void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {}
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}
void WebGPUDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
backend::DescriptorSetLayout&& info) {
constructHandle<WebGPUDescriptorSetLayout>(dslh, std::move(info), mDevice);
}
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
Handle<HwDescriptorSetLayout> dslh) {}
Handle<HwStream> WebGPUDriver::createStreamNative(void* nativeStream) { Handle<HwStream> WebGPUDriver::createStreamNative(void* nativeStream) {
return {}; return {};
} }
@@ -651,14 +240,6 @@ size_t WebGPUDriver::getMaxUniformBufferSize() {
return 16384u; return 16384u;
} }
size_t WebGPUDriver::getMaxTextureSize(SamplerType target) {
return 2048u;
}
size_t WebGPUDriver::getMaxArrayTextureLayers() {
return 256u;
}
void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p, void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
uint32_t byteOffset) { uint32_t byteOffset) {
scheduleDestroy(std::move(p)); scheduleDestroy(std::move(p));
@@ -679,10 +260,6 @@ void WebGPUDriver::resetBufferObject(Handle<HwBufferObject> boh) {
void WebGPUDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index, void WebGPUDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index,
Handle<HwBufferObject> boh) { Handle<HwBufferObject> boh) {
auto* vertexBuffer = handleCast<WGPUVertexBuffer>(vbh);
auto* bufferObject = handleCast<WGPUBufferObject>(boh);
assert_invariant(index < vertexBuffer->buffers.size());
vertexBuffer->setBuffer(bufferObject, index);
} }
void WebGPUDriver::update3DImage(Handle<HwTexture> th, void WebGPUDriver::update3DImage(Handle<HwTexture> th,
@@ -715,71 +292,18 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
} }
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) { void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = {
.label = "command_encoder"
};
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
assert_invariant(mCommandEncoder);
// TODO: Remove this code once WebGPU pipeline is implemented
static float red = 1.0f;
if (red - 0.01 > 0) {
red -= 0.01;
} else {
red = 1.0f;
}
assert_invariant(mTextureView);
wgpu::RenderPassColorAttachment renderPassColorAttachment = {
.view = mTextureView,
// TODO: remove this code once WebGPU Pipeline is implemented with render targets, pipeline and buffers.
.depthSlice = wgpu::kDepthSliceUndefined,
.loadOp = wgpu::LoadOp::Clear,
.storeOp = wgpu::StoreOp::Store,
.clearValue = wgpu::Color{red, 0 , 0 , 1},
};
wgpu::RenderPassDescriptor renderPassDescriptor = {
.colorAttachmentCount = 1,
.colorAttachments = &renderPassColorAttachment,
.depthStencilAttachment = nullptr,
.timestampWrites = nullptr,
};
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
mRenderPassEncoder.SetViewport(params.viewport.left, params.viewport.bottom,
params.viewport.width, params.viewport.height, params.depthRange.near, params.depthRange.far);
} }
void WebGPUDriver::endRenderPass(int) { void WebGPUDriver::endRenderPass(int) {
mRenderPassEncoder.End();
mRenderPassEncoder = nullptr;
wgpu::CommandBufferDescriptor commandBufferDescriptor {
.label = "command_buffer",
};
mCommandBuffer = mCommandEncoder.Finish(&commandBufferDescriptor);
assert_invariant(mCommandBuffer);
} }
void WebGPUDriver::nextSubpass(int) { void WebGPUDriver::nextSubpass(int) {
} }
void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) { void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) {
ASSERT_PRECONDITION_NON_FATAL(drawSch == readSch,
"WebGPU driver does not support distinct draw/read swap chains.");
auto* swapChain = handleCast<WebGPUSwapChain>(drawSch);
mSwapChain = swapChain;
assert_invariant(mSwapChain);
wgpu::Extent2D surfaceSize = mPlatform.getSurfaceExtent(mNativeWindow);
mTextureView = mSwapChain->getCurrentSurfaceTextureView(surfaceSize);
assert_invariant(mTextureView);
} }
void WebGPUDriver::commit(Handle<HwSwapChain> sch) { void WebGPUDriver::commit(Handle<HwSwapChain> sch) {
mCommandEncoder = nullptr;
mQueue.Submit(1, &mCommandBuffer);
mCommandBuffer = nullptr;
mTextureView = nullptr;
assert_invariant(mSwapChain);
mSwapChain->present();
} }
void WebGPUDriver::setPushConstant(backend::ShaderStage stage, uint8_t index, void WebGPUDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
@@ -807,7 +331,7 @@ void WebGPUDriver::readPixels(Handle<HwRenderTarget> src,
scheduleDestroy(std::move(p)); scheduleDestroy(std::move(p));
} }
void WebGPUDriver::readBufferSubData(Handle<HwBufferObject> boh, void WebGPUDriver::readBufferSubData(backend::BufferObjectHandle boh,
uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) { uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) {
scheduleDestroy(std::move(p)); scheduleDestroy(std::move(p));
} }
@@ -859,22 +383,22 @@ void WebGPUDriver::resetState(int) {
} }
void WebGPUDriver::updateDescriptorSetBuffer( void WebGPUDriver::updateDescriptorSetBuffer(
Handle<HwDescriptorSet> dsh, backend::DescriptorSetHandle dsh,
backend::descriptor_binding_t binding, backend::descriptor_binding_t binding,
Handle<HwBufferObject> boh, backend::BufferObjectHandle boh,
uint32_t offset, uint32_t offset,
uint32_t size) { uint32_t size) {
} }
void WebGPUDriver::updateDescriptorSetTexture( void WebGPUDriver::updateDescriptorSetTexture(
Handle<HwDescriptorSet> dsh, backend::DescriptorSetHandle dsh,
backend::descriptor_binding_t binding, backend::descriptor_binding_t binding,
Handle<HwTexture> th, backend::TextureHandle th,
SamplerParams params) { SamplerParams params) {
} }
void WebGPUDriver::bindDescriptorSet( void WebGPUDriver::bindDescriptorSet(
Handle<HwDescriptorSet> dsh, backend::DescriptorSetHandle dsh,
backend::descriptor_set_t set, backend::descriptor_set_t set,
backend::DescriptorSetOffsetArray&& offsets) { backend::DescriptorSetOffsetArray&& offsets) {
} }
+14 -71
View File
@@ -17,62 +17,31 @@
#ifndef TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H #ifndef TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
#define TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H #define TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
#include "WebGPUHandles.h"
#include <backend/platforms/WebGPUPlatform.h>
#include "DriverBase.h"
#include "private/backend/Dispatcher.h"
#include "private/backend/Driver.h" #include "private/backend/Driver.h"
#include "private/backend/HandleAllocator.h" #include "DriverBase.h"
#include <backend/DriverEnums.h>
#include <utils/compiler.h> #include <utils/compiler.h>
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
#include <memory>
#ifndef FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB
# define FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB 8
#endif
namespace filament::backend { namespace filament::backend {
class WebGPUSwapChain;
/**
* WebGPU backend (driver) implementation
*/
class WebGPUDriver final : public DriverBase { class WebGPUDriver final : public DriverBase {
public: WebGPUDriver() noexcept;
~WebGPUDriver() noexcept override; ~WebGPUDriver() noexcept override;
Dispatcher getDispatcher() const noexcept final;
[[nodiscard]] Dispatcher getDispatcher() const noexcept final; public:
[[nodiscard]] static Driver* create(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept; static Driver* create();
private: private:
explicit WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept; ShaderModel getShaderModel() const noexcept final;
[[nodiscard]] ShaderModel getShaderModel() const noexcept final; ShaderLanguage getShaderLanguage() const noexcept final;
[[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final;
uint64_t nextFakeHandle = 1;
// the platform (e.g. OS) specific aspects of the WebGPU backend are strictly only
// handled in the WebGPUPlatform
WebGPUPlatform& mPlatform;
wgpu::Adapter mAdapter = nullptr;
wgpu::Device mDevice = nullptr;
wgpu::Queue mQueue = nullptr;
void* mNativeWindow = nullptr;
WebGPUSwapChain* mSwapChain = nullptr;
uint64_t mNextFakeHandle = 1;
wgpu::CommandEncoder mCommandEncoder = nullptr;
wgpu::TextureView mTextureView = nullptr;
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
wgpu::CommandBuffer mCommandBuffer = nullptr;
WGPURenderTarget* mDefaultRenderTarget = nullptr;
/* /*
* Driver interface * Driver interface
*/ */
template<typename T> template<typename T>
friend class ConcreteDispatcher; friend class ConcreteDispatcher;
@@ -83,39 +52,13 @@ private:
RetType methodName(paramsDecl) override; RetType methodName(paramsDecl) override;
#define DECL_DRIVER_API_RETURN(RetType, methodName, paramsDecl, params) \ #define DECL_DRIVER_API_RETURN(RetType, methodName, paramsDecl, params) \
RetType methodName##S() noexcept override; \ RetType methodName##S() noexcept override { \
UTILS_ALWAYS_INLINE inline void methodName##R(RetType, paramsDecl); return RetType((RetType::HandleId)nextFakeHandle++); } \
UTILS_ALWAYS_INLINE inline void methodName##R(RetType, paramsDecl) { }
#include "private/backend/DriverAPI.inc" #include "private/backend/DriverAPI.inc"
/*
* Memory management
*/
HandleAllocatorWGPU mHandleAllocator;
template<typename D>
Handle<D> allocHandle() {
return mHandleAllocator.allocate<D>();
}
template<typename D, typename B, typename ... ARGS>
D* constructHandle(Handle<B>& handle, ARGS&& ... args) noexcept {
return mHandleAllocator.construct<D>(handle, std::forward<ARGS>(args)...);
}
template<typename D, typename B>
D* handleCast(Handle<B> handle) noexcept {
return mHandleAllocator.handle_cast<D*>(handle);
}
template<typename D, typename B>
void destructHandle(Handle<B>& handle) noexcept {
auto* p = mHandleAllocator.handle_cast<D*>(handle);
return mHandleAllocator.deallocate(handle, p);
}
}; };
}// namespace filament::backend } // namespace filament
#endif // TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H #endif // TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
@@ -1,152 +0,0 @@
/*
* 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 "WebGPUHandles.h"
#include <utility>
namespace {
wgpu::Buffer createIndexBuffer(wgpu::Device const& device, uint8_t elementSize, uint32_t indexCount) {
wgpu::BufferDescriptor descriptor{ .label = "index_buffer",
.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Index,
.size = elementSize * indexCount,
.mappedAtCreation = false };
return device.CreateBuffer(&descriptor);
}
} // namespace
namespace filament::backend {
WGPUIndexBuffer::WGPUIndexBuffer(wgpu::Device const& device, uint8_t elementSize,
uint32_t indexCount)
: buffer(createIndexBuffer(device, elementSize, indexCount)) {}
WGPUVertexBuffer::WGPUVertexBuffer(wgpu::Device const &device, uint32_t vextexCount, uint32_t bufferCount,
Handle<WGPUVertexBufferInfo> vbih)
: HwVertexBuffer(vextexCount),
vbih(vbih),
buffers(bufferCount) {
wgpu::BufferDescriptor descriptor {
.usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Vertex,
.size = vextexCount * bufferCount,
.mappedAtCreation = false };
for (uint32_t i = 0; i < bufferCount; ++i) {
descriptor.label = ("vertex_buffer_" + std::to_string(i)).c_str();
buffers[i] = device.CreateBuffer(&descriptor);
}
}
// TODO: Empty function is a place holder for verxtex buffer updates and should be
// updated for that purpose.
void WGPUVertexBuffer::setBuffer(WGPUBufferObject* bufferObject, uint32_t index) {}
WGPUBufferObject::WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount)
: HwBufferObject(byteCount),
bufferObjectBinding(bindingType) {}
wgpu::ShaderStage WebGPUDescriptorSetLayout::filamentStageToWGPUStage(ShaderStageFlags fFlags) {
wgpu::ShaderStage retStages = wgpu::ShaderStage::None;
if (any(ShaderStageFlags::VERTEX & fFlags)) {
retStages |= wgpu::ShaderStage::Vertex;
}
if (any(ShaderStageFlags::FRAGMENT & fFlags)) {
retStages |= wgpu::ShaderStage::Fragment;
}
if (any(ShaderStageFlags::COMPUTE & fFlags)) {
retStages |= wgpu::ShaderStage::Compute;
}
return retStages;
}
WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout,
wgpu::Device const& device) {
assert_invariant(device);
// TODO: layoutDescriptor has a "Label". Ideally we can get info on what this layout is for
// debugging. For now, hack an incrementing value.
static int layoutNum = 0;
uint samplerCount =
std::count_if(layout.bindings.begin(), layout.bindings.end(), [](auto& fEntry) {
return fEntry.type == DescriptorType::SAMPLER ||
fEntry.type == DescriptorType::SAMPLER_EXTERNAL;
});
std::vector<wgpu::BindGroupLayoutEntry> wEntries;
wEntries.reserve(layout.bindings.size() + samplerCount);
for (auto fEntry: layout.bindings) {
auto& wEntry = wEntries.emplace_back();
wEntry.visibility = filamentStageToWGPUStage(fEntry.stageFlags);
wEntry.binding = fEntry.binding * 2;
switch (fEntry.type) {
// TODO Metal treats these the same. Is this fine?
case DescriptorType::SAMPLER_EXTERNAL:
case DescriptorType::SAMPLER: {
// Sampler binding is 2n+1 due to split.
auto& samplerEntry = wEntries.emplace_back();
samplerEntry.binding = fEntry.binding * 2 + 1;
samplerEntry.visibility = wEntry.visibility;
// We are simply hoping that undefined and defaults suffices here.
samplerEntry.sampler.type = wgpu::SamplerBindingType::Undefined;
wEntry.texture.sampleType = wgpu::TextureSampleType::Undefined;
break;
}
case DescriptorType::UNIFORM_BUFFER: {
wEntry.buffer.hasDynamicOffset =
any(fEntry.flags & DescriptorFlags::DYNAMIC_OFFSET);
wEntry.buffer.type = wgpu::BufferBindingType::Uniform;
// TODO: Ideally we fill minBindingSize
break;
}
case DescriptorType::INPUT_ATTACHMENT: {
// TODO: support INPUT_ATTACHMENT. Metal does not currently.
PANIC_POSTCONDITION("Input Attachment is not supported");
break;
}
case DescriptorType::SHADER_STORAGE_BUFFER: {
// TODO: Vulkan does not support this, can we?
PANIC_POSTCONDITION("Shader storage is not supported");
break;
}
}
// Currently flags are only used to specify dynamic offset.
// UNUSED
// fEntry.count
}
wgpu::BindGroupLayoutDescriptor layoutDescriptor{
// TODO: layoutDescriptor has a "Label". Ideally we can get info on what this layout is for
// debugging. For now, hack an incrementing value.
.label{ "layout_" + std::to_string(++layoutNum) },
.entryCount = wEntries.size(),
.entries = wEntries.data()
};
// TODO Do we need to defer this until we have more info on textures and samplers??
mLayout = device.CreateBindGroupLayout(&layoutDescriptor);
}
WebGPUDescriptorSetLayout::~WebGPUDescriptorSetLayout() {}
}// namespace filament::backend
-167
View File
@@ -1,167 +0,0 @@
/*
* 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.
*/
#ifndef TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
#define TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
#include "DriverBase.h"
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <utils/FixedCapacityVector.h>
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
#include <vector>
namespace filament::backend {
class WGPUProgram final : public HwProgram {
public:
WGPUProgram(wgpu::Device&, Program&);
wgpu::ShaderModule vertexShaderModule = nullptr;
wgpu::ShaderModule fragmentShaderModule = nullptr;
wgpu::ShaderModule computeShaderModule = nullptr;
std::vector<wgpu::ConstantEntry> constants;
};
struct WGPUBufferObject;
// TODO: Currently WGPUVertexBufferInfo is not used by WebGPU for useful task.
// Update the struct when used by WebGPU driver.
struct WGPUVertexBufferInfo : public HwVertexBufferInfo {
WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
attributes(attributes) {}
AttributeArray attributes;
};
struct WGPUVertexBuffer : public HwVertexBuffer {
WGPUVertexBuffer(wgpu::Device const &device, uint32_t vextexCount, uint32_t bufferCount,
Handle<WGPUVertexBufferInfo> vbih);
void setBuffer(WGPUBufferObject *bufferObject, uint32_t index);
Handle<WGPUVertexBufferInfo> vbih;
utils::FixedCapacityVector<wgpu::Buffer> buffers;
};
struct WGPUIndexBuffer : public HwIndexBuffer {
WGPUIndexBuffer(wgpu::Device const &device, uint8_t elementSize,
uint32_t indexCount);
wgpu::Buffer buffer;
};
// TODO: Currently WGPUVertexBufferInfo is not used by WebGPU for useful task.
// Update the struct when used by WebGPU driver.
struct WGPUBufferObject : HwBufferObject {
WGPUBufferObject(BufferObjectBinding bindingType, uint32_t byteCount);
wgpu::Buffer buffer;
const BufferObjectBinding bufferObjectBinding;
};
class WebGPUDescriptorSetLayout : public HwDescriptorSetLayout {
public:
WebGPUDescriptorSetLayout(DescriptorSetLayout const& layout, wgpu::Device const& device);
~WebGPUDescriptorSetLayout();
private:
// TODO: If this is useful elsewhere, remove it from this class
// Convert Filament Shader Stage Flags bitmask to webgpu equivilant
static wgpu::ShaderStage filamentStageToWGPUStage(ShaderStageFlags fFlags);
wgpu::BindGroupLayout mLayout;
};
// TODO: Currently WGPUTexture is not used by WebGPU for useful task.
// Update the struct when used by WebGPU driver.
struct WGPUTexture : public HwTexture {
WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage) noexcept;
// constructors for creating texture views
WGPUTexture(WGPUTexture const* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
wgpu::Texture texture = nullptr;
};
struct WGPURenderPrimitive : public HwRenderPrimitive {
WGPURenderPrimitive();
void setBuffers(WGPUVertexBufferInfo const* const vbi,
WGPUVertexBuffer* vertexBuffer, WGPUIndexBuffer* indexBuffer);
WGPUVertexBuffer* vertexBuffer = nullptr;
WGPUIndexBuffer* indexBuffer = nullptr;
};
// TODO: Currently WGPURenderTarget is not used by WebGPU for useful task.
// Update the struct when used by WebGPU driver.
struct WGPURenderTarget : public HwRenderTarget {
class Attachment {
public:
friend struct WGPURenderTarget;
Attachment() = default;
Attachment(WGPUTexture* gpuTexture, uint8_t level = 0, uint16_t layer = 0)
: level(level),
layer(layer),
texture(gpuTexture->texture),
mWGPUTexture(gpuTexture) {}
uint8_t level = 0;
uint16_t layer = 0;
private:
wgpu::Texture texture = nullptr;
WGPUTexture* mWGPUTexture = nullptr;
};
WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples,
Attachment colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT]);
WGPURenderTarget()
: HwRenderTarget(0, 0),
defaultRenderTarget(true) {}
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor* descriptor,
const RenderPassParams& params);
math::uint2 getAttachmentSize() noexcept;
bool isDefaultRenderTarget() const { return defaultRenderTarget; }
uint8_t getSamples() const { return samples; }
Attachment getDrawColorAttachment(size_t index);
Attachment getReadColorAttachment(size_t index);
private:
static wgpu::LoadOp getLoadAction(const RenderPassParams& params, TargetBufferFlags buffer);
static wgpu::LoadOp getStoreAction(const RenderPassParams& params, TargetBufferFlags buffer);
bool defaultRenderTarget = false;
uint8_t samples = 1;
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
math::uint2 attachmentSize = {};
};
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H

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