Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9a74936103 | ||
|
|
7967157fbb | ||
|
|
597ced13e1 | ||
|
|
c3542b135e | ||
|
|
ca3ff7e08e | ||
|
|
42c760a92f | ||
|
|
327a537bcc | ||
|
|
cfc4f34c18 | ||
|
|
6dac384bd9 | ||
|
|
d666f8a0ba | ||
|
|
d6ae3a57b5 | ||
|
|
c2155f3f98 | ||
|
|
e2a3637413 | ||
|
|
390df4e42c | ||
|
|
21dd1319df | ||
|
|
444ac8d6a6 | ||
|
|
aac1e7dc4c | ||
|
|
5b8a1e5e58 | ||
|
|
ad1b36d2b3 | ||
|
|
315c2c273f | ||
|
|
005d835dbe | ||
|
|
cade94ab2c | ||
|
|
5cd2f5626c | ||
|
|
94bbcbf1c3 | ||
|
|
52c998d2b6 | ||
|
|
527d831c15 | ||
|
|
2790f2e64c | ||
|
|
757640e850 | ||
|
|
9d9abca33a | ||
|
|
bea7a7c73f | ||
|
|
dd62f98784 | ||
|
|
395a3eda9b | ||
|
|
9d18cc35e0 | ||
|
|
2e37ebe70c | ||
|
|
63ef412be1 | ||
|
|
f0996296c1 | ||
|
|
a60d570b3b | ||
|
|
3fdb9f311a | ||
|
|
70f2cda9b6 | ||
|
|
c3e4894d49 | ||
|
|
a128d9143a | ||
|
|
2cf10999f4 | ||
|
|
e0e44da185 | ||
|
|
ce62e0ef06 | ||
|
|
826b885d0e | ||
|
|
5b9a24edf0 | ||
|
|
b651f63e02 | ||
|
|
c48273df3c | ||
|
|
8397a15914 | ||
|
|
d76567b4a8 | ||
|
|
ce62b169cb | ||
|
|
9244f8ee21 | ||
|
|
3c7a871e7e | ||
|
|
31d698e9ca | ||
|
|
769b0db858 | ||
|
|
b40064b1ab | ||
|
|
889a62d617 | ||
|
|
784b3ce565 | ||
|
|
4f77547ab3 | ||
|
|
46c89499d0 | ||
|
|
57c782685b | ||
|
|
bc799c78f6 | ||
|
|
86e688a2a3 | ||
|
|
3dead231d4 | ||
|
|
fd382ce219 | ||
|
|
46358572b7 | ||
|
|
546b986107 | ||
|
|
5245141c46 | ||
|
|
62b182fe04 |
@@ -19,6 +19,8 @@ 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:]`
|
||||||
@@ -44,6 +46,8 @@ jobs:
|
|||||||
|
|
||||||
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'
|
||||||
@@ -60,6 +64,8 @@ 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
|
||||||
@@ -102,9 +108,21 @@ jobs:
|
|||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
- name: Install python prereqs
|
- name: Cache Mesa and deps
|
||||||
run: pip install mako setuptools pyyaml
|
id: mesa-cache
|
||||||
- name: Run script
|
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: |
|
||||||
|
bash test/utils/get_mesa.sh
|
||||||
|
- name: Run Test
|
||||||
run: |
|
run: |
|
||||||
bash test/renderdiff/test.sh
|
bash test/renderdiff/test.sh
|
||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
@@ -122,3 +140,31 @@ jobs:
|
|||||||
run: source ./build/linux/ci-common.sh && ./build.sh -W debug test_filamat filament
|
run: source ./build/linux/ci-common.sh && ./build.sh -W debug test_filamat filament
|
||||||
- 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
|
||||||
|
|||||||
@@ -18,3 +18,18 @@ 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/
|
||||||
|
|||||||
@@ -334,6 +334,9 @@ 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)
|
||||||
@@ -798,6 +801,7 @@ 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)
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.google.android.filament:filament-android:1.59.0'
|
implementation 'com.google.android.filament:filament-android:1.59.3'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
|||||||
iOS projects can use CocoaPods to install the latest release:
|
iOS projects can use CocoaPods to install the latest release:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
pod 'Filament', '~> 1.59.0'
|
pod 'Filament', '~> 1.59.3'
|
||||||
```
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|||||||
@@ -7,6 +7,16 @@ 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.1
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -142,6 +142,12 @@ 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")
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ 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)
|
||||||
@@ -40,6 +44,7 @@ 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)
|
||||||
@@ -55,6 +60,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>:tint>
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ 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)
|
||||||
@@ -123,6 +127,7 @@ 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
|
||||||
@@ -141,6 +146,7 @@ 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:
|
||||||
|
|||||||
@@ -35,6 +35,10 @@ 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)
|
||||||
@@ -121,6 +125,7 @@ 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
|
||||||
)
|
)
|
||||||
@@ -129,7 +134,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 uberzlib log stb ktxreader basis_transcoder zstd uberarchive)
|
target_link_libraries(gltfio-jni filament-jni utils perfetto 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,5 +1,5 @@
|
|||||||
GROUP=com.google.android.filament
|
GROUP=com.google.android.filament
|
||||||
VERSION_NAME=1.59.0
|
VERSION_NAME=1.59.3
|
||||||
|
|
||||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||||
|
|
||||||
|
|||||||
@@ -177,29 +177,6 @@ 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/VulkanDescriptorSetCache.cpp
|
|
||||||
src/vulkan/VulkanDescriptorSetCache.h
|
|
||||||
src/vulkan/VulkanDescriptorSetLayoutCache.cpp
|
|
||||||
src/vulkan/VulkanDescriptorSetLayoutCache.h
|
|
||||||
src/vulkan/VulkanPipelineLayoutCache.cpp
|
|
||||||
src/vulkan/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
|
||||||
@@ -210,6 +187,10 @@ 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
|
||||||
@@ -217,22 +198,43 @@ 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.h
|
|
||||||
src/vulkan/VulkanMemory.cpp
|
src/vulkan/VulkanMemory.cpp
|
||||||
|
src/vulkan/VulkanMemory.h
|
||||||
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)
|
||||||
@@ -253,6 +255,11 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
|||||||
src/webgpu/WebGPUConstants.h
|
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/WebGPUHandles.h
|
||||||
|
src/webgpu/WebGPUSwapChain.cpp
|
||||||
|
src/webgpu/WebGPUSwapChain.h
|
||||||
|
src/webgpu/WGPUProgram.cpp
|
||||||
)
|
)
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
|
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
|
||||||
@@ -302,6 +309,11 @@ 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.
|
||||||
# ==================================================================================================
|
# ==================================================================================================
|
||||||
@@ -496,7 +508,10 @@ if (APPLE OR LINUX)
|
|||||||
test/Arguments.cpp
|
test/Arguments.cpp
|
||||||
test/ImageExpectations.cpp
|
test/ImageExpectations.cpp
|
||||||
test/Lifetimes.cpp
|
test/Lifetimes.cpp
|
||||||
|
test/PlatformRunner.cpp
|
||||||
test/Shader.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
|
||||||
@@ -520,6 +535,9 @@ 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
|
||||||
|
|||||||
@@ -55,4 +55,9 @@ 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
|
||||||
|
|||||||
@@ -104,10 +104,6 @@ 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();
|
||||||
@@ -361,8 +357,7 @@ public:
|
|||||||
VkImageUsageFlags usage);
|
VkImageUsageFlags usage);
|
||||||
|
|
||||||
virtual VkSampler createExternalSampler(SamplerYcbcrConversion chroma,
|
virtual VkSampler createExternalSampler(SamplerYcbcrConversion chroma,
|
||||||
SamplerParams sampler,
|
SamplerParams sampler, uint32_t internalFormat);
|
||||||
uint32_t internalFormat);
|
|
||||||
|
|
||||||
virtual VkImageView createExternalImageView(SamplerYcbcrConversion chroma,
|
virtual VkImageView createExternalImageView(SamplerYcbcrConversion chroma,
|
||||||
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||||
|
|||||||
@@ -38,6 +38,12 @@ public:
|
|||||||
|
|
||||||
[[nodiscard]] wgpu::Instance& getInstance() noexcept { return mInstance; }
|
[[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
|
// either returns a valid surface or panics
|
||||||
[[nodiscard]] wgpu::Surface createSurface(void* nativeWindow, uint64_t flags);
|
[[nodiscard]] wgpu::Surface createSurface(void* nativeWindow, uint64_t flags);
|
||||||
// either returns a valid adapter or panics
|
// either returns a valid adapter or panics
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ void CommandStream::execute(void* buffer) {
|
|||||||
|
|
||||||
Profiler profiler;
|
Profiler profiler;
|
||||||
|
|
||||||
if (SYSTRACE_TAG) {
|
if constexpr (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 (SYSTRACE_TAG) {
|
if constexpr (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();
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ OpenGLProgram::~OpenGLProgram() noexcept {
|
|||||||
delete lazyInitializationData;
|
delete lazyInitializationData;
|
||||||
|
|
||||||
ShaderCompilerService::terminate(mToken);
|
ShaderCompilerService::terminate(mToken);
|
||||||
|
assert_invariant(!mToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete [] mUniformsRecords;
|
delete [] mUniformsRecords;
|
||||||
|
|||||||
@@ -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 <atomic>
|
#include <array>
|
||||||
#include <condition_variable>
|
|
||||||
#include <deque>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <tuple>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
class OpenGLDriver;
|
class OpenGLDriver;
|
||||||
@@ -57,6 +57,8 @@ 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);
|
||||||
|
|
||||||
@@ -82,6 +84,7 @@ 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
|
||||||
@@ -90,6 +93,12 @@ 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);
|
||||||
@@ -97,7 +106,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
struct Job {
|
struct Job {
|
||||||
template<typename FUNC>
|
template<typename FUNC>
|
||||||
Job(FUNC&& fn) : fn(std::forward<FUNC>(fn)) {}
|
Job(FUNC&& fn) : fn(std::forward<FUNC>(fn)) {} // NOLINT(*-explicit-constructor)
|
||||||
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) {
|
||||||
@@ -126,39 +135,49 @@ 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(ShaderCompilerService::program_token_t& token) noexcept;
|
GLuint initialize(program_token_t& token);
|
||||||
|
void ensureTokenIsReady(program_token_t const& token);
|
||||||
|
|
||||||
static void getProgramFromCompilerPool(program_token_t& token) noexcept;
|
void runAtNextTick(CompilerPriorityQueue priority, program_token_t const& token,
|
||||||
|
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 token) noexcept;
|
bool cancelTickOp(program_token_t const& 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
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#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>
|
||||||
@@ -437,6 +438,10 @@ 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,9 +17,6 @@
|
|||||||
#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,13 +24,6 @@
|
|||||||
|
|
||||||
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_NAME(__func__)
|
#define FVK_SYSTRACE_SCOPE() SYSTRACE_CALL()
|
||||||
#define FVK_PROFILE_MARKER(marker) FVK_SYSTRACE_SCOPE()
|
#define FVK_PROFILE_MARKER(marker) SYSTRACE_CALL()
|
||||||
|
|
||||||
#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->getMultiviewAttachmentView(range);
|
return texture->getAttachmentView(range, VK_IMAGE_VIEW_TYPE_2D_ARRAY);
|
||||||
}
|
}
|
||||||
return texture->getAttachmentView(range);
|
return texture->getAttachmentView(range, VK_IMAGE_VIEW_TYPE_2D);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VulkanAttachment::isDepth() const {
|
bool VulkanAttachment::isDepth() const {
|
||||||
@@ -68,11 +68,11 @@ bool VulkanAttachment::isDepth() const {
|
|||||||
VkImageSubresourceRange VulkanAttachment::getSubresourceRange() const {
|
VkImageSubresourceRange VulkanAttachment::getSubresourceRange() const {
|
||||||
assert_invariant(texture);
|
assert_invariant(texture);
|
||||||
return {
|
return {
|
||||||
.aspectMask = texture->getImageAspect(),
|
.aspectMask = texture->getImageAspect(),
|
||||||
.baseMipLevel = uint32_t(level),
|
.baseMipLevel = uint32_t(level),
|
||||||
.levelCount = 1,
|
.levelCount = 1,
|
||||||
.baseArrayLayer = uint32_t(layer),
|
.baseArrayLayer = uint32_t(layer),
|
||||||
.layerCount = layerCount,
|
.layerCount = layerCount,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@
|
|||||||
#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"
|
||||||
|
|
||||||
@@ -30,8 +29,6 @@
|
|||||||
|
|
||||||
#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)
|
||||||
|
|
||||||
@@ -73,13 +70,11 @@ struct VulkanRenderPass {
|
|||||||
// context are stored in VulkanPlatform.
|
// context are stored in VulkanPlatform.
|
||||||
struct VulkanContext {
|
struct VulkanContext {
|
||||||
public:
|
public:
|
||||||
inline uint32_t selectMemoryType(uint32_t flags, VkFlags reqs) const {
|
static uint32_t selectMemoryType(VkPhysicalDeviceMemoryProperties const& memoryProperties,
|
||||||
if ((reqs & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) {
|
uint32_t flags, VkFlags reqs) {
|
||||||
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 ((mMemoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) {
|
if ((memoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -88,6 +83,13 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -121,7 +123,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline bool isMultiviewEnabled() const noexcept {
|
inline bool isMultiviewEnabled() const noexcept {
|
||||||
return mMultiviewEnabled;
|
return mPhysicalDeviceVk11Features.multiview == VK_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool isClipDistanceSupported() const noexcept {
|
inline bool isClipDistanceSupported() const noexcept {
|
||||||
@@ -145,6 +147,9 @@ 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,
|
||||||
};
|
};
|
||||||
@@ -157,7 +162,6 @@ 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;
|
||||||
|
|
||||||
|
|||||||
@@ -260,7 +260,6 @@ VulkanDescriptorSetCache::~VulkanDescriptorSetCache() = default;
|
|||||||
|
|
||||||
void VulkanDescriptorSetCache::terminate() noexcept{
|
void VulkanDescriptorSetCache::terminate() noexcept{
|
||||||
mDescriptorPool.reset();
|
mDescriptorPool.reset();
|
||||||
clearHistory();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// bind() is not really binding the set but just stashing until we have all the info
|
// bind() is not really binding the set but just stashing until we have all the info
|
||||||
@@ -283,18 +282,25 @@ void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
|
|||||||
fvkutils::DescriptorSetMask curMask = setMask;
|
fvkutils::DescriptorSetMask curMask = setMask;
|
||||||
|
|
||||||
auto& updateSets = mStashedSets;
|
auto& updateSets = mStashedSets;
|
||||||
auto& lastBoundSets = mLastBoundInfo.boundSets;
|
bool const pipelineLayoutIsSame = mLastBoundInfo.pipelineLayout == pipelineLayout;
|
||||||
|
|
||||||
setMask.forEachSetBit([&](size_t index) {
|
if (pipelineLayoutIsSame) {
|
||||||
if (!updateSets[index] || updateSets[index] == lastBoundSets[index]) {
|
auto& lastBoundSets = mLastBoundInfo.boundSets;
|
||||||
curMask.unset(index);
|
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 (curMask.none() &&
|
if (!updateSets[index]) {
|
||||||
(mLastBoundInfo.pipelineLayout == pipelineLayout && mLastBoundInfo.setMask == setMask &&
|
curMask.unset(index);
|
||||||
mLastBoundInfo.boundSets == updateSets)) {
|
}
|
||||||
return;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
curMask.forEachSetBit([&updateSets, commands, pipelineLayout](size_t index) {
|
curMask.forEachSetBit([&updateSets, commands, pipelineLayout](size_t index) {
|
||||||
@@ -302,7 +308,7 @@ void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
|
|||||||
auto set = updateSets[index];
|
auto set = updateSets[index];
|
||||||
VkCommandBuffer const cmdbuffer = commands->buffer();
|
VkCommandBuffer const cmdbuffer = commands->buffer();
|
||||||
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index,
|
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index,
|
||||||
1, &set->vkSet, set->uniqueDynamicUboCount, set->getOffsets()->data());
|
1, &set->getVkSet(), set->uniqueDynamicUboCount, set->getOffsets()->data());
|
||||||
commands->acquire(set);
|
commands->acquire(set);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -330,8 +336,7 @@ void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescri
|
|||||||
}
|
}
|
||||||
VkWriteDescriptorSet const descriptorWrite = {
|
VkWriteDescriptorSet const descriptorWrite = {
|
||||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||||
.pNext = nullptr,
|
.dstSet = set->getVkSet(),
|
||||||
.dstSet = set->vkSet,
|
|
||||||
.dstBinding = binding,
|
.dstBinding = binding,
|
||||||
.descriptorCount = 1,
|
.descriptorCount = 1,
|
||||||
.descriptorType = type,
|
.descriptorType = type,
|
||||||
@@ -344,26 +349,25 @@ void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescri
|
|||||||
void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||||
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
|
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
|
||||||
VkSampler sampler) noexcept {
|
VkSampler sampler) noexcept {
|
||||||
VkDescriptorImageInfo info{
|
VkImageSubresourceRange range = texture->getPrimaryViewRange();
|
||||||
.sampler = sampler,
|
|
||||||
};
|
|
||||||
VkImageSubresourceRange const range = texture->getPrimaryViewRange();
|
|
||||||
VkImageViewType const expectedType = texture->getViewType();
|
VkImageViewType const expectedType = texture->getViewType();
|
||||||
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
|
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
|
||||||
expectedType == VK_IMAGE_VIEW_TYPE_2D) {
|
expectedType == VK_IMAGE_VIEW_TYPE_2D) {
|
||||||
// If the sampler is part of a mipmapped depth texture, where one of the level *can* be
|
// 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
|
// an attachment, then the range for this view has exactly one level and one layer.
|
||||||
// view for an attachment. Therefore, we can use getAttachmentView to get a
|
range.levelCount = 1;
|
||||||
// corresponding VkImageView.
|
range.layerCount = 1;
|
||||||
info.imageView = texture->getAttachmentView(range);
|
|
||||||
} else {
|
|
||||||
info.imageView = texture->getViewForType(range, expectedType);
|
|
||||||
}
|
}
|
||||||
info.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout());
|
VkDescriptorImageInfo info{
|
||||||
|
.sampler = sampler,
|
||||||
|
.imageView = texture->getView(range),
|
||||||
|
.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout()),
|
||||||
|
};
|
||||||
|
|
||||||
VkWriteDescriptorSet const descriptorWrite = {
|
VkWriteDescriptorSet const descriptorWrite = {
|
||||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||||
.pNext = nullptr,
|
.pNext = nullptr,
|
||||||
.dstSet = set->vkSet,
|
.dstSet = set->getVkSet(),
|
||||||
.dstBinding = binding,
|
.dstBinding = binding,
|
||||||
.descriptorCount = 1,
|
.descriptorCount = 1,
|
||||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||||
@@ -381,10 +385,10 @@ void VulkanDescriptorSetCache::updateInputAttachment(
|
|||||||
|
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet(
|
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet(
|
||||||
Handle<HwDescriptorSet> handle, fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
|
Handle<HwDescriptorSet> handle, fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
|
||||||
auto const vkSet = mDescriptorPool->obtainSet(layout);
|
auto const vkSet = getVkSet(layout);
|
||||||
auto const& count = layout->count;
|
auto const& count = layout->count;
|
||||||
auto const vklayout = layout->getVkLayout();
|
auto const vklayout = layout->getVkLayout();
|
||||||
return fvkmemory::resource_ptr<VulkanDescriptorSet>::make(mResourceManager, handle, vkSet,
|
auto set = fvkmemory::resource_ptr<VulkanDescriptorSet>::make(mResourceManager, handle,
|
||||||
layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
|
layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
|
||||||
[vkSet, count, vklayout, this](VulkanDescriptorSet*) {
|
[vkSet, count, vklayout, this](VulkanDescriptorSet*) {
|
||||||
// Note that mDescriptorPool could be gone due to terminate (when the backend shuts
|
// Note that mDescriptorPool could be gone due to terminate (when the backend shuts
|
||||||
@@ -393,10 +397,20 @@ fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet
|
|||||||
mDescriptorPool->recycle(count, vklayout, vkSet);
|
mDescriptorPool->recycle(count, vklayout, vkSet);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
set->setVkSet(vkSet);
|
||||||
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanDescriptorSetCache::clearHistory() {
|
VkDescriptorSet VulkanDescriptorSetCache::getVkSet(
|
||||||
mStashedSets = {};
|
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
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ public:
|
|||||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT;
|
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT;
|
||||||
|
|
||||||
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
|
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
|
||||||
|
using DescriptorSetArray =
|
||||||
|
std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>, UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||||
|
|
||||||
VulkanDescriptorSetCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
|
VulkanDescriptorSetCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
|
||||||
~VulkanDescriptorSetCache();
|
~VulkanDescriptorSetCache();
|
||||||
@@ -68,14 +70,21 @@ public:
|
|||||||
fvkmemory::resource_ptr<VulkanDescriptorSet> createSet(Handle<HwDescriptorSet> handle,
|
fvkmemory::resource_ptr<VulkanDescriptorSet> createSet(Handle<HwDescriptorSet> handle,
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
||||||
|
|
||||||
void clearHistory();
|
// 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:
|
private:
|
||||||
class DescriptorInfinitePool;
|
class DescriptorInfinitePool;
|
||||||
|
|
||||||
using DescriptorSetArray =
|
|
||||||
std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>, UNIQUE_DESCRIPTOR_SET_COUNT>;
|
|
||||||
|
|
||||||
VkDevice mDevice;
|
VkDevice mDevice;
|
||||||
fvkmemory::ResourceManager* mResourceManager;
|
fvkmemory::ResourceManager* mResourceManager;
|
||||||
std::unique_ptr<DescriptorInfinitePool> mDescriptorPool;
|
std::unique_ptr<DescriptorInfinitePool> mDescriptorPool;
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
#include "VulkanHandles.h"
|
#include "VulkanHandles.h"
|
||||||
|
|
||||||
|
#include <utils/Hash.h>
|
||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@@ -58,6 +60,56 @@ uint32_t appendBindings(VkDescriptorSetLayoutBinding* toBind, VkDescriptorType t
|
|||||||
return count;
|
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
|
} // anonymous namespace
|
||||||
|
|
||||||
VulkanDescriptorSetLayoutCache::VulkanDescriptorSetLayoutCache(VkDevice device,
|
VulkanDescriptorSetLayoutCache::VulkanDescriptorSetLayoutCache(VkDevice device,
|
||||||
@@ -73,37 +125,44 @@ void VulkanDescriptorSetLayoutCache::terminate() noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(
|
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> VulkanDescriptorSetLayoutCache::createLayout(
|
||||||
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info) {
|
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info) {
|
||||||
auto layout = fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::make(mResourceManager, handle,
|
auto layout = fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::make(mResourceManager, handle,
|
||||||
info);
|
info);
|
||||||
VkDescriptorSetLayout vklayout = VK_NULL_HANDLE;
|
layout->setVkLayout(getVkLayout(layout->bitmask));
|
||||||
auto const& bitmasks = layout->bitmask;
|
|
||||||
if (auto itr = mVkLayouts.find(bitmasks); itr != mVkLayouts.end()) {
|
|
||||||
vklayout = itr->second;
|
|
||||||
} else {
|
|
||||||
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 += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
|
||||||
bitmasks.sampler);
|
|
||||||
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,
|
|
||||||
.pNext = nullptr,
|
|
||||||
.bindingCount = count,
|
|
||||||
.pBindings = toBind,
|
|
||||||
};
|
|
||||||
|
|
||||||
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout);
|
|
||||||
mVkLayouts[bitmasks] = vklayout;
|
|
||||||
}
|
|
||||||
layout->setVkLayout(vklayout);
|
|
||||||
return layout;
|
return layout;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,12 +26,11 @@
|
|||||||
#include <backend/TargetBufferInfo.h>
|
#include <backend/TargetBufferInfo.h>
|
||||||
|
|
||||||
#include <utils/bitset.h>
|
#include <utils/bitset.h>
|
||||||
|
#include <utils/FixedCapacityVector.h>
|
||||||
|
|
||||||
#include <bluevk/BlueVK.h>
|
#include <bluevk/BlueVK.h>
|
||||||
#include <tsl/robin_map.h>
|
#include <tsl/robin_map.h>
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
class VulkanDescriptorSetLayoutCache {
|
class VulkanDescriptorSetLayoutCache {
|
||||||
@@ -41,21 +40,34 @@ public:
|
|||||||
|
|
||||||
void terminate() noexcept;
|
void terminate() noexcept;
|
||||||
|
|
||||||
|
// Just a wrapper around getVkLayout()
|
||||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> createLayout(
|
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> createLayout(
|
||||||
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info);
|
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:
|
private:
|
||||||
VkDevice mDevice;
|
VkDevice mDevice;
|
||||||
fvkmemory::ResourceManager* mResourceManager;
|
fvkmemory::ResourceManager* mResourceManager;
|
||||||
|
|
||||||
using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask;
|
struct LayoutKey {
|
||||||
using BitmaskGroupHashFn = utils::hash::MurmurHashFn<BitmaskGroup>;
|
// this describes the layout using bitset.
|
||||||
struct BitmaskGroupEqual {
|
VulkanDescriptorSetLayout::Bitmask bitmask = {};
|
||||||
bool operator()(BitmaskGroup const& k1, BitmaskGroup const& k2) const { return k1 == k2; }
|
// 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<BitmaskGroup, VkDescriptorSetLayout, BitmaskGroupHashFn, BitmaskGroupEqual>
|
tsl::robin_map<LayoutKey, VkDescriptorSetLayout, LayoutKeyHashFn, LayoutKeyEqual> mVkLayouts;
|
||||||
mVkLayouts;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -25,10 +25,12 @@
|
|||||||
#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>
|
||||||
@@ -212,6 +214,7 @@ 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()),
|
||||||
@@ -324,7 +327,7 @@ void VulkanDriver::terminate() {
|
|||||||
|
|
||||||
mStagePool.terminate();
|
mStagePool.terminate();
|
||||||
mPipelineCache.terminate();
|
mPipelineCache.terminate();
|
||||||
mFramebufferCache.reset();
|
mFramebufferCache.terminate();
|
||||||
mSamplerCache.terminate();
|
mSamplerCache.terminate();
|
||||||
mDescriptorSetLayoutCache.terminate();
|
mDescriptorSetLayoutCache.terminate();
|
||||||
mDescriptorSetCache.terminate();
|
mDescriptorSetCache.terminate();
|
||||||
@@ -363,7 +366,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.clearHistory();
|
mDescriptorSetCache.gc();
|
||||||
mStagePool.gc();
|
mStagePool.gc();
|
||||||
mFramebufferCache.gc();
|
mFramebufferCache.gc();
|
||||||
mPipelineCache.gc();
|
mPipelineCache.gc();
|
||||||
@@ -418,7 +421,11 @@ 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);
|
||||||
|
|
||||||
VkSampler const vksampler = mSamplerCache.getSampler(params);
|
// TODO: YcbcrConversion?
|
||||||
|
VulkanSamplerCache::Params cacheParams = {
|
||||||
|
.sampler = params,
|
||||||
|
};
|
||||||
|
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
|
||||||
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
|
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -556,7 +563,7 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
|
|||||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||||
Platform::ExternalImageHandleRef externalImage) {
|
Platform::ExternalImageHandleRef externalImage) {
|
||||||
FVK_SYSTRACE_SCOPE();
|
FVK_SYSTRACE_SCOPE();
|
||||||
const auto& metadata = mPlatform->getExternalImageMetadata(externalImage);
|
auto const& metadata = mPlatform->getExternalImageMetadata(externalImage);
|
||||||
if (metadata.isProtected) {
|
if (metadata.isProtected) {
|
||||||
usage |= backend::TextureUsage::PROTECTED;
|
usage |= backend::TextureUsage::PROTECTED;
|
||||||
}
|
}
|
||||||
@@ -566,7 +573,7 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
|
|||||||
vkUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
vkUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (any(usage & (TextureUsage::BLIT_DST & TextureUsage::UPLOADABLE))) {
|
if (any(usage & (TextureUsage::BLIT_DST | TextureUsage::UPLOADABLE))) {
|
||||||
vkUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
vkUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,13 +589,15 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
|
|||||||
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex != VK_MAX_MEMORY_TYPES)
|
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex != VK_MAX_MEMORY_TYPES)
|
||||||
<< "failed to find a valid memory type for external image memory.";
|
<< "failed to find a valid memory type for external image memory.";
|
||||||
|
|
||||||
const auto& data =
|
VkImage vkimg;
|
||||||
|
VkDeviceMemory deviceMemory;
|
||||||
|
std::tie(vkimg, deviceMemory) =
|
||||||
mPlatform->createExternalImageData(externalImage, metadata, memoryTypeIndex, vkUsage);
|
mPlatform->createExternalImageData(externalImage, metadata, memoryTypeIndex, vkUsage);
|
||||||
|
|
||||||
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
|
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mContext,
|
||||||
mAllocator, &mResourceManager, &mCommands, data.first, data.second, metadata.format,
|
mPlatform->getDevice(), mAllocator, &mResourceManager, &mCommands, vkimg, deviceMemory,
|
||||||
metadata.samples, metadata.width, metadata.height, metadata.layerCount, usage,
|
metadata.format, VK_NULL_HANDLE, metadata.samples, metadata.width, metadata.height,
|
||||||
mStagePool);
|
metadata.layerCount, usage, mStagePool);
|
||||||
|
|
||||||
texture.inc();
|
texture.inc();
|
||||||
}
|
}
|
||||||
@@ -597,13 +606,14 @@ 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,
|
||||||
@@ -611,6 +621,7 @@ 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) {
|
||||||
@@ -1512,7 +1523,7 @@ void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
|
|||||||
swapChain->acquire(resized);
|
swapChain->acquire(resized);
|
||||||
|
|
||||||
if (resized) {
|
if (resized) {
|
||||||
mFramebufferCache.reset();
|
mFramebufferCache.resetFramebuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (UTILS_LIKELY(mDefaultRenderTarget)) {
|
if (UTILS_LIKELY(mDefaultRenderTarget)) {
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
#include "VulkanSamplerCache.h"
|
#include "VulkanSamplerCache.h"
|
||||||
#include "VulkanStagePool.h"
|
#include "VulkanStagePool.h"
|
||||||
#include "VulkanQueryManager.h"
|
#include "VulkanQueryManager.h"
|
||||||
|
#include "VulkanYcbcrConversionCache.h"
|
||||||
#include "vulkan/VulkanDescriptorSetCache.h"
|
#include "vulkan/VulkanDescriptorSetCache.h"
|
||||||
#include "vulkan/VulkanDescriptorSetLayoutCache.h"
|
#include "vulkan/VulkanDescriptorSetLayoutCache.h"
|
||||||
#include "vulkan/VulkanPipelineLayoutCache.h"
|
#include "vulkan/VulkanPipelineLayoutCache.h"
|
||||||
@@ -135,6 +136,7 @@ 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;
|
||||||
|
|||||||
@@ -340,15 +340,21 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
|
|||||||
return renderPass;
|
return renderPass;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanFboCache::reset() noexcept {
|
void VulkanFboCache::resetFramebuffers() noexcept {
|
||||||
for (auto pair : mFramebufferCache) {
|
for (const 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,8 +106,11 @@ 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 reset() noexcept;
|
void terminate() noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VkDevice mDevice;
|
VkDevice mDevice;
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
|
|
||||||
#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;
|
||||||
@@ -89,8 +90,9 @@ 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;
|
||||||
|
|||||||
@@ -66,18 +66,21 @@ 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) == 32);
|
static_assert(sizeof(Bitmask) == 40);
|
||||||
|
|
||||||
// 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.
|
||||||
@@ -119,10 +122,16 @@ 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 getVkLayout() const { return mVkLayout; }
|
VkDescriptorSetLayout const& getVkLayout() const noexcept { 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;
|
||||||
@@ -137,12 +146,11 @@ 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(VkDescriptorSet rawSet,
|
VulkanDescriptorSet(
|
||||||
fvkutils::UniformBufferBitmask const& dynamicUboMask,
|
fvkutils::UniformBufferBitmask const& dynamicUboMask,
|
||||||
uint8_t uniqueDynamicUboCount,
|
uint8_t uniqueDynamicUboCount,
|
||||||
OnRecycle&& onRecycleFn)
|
OnRecycle&& onRecycleFn)
|
||||||
: vkSet(rawSet),
|
: dynamicUboMask(dynamicUboMask),
|
||||||
dynamicUboMask(dynamicUboMask),
|
|
||||||
uniqueDynamicUboCount(uniqueDynamicUboCount),
|
uniqueDynamicUboCount(uniqueDynamicUboCount),
|
||||||
mOnRecycleFn(std::move(onRecycleFn)) {}
|
mOnRecycleFn(std::move(onRecycleFn)) {}
|
||||||
|
|
||||||
@@ -152,6 +160,13 @@ 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);
|
||||||
}
|
}
|
||||||
@@ -163,11 +178,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;
|
||||||
|
|||||||
@@ -35,7 +35,12 @@ 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;
|
||||||
@@ -215,7 +220,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
|||||||
PipelineCacheEntry cacheEntry = {
|
PipelineCacheEntry cacheEntry = {
|
||||||
.lastUsed = mCurrentTime,
|
.lastUsed = mCurrentTime,
|
||||||
};
|
};
|
||||||
VkResult error = vkCreateGraphicsPipelines(mDevice, VK_NULL_HANDLE, 1, &pipelineCreateInfo,
|
VkResult error = vkCreateGraphicsPipelines(mDevice, mPipelineCache, 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) {
|
||||||
@@ -271,6 +276,8 @@ void VulkanPipelineCache::terminate() noexcept {
|
|||||||
}
|
}
|
||||||
mPipelines.clear();
|
mPipelines.clear();
|
||||||
mBoundPipeline = {};
|
mBoundPipeline = {};
|
||||||
|
|
||||||
|
vkDestroyPipelineCache(mDevice, mPipelineCache, VKALLOC);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanPipelineCache::gc() noexcept {
|
void VulkanPipelineCache::gc() noexcept {
|
||||||
|
|||||||
@@ -18,24 +18,18 @@
|
|||||||
#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 {
|
||||||
|
|
||||||
@@ -204,6 +198,10 @@ 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,7 +14,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "vulkan/VulkanSamplerCache.h"
|
#include "VulkanSamplerCache.h"
|
||||||
|
|
||||||
|
#include "VulkanConstants.h"
|
||||||
#include "vulkan/utils/Conversion.h"
|
#include "vulkan/utils/Conversion.h"
|
||||||
|
|
||||||
#include <utils/Panic.h>
|
#include <utils/Panic.h>
|
||||||
@@ -26,41 +28,48 @@ namespace filament::backend {
|
|||||||
VulkanSamplerCache::VulkanSamplerCache(VkDevice device)
|
VulkanSamplerCache::VulkanSamplerCache(VkDevice device)
|
||||||
: mDevice(device) {}
|
: mDevice(device) {}
|
||||||
|
|
||||||
VkSampler VulkanSamplerCache::getSampler(SamplerParams params) noexcept {
|
VkSampler VulkanSamplerCache::getSampler(Params 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;
|
||||||
}
|
}
|
||||||
VkSamplerCreateInfo samplerInfo {
|
VkSamplerYcbcrConversionInfo ycbcrConversion = {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
||||||
|
.conversion = params.conversion,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto const& samplerParams = params.sampler;
|
||||||
|
VkSamplerCreateInfo samplerInfo{
|
||||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||||
.magFilter = fvkutils::getFilter(params.filterMag),
|
.pNext = params.conversion != VK_NULL_HANDLE ? &ycbcrConversion : VK_NULL_HANDLE,
|
||||||
.minFilter = fvkutils::getFilter(params.filterMin),
|
.magFilter = fvkutils::getFilter(samplerParams.filterMag),
|
||||||
.mipmapMode = fvkutils::getMipmapMode(params.filterMin),
|
.minFilter = fvkutils::getFilter(samplerParams.filterMin),
|
||||||
.addressModeU = fvkutils::getWrapMode(params.wrapS),
|
.mipmapMode = fvkutils::getMipmapMode(samplerParams.filterMin),
|
||||||
.addressModeV = fvkutils::getWrapMode(params.wrapT),
|
.addressModeU = fvkutils::getWrapMode(samplerParams.wrapS),
|
||||||
.addressModeW = fvkutils::getWrapMode(params.wrapR),
|
.addressModeV = fvkutils::getWrapMode(samplerParams.wrapT),
|
||||||
.anisotropyEnable = params.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
|
.addressModeW = fvkutils::getWrapMode(samplerParams.wrapR),
|
||||||
.maxAnisotropy = (float)(1u << params.anisotropyLog2),
|
.anisotropyEnable = samplerParams.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
|
||||||
.compareEnable = fvkutils::getCompareEnable(params.compareMode),
|
.maxAnisotropy = (float) (1u << samplerParams.anisotropyLog2),
|
||||||
.compareOp = fvkutils::getCompareOp(params.compareFunc),
|
.compareEnable = fvkutils::getCompareEnable(samplerParams.compareMode),
|
||||||
|
.compareOp = fvkutils::getCompareOp(samplerParams.compareFunc),
|
||||||
.minLod = 0.0f,
|
.minLod = 0.0f,
|
||||||
.maxLod = fvkutils::getMaxLod(params.filterMin),
|
.maxLod = fvkutils::getMaxLod(samplerParams.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);
|
||||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create sampler."
|
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create sampler."
|
||||||
<< " error=" << static_cast<int32_t>(result);
|
<< " error=" << static_cast<int32_t>(result);
|
||||||
mCache.insert({params, sampler});
|
mCache.insert({ params, sampler });
|
||||||
return sampler;
|
return sampler;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VulkanSamplerCache::terminate() noexcept {
|
void VulkanSamplerCache::terminate() noexcept {
|
||||||
for (auto pair : mCache) {
|
for (auto pair: mCache) {
|
||||||
vkDestroySampler(mDevice, pair.second, VKALLOC);
|
vkDestroySampler(mDevice, pair.second, VKALLOC);
|
||||||
}
|
}
|
||||||
mCache.clear();
|
mCache.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace filament::backend
|
}// namespace filament::backend
|
||||||
|
|||||||
@@ -17,8 +17,11 @@
|
|||||||
#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 "VulkanContext.h"
|
#include <backend/DriverEnums.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 {
|
||||||
@@ -26,12 +29,28 @@ 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(SamplerParams params) noexcept;
|
VkSampler getSampler(Params 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,6 +32,7 @@ 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),
|
||||||
@@ -77,16 +78,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,
|
||||||
device, mAllocator, mResourceManager, mCommands, color, VK_NULL_HANDLE,
|
mContext, device, mAllocator, mResourceManager, mCommands, color, VK_NULL_HANDLE,
|
||||||
bundle.colorFormat, 1, bundle.extent.width, bundle.extent.height, bundle.layerCount, colorUsage,
|
bundle.colorFormat, VK_NULL_HANDLE /*ycrcb */, 1, bundle.extent.width,
|
||||||
mStagePool);
|
bundle.extent.height, bundle.layerCount, colorUsage, mStagePool);
|
||||||
mColors.push_back(colorTexture);
|
mColors.push_back(colorTexture);
|
||||||
}
|
}
|
||||||
|
|
||||||
mDepth = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager, device,
|
mDepth = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager, mContext, device,
|
||||||
mAllocator, mResourceManager, mCommands, bundle.depth, VK_NULL_HANDLE,
|
mAllocator, mResourceManager, mCommands, bundle.depth, VK_NULL_HANDLE,
|
||||||
bundle.depthFormat, 1, bundle.extent.width, bundle.extent.height, bundle.layerCount, depthUsage,
|
bundle.depthFormat, VK_NULL_HANDLE /*ycrcb */, 1, bundle.extent.width,
|
||||||
mStagePool);
|
bundle.extent.height, bundle.layerCount, depthUsage, mStagePool);
|
||||||
|
|
||||||
mExtent = bundle.extent;
|
mExtent = bundle.extent;
|
||||||
mLayerCount = bundle.layerCount;
|
mLayerCount = bundle.layerCount;
|
||||||
@@ -107,11 +108,6 @@ 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();
|
||||||
@@ -145,7 +141,6 @@ 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,6 +19,7 @@
|
|||||||
|
|
||||||
#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"
|
||||||
@@ -28,7 +29,6 @@
|
|||||||
#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,7 +49,9 @@ struct VulkanSwapChain : public HwSwapChain, fvkmemory::Resource {
|
|||||||
|
|
||||||
void present();
|
void present();
|
||||||
|
|
||||||
void acquire(bool& reized);
|
// Acquire a new image from the swapchain. If the image is not available it would wait until it
|
||||||
|
// 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;
|
||||||
@@ -83,6 +85,7 @@ 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;
|
||||||
@@ -98,7 +101,6 @@ 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;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#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"
|
||||||
@@ -54,7 +55,6 @@ 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,
|
auto const compose = [](VkComponentMapping const& prev,
|
||||||
VkComponentMapping const& next) -> VkComponentMapping {
|
VkComponentMapping const& next) -> VkComponentMapping {
|
||||||
VkComponentSwizzle vals[4] = { next.r, next.g, next.b, next.a };
|
VkComponentSwizzle vals[4] = { next.r, next.g, next.b, next.a };
|
||||||
@@ -151,35 +151,152 @@ 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(VkDevice device, VmaAllocator allocator,
|
VulkanTextureState::VulkanTextureState(VulkanStagePool& stagePool, VulkanCommands* commands,
|
||||||
VulkanCommands* commands, VulkanStagePool& stagePool, VkFormat format,
|
VmaAllocator allocator, VkDevice device, VkImage image, VkDeviceMemory deviceMemory,
|
||||||
VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VulkanLayout defaultLayout,
|
VkFormat format, VkImageViewType viewType, uint8_t levels, uint8_t layerCount,
|
||||||
|
VkSamplerYcbcrConversion ycbcrConversion, bool isExternalFormat, VkImageUsageFlags usage,
|
||||||
bool isProtected)
|
bool isProtected)
|
||||||
: mVkFormat(format),
|
: mStagePool(stagePool),
|
||||||
mViewType(viewType),
|
|
||||||
mFullViewRange{fvkutils::getImageAspect(format), 0, levels, 0, layerCount},
|
|
||||||
mDefaultLayout(defaultLayout),
|
|
||||||
mIsProtected(isProtected),
|
|
||||||
mStagePool(stagePool),
|
|
||||||
mDevice(device),
|
|
||||||
mAllocator(allocator),
|
|
||||||
mCommands(commands),
|
mCommands(commands),
|
||||||
mIsTransientAttachment(false) {}
|
mAllocator(allocator),
|
||||||
|
mDevice(device),
|
||||||
|
mTextureImage(image),
|
||||||
|
mTextureImageMemory(deviceMemory),
|
||||||
|
mVkFormat(format),
|
||||||
|
mViewType(viewType),
|
||||||
|
mFullViewRange{ fvkutils::getImageAspect(format), 0, levels, 0, layerCount },
|
||||||
|
mYcbcr{ ycbcrConversion, isExternalFormat },
|
||||||
|
mDefaultLayout(getDefaultLayoutImpl(usage)),
|
||||||
|
mUsage(usage),
|
||||||
|
mIsProtected(isProtected) {}
|
||||||
|
|
||||||
// Constructor for internally passed VkImage
|
VulkanTextureState::~VulkanTextureState() {
|
||||||
VulkanTexture::VulkanTexture(VkDevice device, VmaAllocator allocator,
|
if (mTextureImageMemory != VK_NULL_HANDLE) {
|
||||||
|
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, uint8_t samples, uint32_t width,
|
VkDeviceMemory memory, VkFormat format, VkSamplerYcbcrConversion conversion,
|
||||||
uint32_t height, uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool)
|
uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage tusage,
|
||||||
: HwTexture(getSamplerTypeFromDepth(depth), 1, samples, width, height, depth, TextureFormat::UNUSED,
|
VulkanStagePool& stagePool)
|
||||||
tusage),
|
: HwTexture(getSamplerTypeFromDepth(depth), 1, samples, width, height, depth,
|
||||||
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, device,
|
TextureFormat::UNUSED, tusage),
|
||||||
allocator, commands, stagePool, format, fvkutils::getViewType(SamplerType::SAMPLER_2D),
|
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, stagePool,
|
||||||
/*mipLevels=*/1, getLayerCountFromDepth(depth), getDefaultLayoutImpl(tusage), any(usage & TextureUsage::PROTECTED))) {
|
commands, allocator, device, image, memory, format,
|
||||||
mState->mTextureImage = image;
|
fvkutils::getViewType(SamplerType::SAMPLER_2D),
|
||||||
mState->mTextureImageMemory = memory;
|
/*mipLevels=*/1, getLayerCountFromDepth(depth), conversion,
|
||||||
|
/*isExternalFormat=*/false,
|
||||||
|
getUsage(context, samples, VK_NULL_HANDLE, format, tusage),
|
||||||
|
any(usage & TextureUsage::PROTECTED))) {
|
||||||
mPrimaryViewRange = mState->mFullViewRange;
|
mPrimaryViewRange = mState->mFullViewRange;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -189,21 +306,19 @@ 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 = mState->mVkFormat,
|
.format = vkFormat,
|
||||||
.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 = 0,
|
.usage = getUsage(context, samples, physicalDevice, vkFormat, tusage),
|
||||||
};
|
};
|
||||||
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()) {
|
||||||
@@ -233,79 +348,17 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
|||||||
imageInfo.arrayLayers = depth * 6;
|
imageInfo.arrayLayers = depth * 6;
|
||||||
imageInfo.extent.depth = 1;
|
imageInfo.extent.depth = 1;
|
||||||
}
|
}
|
||||||
if (any(usage & TextureUsage::PROTECTED)) {
|
if (isProtected) {
|
||||||
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) &&
|
|
||||||
// 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(usage & TextureUsage::DEPTH_ATTACHMENT) && samples > 1);
|
|
||||||
|
|
||||||
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).
|
||||||
const auto& limits = context.getPhysicalDeviceLimits();
|
auto const& 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(mState->mVkFormat)
|
fvkutils::isVkDepthFormat(vkFormat)
|
||||||
? limits.sampledImageDepthSampleCounts
|
? limits.sampledImageDepthSampleCounts
|
||||||
: limits.sampledImageColorSampleCounts);
|
: limits.sampledImageColorSampleCounts);
|
||||||
}
|
}
|
||||||
@@ -319,33 +372,36 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
|||||||
this->samples = samples;
|
this->samples = samples;
|
||||||
imageInfo.samples = (VkSampleCountFlagBits) samples;
|
imageInfo.samples = (VkSampleCountFlagBits) samples;
|
||||||
|
|
||||||
VkResult result = vkCreateImage(mState->mDevice, &imageInfo, VKALLOC, &mState->mTextureImage);
|
VkImage textureImage;
|
||||||
|
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 = " << mState->mTextureImage << ", "
|
<< "image = " << textureImage << ", "
|
||||||
<< "result = " << result << ", "
|
<< "result = " << result << ", "
|
||||||
<< "handle = " << utils::io::hex << mState->mTextureImage << utils::io::dec << ", "
|
<< "handle = " << utils::io::hex << textureImage << 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>(usage) << ", "
|
<< "TextureUsage = " << static_cast<int>(tusage) << ", "
|
||||||
<< "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 = " << mState->mVkFormat << utils::io::endl;
|
<< "format = " << vkFormat << 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(mState->mDevice, mState->mTextureImage, &memReqs);
|
vkGetImageMemoryRequirements(device, textureImage, &memReqs);
|
||||||
|
|
||||||
const VkFlags requiredMemoryFlags =
|
bool const useTransientAttachment = imageInfo.usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
|
||||||
|
|
||||||
|
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) |
|
||||||
(mState->mIsProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
|
(isProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
|
||||||
uint32_t memoryTypeIndex
|
uint32_t memoryTypeIndex
|
||||||
= context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags);
|
= context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags);
|
||||||
|
|
||||||
@@ -357,21 +413,22 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
|||||||
.allocationSize = memReqs.size,
|
.allocationSize = memReqs.size,
|
||||||
.memoryTypeIndex = memoryTypeIndex,
|
.memoryTypeIndex = memoryTypeIndex,
|
||||||
};
|
};
|
||||||
result = vkAllocateMemory(mState->mDevice, &allocInfo, nullptr, &mState->mTextureImageMemory);
|
VkDeviceMemory textureImageMemory;
|
||||||
|
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(mState->mDevice, mState->mTextureImage, mState->mTextureImageMemory,
|
result = vkBindImageMemory(device, textureImage, textureImageMemory, 0);
|
||||||
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
|
||||||
@@ -380,8 +437,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;
|
||||||
@@ -392,21 +449,10 @@ 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) {
|
||||||
@@ -513,20 +559,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.
|
||||||
const int layer = 0;
|
constexpr int layer = 0;
|
||||||
|
|
||||||
const VkOffset3D rect[2] { {0, 0, 0}, {int32_t(width), int32_t(height), 1} };
|
VkOffset3D const rect[2] { {0, 0, 0}, {int32_t(width), int32_t(height), 1} };
|
||||||
|
|
||||||
const VkImageAspectFlags aspect = getImageAspect();
|
VkImageAspectFlags const aspect = getImageAspect();
|
||||||
|
|
||||||
const VkImageBlit blitRegions[1] = {{
|
VkImageBlit const 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] }
|
||||||
}};
|
}};
|
||||||
|
|
||||||
const VkImageSubresourceRange range = { aspect, miplevel, 1, layer, 1 };
|
VkImageSubresourceRange const 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);
|
||||||
@@ -542,41 +588,21 @@ VulkanLayout VulkanTexture::getDefaultLayout() const {
|
|||||||
return mState->mDefaultLayout;
|
return mState->mDefaultLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange range) {
|
VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange const& range, VkImageViewType type) {
|
||||||
range.levelCount = 1;
|
assert_invariant(mState->mYcbcr.conversion == VK_NULL_HANDLE &&
|
||||||
range.layerCount = 1;
|
"We are not yet supporting external image as attachments.");
|
||||||
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D, {});
|
if (type == VK_IMAGE_VIEW_TYPE_2D) {
|
||||||
}
|
VkImageSubresourceRange copy = range;
|
||||||
|
copy.levelCount = 1;
|
||||||
VkImageView VulkanTexture::getMultiviewAttachmentView(VkImageSubresourceRange range) {
|
copy.layerCount = 1;
|
||||||
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D_ARRAY, {});
|
return mState->getImageView(copy, type, {});
|
||||||
}
|
} else {
|
||||||
|
return mState->getImageView(range, type, {});
|
||||||
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,
|
VkImageView VulkanTexture::getView(VkImageSubresourceRange const& range) {
|
||||||
.flags = 0,
|
return mState->getImageView(range, mState->mViewType, mSwizzle);
|
||||||
.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 {
|
||||||
@@ -738,6 +764,21 @@ 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;
|
||||||
|
|||||||
@@ -19,7 +19,10 @@
|
|||||||
|
|
||||||
#include "DriverBase.h"
|
#include "DriverBase.h"
|
||||||
|
|
||||||
#include "VulkanBuffer.h"
|
#include "VulkanCommands.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"
|
||||||
@@ -31,10 +34,14 @@
|
|||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
|
struct VulkanTexture;
|
||||||
|
|
||||||
struct VulkanTextureState : public fvkmemory::Resource {
|
struct VulkanTextureState : public fvkmemory::Resource {
|
||||||
VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
|
VulkanTextureState(VulkanStagePool& stagePool, VulkanCommands* commands, VmaAllocator allocator,
|
||||||
VulkanStagePool& stagePool, VkFormat format, VkImageViewType viewType, uint8_t levels,
|
VkDevice device, VkImage image, VkDeviceMemory deviceMemory, VkFormat format,
|
||||||
uint8_t layerCount, VulkanLayout defaultLayout, bool isProtected);
|
VkImageViewType viewType, uint8_t levels, uint8_t layerCount,
|
||||||
|
VkSamplerYcbcrConversion ycbcrConversion, bool isExternalFormat,
|
||||||
|
VkImageUsageFlags usage, bool isProtected);
|
||||||
|
|
||||||
~VulkanTextureState();
|
~VulkanTextureState();
|
||||||
|
|
||||||
@@ -57,29 +64,50 @@ 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);
|
||||||
|
|
||||||
using ImageViewHash = utils::hash::MurmurHashFn<ImageViewKey>;
|
VkImageView getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
|
||||||
|
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;
|
|
||||||
|
|
||||||
bool mIsProtected = false;
|
// Note that this parameter is not constant due to the fact that AHB can force a change in the
|
||||||
|
// 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;
|
std::unordered_map<ImageViewKey, VkImageView, ImageViewHash> mCachedImageViews;
|
||||||
VulkanStagePool& mStagePool;
|
|
||||||
VkDevice mDevice;
|
friend struct VulkanTexture;
|
||||||
VmaAllocator mAllocator;
|
|
||||||
VulkanCommands* mCommands;
|
|
||||||
bool mIsTransientAttachment;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
||||||
@@ -91,10 +119,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)
|
||||||
// The texture will never destroy the given VkImage, but it does manages its subresources.
|
VulkanTexture(VulkanContext const& context, VkDevice device, VmaAllocator allocator,
|
||||||
VulkanTexture(VkDevice device, VmaAllocator allocator,
|
|
||||||
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image,
|
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image,
|
||||||
VkDeviceMemory memory, VkFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth,
|
VkDeviceMemory memory, VkFormat format, VkSamplerYcbcrConversion conversion,
|
||||||
|
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
|
||||||
@@ -113,11 +141,6 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -132,20 +155,11 @@ 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 single subresource that can be used as a render
|
// Gets or creates a cached VkImageView for a subresource that can be used as a render
|
||||||
// target attachment. Unlike the primary image view, this always has type VK_IMAGE_VIEW_TYPE_2D
|
// target attachment. Unlike the primary image view, this always the identity swizzle.
|
||||||
// and the identity swizzle.
|
VkImageView getAttachmentView(VkImageSubresourceRange const& range, VkImageViewType type);
|
||||||
VkImageView getAttachmentView(VkImageSubresourceRange range);
|
|
||||||
|
|
||||||
// Gets or creates a cached VkImageView for a single subresource that can be used as a render
|
VkImageView getView(VkImageSubresourceRange const& range);
|
||||||
// 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;
|
||||||
@@ -165,7 +179,7 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool isTransientAttachment() const {
|
bool isTransientAttachment() const {
|
||||||
return mState->mIsTransientAttachment;
|
return mState->mUsage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getIsProtected() const {
|
bool getIsProtected() const {
|
||||||
@@ -192,6 +206,11 @@ 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
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
@@ -211,6 +211,7 @@ 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__)
|
||||||
@@ -326,7 +327,9 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
|
VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
|
||||||
VkPhysicalDeviceFeatures2 const& features, uint32_t graphicsQueueFamilyIndex,
|
VkPhysicalDeviceFeatures2 const& features,
|
||||||
|
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;
|
||||||
@@ -359,14 +362,28 @@ 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();
|
||||||
@@ -383,7 +400,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 = VK_TRUE,
|
.multiview = vk11Features.multiview,
|
||||||
.multiviewGeometryShader = VK_FALSE,
|
.multiviewGeometryShader = VK_FALSE,
|
||||||
.multiviewTessellationShader = VK_FALSE,
|
.multiviewTessellationShader = VK_FALSE,
|
||||||
};
|
};
|
||||||
@@ -734,6 +751,7 @@ 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);
|
||||||
|
|
||||||
// Initialize the following fields: physicalDeviceProperties, memoryProperties,
|
// Initialize the following fields: physicalDeviceProperties, memoryProperties,
|
||||||
@@ -795,10 +813,10 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mImpl->mDevice == VK_NULL_HANDLE) {
|
if (mImpl->mDevice == VK_NULL_HANDLE) {
|
||||||
mImpl->mDevice =
|
mImpl->mDevice = createLogicalDevice(mImpl->mPhysicalDevice,
|
||||||
createLogicalDevice(mImpl->mPhysicalDevice, context.mPhysicalDeviceFeatures,
|
context.mPhysicalDeviceFeatures, context.mPhysicalDeviceVk11Features,
|
||||||
mImpl->mGraphicsQueueFamilyIndex, mImpl->mProtectedGraphicsQueueFamilyIndex,
|
mImpl->mGraphicsQueueFamilyIndex, mImpl->mProtectedGraphicsQueueFamilyIndex,
|
||||||
deviceExts, requestPortabilitySubsetImageView2DOn3DImage);
|
deviceExts, requestPortabilitySubsetImageView2DOn3DImage);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_invariant(mImpl->mDevice != VK_NULL_HANDLE);
|
assert_invariant(mImpl->mDevice != VK_NULL_HANDLE);
|
||||||
@@ -826,12 +844,10 @@ 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
|
||||||
|
|||||||
@@ -295,9 +295,8 @@ VulkanPlatformAndroid::ImageData VulkanPlatformAndroid::createExternalImageData(
|
|||||||
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device, SamplerYcbcrConversion chroma,
|
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device, SamplerYcbcrConversion chroma,
|
||||||
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||||
VkImageViewType viewType, VkComponentMapping swizzle){
|
VkImageViewType viewType, VkComponentMapping swizzle){
|
||||||
VkExternalFormatANDROID externalFormat = {
|
VkExternalFormatANDROID externalFormat = {
|
||||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||||
.pNext = nullptr,
|
|
||||||
.externalFormat = internalFormat,
|
.externalFormat = internalFormat,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -322,7 +321,6 @@ VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device, Sampler
|
|||||||
|
|
||||||
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
|
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
|
||||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
||||||
.pNext = nullptr,
|
|
||||||
.conversion = conversion,
|
.conversion = conversion,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -350,7 +348,6 @@ VkSampler VulkanPlatform::createExternalSamplerImpl(
|
|||||||
uint32_t internalFormat) {
|
uint32_t internalFormat) {
|
||||||
VkExternalFormatANDROID externalFormat = {
|
VkExternalFormatANDROID externalFormat = {
|
||||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||||
.pNext = nullptr,
|
|
||||||
.externalFormat = internalFormat,
|
.externalFormat = internalFormat,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -24,28 +24,12 @@
|
|||||||
#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>
|
|
||||||
|
|
||||||
#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
|
|
||||||
#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
|
#endif
|
||||||
|
|
||||||
using namespace bluevk;
|
using namespace bluevk;
|
||||||
@@ -54,11 +38,7 @@ namespace filament::backend {
|
|||||||
|
|
||||||
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
|
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
|
||||||
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;
|
||||||
}
|
}
|
||||||
@@ -90,36 +70,20 @@ VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
|
|||||||
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
|
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.";
|
|
||||||
|
|
||||||
// Create the VkSurface.
|
|
||||||
FILAMENT_CHECK_POSTCONDITION(vkCreateMacOSSurfaceMVK)
|
|
||||||
<< "Unable to load vkCreateMacOSSurfaceMVK.";
|
|
||||||
VkMacOSSurfaceCreateInfoMVK createInfo = {};
|
|
||||||
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
|
|
||||||
createInfo.pView = (__bridge void*) nsview;
|
|
||||||
VkResult result = vkCreateMacOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
|
|
||||||
(VkSurfaceKHR*) &surface);
|
|
||||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
|
||||||
<< "vkCreateMacOSSurfaceMVK. error=" << static_cast<int32_t>(result);
|
|
||||||
#elif defined(FILAMENT_IOS) && defined(METAL_AVAILABLE)
|
|
||||||
CAMetalLayer* metalLayer = (CAMetalLayer*) nativeWindow;
|
|
||||||
// Create the VkSurface.
|
// Create the VkSurface.
|
||||||
FILAMENT_CHECK_POSTCONDITION(vkCreateIOSSurfaceMVK)
|
FILAMENT_CHECK_POSTCONDITION(vkCreateMacOSSurfaceMVK)
|
||||||
<< "Unable to load vkCreateIOSSurfaceMVK function.";
|
<< "Unable to load vkCreateMacOSSurfaceMVK.";
|
||||||
VkIOSSurfaceCreateInfoMVK createInfo = {};
|
VkMacOSSurfaceCreateInfoMVK createInfo = {};
|
||||||
createInfo.sType = VK_STRUCTURE_TYPE_IOS_SURFACE_CREATE_INFO_MVK;
|
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
|
||||||
createInfo.pNext = NULL;
|
createInfo.pView = (__bridge void*) nsview;
|
||||||
createInfo.flags = 0;
|
VkResult result = vkCreateMacOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
|
||||||
createInfo.pView = metalLayer;
|
|
||||||
VkResult result = vkCreateIOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
|
|
||||||
(VkSurfaceKHR*) &surface);
|
(VkSurfaceKHR*) &surface);
|
||||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||||
<< "vkCreateIOSSurfaceMVK failed. error=" << static_cast<int32_t>(result);
|
<< "vkCreateMacOSSurfaceMVK. error=" << static_cast<int32_t>(result);
|
||||||
#endif
|
return std::make_tuple(surface, VkExtent2D{});
|
||||||
return std::make_tuple(surface, VkExtent2D{});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace filament::backend
|
} // namespace filament::backend
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -668,4 +668,111 @@ 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
|
||||||
|
|||||||
@@ -69,6 +69,12 @@ VkSamplerYcbcrModelConversion getYcbcrModelConversion(SamplerYcbcrModelConversio
|
|||||||
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range);
|
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range);
|
||||||
VkChromaLocation getChromaLocation(ChromaLocation loc);
|
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,6 +348,10 @@ 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.
|
||||||
|
|||||||
@@ -193,49 +193,6 @@ uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask) {
|
|||||||
return mostSignificantBit((sampleCount - 1) & mask);
|
return mostSignificantBit((sampleCount - 1) & mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
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.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace filament::backend::fvkutils
|
} // namespace filament::backend::fvkutils
|
||||||
|
|
||||||
bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b) {
|
bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b) {
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "webgpu/WebGPUDriver.h"
|
#include "webgpu/WebGPUDriver.h"
|
||||||
|
|
||||||
|
#include "WebGPUSwapChain.h"
|
||||||
#include "webgpu/WebGPUConstants.h"
|
#include "webgpu/WebGPUConstants.h"
|
||||||
#include <backend/platforms/WebGPUPlatform.h>
|
#include <backend/platforms/WebGPUPlatform.h>
|
||||||
|
|
||||||
@@ -35,6 +36,7 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -116,7 +118,6 @@ void printLimits(wgpu::Limits const& limits) {
|
|||||||
printLimit("maxBufferSize", limits.maxBufferSize);
|
printLimit("maxBufferSize", limits.maxBufferSize);
|
||||||
printLimit("maxVertexAttributes", limits.maxVertexAttributes);
|
printLimit("maxVertexAttributes", limits.maxVertexAttributes);
|
||||||
printLimit("maxVertexBufferArrayStride", limits.maxVertexBufferArrayStride);
|
printLimit("maxVertexBufferArrayStride", limits.maxVertexBufferArrayStride);
|
||||||
printLimit("maxInterStageShaderComponents", limits.maxInterStageShaderComponents);
|
|
||||||
printLimit("maxInterStageShaderVariables", limits.maxInterStageShaderVariables);
|
printLimit("maxInterStageShaderVariables", limits.maxInterStageShaderVariables);
|
||||||
printLimit("maxColorAttachments", limits.maxColorAttachments);
|
printLimit("maxColorAttachments", limits.maxColorAttachments);
|
||||||
printLimit("maxColorAttachmentBytesPerSample", limits.maxColorAttachmentBytesPerSample);
|
printLimit("maxColorAttachmentBytesPerSample", limits.maxColorAttachmentBytesPerSample);
|
||||||
@@ -158,8 +159,6 @@ void printAdapterDetails(wgpu::Adapter const& adapter) {
|
|||||||
FWGPU_LOGI << " vendor ID: " << adapterInfo.vendorID << 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 min size: " << adapterInfo.subgroupMinSize << utils::io::endl;
|
||||||
FWGPU_LOGI << " subgroup max size: " << adapterInfo.subgroupMaxSize << utils::io::endl;
|
FWGPU_LOGI << " subgroup max size: " << adapterInfo.subgroupMaxSize << utils::io::endl;
|
||||||
FWGPU_LOGI << " compatibility mode: " << bool(adapterInfo.compatibilityMode)
|
|
||||||
<< utils::io::endl;
|
|
||||||
FWGPU_LOGI << " power preference: " << powerPreferenceStream.str() << utils::io::endl;
|
FWGPU_LOGI << " power preference: " << powerPreferenceStream.str() << utils::io::endl;
|
||||||
}
|
}
|
||||||
wgpu::SupportedFeatures supportedFeatures{};
|
wgpu::SupportedFeatures supportedFeatures{};
|
||||||
@@ -175,55 +174,17 @@ void printAdapterDetails(wgpu::Adapter const& adapter) {
|
|||||||
FWGPU_LOGI << " " << nameStream.str().data() << utils::io::endl;
|
FWGPU_LOGI << " " << nameStream.str().data() << utils::io::endl;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
wgpu::SupportedLimits supportedLimits{};
|
wgpu::Limits supportedLimits{};
|
||||||
if (!adapter.GetLimits(&supportedLimits)) {
|
if (!adapter.GetLimits(&supportedLimits)) {
|
||||||
FWGPU_LOGW << "Failed to get WebGPU adapter supported limits" << utils::io::endl;
|
FWGPU_LOGW << "Failed to get WebGPU adapter supported limits" << utils::io::endl;
|
||||||
} else {
|
} else {
|
||||||
FWGPU_LOGI << "WebGPU adapter supported limits:" << utils::io::endl;
|
FWGPU_LOGI << "WebGPU adapter supported limits:" << utils::io::endl;
|
||||||
printLimits(supportedLimits.limits);
|
printLimits(supportedLimits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
|
||||||
void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabilities) {
|
|
||||||
std::stringstream usages_stream{};
|
|
||||||
usages_stream << capabilities.usages;
|
|
||||||
FWGPU_LOGI << "WebGPU surface capabilities:" << utils::io::endl;
|
|
||||||
FWGPU_LOGI << " surface usages: " << usages_stream.str().data() << utils::io::endl;
|
|
||||||
FWGPU_LOGI << " surface formats (" << capabilities.formatCount << "):" << utils::io::endl;
|
|
||||||
if (capabilities.formatCount > 0 && capabilities.formats != nullptr) {
|
|
||||||
std::for_each(capabilities.formats, capabilities.formats + capabilities.formatCount,
|
|
||||||
[](wgpu::TextureFormat const format) {
|
|
||||||
std::stringstream format_stream{};
|
|
||||||
format_stream << format;
|
|
||||||
FWGPU_LOGI << " " << format_stream.str().data() << utils::io::endl;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
FWGPU_LOGI << " surface present modes (" << capabilities.presentModeCount
|
|
||||||
<< "):" << utils::io::endl;
|
|
||||||
if (capabilities.presentModeCount > 0 && capabilities.presentModes != nullptr) {
|
|
||||||
std::for_each(capabilities.presentModes,
|
|
||||||
capabilities.presentModes + capabilities.presentModeCount,
|
|
||||||
[](wgpu::PresentMode const presentMode) {
|
|
||||||
std::stringstream present_mode_stream{};
|
|
||||||
present_mode_stream << presentMode;
|
|
||||||
FWGPU_LOGI << " " << present_mode_stream.str().data() << utils::io::endl;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
FWGPU_LOGI << " surface alpha modes (" << capabilities.alphaModeCount
|
|
||||||
<< "):" << utils::io::endl;
|
|
||||||
if (capabilities.alphaModeCount > 0 && capabilities.alphaModes != nullptr) {
|
|
||||||
std::for_each(capabilities.alphaModes,
|
|
||||||
capabilities.alphaModes + capabilities.alphaModeCount,
|
|
||||||
[](wgpu::CompositeAlphaMode const alphaMode) {
|
|
||||||
std::stringstream alpha_mode_stream{};
|
|
||||||
alpha_mode_stream << alphaMode;
|
|
||||||
FWGPU_LOGI << " " << alpha_mode_stream.str().data() << utils::io::endl;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||||
void printDeviceDetails(wgpu::Device const& device) {
|
void printDeviceDetails(wgpu::Device const& device) {
|
||||||
@@ -240,12 +201,12 @@ void printDeviceDetails(wgpu::Device const& device) {
|
|||||||
FWGPU_LOGI << " " << nameStream.str().data() << utils::io::endl;
|
FWGPU_LOGI << " " << nameStream.str().data() << utils::io::endl;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
wgpu::SupportedLimits supportedLimits{};
|
wgpu::Limits supportedLimits{};
|
||||||
if (!device.GetLimits(&supportedLimits)) {
|
if (!device.GetLimits(&supportedLimits)) {
|
||||||
FWGPU_LOGW << "Failed to get WebGPU supported device limits" << utils::io::endl;
|
FWGPU_LOGW << "Failed to get WebGPU supported device limits" << utils::io::endl;
|
||||||
} else {
|
} else {
|
||||||
FWGPU_LOGI << "WebGPU device supported limits:" << utils::io::endl;
|
FWGPU_LOGI << "WebGPU device supported limits:" << utils::io::endl;
|
||||||
printLimits(supportedLimits.limits);
|
printLimits(supportedLimits);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -267,6 +228,14 @@ WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfi
|
|||||||
driverConfig.disableHeapHandleTags) {
|
driverConfig.disableHeapHandleTags) {
|
||||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||||
printInstanceDetails(mPlatform.getInstance());
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -296,6 +265,7 @@ 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,
|
||||||
@@ -325,15 +295,27 @@ 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) {
|
||||||
@@ -343,12 +325,19 @@ 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) {
|
||||||
@@ -358,13 +347,16 @@ 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 {
|
Handle<HwSwapChain> WebGPUDriver::createSwapChainS() noexcept {
|
||||||
return Handle<HwSwapChain>((Handle<HwSwapChain>::HandleId) mNextFakeHandle++);
|
return allocHandle<WebGPUSwapChain>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwSwapChain> WebGPUDriver::createSwapChainHeadlessS() noexcept {
|
Handle<HwSwapChain> WebGPUDriver::createSwapChainHeadlessS() noexcept {
|
||||||
@@ -372,7 +364,7 @@ Handle<HwSwapChain> WebGPUDriver::createSwapChainHeadlessS() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwTexture> WebGPUDriver::createTextureS() noexcept {
|
Handle<HwTexture> WebGPUDriver::createTextureS() noexcept {
|
||||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
return allocHandle<WGPUTexture>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
|
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
|
||||||
@@ -380,7 +372,7 @@ Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwProgram> WebGPUDriver::createProgramS() noexcept {
|
Handle<HwProgram> WebGPUDriver::createProgramS() noexcept {
|
||||||
return Handle<HwProgram>((Handle<HwProgram>::HandleId) mNextFakeHandle++);
|
return allocHandle<WGPUProgram>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwFence> WebGPUDriver::createFenceS() noexcept {
|
Handle<HwFence> WebGPUDriver::createFenceS() noexcept {
|
||||||
@@ -400,15 +392,15 @@ Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
|
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
|
||||||
return Handle<HwBufferObject>((Handle<HwBufferObject>::HandleId) mNextFakeHandle++);
|
return allocHandle<WGPUBufferObject>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwRenderTarget> WebGPUDriver::createRenderTargetS() noexcept {
|
Handle<HwRenderTarget> WebGPUDriver::createRenderTargetS() noexcept {
|
||||||
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
|
return allocHandle<WGPURenderTarget>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwVertexBuffer> WebGPUDriver::createVertexBufferS() noexcept {
|
Handle<HwVertexBuffer> WebGPUDriver::createVertexBufferS() noexcept {
|
||||||
return Handle<HwVertexBuffer>((Handle<HwVertexBuffer>::HandleId) mNextFakeHandle++);
|
return allocHandle<WGPUVertexBuffer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwDescriptorSet> WebGPUDriver::createDescriptorSetS() noexcept {
|
Handle<HwDescriptorSet> WebGPUDriver::createDescriptorSetS() noexcept {
|
||||||
@@ -416,11 +408,11 @@ Handle<HwDescriptorSet> WebGPUDriver::createDescriptorSetS() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwRenderPrimitive> WebGPUDriver::createRenderPrimitiveS() noexcept {
|
Handle<HwRenderPrimitive> WebGPUDriver::createRenderPrimitiveS() noexcept {
|
||||||
return Handle<HwRenderPrimitive>((Handle<HwRenderPrimitive>::HandleId) mNextFakeHandle++);
|
return allocHandle<WGPURenderPrimitive>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
|
Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
|
||||||
return Handle<HwVertexBufferInfo>((Handle<HwVertexBufferInfo>::HandleId) mNextFakeHandle++);
|
return allocHandle<WGPUVertexBufferInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
|
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
|
||||||
@@ -428,12 +420,11 @@ Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
|
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
|
||||||
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
|
return allocHandle<WGPURenderTarget>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcept {
|
Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcept {
|
||||||
return Handle<HwDescriptorSetLayout>(
|
return allocHandle<WebGPUDescriptorSetLayout>();
|
||||||
(Handle<HwDescriptorSetLayout>::HandleId) mNextFakeHandle++);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle<HwTexture> WebGPUDriver::createTextureExternalImageS() noexcept {
|
Handle<HwTexture> WebGPUDriver::createTextureExternalImageS() noexcept {
|
||||||
@@ -449,25 +440,15 @@ Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
|
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
|
||||||
mSurface= mPlatform.createSurface(nativeWindow, flags);
|
mNativeWindow = nativeWindow;
|
||||||
mAdapter = mPlatform.requestAdapter(mSurface);
|
assert_invariant(!mSwapChain);
|
||||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
wgpu::Surface surface = mPlatform.createSurface(nativeWindow, flags);
|
||||||
printAdapterDetails(mAdapter);
|
|
||||||
wgpu::SurfaceCapabilities surfaceCapabilities{};
|
|
||||||
if (!mSurface.GetCapabilities(mAdapter, &surfaceCapabilities)) {
|
|
||||||
FWGPU_LOGW << "Failed to get WebGPU surface capabilities" << utils::io::endl;
|
|
||||||
} else {
|
|
||||||
printSurfaceCapabilitiesDetails(surfaceCapabilities);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
mDevice = mPlatform.requestDevice(mAdapter);
|
|
||||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
|
||||||
printDeviceDetails(mDevice);
|
|
||||||
#endif
|
|
||||||
mQueue = mDevice.GetQueue();
|
mQueue = mDevice.GetQueue();
|
||||||
// TODO configure the surface (maybe before or after creating the swapchain?
|
wgpu::Extent2D surfaceSize = mPlatform.getSurfaceExtent(mNativeWindow);
|
||||||
// how do we get the surface extent?)
|
mSwapChain = constructHandle<WebGPUSwapChain>(sch, std::move(surface), surfaceSize, mAdapter,
|
||||||
// TODO actually create the swapchain
|
mDevice, flags);
|
||||||
|
assert_invariant(mSwapChain);
|
||||||
FWGPU_LOGW << "WebGPU support is still essentially a no-op at this point in development (only "
|
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, "
|
"background components have been instantiated/selected, such as surface/screen, "
|
||||||
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
|
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
|
||||||
@@ -527,9 +508,15 @@ void WebGPUDriver::importTextureR(Handle<HwTexture> th, intptr_t id, SamplerType
|
|||||||
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
|
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
|
||||||
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {}
|
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {}
|
||||||
|
|
||||||
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {}
|
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
|
||||||
|
constructHandle<WGPUProgram>(ph, mDevice, program);
|
||||||
|
}
|
||||||
|
|
||||||
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {}
|
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,
|
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets,
|
||||||
uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color,
|
uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color,
|
||||||
@@ -540,7 +527,9 @@ void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {}
|
|||||||
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}
|
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}
|
||||||
|
|
||||||
void WebGPUDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
|
void WebGPUDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
|
||||||
backend::DescriptorSetLayout&& info) {}
|
backend::DescriptorSetLayout&& info) {
|
||||||
|
constructHandle<WebGPUDescriptorSetLayout>(dslh, std::move(info), mDevice);
|
||||||
|
}
|
||||||
|
|
||||||
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
|
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
|
||||||
Handle<HwDescriptorSetLayout> dslh) {}
|
Handle<HwDescriptorSetLayout> dslh) {}
|
||||||
@@ -690,6 +679,10 @@ 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,
|
||||||
@@ -722,18 +715,71 @@ 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,
|
||||||
@@ -761,7 +807,7 @@ void WebGPUDriver::readPixels(Handle<HwRenderTarget> src,
|
|||||||
scheduleDestroy(std::move(p));
|
scheduleDestroy(std::move(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::readBufferSubData(backend::BufferObjectHandle boh,
|
void WebGPUDriver::readBufferSubData(Handle<HwBufferObject> 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));
|
||||||
}
|
}
|
||||||
@@ -813,22 +859,22 @@ void WebGPUDriver::resetState(int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::updateDescriptorSetBuffer(
|
void WebGPUDriver::updateDescriptorSetBuffer(
|
||||||
backend::DescriptorSetHandle dsh,
|
Handle<HwDescriptorSet> dsh,
|
||||||
backend::descriptor_binding_t binding,
|
backend::descriptor_binding_t binding,
|
||||||
backend::BufferObjectHandle boh,
|
Handle<HwBufferObject> boh,
|
||||||
uint32_t offset,
|
uint32_t offset,
|
||||||
uint32_t size) {
|
uint32_t size) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::updateDescriptorSetTexture(
|
void WebGPUDriver::updateDescriptorSetTexture(
|
||||||
backend::DescriptorSetHandle dsh,
|
Handle<HwDescriptorSet> dsh,
|
||||||
backend::descriptor_binding_t binding,
|
backend::descriptor_binding_t binding,
|
||||||
backend::TextureHandle th,
|
Handle<HwTexture> th,
|
||||||
SamplerParams params) {
|
SamplerParams params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::bindDescriptorSet(
|
void WebGPUDriver::bindDescriptorSet(
|
||||||
backend::DescriptorSetHandle dsh,
|
Handle<HwDescriptorSet> dsh,
|
||||||
backend::descriptor_set_t set,
|
backend::descriptor_set_t set,
|
||||||
backend::DescriptorSetOffsetArray&& offsets) {
|
backend::DescriptorSetOffsetArray&& offsets) {
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#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 <backend/platforms/WebGPUPlatform.h>
|
||||||
|
|
||||||
#include "DriverBase.h"
|
#include "DriverBase.h"
|
||||||
@@ -30,6 +31,7 @@
|
|||||||
#include <webgpu/webgpu_cpp.h>
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
#ifndef FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB
|
#ifndef FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB
|
||||||
# define FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB 8
|
# define FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB 8
|
||||||
@@ -37,6 +39,8 @@
|
|||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
|
class WebGPUSwapChain;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebGPU backend (driver) implementation
|
* WebGPU backend (driver) implementation
|
||||||
*/
|
*/
|
||||||
@@ -55,16 +59,20 @@ private:
|
|||||||
// the platform (e.g. OS) specific aspects of the WebGPU backend are strictly only
|
// the platform (e.g. OS) specific aspects of the WebGPU backend are strictly only
|
||||||
// handled in the WebGPUPlatform
|
// handled in the WebGPUPlatform
|
||||||
WebGPUPlatform& mPlatform;
|
WebGPUPlatform& mPlatform;
|
||||||
wgpu::Surface mSurface = nullptr;
|
|
||||||
wgpu::Adapter mAdapter = nullptr;
|
wgpu::Adapter mAdapter = nullptr;
|
||||||
wgpu::Device mDevice = nullptr;
|
wgpu::Device mDevice = nullptr;
|
||||||
wgpu::Queue mQueue = nullptr;
|
wgpu::Queue mQueue = nullptr;
|
||||||
|
void* mNativeWindow = nullptr;
|
||||||
|
WebGPUSwapChain* mSwapChain = nullptr;
|
||||||
uint64_t mNextFakeHandle = 1;
|
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;
|
||||||
|
|
||||||
@@ -91,6 +99,21 @@ private:
|
|||||||
return mHandleAllocator.allocate<D>();
|
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::backend
|
||||||
|
|||||||
@@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
@@ -0,0 +1,276 @@
|
|||||||
|
/*
|
||||||
|
* 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 "webgpu/WebGPUSwapChain.h"
|
||||||
|
|
||||||
|
#include "webgpu/WebGPUConstants.h"
|
||||||
|
|
||||||
|
#include "backend/DriverEnums.h"
|
||||||
|
|
||||||
|
#include <utils/Panic.h>
|
||||||
|
#include <utils/ostream.h>
|
||||||
|
|
||||||
|
#include <dawn/webgpu_cpp_print.h>
|
||||||
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||||
|
void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabilities) {
|
||||||
|
std::stringstream usagesStream{};
|
||||||
|
usagesStream << capabilities.usages;
|
||||||
|
FWGPU_LOGI << "WebGPU surface capabilities:" << utils::io::endl;
|
||||||
|
FWGPU_LOGI << " surface usages: " << usagesStream.str().data() << utils::io::endl;
|
||||||
|
FWGPU_LOGI << " surface formats (" << capabilities.formatCount << "):" << utils::io::endl;
|
||||||
|
if (capabilities.formatCount > 0 && capabilities.formats != nullptr) {
|
||||||
|
std::for_each(capabilities.formats, capabilities.formats + capabilities.formatCount,
|
||||||
|
[](wgpu::TextureFormat const format) {
|
||||||
|
std::stringstream formatStream{};
|
||||||
|
formatStream << format;
|
||||||
|
FWGPU_LOGI << " " << formatStream.str().data() << utils::io::endl;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
FWGPU_LOGI << " surface present modes (" << capabilities.presentModeCount
|
||||||
|
<< "):" << utils::io::endl;
|
||||||
|
if (capabilities.presentModeCount > 0 && capabilities.presentModes != nullptr) {
|
||||||
|
std::for_each(capabilities.presentModes,
|
||||||
|
capabilities.presentModes + capabilities.presentModeCount,
|
||||||
|
[](wgpu::PresentMode const presentMode) {
|
||||||
|
std::stringstream presentModeStream{};
|
||||||
|
presentModeStream << presentMode;
|
||||||
|
FWGPU_LOGI << " " << presentModeStream.str().data() << utils::io::endl;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
FWGPU_LOGI << " surface alpha modes (" << capabilities.alphaModeCount
|
||||||
|
<< "):" << utils::io::endl;
|
||||||
|
if (capabilities.alphaModeCount > 0 && capabilities.alphaModes != nullptr) {
|
||||||
|
std::for_each(capabilities.alphaModes,
|
||||||
|
capabilities.alphaModes + capabilities.alphaModeCount,
|
||||||
|
[](wgpu::CompositeAlphaMode const alphaMode) {
|
||||||
|
std::stringstream alphaModeStream{};
|
||||||
|
alphaModeStream << alphaMode;
|
||||||
|
FWGPU_LOGI << " " << alphaModeStream.str().data() << utils::io::endl;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||||
|
void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config) {
|
||||||
|
std::stringstream formatStream{};
|
||||||
|
formatStream << config.format;
|
||||||
|
std::stringstream usageStream{};
|
||||||
|
usageStream << config.usage;
|
||||||
|
std::stringstream alphaModeStream{};
|
||||||
|
alphaModeStream << config.alphaMode;
|
||||||
|
std::stringstream presentModeStream{};
|
||||||
|
presentModeStream << config.presentMode;
|
||||||
|
FWGPU_LOGI << "WebGPU surface configuration:" << utils::io::endl;
|
||||||
|
FWGPU_LOGI << " surface format: " << formatStream.str() << utils::io::endl;
|
||||||
|
FWGPU_LOGI << " surface usage: " << usageStream.str() << utils::io::endl;
|
||||||
|
FWGPU_LOGI << " surface view formats (" << config.viewFormatCount << "):" << utils::io::endl;
|
||||||
|
if (config.viewFormatCount > 0 && config.viewFormats != nullptr) {
|
||||||
|
std::for_each(config.viewFormats, config.viewFormats + config.viewFormatCount,
|
||||||
|
[](wgpu::TextureFormat const viewFormat) {
|
||||||
|
std::stringstream viewFormatStream{};
|
||||||
|
viewFormatStream << viewFormat;
|
||||||
|
FWGPU_LOGI << " " << viewFormatStream.str().data() << utils::io::endl;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
FWGPU_LOGI << " surface alpha mode: " << alphaModeStream.str() << utils::io::endl;
|
||||||
|
FWGPU_LOGI << " surface width: " << config.width << utils::io::endl;
|
||||||
|
FWGPU_LOGI << " surface height: " << config.height << utils::io::endl;
|
||||||
|
FWGPU_LOGI << " surface present mode: " << presentModeStream.str() << utils::io::endl;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
|
||||||
|
wgpu::TextureFormat const* availableFormats, bool useSRGBColorSpace) {
|
||||||
|
const std::array expectedColorFormats =
|
||||||
|
useSRGBColorSpace ?
|
||||||
|
std::array{
|
||||||
|
wgpu::TextureFormat::RGBA8UnormSrgb,
|
||||||
|
wgpu::TextureFormat::BGRA8UnormSrgb }
|
||||||
|
: std::array{
|
||||||
|
wgpu::TextureFormat::RGBA8Unorm,
|
||||||
|
wgpu::TextureFormat::BGRA8Unorm };
|
||||||
|
auto const firstFoundColorFormat = std::find_first_of(expectedColorFormats.begin(),
|
||||||
|
expectedColorFormats.end(), availableFormats, availableFormats + availableFormatsCount);
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(firstFoundColorFormat != expectedColorFormats.end())
|
||||||
|
<< "Cannot find a suitable WebGPU swapchain "
|
||||||
|
<< (useSRGBColorSpace ? "sRGB" : "non-standard (e.g. linear) RGB") << " color format";
|
||||||
|
return *firstFoundColorFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu::PresentMode selectPresentMode(size_t availablePresentModesCount,
|
||||||
|
wgpu::PresentMode const* availablePresentModes) {
|
||||||
|
// Verify that our chosen present mode is supported. In practice all devices support the FIFO
|
||||||
|
// mode, but we check for it anyway for completeness. (and to avoid validation warnings)
|
||||||
|
const wgpu::PresentMode desiredPresentMode = wgpu::PresentMode::Fifo;
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(
|
||||||
|
std::any_of(availablePresentModes, availablePresentModes + availablePresentModesCount,
|
||||||
|
[](const wgpu::PresentMode availablePresentMode) {
|
||||||
|
return availablePresentMode == desiredPresentMode;
|
||||||
|
}))
|
||||||
|
<< "Cannot find a suitable WebGPU swapchain present mode";
|
||||||
|
return desiredPresentMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu::CompositeAlphaMode selectAlphaMode(size_t availableAlphaModesCount,
|
||||||
|
wgpu::CompositeAlphaMode const* availableAlphaModes) {
|
||||||
|
bool autoAvailable = false;
|
||||||
|
bool inheritAvailable = false;
|
||||||
|
bool opaqueAvailable = false;
|
||||||
|
bool premultipliedAvailable = false;
|
||||||
|
bool unpremultipliedAvailable = false;
|
||||||
|
std::for_each(availableAlphaModes, availableAlphaModes + availableAlphaModesCount,
|
||||||
|
[&](const wgpu::CompositeAlphaMode alphMode) {
|
||||||
|
switch (alphMode) {
|
||||||
|
// in practice, the surface capabilities would not list Auto,
|
||||||
|
// but for completeness and defensive programming we can leverage it
|
||||||
|
// if it explicitly comes back as available/supported
|
||||||
|
case wgpu::CompositeAlphaMode::Auto:
|
||||||
|
autoAvailable = true;
|
||||||
|
break;
|
||||||
|
case wgpu::CompositeAlphaMode::Opaque:
|
||||||
|
opaqueAvailable = true;
|
||||||
|
break;
|
||||||
|
case wgpu::CompositeAlphaMode::Premultiplied:
|
||||||
|
premultipliedAvailable = true;
|
||||||
|
break;
|
||||||
|
case wgpu::CompositeAlphaMode::Unpremultiplied:
|
||||||
|
unpremultipliedAvailable = true;
|
||||||
|
break;
|
||||||
|
case wgpu::CompositeAlphaMode::Inherit:
|
||||||
|
inheritAvailable = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (autoAvailable || inheritAvailable || opaqueAvailable) {
|
||||||
|
return wgpu::CompositeAlphaMode::Auto;
|
||||||
|
} else if (premultipliedAvailable) {
|
||||||
|
// In practice, we do not expect this to possibly happen, as opaque should be supported,
|
||||||
|
// which we select first. However, if the underlying system actually does not support
|
||||||
|
// opaque we allow it to be tested, but warn that this may not likely work as they expect
|
||||||
|
// (untested territory).
|
||||||
|
// We prefer premultiplied to unpremuliplied until that assumption should be adjusted.
|
||||||
|
FWGPU_LOGW << "Auto, Inherit, & Opaque composite alpha modes not supported. Filament has "
|
||||||
|
"historically used these. Premultiplied alpha composite mode for "
|
||||||
|
"transparency is being selected as a fallback, but may not work as expected."
|
||||||
|
<< utils::io::endl;
|
||||||
|
return wgpu::CompositeAlphaMode::Premultiplied;
|
||||||
|
} else {
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(unpremultipliedAvailable)
|
||||||
|
<< "No available composite alpha modes? Unknown/unhandled composite alpha mode?";
|
||||||
|
// Again, we don't expect this in practice, but allow if for the same reason as premultiplied.
|
||||||
|
// We prefer premultiplied to unpremuliplied until that assumption should be adjusted.
|
||||||
|
FWGPU_LOGW << "Auto, Inherit, & Opaque composite alpha modes not supported. Filament has "
|
||||||
|
"historically used these. Unpremultiplied alpha composite mode for "
|
||||||
|
"transparency is being selected as a fallback "
|
||||||
|
"(premulitipled is not available either), but may not work as expected."
|
||||||
|
<< utils::io::endl;
|
||||||
|
return wgpu::CompositeAlphaMode::Unpremultiplied;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void initConfig(wgpu::SurfaceConfiguration& config, wgpu::Device const& device,
|
||||||
|
wgpu::SurfaceCapabilities const& capabilities, wgpu::Extent2D const& surfaceSize,
|
||||||
|
bool useSRGBColorSpace) {
|
||||||
|
config.device = device;
|
||||||
|
config.usage = wgpu::TextureUsage::RenderAttachment;
|
||||||
|
config.width = surfaceSize.width;
|
||||||
|
config.height = surfaceSize.height;
|
||||||
|
config.format =
|
||||||
|
selectColorFormat(capabilities.formatCount, capabilities.formats, useSRGBColorSpace);
|
||||||
|
config.presentMode =
|
||||||
|
selectPresentMode(capabilities.presentModeCount, capabilities.presentModes);
|
||||||
|
config.alphaMode = selectAlphaMode(capabilities.alphaModeCount, capabilities.alphaModes);
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace
|
||||||
|
|
||||||
|
namespace filament::backend {
|
||||||
|
|
||||||
|
WebGPUSwapChain::WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const& surfaceSize,
|
||||||
|
wgpu::Adapter& adapter, wgpu::Device& device, uint64_t flags)
|
||||||
|
: mSurface(surface) {
|
||||||
|
wgpu::SurfaceCapabilities capabilities = {};
|
||||||
|
if (!mSurface.GetCapabilities(adapter, &capabilities)) {
|
||||||
|
FWGPU_LOGW << "Failed to get WebGPU surface capabilities" << utils::io::endl;
|
||||||
|
} else {
|
||||||
|
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||||
|
printSurfaceCapabilitiesDetails(capabilities);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
const bool useSRGBColorSpace = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0;
|
||||||
|
initConfig(mConfig, device, capabilities, surfaceSize, useSRGBColorSpace);
|
||||||
|
mSurface.Configure(&mConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
WebGPUSwapChain::~WebGPUSwapChain() {
|
||||||
|
mSurface.Unconfigure();
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebGPUSwapChain::setExtent(wgpu::Extent2D const& currentSurfaceSize) {
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(currentSurfaceSize.width > 0 || currentSurfaceSize.height > 0)
|
||||||
|
<< "WebGPUSwapChain::setExtent: Invalid width " << currentSurfaceSize.width
|
||||||
|
<< " and/or height " << currentSurfaceSize.height << " requested.";
|
||||||
|
if (mConfig.width != currentSurfaceSize.width || mConfig.height != currentSurfaceSize.height) {
|
||||||
|
mConfig.width = currentSurfaceSize.width;
|
||||||
|
mConfig.height = currentSurfaceSize.height;
|
||||||
|
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||||
|
printSurfaceConfiguration(mConfig);
|
||||||
|
#endif
|
||||||
|
FWGPU_LOGD << "Resizing to width " << mConfig.width << " height " << mConfig.height
|
||||||
|
<< utils::io::endl;
|
||||||
|
// TODO we may need to ensure no surface texture is flight when we do this. some
|
||||||
|
// synchronization may be necessary
|
||||||
|
mSurface.Configure(&mConfig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wgpu::TextureView WebGPUSwapChain::getCurrentSurfaceTextureView(
|
||||||
|
wgpu::Extent2D const& currentSurfaceSize) {
|
||||||
|
setExtent(currentSurfaceSize);
|
||||||
|
wgpu::SurfaceTexture surfaceTexture;
|
||||||
|
mSurface.GetCurrentTexture(&surfaceTexture);
|
||||||
|
if (surfaceTexture.status != wgpu::SurfaceGetCurrentTextureStatus::SuccessOptimal) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
// Create a view for this surface texture
|
||||||
|
// TODO: review these initiliazations as webgpu pipeline gets mature
|
||||||
|
wgpu::TextureViewDescriptor textureViewDescriptor = {
|
||||||
|
.label = "texture_view",
|
||||||
|
.format = surfaceTexture.texture.GetFormat(),
|
||||||
|
.dimension = wgpu::TextureViewDimension::e2D,
|
||||||
|
.baseMipLevel = 0,
|
||||||
|
.mipLevelCount = 1,
|
||||||
|
.baseArrayLayer = 0,
|
||||||
|
.arrayLayerCount = 1
|
||||||
|
};
|
||||||
|
return surfaceTexture.texture.CreateView(&textureViewDescriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebGPUSwapChain::present() {
|
||||||
|
assert_invariant(mSurface);
|
||||||
|
mSurface.Present();
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace filament::backend
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* 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_WEBGPUSWAPCHAIN_H
|
||||||
|
#define TNT_FILAMENT_BACKEND_WEBGPUSWAPCHAIN_H
|
||||||
|
|
||||||
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
|
#include "DriverBase.h"
|
||||||
|
#include <backend/Platform.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace filament::backend {
|
||||||
|
|
||||||
|
class WebGPUSwapChain final : public Platform::SwapChain, HwSwapChain {
|
||||||
|
public:
|
||||||
|
WebGPUSwapChain(wgpu::Surface&& surface, wgpu::Extent2D const& surfaceSize,
|
||||||
|
wgpu::Adapter& adapter, wgpu::Device& device, uint64_t flags);
|
||||||
|
~WebGPUSwapChain();
|
||||||
|
|
||||||
|
wgpu::TextureView getCurrentSurfaceTextureView(wgpu::Extent2D const&);
|
||||||
|
|
||||||
|
void present();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void setExtent(wgpu::Extent2D const&);
|
||||||
|
|
||||||
|
wgpu::Surface mSurface = {};
|
||||||
|
wgpu::SurfaceConfiguration mConfig = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace filament::backend
|
||||||
|
|
||||||
|
#endif //TNT_FILAMENT_BACKEND_WEBGPUSWAPCHAIN_H
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include <utils/Panic.h>
|
#include <utils/Panic.h>
|
||||||
|
|
||||||
|
#include <android/native_window.h>
|
||||||
#include <webgpu/webgpu_cpp.h>
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -28,6 +29,14 @@
|
|||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
|
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||||
|
ANativeWindow* window = static_cast<ANativeWindow*>(nativeWindow);
|
||||||
|
return wgpu::Extent2D{
|
||||||
|
.width = static_cast<uint32_t>(ANativeWindow_getWidth(window)),
|
||||||
|
.height = static_cast<uint32_t>(ANativeWindow_getHeight(window))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||||
wgpu::SurfaceSourceAndroidNativeWindow surfaceSourceAndroidWindow{};
|
wgpu::SurfaceSourceAndroidNativeWindow surfaceSourceAndroidWindow{};
|
||||||
surfaceSourceAndroidWindow.window = nativeWindow;
|
surfaceSourceAndroidWindow.window = nativeWindow;
|
||||||
|
|||||||
@@ -24,21 +24,8 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
// Platform specific includes and defines
|
// Platform specific includes and defines
|
||||||
#if defined(__APPLE__)
|
#include <Cocoa/Cocoa.h>
|
||||||
#include <Cocoa/Cocoa.h>
|
#import <QuartzCore/CAMetalLayer.h>
|
||||||
#import <QuartzCore/CAMetalLayer.h>
|
|
||||||
#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
|
|
||||||
// is this needed?
|
|
||||||
#define METALVIEW_TAG 255
|
|
||||||
#else
|
|
||||||
#error Not a supported Apple + WebGPU platform
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apple (Mac OS and IOS) specific implementation aspects of the WebGPU backend
|
* Apple (Mac OS and IOS) specific implementation aspects of the WebGPU backend
|
||||||
@@ -46,14 +33,19 @@
|
|||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
|
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||||
|
// Both IOS and MacOS expects CAMetalLayer.
|
||||||
|
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*) nativeWindow;
|
||||||
|
return wgpu::Extent2D{
|
||||||
|
.width = static_cast<uint32_t>(metalLayer.drawableSize.width),
|
||||||
|
.height = static_cast<uint32_t>(metalLayer.drawableSize.height)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||||
wgpu::Surface surface = nullptr;
|
wgpu::Surface surface = nullptr;
|
||||||
#if defined(__APPLE__)
|
// Both IOS and MacOS expects CAMetalLayer.
|
||||||
auto nsView = (__bridge NSView*) nativeWindow;
|
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*) nativeWindow;
|
||||||
FILAMENT_CHECK_POSTCONDITION(nsView) << "Unable to obtain Metal-backed NSView.";
|
|
||||||
[nsView setWantsLayer:YES];
|
|
||||||
id metalLayer = [CAMetalLayer layer];
|
|
||||||
[nsView setLayer:metalLayer];
|
|
||||||
wgpu::SurfaceSourceMetalLayer surfaceSourceMetalLayer{};
|
wgpu::SurfaceSourceMetalLayer surfaceSourceMetalLayer{};
|
||||||
surfaceSourceMetalLayer.layer = (__bridge void*) metalLayer;
|
surfaceSourceMetalLayer.layer = (__bridge void*) metalLayer;
|
||||||
wgpu::SurfaceDescriptor surfaceDescriptor = {
|
wgpu::SurfaceDescriptor surfaceDescriptor = {
|
||||||
@@ -62,19 +54,6 @@ wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags
|
|||||||
};
|
};
|
||||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Metal-backed surface.";
|
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Metal-backed surface.";
|
||||||
#elif defined(FILAMENT_IOS)
|
|
||||||
CAMetalLayer* metalLayer = (CAMetalLayer*) nativeWindow;
|
|
||||||
wgpu::SurfaceSourceMetalLayer surfaceSourceMetalLayer{};
|
|
||||||
surfaceSourceMetalLayer.layer = (__bridge void*) metalLayer;
|
|
||||||
wgpu::SurfaceDescriptor surfaceDescriptor = {
|
|
||||||
.nextInChain = &surfaceSourceMetalLayer,
|
|
||||||
.label = "metal_surface",
|
|
||||||
};
|
|
||||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
|
||||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Metal-backed surface.";
|
|
||||||
#else
|
|
||||||
#error Not a supported Apple + WebGPU platform
|
|
||||||
#endif
|
|
||||||
return surface;
|
return surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,6 +79,74 @@
|
|||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
|
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||||
|
auto surfaceExtent = wgpu::Extent2D{};
|
||||||
|
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||||
|
wl* ptrval = reinterpret_cast<wl*>(nativeWindow);
|
||||||
|
surfaceExtent.width = ptrval->width;
|
||||||
|
surfaceExtent.height = ptrval->height;
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||||
|
<< "Unable to get window size for Linux Wayland-backed surface.";
|
||||||
|
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11)
|
||||||
|
if (g_x11.library == nullptr) {
|
||||||
|
g_x11.library = dlopen(LIBRARY_X11, RTLD_LOCAL | RTLD_NOW);
|
||||||
|
FILAMENT_CHECK_PRECONDITION(g_x11.library) << "Unable to open X11 library.";
|
||||||
|
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||||
|
g_x11.xcbConnect = (XCB_CONNECT) dlsym(g_x11.library, "xcb_connect");
|
||||||
|
int screen = 0;
|
||||||
|
g_x11.connection = g_x11.xcbConnect(nullptr, &screen);
|
||||||
|
#endif
|
||||||
|
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||||
|
g_x11.openDisplay = (X11_OPEN_DISPLAY) dlsym(g_x11.library, "XOpenDisplay");
|
||||||
|
g_x11.display = g_x11.openDisplay(NULL);
|
||||||
|
FILAMENT_CHECK_PRECONDITION(g_x11.display) << "Unable to open X11 display.";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#if defined(FILAMENT_SUPPORTS_XCB) || defined(FILAMENT_SUPPORTS_XLIB)
|
||||||
|
bool useXcb = false;
|
||||||
|
#endif
|
||||||
|
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||||
|
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||||
|
useXcb = (SWAP_CHAIN_CONFIG_ENABLE_XCB) != 0;
|
||||||
|
#else
|
||||||
|
useXcb = true;
|
||||||
|
#endif
|
||||||
|
if (useXcb) {
|
||||||
|
const xcb_setup_t* setup = xcb_get_setup(g_x11.connection);
|
||||||
|
xcb_screen_iterator_t screen_iter = xcb_setup_roots_iterator(setup);
|
||||||
|
xcb_screen_t* screen = screen_iter.data;
|
||||||
|
surfaceExtent.width = static_cast<uint32_t>(screen->width_in_pixels);
|
||||||
|
surfaceExtent.height = static_cast<uint32_t>(screen->height_in_pixels);
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||||
|
<< "Unable to get window surface size for Linux (or FreeBSD) "
|
||||||
|
"XCB-backed surface.";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||||
|
if (!useXcb) {
|
||||||
|
int screenNumber = DefaultScreen(g_x11.display);
|
||||||
|
Screen* screen = ScreenOfDisplay(g_x11.display, screenNumber);
|
||||||
|
surfaceExtent.width = static_cast<uint32_t>(WidthOfScreen(screen));
|
||||||
|
surfaceExtent.height = static_cast<uint32_t>(HeightOfScreen(screen));
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||||
|
<< "Unable to get window surface size for Linux (or FreeBSD) "
|
||||||
|
"XLib-backed surface.";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||||
|
<< "Cannot get window surface size for X11 surface for Linux (or FreeBSD) OS "
|
||||||
|
"(not built with support for XCB or XLIB?)";
|
||||||
|
#elif defined(__linux__)
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||||
|
<< "Cannot get window surface size for Linux (or FreeBSD) OS "
|
||||||
|
"(not built with support for Wayland or X11?)";
|
||||||
|
#else
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(surfaceExtent.width != 0 && surfaceExtent.height != 0)
|
||||||
|
<< "Not a supported (Linux) OS + WebGPU platform";
|
||||||
|
#endif
|
||||||
|
return surfaceExtent;
|
||||||
|
}
|
||||||
|
|
||||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t flags) {
|
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t flags) {
|
||||||
wgpu::Surface surface = nullptr;
|
wgpu::Surface surface = nullptr;
|
||||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||||
|
|||||||
@@ -22,12 +22,24 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Windows OS specific implementation aspects of the WebGPU backend
|
* Windows OS specific implementation aspects of the WebGPU backend
|
||||||
*/
|
*/
|
||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
|
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||||
|
HWND window = static_cast<HWND>(nativeWindow);
|
||||||
|
RECT windowRect;
|
||||||
|
GetWindowRect(window, &windowRect);
|
||||||
|
return wgpu::Extent2D{
|
||||||
|
.width = static_cast<uint32_t>(windowRect.right - windowRect.left),
|
||||||
|
.height = static_cast<uint32_t>(windowRect.bottom - windowRect.top)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||||
// TODO verify this is necessary for Dawn implementation as well:
|
// TODO verify this is necessary for Dawn implementation as well:
|
||||||
// On (at least) NVIDIA drivers, the Vulkan implementation (specifically the call to
|
// On (at least) NVIDIA drivers, the Vulkan implementation (specifically the call to
|
||||||
@@ -41,12 +53,12 @@ wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags
|
|||||||
wgpu::SurfaceSourceWindowsHWND surfaceSourceWin{};
|
wgpu::SurfaceSourceWindowsHWND surfaceSourceWin{};
|
||||||
surfaceSourceWin.hinstance = GetModuleHandle(nullptr);
|
surfaceSourceWin.hinstance = GetModuleHandle(nullptr);
|
||||||
surfaceSourceWin.hwnd = nativeWindow;
|
surfaceSourceWin.hwnd = nativeWindow;
|
||||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
const wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||||
.nextInChain = &surfaceSourceWin,
|
.nextInChain = &surfaceSourceWin,
|
||||||
.label = "windows_surface"
|
.label = "windows_surface"
|
||||||
};
|
};
|
||||||
wgpu::Surface surface = mInstance.CreateSurface(&surfaceDescriptor);
|
wgpu::Surface surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Windows-backed surface.";
|
FILAMENT_CHECK_POSTCONDITION(surface.Get() != nullptr) << "Unable to create Windows-backed surface.";
|
||||||
return surface;
|
return surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -43,24 +43,29 @@ using namespace image;
|
|||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
Backend BackendTest::sBackend = Backend::NOOP;
|
Backend BackendTest::sBackend = Backend::NOOP;
|
||||||
|
OperatingSystem BackendTest::sOperatingSystem = OperatingSystem::OTHER;
|
||||||
bool BackendTest::sIsMobilePlatform = false;
|
bool BackendTest::sIsMobilePlatform = false;
|
||||||
|
|
||||||
void BackendTest::init(Backend backend, bool isMobilePlatform) {
|
void BackendTest::init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform) {
|
||||||
sBackend = backend;
|
sBackend = backend;
|
||||||
|
sOperatingSystem = operatingSystem;
|
||||||
sIsMobilePlatform = isMobilePlatform;
|
sIsMobilePlatform = isMobilePlatform;
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendTest::BackendTest() : commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE,
|
BackendTest::BackendTest() : commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE,
|
||||||
CONFIG_COMMAND_BUFFERS_SIZE, /*mPaused=*/false) {
|
CONFIG_COMMAND_BUFFERS_SIZE, /*mPaused=*/false) {
|
||||||
initializeDriver();
|
initializeDriver();
|
||||||
|
mImageExpectations.emplace(getDriverApi());
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendTest::~BackendTest() {
|
BackendTest::~BackendTest() {
|
||||||
|
// Ensure all graphics commands and callbacks are finished.
|
||||||
|
flushAndWait();
|
||||||
|
mImageExpectations->evaluate();
|
||||||
// Note: Don't terminate the driver for OpenGL, as it wipes away the context and removes the buffer from the screen.
|
// Note: Don't terminate the driver for OpenGL, as it wipes away the context and removes the buffer from the screen.
|
||||||
if (sBackend == Backend::OPENGL) {
|
if (sBackend == Backend::OPENGL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
flushAndWait();
|
|
||||||
driver->terminate();
|
driver->terminate();
|
||||||
delete driver;
|
delete driver;
|
||||||
}
|
}
|
||||||
@@ -154,49 +159,16 @@ void BackendTest::renderTriangle(
|
|||||||
api.endRenderPass();
|
api.endRenderPass();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendTest::readPixelsAndAssertHash(const char* testName, size_t width, size_t height,
|
bool BackendTest::matchesEnvironment(Backend backend) {
|
||||||
Handle<HwRenderTarget> rt, uint32_t expectedHash, bool exportScreenshot) {
|
return sBackend == backend;
|
||||||
void* buffer = calloc(1, width * height * 4);
|
}
|
||||||
|
|
||||||
struct Capture {
|
bool BackendTest::matchesEnvironment(OperatingSystem operatingSystem) {
|
||||||
uint32_t expectedHash;
|
return sOperatingSystem == operatingSystem;
|
||||||
char* name;
|
}
|
||||||
bool exportScreenshot;
|
|
||||||
size_t width, height;
|
|
||||||
};
|
|
||||||
auto* c = new Capture();
|
|
||||||
c->expectedHash = expectedHash;
|
|
||||||
c->name = strdup(testName);
|
|
||||||
c->exportScreenshot = exportScreenshot;
|
|
||||||
c->width = width;
|
|
||||||
c->height = height;
|
|
||||||
|
|
||||||
PixelBufferDescriptor pbd(buffer, width * height * 4, PixelDataFormat::RGBA, PixelDataType::UBYTE,
|
bool BackendTest::matchesEnvironment(OperatingSystem operatingSystem, Backend backend) {
|
||||||
1, 0, 0, width, [](void* buffer, size_t size, void* user) {
|
return matchesEnvironment(operatingSystem) && matchesEnvironment(backend);
|
||||||
auto* c = (Capture*)user;
|
|
||||||
|
|
||||||
// Export a screenshot, if requested.
|
|
||||||
if (c->exportScreenshot) {
|
|
||||||
#ifndef FILAMENT_IOS
|
|
||||||
LinearImage image(c->width, c->height, 4);
|
|
||||||
image = toLinearWithAlpha<uint8_t>(c->width, c->height, c->width * 4,
|
|
||||||
(uint8_t*) buffer);
|
|
||||||
const std::string png = std::string(c->name) + ".png";
|
|
||||||
std::ofstream outputStream(png.c_str(), std::ios::binary | std::ios::trunc);
|
|
||||||
ImageEncoder::encode(outputStream, ImageEncoder::Format::PNG, image, "",
|
|
||||||
png);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hash the contents of the buffer and check that they match.
|
|
||||||
uint32_t hash = utils::hash::murmur3((const uint32_t*) buffer, size / 4, 0);
|
|
||||||
ASSERT_EQ(hash, c->expectedHash) << c->name << " failed: hashes do not match." << std::endl;
|
|
||||||
|
|
||||||
free(buffer);
|
|
||||||
free(c->name);
|
|
||||||
free(c);
|
|
||||||
}, (void*)c);
|
|
||||||
getDriverApi().readPixels(rt, 0, 0, width, height, std::move(pbd));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Environment : public ::testing::Environment {
|
class Environment : public ::testing::Environment {
|
||||||
@@ -210,8 +182,8 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void initTests(Backend backend, bool isMobile, int& argc, char* argv[]) {
|
void initTests(Backend backend, OperatingSystem operatingSystem, bool isMobile, int& argc, char* argv[]) {
|
||||||
BackendTest::init(backend, isMobile);
|
BackendTest::init(backend, operatingSystem, isMobile);
|
||||||
::testing::InitGoogleTest(&argc, argv);
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
::testing::AddGlobalTestEnvironment(new Environment);
|
::testing::AddGlobalTestEnvironment(new Environment);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,15 +25,17 @@
|
|||||||
#include "private/backend/DriverApi.h"
|
#include "private/backend/DriverApi.h"
|
||||||
|
|
||||||
#include "PlatformRunner.h"
|
#include "PlatformRunner.h"
|
||||||
|
#include "ImageExpectations.h"
|
||||||
|
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
class BackendTest : public ::testing::Test {
|
class BackendTest : public ::testing::Test {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
static void init(Backend backend, bool isMobilePlatform);
|
static void init(Backend backend, OperatingSystem operatingSystem, bool isMobilePlatform);
|
||||||
|
|
||||||
static Backend sBackend;
|
static Backend sBackend;
|
||||||
|
static OperatingSystem sOperatingSystem;
|
||||||
static bool sIsMobilePlatform;
|
static bool sIsMobilePlatform;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -64,13 +66,14 @@ protected:
|
|||||||
filament::backend::Handle<filament::backend::HwProgram> program,
|
filament::backend::Handle<filament::backend::HwProgram> program,
|
||||||
const filament::backend::RenderPassParams& params);
|
const filament::backend::RenderPassParams& params);
|
||||||
|
|
||||||
void readPixelsAndAssertHash(const char* testName, size_t width, size_t height,
|
|
||||||
filament::backend::Handle<filament::backend::HwRenderTarget> rt, uint32_t expectedHash,
|
|
||||||
bool exportScreenshot = false);
|
|
||||||
|
|
||||||
filament::backend::DriverApi& getDriverApi() { return *commandStream; }
|
filament::backend::DriverApi& getDriverApi() { return *commandStream; }
|
||||||
filament::backend::Driver& getDriver() { return *driver; }
|
filament::backend::Driver& getDriver() { return *driver; }
|
||||||
|
|
||||||
|
ImageExpectations& getExpectations() { return *mImageExpectations; }
|
||||||
|
|
||||||
|
static bool matchesEnvironment(Backend backend);
|
||||||
|
static bool matchesEnvironment(OperatingSystem operatingSystem);
|
||||||
|
static bool matchesEnvironment(OperatingSystem operatingSystem, Backend backend);
|
||||||
private:
|
private:
|
||||||
|
|
||||||
filament::backend::Driver* driver = nullptr;
|
filament::backend::Driver* driver = nullptr;
|
||||||
@@ -78,6 +81,10 @@ private:
|
|||||||
std::unique_ptr<filament::backend::DriverApi> commandStream;
|
std::unique_ptr<filament::backend::DriverApi> commandStream;
|
||||||
|
|
||||||
filament::backend::Handle<filament::backend::HwBufferObject> uniform;
|
filament::backend::Handle<filament::backend::HwBufferObject> uniform;
|
||||||
|
|
||||||
|
// This isn't truly optional, it just needs to delay construction until after the driver has
|
||||||
|
// been initialized
|
||||||
|
std::optional<ImageExpectations> mImageExpectations;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
#include "ImageExpectations.h"
|
#include "ImageExpectations.h"
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "absl/strings/str_format.h"
|
#include "absl/strings/str_format.h"
|
||||||
#include "utils/Hash.h"
|
#include "utils/Hash.h"
|
||||||
@@ -28,14 +27,17 @@
|
|||||||
#ifndef FILAMENT_IOS
|
#ifndef FILAMENT_IOS
|
||||||
|
|
||||||
#include <imageio/ImageEncoder.h>
|
#include <imageio/ImageEncoder.h>
|
||||||
|
#include <imageio/ImageDecoder.h>
|
||||||
#include <image/ColorTransform.h>
|
#include <image/ColorTransform.h>
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ScreenshotParams::ScreenshotParams(int width, int height, std::string fileName,
|
ScreenshotParams::ScreenshotParams(int width, int height, std::string fileName,
|
||||||
uint32_t expectedPixelHash)
|
uint32_t expectedHash)
|
||||||
: mWidth(width), mHeight(height), mFileName(std::move(fileName)),
|
: mWidth(width),
|
||||||
mExpectedPixelHash(expectedPixelHash) {}
|
mHeight(height),
|
||||||
|
mExpectedPixelHash(expectedHash),
|
||||||
|
mFileName(std::move(fileName)) {}
|
||||||
|
|
||||||
int ScreenshotParams::width() const {
|
int ScreenshotParams::width() const {
|
||||||
return mWidth;
|
return mWidth;
|
||||||
@@ -49,24 +51,28 @@ uint32_t ScreenshotParams::expectedHash() const {
|
|||||||
return mExpectedPixelHash;
|
return mExpectedPixelHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ScreenshotParams::outputDirectoryPath() const {
|
std::string ScreenshotParams::actualDirectoryPath() {
|
||||||
return ".";
|
return "images/actual_images";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ScreenshotParams::generatedActualFileName() const {
|
std::string ScreenshotParams::actualFileName() const {
|
||||||
return absl::StrFormat("%s_actual.png", mFileName);
|
return absl::StrFormat("%s_actual.png", mFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ScreenshotParams::generatedActualFilePath() const {
|
std::string ScreenshotParams::actualFilePath() const {
|
||||||
return absl::StrFormat("%s/%s", outputDirectoryPath(), generatedActualFileName());
|
return absl::StrFormat("%s/%s", actualDirectoryPath(), actualFileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ScreenshotParams::goldenFileName() const {
|
std::string ScreenshotParams::expectedDirectoryPath() {
|
||||||
return absl::StrFormat("%s_golden.png", mFileName);
|
return "images/expected_images";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string ScreenshotParams::goldenFilePath() const {
|
std::string ScreenshotParams::expectedFileName() const {
|
||||||
return absl::StrFormat("%s/%s", outputDirectoryPath(), goldenFileName());
|
return absl::StrFormat("%s.png", mFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ScreenshotParams::expectedFilePath() const {
|
||||||
|
return absl::StrFormat("%s/%s", expectedDirectoryPath(), expectedFileName());
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageExpectation::ImageExpectation(const char* fileName, int lineNumber,
|
ImageExpectation::ImageExpectation(const char* fileName, int lineNumber,
|
||||||
@@ -91,11 +97,22 @@ void ImageExpectation::evaluate() {
|
|||||||
|
|
||||||
void ImageExpectation::compareImage() const {
|
void ImageExpectation::compareImage() const {
|
||||||
bool bytesFilled = mResult.bytesFilled();
|
bool bytesFilled = mResult.bytesFilled();
|
||||||
|
// If this fails, it likely means that BackendTest::flushAndWait needs to be called before
|
||||||
|
// ImageExpectations is evaluated or destroyed.
|
||||||
EXPECT_THAT(bytesFilled, testing::IsTrue())
|
EXPECT_THAT(bytesFilled, testing::IsTrue())
|
||||||
<< "Render target wasn't copied to the buffer for " << mFileName;
|
<< "Render target wasn't copied to the buffer for " << mFileName;
|
||||||
if (bytesFilled) {
|
if (bytesFilled) {
|
||||||
|
// Rather than directly compare the two images compare their hashes because comparing very
|
||||||
|
// large arrays generates way too much debug output to be useful.
|
||||||
uint32_t actualHash = mResult.hash();
|
uint32_t actualHash = mResult.hash();
|
||||||
EXPECT_THAT(actualHash, testing::Eq(mParams.expectedHash()));
|
#ifndef FILAMENT_IOS
|
||||||
|
LoadedPng loadedImage(mParams.expectedFilePath());
|
||||||
|
uint32_t loadedImageHash = loadedImage.hash();
|
||||||
|
EXPECT_THAT(actualHash, testing::Eq(loadedImageHash)) << mParams.expectedFileName();
|
||||||
|
#endif
|
||||||
|
// For builds that can't load PNGs (currently iOS only) use the expected hash.
|
||||||
|
EXPECT_THAT(actualHash, testing::Eq(mParams.expectedHash())) << mParams.expectedFileName();
|
||||||
|
// TODO: Add better debug output, such as generating a diff image.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,12 +126,13 @@ ImageExpectations::~ImageExpectations() {
|
|||||||
|
|
||||||
void ImageExpectations::addExpectation(const char* fileName, int lineNumber,
|
void ImageExpectations::addExpectation(const char* fileName, int lineNumber,
|
||||||
filament::backend::RenderTargetHandle renderTarget, ScreenshotParams params) {
|
filament::backend::RenderTargetHandle renderTarget, ScreenshotParams params) {
|
||||||
mExpectations.emplace_back(fileName, lineNumber, mApi, std::move(params), renderTarget);
|
mExpectations.emplace_back(std::make_unique<ImageExpectation>(fileName, lineNumber, mApi,
|
||||||
|
std::move(params), renderTarget));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageExpectations::evaluate() {
|
void ImageExpectations::evaluate() {
|
||||||
for (auto& expectation: mExpectations) {
|
for (auto& expectation: mExpectations) {
|
||||||
expectation.evaluate();
|
expectation->evaluate();
|
||||||
}
|
}
|
||||||
mExpectations.clear();
|
mExpectations.clear();
|
||||||
}
|
}
|
||||||
@@ -122,32 +140,28 @@ void ImageExpectations::evaluate() {
|
|||||||
RenderTargetDump::RenderTargetDump(filament::backend::DriverApi& api,
|
RenderTargetDump::RenderTargetDump(filament::backend::DriverApi& api,
|
||||||
filament::backend::RenderTargetHandle renderTarget, const ScreenshotParams& params)
|
filament::backend::RenderTargetHandle renderTarget, const ScreenshotParams& params)
|
||||||
: mInternal(std::make_unique<RenderTargetDump::Internal>(params)) {
|
: mInternal(std::make_unique<RenderTargetDump::Internal>(params)) {
|
||||||
#ifdef FILAMENT_IOS
|
|
||||||
bytesFilled_ = true;
|
|
||||||
bytes_.resize(size);
|
|
||||||
std::fill(bytes_.begin(), bytes_.end(), 0);
|
|
||||||
#else
|
|
||||||
const size_t size = mInternal->params.width() * mInternal->params.height() * 4;
|
const size_t size = mInternal->params.width() * mInternal->params.height() * 4;
|
||||||
mInternal->bytes.resize(size);
|
mInternal->bytes.resize(size);
|
||||||
|
|
||||||
auto cb = [](void* buffer, size_t size, void* user) {
|
auto cb = [](void* buffer, size_t size, void* user) {
|
||||||
auto* internal = static_cast<RenderTargetDump::Internal*>(user);
|
auto* internal = static_cast<RenderTargetDump::Internal*>(user);
|
||||||
|
internal->bytesFilled = true;
|
||||||
|
#ifndef FILAMENT_IOS
|
||||||
image::LinearImage image(internal->params.width(), internal->params.width(), 4);
|
image::LinearImage image(internal->params.width(), internal->params.width(), 4);
|
||||||
image = image::toLinearWithAlpha<uint8_t>(internal->params.width(),
|
image = image::toLinearWithAlpha<uint8_t>(internal->params.width(),
|
||||||
internal->params.height(),
|
internal->params.height(),
|
||||||
internal->params.width() * 4, (uint8_t*)buffer);
|
internal->params.width() * 4, (uint8_t*)buffer);
|
||||||
std::string filePath = internal->params.generatedActualFilePath();
|
std::string filePath = internal->params.actualFilePath();
|
||||||
std::ofstream pngStream(filePath, std::ios::binary | std::ios::trunc);
|
std::ofstream pngStream(filePath, std::ios::binary | std::ios::trunc);
|
||||||
image::ImageEncoder::encode(pngStream, image::ImageEncoder::Format::PNG, image, "",
|
image::ImageEncoder::encode(pngStream, image::ImageEncoder::Format::PNG, image, "",
|
||||||
filePath);
|
filePath);
|
||||||
internal->bytesFilled = true;
|
#endif
|
||||||
};
|
};
|
||||||
filament::backend::PixelBufferDescriptor pb(mInternal->bytes.data(), size,
|
filament::backend::PixelBufferDescriptor pb(mInternal->bytes.data(), size,
|
||||||
filament::backend::PixelDataFormat::RGBA, filament::backend::PixelDataType::UBYTE, cb,
|
filament::backend::PixelDataFormat::RGBA, filament::backend::PixelDataType::UBYTE, cb,
|
||||||
(void*)mInternal.get());
|
(void*)mInternal.get());
|
||||||
api.readPixels(renderTarget, 0, 0, mInternal->params.width(), mInternal->params.height(),
|
api.readPixels(renderTarget, 0, 0, mInternal->params.width(), mInternal->params.height(),
|
||||||
std::move(pb));
|
std::move(pb));
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderTargetDump::~RenderTargetDump() {
|
RenderTargetDump::~RenderTargetDump() {
|
||||||
@@ -170,3 +184,29 @@ bool RenderTargetDump::bytesFilled() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
RenderTargetDump::Internal::Internal(const ScreenshotParams& params) : params(params) {}
|
RenderTargetDump::Internal::Internal(const ScreenshotParams& params) : params(params) {}
|
||||||
|
|
||||||
|
LoadedPng::LoadedPng(std::string filePath) : mFilePath(std::move(filePath)) {
|
||||||
|
#ifndef FILAMENT_IOS
|
||||||
|
std::ifstream pngStream(mFilePath, std::ios::binary);
|
||||||
|
image::LinearImage loadedImage = image::ImageDecoder::decode(pngStream, filePath,
|
||||||
|
image::ImageDecoder::ColorSpace::LINEAR);
|
||||||
|
size_t valuesInImage = loadedImage.getWidth() * loadedImage.getHeight() *
|
||||||
|
loadedImage.getChannels();
|
||||||
|
// The linear image is loaded with each component as [0.0, 1.0] but should be [0, 255], so
|
||||||
|
// convert them.
|
||||||
|
mBytes = std::vector<unsigned char>(valuesInImage);
|
||||||
|
for (int i = 0; i < valuesInImage; ++i) {
|
||||||
|
mBytes[i] = static_cast<uint8_t>(loadedImage.get<float>()[i] * 255.0f);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
// For platforms that don't support the image loading library, leave the loaded data blank.
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t LoadedPng::hash() const {
|
||||||
|
EXPECT_THAT(mBytes, testing::Not(testing::IsEmpty()))
|
||||||
|
<< "Failed to load expected test result: " << mFilePath;
|
||||||
|
if (mBytes.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return utils::hash::murmur3((uint32_t*)mBytes.data(), mBytes.size() / 4, 0);
|
||||||
|
}
|
||||||
|
|||||||
@@ -46,11 +46,12 @@ public:
|
|||||||
int height() const;
|
int height() const;
|
||||||
uint32_t expectedHash() const;
|
uint32_t expectedHash() const;
|
||||||
|
|
||||||
std::string outputDirectoryPath() const;
|
static std::string actualDirectoryPath();
|
||||||
std::string generatedActualFileName() const;
|
std::string actualFileName() const;
|
||||||
std::string generatedActualFilePath() const;
|
std::string actualFilePath() const;
|
||||||
std::string goldenFileName() const;
|
static std::string expectedDirectoryPath();
|
||||||
std::string goldenFilePath() const;
|
std::string expectedFileName() const;
|
||||||
|
std::string expectedFilePath() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int mWidth;
|
int mWidth;
|
||||||
@@ -98,6 +99,17 @@ private:
|
|||||||
std::unique_ptr<Internal> mInternal;
|
std::unique_ptr<Internal> mInternal;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class LoadedPng {
|
||||||
|
public:
|
||||||
|
explicit LoadedPng(std::string filePath);
|
||||||
|
|
||||||
|
uint32_t hash() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string mFilePath;
|
||||||
|
std::vector<unsigned char> mBytes;
|
||||||
|
};
|
||||||
|
|
||||||
class ImageExpectation {
|
class ImageExpectation {
|
||||||
public:
|
public:
|
||||||
ImageExpectation(const char* fileName, int lineNumber, filament::backend::DriverApi& api,
|
ImageExpectation(const char* fileName, int lineNumber, filament::backend::DriverApi& api,
|
||||||
@@ -130,7 +142,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
filament::backend::DriverApi& mApi;
|
filament::backend::DriverApi& mApi;
|
||||||
std::vector<ImageExpectation> mExpectations;
|
// Store expectations in unique pointers because they are self referential.
|
||||||
|
std::vector<std::unique_ptr<ImageExpectation>> mExpectations;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //TNT_IMAGE_EXPECTATIONS_H
|
#endif //TNT_IMAGE_EXPECTATIONS_H
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 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 "PlatformRunner.h"
|
||||||
|
|
||||||
|
namespace utils {
|
||||||
|
|
||||||
|
template<>
|
||||||
|
CString to_string<test::Backend>(test::Backend backend) noexcept {
|
||||||
|
switch (backend) {
|
||||||
|
case test::Backend::OPENGL: {
|
||||||
|
return "OpenGL";
|
||||||
|
}
|
||||||
|
case test::Backend::VULKAN: {
|
||||||
|
return "Vulkan";
|
||||||
|
}
|
||||||
|
case test::Backend::METAL: {
|
||||||
|
return "Metal";
|
||||||
|
}
|
||||||
|
case test::Backend::WEBGPU: {
|
||||||
|
return "WebGPU";
|
||||||
|
}
|
||||||
|
case test::Backend::NOOP:
|
||||||
|
default: {
|
||||||
|
return "No-op";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
CString to_string(test::OperatingSystem os) noexcept {
|
||||||
|
switch (os) {
|
||||||
|
case test::OperatingSystem::LINUX: {
|
||||||
|
return "Linux";
|
||||||
|
}
|
||||||
|
case test::OperatingSystem::APPLE: {
|
||||||
|
return "Apple";
|
||||||
|
}
|
||||||
|
case test::OperatingSystem::OTHER:
|
||||||
|
default: {
|
||||||
|
return "Other";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace utils
|
||||||
|
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
#include "utils/CString.h"
|
||||||
|
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
@@ -34,6 +35,15 @@ enum class Backend : uint8_t {
|
|||||||
NOOP = 5,
|
NOOP = 5,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class OperatingSystem: uint8_t {
|
||||||
|
OTHER = 1,
|
||||||
|
// Also represents android phones.
|
||||||
|
LINUX = 2,
|
||||||
|
// Also represents iOS phones.
|
||||||
|
APPLE = 3,
|
||||||
|
// TODO: When tests support windows add it here.
|
||||||
|
};
|
||||||
|
|
||||||
struct NativeView {
|
struct NativeView {
|
||||||
void* ptr = nullptr;
|
void* ptr = nullptr;
|
||||||
size_t width = 0, height = 0;
|
size_t width = 0, height = 0;
|
||||||
@@ -51,9 +61,10 @@ NativeView getNativeView();
|
|||||||
* No tests will be run yet.
|
* No tests will be run yet.
|
||||||
*
|
*
|
||||||
* @param backend The backend to run the tests on.
|
* @param backend The backend to run the tests on.
|
||||||
|
* @param operatingSystem The operating system the tests are being run on.
|
||||||
* @param isMobile True if the platform is a mobile platform (iOS or Android).
|
* @param isMobile True if the platform is a mobile platform (iOS or Android).
|
||||||
*/
|
*/
|
||||||
void initTests(Backend backend, bool isMobile, int& argc, char* argv[]);
|
void initTests(Backend backend, OperatingSystem operatingSystem, bool isMobile, int& argc, char* argv[]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test runners should call runTests when they are ready for tests to be run.
|
* Test runners should call runTests when they are ready for tests to be run.
|
||||||
@@ -68,6 +79,6 @@ int runTests();
|
|||||||
*/
|
*/
|
||||||
Backend parseArgumentsForBackend(int argc, char* argv[]);
|
Backend parseArgumentsForBackend(int argc, char* argv[]);
|
||||||
|
|
||||||
}
|
} // namespace test
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -23,30 +23,44 @@ namespace test {
|
|||||||
|
|
||||||
using namespace filament::backend;
|
using namespace filament::backend;
|
||||||
|
|
||||||
Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) {
|
Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) : mCleanup(cleanup) {
|
||||||
utils::FixedCapacityVector<DescriptorSetLayoutBinding> kLayouts(config.uniformNames.size());
|
utils::FixedCapacityVector<DescriptorSetLayoutBinding> kLayouts(config.uniforms.size());
|
||||||
for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
|
for (unsigned char i = 0; i < config.uniforms.size(); ++i) {
|
||||||
kLayouts[i] =
|
kLayouts[i] = {
|
||||||
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, i };
|
config.uniforms[i].type.value_or(DescriptorType::UNIFORM_BUFFER),
|
||||||
};
|
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, i };
|
||||||
|
}
|
||||||
|
|
||||||
|
// This assumes that the uniforms will all be in a single descriptor set at index 1.
|
||||||
|
// If there are shaders with uniforms in other sets then ShaderConfig will need to be expanded
|
||||||
|
// to accommodate that.
|
||||||
|
size_t kDescriptorSetIndex = 1;
|
||||||
filamat::DescriptorSets descriptors;
|
filamat::DescriptorSets descriptors;
|
||||||
for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
|
descriptors[kDescriptorSetIndex] = filamat::DescriptorSetInfo(config.uniforms.size());
|
||||||
descriptors[i + 1] = {{ config.uniformNames[i], kLayouts[i], {}}};
|
for (unsigned char i = 0; i < config.uniforms.size(); ++i) {
|
||||||
|
descriptors[kDescriptorSetIndex][i] = {
|
||||||
|
config.uniforms[i].name, kLayouts[i], config.uniforms[i].samplerInfo };
|
||||||
}
|
}
|
||||||
ShaderGenerator shaderGen(
|
ShaderGenerator shaderGen(
|
||||||
std::move(config.vertexShader), std::move(config.fragmentShader), BackendTest::sBackend,
|
std::move(config.vertexShader), std::move(config.fragmentShader), BackendTest::sBackend,
|
||||||
BackendTest::sIsMobilePlatform, std::move(descriptors));
|
BackendTest::sIsMobilePlatform, std::move(descriptors));
|
||||||
Program prog = shaderGen.getProgram(api);
|
Program prog = shaderGen.getProgram(api);
|
||||||
for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
|
|
||||||
prog.descriptorBindings(1, {{ config.uniformNames[i], DescriptorType::UNIFORM_BUFFER, i }});
|
Program::DescriptorBindingsInfo bindingsInfo(config.uniforms.size());
|
||||||
|
for (unsigned char i = 0; i < config.uniforms.size(); ++i) {
|
||||||
|
bindingsInfo[i] = {
|
||||||
|
config.uniforms[i].name,
|
||||||
|
config.uniforms[i].type.value_or(DescriptorType::UNIFORM_BUFFER), i };
|
||||||
}
|
}
|
||||||
|
prog.descriptorBindings(1, bindingsInfo);
|
||||||
mProgram = cleanup.add(api.createProgram(std::move(prog)));
|
mProgram = cleanup.add(api.createProgram(std::move(prog)));
|
||||||
|
|
||||||
mDescriptorSetLayout = cleanup.add(
|
mDescriptorSetLayout = cleanup.add(
|
||||||
api.createDescriptorSetLayout(DescriptorSetLayout{ kLayouts }));
|
api.createDescriptorSetLayout(DescriptorSetLayout{ kLayouts }));
|
||||||
|
}
|
||||||
|
|
||||||
mDescriptorSet = cleanup.add(api.createDescriptorSet(mDescriptorSetLayout));
|
filament::backend::DescriptorSetHandle Shader::createDescriptorSet(DriverApi& api) const {
|
||||||
|
return mCleanup.add(api.createDescriptorSet(mDescriptorSetLayout));
|
||||||
}
|
}
|
||||||
|
|
||||||
filament::backend::ProgramHandle Shader::getProgram() const {
|
filament::backend::ProgramHandle Shader::getProgram() const {
|
||||||
@@ -57,8 +71,4 @@ filament::backend::DescriptorSetLayoutHandle Shader::getDescriptorSetLayout() co
|
|||||||
return mDescriptorSetLayout;
|
return mDescriptorSetLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
filament::backend::DescriptorSetHandle Shader::getDescriptorSet() const {
|
|
||||||
return mDescriptorSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
|
|||||||
@@ -18,14 +18,22 @@
|
|||||||
#define TNT_SHADER_H
|
#define TNT_SHADER_H
|
||||||
|
|
||||||
#include "Lifetimes.h"
|
#include "Lifetimes.h"
|
||||||
|
#include "private/filament/SamplerInterfaceBlock.h"
|
||||||
|
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
|
struct UniformConfig {
|
||||||
|
utils::CString name;
|
||||||
|
// If not specified this will be DescriptorType::UNIFORM_BUFFER
|
||||||
|
std::optional<filament::backend::DescriptorType> type;
|
||||||
|
std::optional<filament::SamplerInterfaceBlock::SamplerInfo> samplerInfo;
|
||||||
|
};
|
||||||
|
|
||||||
// All describing a shader that should be created.
|
// All describing a shader that should be created.
|
||||||
struct ShaderConfig {
|
struct ShaderConfig {
|
||||||
std::string vertexShader;
|
std::string vertexShader;
|
||||||
std::string fragmentShader;
|
std::string fragmentShader;
|
||||||
std::vector<utils::CString> uniformNames;
|
std::vector<UniformConfig> uniforms;
|
||||||
};
|
};
|
||||||
|
|
||||||
// All values describing a uniform.
|
// All values describing a uniform.
|
||||||
@@ -35,6 +43,7 @@ struct ResolvedUniformBindingConfig {
|
|||||||
uint32_t byteOffset;
|
uint32_t byteOffset;
|
||||||
filament::backend::descriptor_set_t set;
|
filament::backend::descriptor_set_t set;
|
||||||
filament::backend::descriptor_binding_t binding;
|
filament::backend::descriptor_binding_t binding;
|
||||||
|
std::optional<filament::backend::DescriptorSetHandle> descriptorSet;
|
||||||
};
|
};
|
||||||
|
|
||||||
// An equivalent to ResolvedUniformBindingConfig with all fields optional.
|
// An equivalent to ResolvedUniformBindingConfig with all fields optional.
|
||||||
@@ -46,6 +55,7 @@ struct UniformBindingConfig {
|
|||||||
std::optional<uint32_t> byteOffset;
|
std::optional<uint32_t> byteOffset;
|
||||||
std::optional<filament::backend::descriptor_set_t> set;
|
std::optional<filament::backend::descriptor_set_t> set;
|
||||||
std::optional<filament::backend::descriptor_binding_t> binding;
|
std::optional<filament::backend::descriptor_binding_t> binding;
|
||||||
|
std::optional<filament::backend::DescriptorSetHandle> descriptorSet;
|
||||||
|
|
||||||
template<typename UniformType>
|
template<typename UniformType>
|
||||||
ResolvedUniformBindingConfig resolve();
|
ResolvedUniformBindingConfig resolve();
|
||||||
@@ -79,12 +89,14 @@ public:
|
|||||||
|
|
||||||
filament::backend::ProgramHandle getProgram() const;
|
filament::backend::ProgramHandle getProgram() const;
|
||||||
filament::backend::DescriptorSetLayoutHandle getDescriptorSetLayout() const;
|
filament::backend::DescriptorSetLayoutHandle getDescriptorSetLayout() const;
|
||||||
filament::backend::DescriptorSetHandle getDescriptorSet() const;
|
|
||||||
|
filament::backend::DescriptorSetHandle createDescriptorSet(
|
||||||
|
filament::backend::DriverApi& api) const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
Cleanup& mCleanup;
|
||||||
filament::backend::ProgramHandle mProgram;
|
filament::backend::ProgramHandle mProgram;
|
||||||
filament::backend::DescriptorSetLayoutHandle mDescriptorSetLayout;
|
filament::backend::DescriptorSetLayoutHandle mDescriptorSetLayout;
|
||||||
filament::backend::DescriptorSetHandle mDescriptorSet;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename UniformType>
|
template<typename UniformType>
|
||||||
@@ -95,7 +107,8 @@ ResolvedUniformBindingConfig UniformBindingConfig::resolve() {
|
|||||||
.bufferSize = bufferSize.value_or(resolvedDataSize),
|
.bufferSize = bufferSize.value_or(resolvedDataSize),
|
||||||
.byteOffset = byteOffset.value_or(0),
|
.byteOffset = byteOffset.value_or(0),
|
||||||
.set = set.value_or(1),
|
.set = set.value_or(1),
|
||||||
.binding = binding.value_or(0)
|
.binding = binding.value_or(0),
|
||||||
|
.descriptorSet = descriptorSet
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,9 +133,16 @@ void Shader::bindUniform(filament::backend::DriverApi& api,
|
|||||||
UniformBindingConfig config) const {
|
UniformBindingConfig config) const {
|
||||||
auto resolvedConfig = config.resolve<UniformType>();
|
auto resolvedConfig = config.resolve<UniformType>();
|
||||||
|
|
||||||
api.updateDescriptorSetBuffer(getDescriptorSet(), resolvedConfig.binding, hwBuffer, 0,
|
filament::backend::DescriptorSetHandle descriptorSet;
|
||||||
|
if (resolvedConfig.descriptorSet.has_value()) {
|
||||||
|
descriptorSet = *resolvedConfig.descriptorSet;
|
||||||
|
} else {
|
||||||
|
descriptorSet = createDescriptorSet(api);
|
||||||
|
}
|
||||||
|
|
||||||
|
api.updateDescriptorSetBuffer(descriptorSet, resolvedConfig.binding, hwBuffer, 0,
|
||||||
resolvedConfig.bufferSize);
|
resolvedConfig.bufferSize);
|
||||||
api.bindDescriptorSet(getDescriptorSet(), resolvedConfig.set, {});
|
api.bindDescriptorSet(descriptorSet, resolvedConfig.set, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename UniformType>
|
template<typename UniformType>
|
||||||
|
|||||||
@@ -0,0 +1,253 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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 "SharedShaders.h"
|
||||||
|
|
||||||
|
#include "Shader.h"
|
||||||
|
#include "absl/strings/str_format.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
using namespace filament::backend;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// A shader stored in pieces so that uniform declarations can be injected.
|
||||||
|
struct ShaderText {
|
||||||
|
std::string mPrefix;
|
||||||
|
std::string mBody;
|
||||||
|
|
||||||
|
std::string withUniform(const std::string& uniformText) const {
|
||||||
|
return absl::StrFormat("%s\n%s\n%s", mPrefix.c_str(), uniformText.c_str(), mBody.c_str());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<ShaderText> GetGlslVertexShader(VertexShaderType type) {
|
||||||
|
switch (type) {
|
||||||
|
case VertexShaderType::Noop: {
|
||||||
|
return ShaderText{
|
||||||
|
R"(
|
||||||
|
#version 450 core
|
||||||
|
layout(location = 0) in vec4 mesh_position;
|
||||||
|
)", R"(
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
||||||
|
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||||
|
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||||
|
gl_Position.y = -gl_Position.y;
|
||||||
|
#endif
|
||||||
|
})" };
|
||||||
|
}
|
||||||
|
case VertexShaderType::Simple: {
|
||||||
|
return ShaderText{
|
||||||
|
R"(
|
||||||
|
#version 450 core
|
||||||
|
layout(location = 0) in vec4 mesh_position;
|
||||||
|
)", R"(
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(
|
||||||
|
mesh_position.xy * (params.scaleMinusOne.xy + 1.0) + params.offset.xy,
|
||||||
|
params.scaleMinusOne.z + 1.0,
|
||||||
|
1.0);
|
||||||
|
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||||
|
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||||
|
gl_Position.y = -gl_Position.y;
|
||||||
|
#endif
|
||||||
|
})" };
|
||||||
|
}
|
||||||
|
case VertexShaderType::Textured: {
|
||||||
|
return ShaderText{
|
||||||
|
R"(
|
||||||
|
#version 450 core
|
||||||
|
layout(location = 0) in vec4 mesh_position;
|
||||||
|
layout(location = 0) out vec2 uv;
|
||||||
|
)", R"(
|
||||||
|
void main() {
|
||||||
|
gl_Position = vec4(mesh_position.xy, 0.0, 1.0);
|
||||||
|
uv = (mesh_position.xy * 0.5 + 0.5);
|
||||||
|
#if defined(TARGET_VULKAN_ENVIRONMENT)
|
||||||
|
// In Vulkan, clip space is Y-down. In OpenGL and Metal, clip space is Y-up.
|
||||||
|
gl_Position.y = -gl_Position.y;
|
||||||
|
#endif
|
||||||
|
})" };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ShaderText> GetGlslFragmentShader(FragmentShaderType type) {
|
||||||
|
switch (type) {
|
||||||
|
case FragmentShaderType::White: {
|
||||||
|
return ShaderText{
|
||||||
|
R"(
|
||||||
|
#version 450 core
|
||||||
|
precision mediump int; precision highp float;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
)", R"(
|
||||||
|
void main() {
|
||||||
|
fragColor = vec4(1.0, 1.0, 1.0, 1.0);
|
||||||
|
})" };
|
||||||
|
}
|
||||||
|
case FragmentShaderType::SolidColored: {
|
||||||
|
return ShaderText{
|
||||||
|
R"(
|
||||||
|
#version 450 core
|
||||||
|
precision mediump int; precision highp float;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
)", R"(
|
||||||
|
void main() {
|
||||||
|
fragColor = params.color;
|
||||||
|
})" };
|
||||||
|
}
|
||||||
|
case FragmentShaderType::Textured: {
|
||||||
|
return ShaderText{
|
||||||
|
R"(
|
||||||
|
#version 450 core
|
||||||
|
precision mediump int; precision highp float;
|
||||||
|
layout(location = 0) out vec4 fragColor;
|
||||||
|
layout(location = 0) in vec2 uv;
|
||||||
|
)", R"(
|
||||||
|
void main() {
|
||||||
|
fragColor = texture(test_tex, uv);
|
||||||
|
})" };
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string> GetGlslUniform(ShaderUniformType type) {
|
||||||
|
switch (type) {
|
||||||
|
case ShaderUniformType::None: {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
case ShaderUniformType::Simple: {
|
||||||
|
return R"(
|
||||||
|
layout(binding = 0, set = 1) uniform Params {
|
||||||
|
highp vec4 color;
|
||||||
|
// Use scaleMinusOne instead of scale so that a 0 initialized value is a good default
|
||||||
|
highp vec4 scaleMinusOne;
|
||||||
|
highp vec4 offset;
|
||||||
|
} params;
|
||||||
|
)";
|
||||||
|
}
|
||||||
|
case ShaderUniformType::SimpleWithPadding: {
|
||||||
|
return R"(
|
||||||
|
layout(binding = 0, set = 1) uniform Params {
|
||||||
|
highp vec4 padding[4]; // offset of 64 bytes
|
||||||
|
|
||||||
|
highp vec4 color;
|
||||||
|
// Use scaleMinusOne instead of scale so that a 0 initialized value is a good default
|
||||||
|
highp vec4 scaleMinusOne;
|
||||||
|
highp vec4 offset;
|
||||||
|
} params;
|
||||||
|
)";
|
||||||
|
}
|
||||||
|
case ShaderUniformType::Sampler: {
|
||||||
|
return R"(
|
||||||
|
layout(location = 0, set = 1) uniform sampler2D test_tex;
|
||||||
|
)";
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<UniformConfig> GetUniformConfig(ShaderUniformType type) {
|
||||||
|
switch (type) {
|
||||||
|
case ShaderUniformType::None: {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
case ShaderUniformType::Simple: {
|
||||||
|
return {{ "Params" }};
|
||||||
|
}
|
||||||
|
case ShaderUniformType::SimpleWithPadding: {
|
||||||
|
return {{ "Params" }};
|
||||||
|
}
|
||||||
|
case ShaderUniformType::Sampler: {
|
||||||
|
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo{
|
||||||
|
"backend_test", "test_tex", 0,
|
||||||
|
SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false };
|
||||||
|
return {{
|
||||||
|
"test_tex", DescriptorType::SAMPLER, samplerInfo
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderLanguage getShaderLanguage(const Backend& backend) {
|
||||||
|
switch (backend) {
|
||||||
|
case Backend::METAL:
|
||||||
|
return ShaderLanguage::MSL;
|
||||||
|
case Backend::WEBGPU:
|
||||||
|
return ShaderLanguage::WGSL;
|
||||||
|
case Backend::VULKAN:
|
||||||
|
case Backend::NOOP:
|
||||||
|
case Backend::OPENGL:
|
||||||
|
default: {
|
||||||
|
return ShaderLanguage::GLSL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
Shader SharedShaders::makeShader(filament::backend::DriverApi& api, Cleanup& cleanup,
|
||||||
|
ShaderRequest request) {
|
||||||
|
std::optional<ShaderText> vertex;
|
||||||
|
std::optional<ShaderText> fragment;
|
||||||
|
std::optional<std::string> uniform;
|
||||||
|
if (getShaderLanguage(BackendTest::sBackend) != ShaderLanguage::GLSL) {
|
||||||
|
// TODO: If any shaders need backend/shader language specific shaders rather than transpiled
|
||||||
|
// versions of the GLSL shader, check environment.
|
||||||
|
}
|
||||||
|
vertex = GetGlslVertexShader(request.mVertexType);
|
||||||
|
fragment = GetGlslFragmentShader(request.mFragmentType);
|
||||||
|
uniform = GetGlslUniform(request.mUniformType);
|
||||||
|
if (vertex.has_value() && fragment.has_value() && uniform.has_value()) {
|
||||||
|
return Shader(
|
||||||
|
api, cleanup, ShaderConfig{
|
||||||
|
vertex->withUniform(*uniform), fragment->withUniform(*uniform),
|
||||||
|
GetUniformConfig(request.mUniformType)}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SharedShaders::getVertexShaderText(VertexShaderType vertex, ShaderUniformType uniform) {
|
||||||
|
std::optional<ShaderText> vertexText = GetGlslVertexShader(vertex);
|
||||||
|
std::optional<std::string> uniformText = GetGlslUniform(uniform);
|
||||||
|
if (!vertexText.has_value() || !uniformText.has_value()) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return vertexText->withUniform(*uniformText);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SharedShaders::getFragmentShaderText(FragmentShaderType fragment,
|
||||||
|
ShaderUniformType uniform) {
|
||||||
|
std::optional<ShaderText> fragmentText = GetGlslFragmentShader(fragment);
|
||||||
|
std::optional<std::string> uniformText = GetGlslUniform(uniform);
|
||||||
|
if (!fragmentText.has_value() || !uniformText.has_value()) {
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
return fragmentText->withUniform(*uniformText);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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_SHAREDSHADERS_H
|
||||||
|
#define TNT_SHAREDSHADERS_H
|
||||||
|
|
||||||
|
#include "Shader.h"
|
||||||
|
#include "SharedShadersConstants.h"
|
||||||
|
#include "Lifetimes.h"
|
||||||
|
#include "PlatformRunner.h"
|
||||||
|
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
enum class ShaderLanguage : uint8_t {
|
||||||
|
GLSL,
|
||||||
|
MSL,
|
||||||
|
WGSL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShaderRequest {
|
||||||
|
VertexShaderType mVertexType;
|
||||||
|
FragmentShaderType mFragmentType;
|
||||||
|
ShaderUniformType mUniformType;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SharedShaders {
|
||||||
|
public:
|
||||||
|
static Shader makeShader(filament::backend::DriverApi& api, Cleanup& cleanup,
|
||||||
|
ShaderRequest request);
|
||||||
|
static std::string getVertexShaderText(VertexShaderType vertex, ShaderUniformType uniform);
|
||||||
|
static std::string getFragmentShaderText(FragmentShaderType fragment,
|
||||||
|
ShaderUniformType uniform);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
|
||||||
|
#endif //TNT_SHAREDSHADERS_H
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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_SHAREDSHADERSCONSTANTS_H
|
||||||
|
#define TNT_SHAREDSHADERSCONSTANTS_H
|
||||||
|
|
||||||
|
#include "math/mathfwd.h"
|
||||||
|
|
||||||
|
enum class ShaderUniformType : uint8_t {
|
||||||
|
None,
|
||||||
|
Simple,
|
||||||
|
SimpleWithPadding,
|
||||||
|
Sampler,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SimpleMaterialParams {
|
||||||
|
filament::math::float4 color;
|
||||||
|
// 1.0 will be added to this value before use.
|
||||||
|
// The XY values are used to scale position inputs and the Z value is used to set the output
|
||||||
|
// position's Z value.
|
||||||
|
filament::math::float4 scaleMinusOne;
|
||||||
|
// Offset will be applied after scale
|
||||||
|
filament::math::float4 offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SimpleWithPaddingMaterialParams {
|
||||||
|
// The associated uniform structure in the shader will have 64 bytes of padding at the beginning
|
||||||
|
// So users of this struct will need to add 64 bytes to its size and offset all uniform writes.
|
||||||
|
filament::math::float4 color;
|
||||||
|
// 1.0 will be added to this value before use.
|
||||||
|
// The XY values are used to scale position inputs and the Z value is used to set the output
|
||||||
|
// position's Z value.
|
||||||
|
filament::math::float4 scaleMinusOne;
|
||||||
|
// Offset will be applied after scale
|
||||||
|
filament::math::float4 offset;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class VertexShaderType : uint8_t {
|
||||||
|
Noop,
|
||||||
|
Simple,
|
||||||
|
Textured
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class FragmentShaderType : uint8_t {
|
||||||
|
White,
|
||||||
|
SolidColored,
|
||||||
|
Textured
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //TNT_SHAREDSHADERSCONSTANTS_H
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* 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 "Skip.h"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
SkipEnvironment::SkipEnvironment(test::Backend backend) : backend(backend) {}
|
||||||
|
SkipEnvironment::SkipEnvironment(test::OperatingSystem os) : os(os) {}
|
||||||
|
SkipEnvironment::SkipEnvironment(test::OperatingSystem os, test::Backend backend)
|
||||||
|
: backend(backend),
|
||||||
|
os(os) {}
|
||||||
|
|
||||||
|
bool SkipEnvironment::matches() {
|
||||||
|
bool backendMatches = !backend.has_value() || *backend == BackendTest::sBackend;
|
||||||
|
bool osMatches = !os.has_value() || *os == BackendTest::sOperatingSystem;
|
||||||
|
bool isMobileMatches = !isMobile.has_value() || *isMobile == BackendTest::sIsMobilePlatform;
|
||||||
|
return backendMatches && osMatches && isMobileMatches;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SkipEnvironment::describe() {
|
||||||
|
std::stringstream result;
|
||||||
|
if (matches()) {
|
||||||
|
result << "environment matches because " << describe_actual_environment() << ".";
|
||||||
|
} else {
|
||||||
|
result << "environment does not match because " << describe_requirements() << " but "
|
||||||
|
<< describe_actual_environment() << ".";
|
||||||
|
}
|
||||||
|
return result.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SkipEnvironment::describe_actual_environment() {
|
||||||
|
bool resultWritten = false;
|
||||||
|
std::stringstream reality;
|
||||||
|
if (backend.has_value()) {
|
||||||
|
reality << "backend was " << utils::to_string(BackendTest::sBackend).c_str();
|
||||||
|
resultWritten = true;
|
||||||
|
}
|
||||||
|
if (os.has_value()) {
|
||||||
|
if (resultWritten) {
|
||||||
|
reality << ", and ";
|
||||||
|
}
|
||||||
|
reality << "operating system was "
|
||||||
|
<< utils::to_string(BackendTest::sOperatingSystem).c_str();
|
||||||
|
resultWritten = true;
|
||||||
|
}
|
||||||
|
if (isMobile.has_value()) {
|
||||||
|
if (resultWritten) {
|
||||||
|
reality << ", and ";
|
||||||
|
}
|
||||||
|
reality << "device " << (BackendTest::sIsMobilePlatform ? "was" : "was not") << " mobile";
|
||||||
|
resultWritten = true;
|
||||||
|
}
|
||||||
|
return reality.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SkipEnvironment::describe_requirements() {
|
||||||
|
bool resultWritten = false;
|
||||||
|
std::stringstream requirement;
|
||||||
|
if (backend.has_value()) {
|
||||||
|
requirement << "backend needs to be " << utils::to_string(*backend).c_str();
|
||||||
|
resultWritten = true;
|
||||||
|
}
|
||||||
|
if (os.has_value()) {
|
||||||
|
if (resultWritten) {
|
||||||
|
requirement << ", and ";
|
||||||
|
}
|
||||||
|
requirement << "operating system needs to be " << utils::to_string(*os).c_str();
|
||||||
|
resultWritten = true;
|
||||||
|
}
|
||||||
|
if (isMobile.has_value() && BackendTest::sIsMobilePlatform != isMobile) {
|
||||||
|
if (resultWritten) {
|
||||||
|
requirement << ", and ";
|
||||||
|
}
|
||||||
|
requirement << "device needs to " << (*isMobile ? "be" : "not be") << " mobile";
|
||||||
|
resultWritten = true;
|
||||||
|
}
|
||||||
|
return requirement.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* 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_SKIP_H
|
||||||
|
#define TNT_SKIP_H
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
#include "BackendTest.h"
|
||||||
|
|
||||||
|
// skipEnvironment must be a test::SkipEnvironment
|
||||||
|
#define SKIP_IF(skipEnvironment) \
|
||||||
|
do { \
|
||||||
|
SkipEnvironment skip(skipEnvironment); \
|
||||||
|
if (skip.matches()) { \
|
||||||
|
GTEST_SKIP() << "Skipping test as the " << skip.describe(); \
|
||||||
|
} \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
struct SkipEnvironment {
|
||||||
|
SkipEnvironment(const SkipEnvironment&) = default;
|
||||||
|
explicit SkipEnvironment(test::Backend backend);
|
||||||
|
explicit SkipEnvironment(test::OperatingSystem os);
|
||||||
|
SkipEnvironment(test::OperatingSystem os, test::Backend backend);
|
||||||
|
|
||||||
|
std::optional<test::Backend> backend;
|
||||||
|
std::optional<test::OperatingSystem> os;
|
||||||
|
std::optional<bool> isMobile;
|
||||||
|
|
||||||
|
bool matches();
|
||||||
|
// Describes the current state of either matching or mismatching.
|
||||||
|
std::string describe();
|
||||||
|
// Describe all the non-null requirements.
|
||||||
|
std::string describe_requirements();
|
||||||
|
// Describes the environment's status for all the attributes that are non-null.
|
||||||
|
std::string describe_actual_environment();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
|
||||||
|
#endif// TNT_SKIP_H
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 982 B |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.1 KiB |