Compare commits
272 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd528e57ab | ||
|
|
4e648b224f | ||
|
|
04c7f84c6f | ||
|
|
8c31f46683 | ||
|
|
563c32b95b | ||
|
|
ab0063bc6b | ||
|
|
b425d63b95 | ||
|
|
02d2e2f644 | ||
|
|
ef488fdf57 | ||
|
|
4836f94635 | ||
|
|
c2b5f08bc7 | ||
|
|
d05c61fe9a | ||
|
|
af48bc3c74 | ||
|
|
65dfac9637 | ||
|
|
1ae82d325c | ||
|
|
f0f7e299d2 | ||
|
|
a3131a64b6 | ||
|
|
36120106cd | ||
|
|
7e96216b6c | ||
|
|
a9793b3cf6 | ||
|
|
2260794a55 | ||
|
|
d83b3858b3 | ||
|
|
654a38c3bf | ||
|
|
bf602516ec | ||
|
|
9e119937af | ||
|
|
859c5edb49 | ||
|
|
11a3c06418 | ||
|
|
3fab93bf3d | ||
|
|
20caeb3889 | ||
|
|
6e5930c2a0 | ||
|
|
486b9eef1e | ||
|
|
9b0718199f | ||
|
|
3e644b25f0 | ||
|
|
dc6608350b | ||
|
|
fadd5eb953 | ||
|
|
8b24950429 | ||
|
|
b48b6136ba | ||
|
|
5d9337e6c2 | ||
|
|
142b73d9d7 | ||
|
|
5707043d96 | ||
|
|
4e6ae2b714 | ||
|
|
ca0f98c513 | ||
|
|
70b87510a2 | ||
|
|
a9e8f40287 | ||
|
|
31b836282d | ||
|
|
6ccfeddf26 | ||
|
|
9c6020a77a | ||
|
|
e912dc2dc5 | ||
|
|
a27260b87f | ||
|
|
653a015991 | ||
|
|
ef703bb4be | ||
|
|
f9c8e65ef3 | ||
|
|
c43051728c | ||
|
|
cdd9c4aebe | ||
|
|
f3a61f100c | ||
|
|
20acc01fcd | ||
|
|
d640ba853b | ||
|
|
50d9d9f139 | ||
|
|
0774ce6b5e | ||
|
|
60db518b75 | ||
|
|
7be9cdc7f8 | ||
|
|
af792e3d18 | ||
|
|
3c5316f1e9 | ||
|
|
f93677548d | ||
|
|
1eff66e4ec | ||
|
|
34f8b9aa20 | ||
|
|
6d07443188 | ||
|
|
1f33a6efd2 | ||
|
|
4127f619e1 | ||
|
|
13a23703ad | ||
|
|
57f6ca625c | ||
|
|
02f5903b67 | ||
|
|
c39a870abc | ||
|
|
09b8008e17 | ||
|
|
59b59cf6be | ||
|
|
0df6013263 | ||
|
|
d5041fced7 | ||
|
|
9c54b8a777 | ||
|
|
8a534d0940 | ||
|
|
130825422e | ||
|
|
984006ee25 | ||
|
|
b3cc4d11b8 | ||
|
|
8523f4e970 | ||
|
|
b676002521 | ||
|
|
6b7450dc0b | ||
|
|
8bdf7bd1e5 | ||
|
|
7b384fb5e8 | ||
|
|
1262cb286c | ||
|
|
20dc6d479b | ||
|
|
ee6f3fb1dc | ||
|
|
b9a9586abb | ||
|
|
6ee20a57aa | ||
|
|
368fa2bf39 | ||
|
|
8f642892b4 | ||
|
|
0736f3c3b3 | ||
|
|
5435a8ed3b | ||
|
|
5fd7a4e153 | ||
|
|
20ff230b92 | ||
|
|
44ff79ad34 | ||
|
|
102d2db008 | ||
|
|
6a7767f4e4 | ||
|
|
5c9039e650 | ||
|
|
1203c24f06 | ||
|
|
9704d27aeb | ||
|
|
c6f2c3fc1c | ||
|
|
2faf868341 | ||
|
|
628d387cbd | ||
|
|
81f6260843 | ||
|
|
75a1c6d7a8 | ||
|
|
1a9063d53a | ||
|
|
8c76370e2d | ||
|
|
6062b3c8c6 | ||
|
|
d9186c44ba | ||
|
|
bdc15a5c2d | ||
|
|
acfe9298d9 | ||
|
|
6d06e00934 | ||
|
|
b4021a1fbc | ||
|
|
2c9559f9b8 | ||
|
|
57f6e5371b | ||
|
|
e2da13f817 | ||
|
|
addae314b4 | ||
|
|
5e3c51822d | ||
|
|
9d817a0cc9 | ||
|
|
9fa3cbfcde | ||
|
|
c81ece5c3c | ||
|
|
95ecc3d1e4 | ||
|
|
cabaa4323e | ||
|
|
d6b2ec49b0 | ||
|
|
93004aa5bc | ||
|
|
27fa8ab46d | ||
|
|
aae48c1121 | ||
|
|
918ce935b8 | ||
|
|
614990f413 | ||
|
|
6f37e07dba | ||
|
|
171b3279e0 | ||
|
|
da058028fd | ||
|
|
55c75453df | ||
|
|
f5eff10532 | ||
|
|
56a0364534 | ||
|
|
c8aae3cdaa | ||
|
|
4f94c575c9 | ||
|
|
1a29f020c9 | ||
|
|
d5847a3c6a | ||
|
|
3dbae482d3 | ||
|
|
27572e3be6 | ||
|
|
dedf276e26 | ||
|
|
111ad96134 | ||
|
|
2a67152dda | ||
|
|
2f62978e4c | ||
|
|
fdb90f2f49 | ||
|
|
b3a1cfe7c9 | ||
|
|
d273838e07 | ||
|
|
5418b8c4cc | ||
|
|
724cab3623 | ||
|
|
a1de8c924d | ||
|
|
d9faea264a | ||
|
|
72765a5b0a | ||
|
|
e1beabaa98 | ||
|
|
ebaee14b8b | ||
|
|
d4f08dafbb | ||
|
|
753fb102c4 | ||
|
|
b219113a55 | ||
|
|
6116c2f9eb | ||
|
|
95fdba0c5d | ||
|
|
9c56517370 | ||
|
|
08f06f4298 | ||
|
|
b1794a82ca | ||
|
|
56bd2871ec | ||
|
|
5a9c47bcb5 | ||
|
|
51c2fa2728 | ||
|
|
334c71de52 | ||
|
|
001098098b | ||
|
|
9140d44b29 | ||
|
|
8b0d65768a | ||
|
|
35a00b1d84 | ||
|
|
b517a45213 | ||
|
|
f8057b9a5b | ||
|
|
d9ee526a72 | ||
|
|
349bf7be38 | ||
|
|
164d7507bb | ||
|
|
a01d282f14 | ||
|
|
b007e9137e | ||
|
|
06bae26e1b | ||
|
|
1be3e13427 | ||
|
|
e7c67d1adb | ||
|
|
9b67d80961 | ||
|
|
b066678a37 | ||
|
|
b10d5c58aa | ||
|
|
3c3296a114 | ||
|
|
61501ba122 | ||
|
|
000bff5ce1 | ||
|
|
99ba40e965 | ||
|
|
47d45a64fa | ||
|
|
4116af7971 | ||
|
|
2fab93faff | ||
|
|
b92c5cab07 | ||
|
|
8ed9678cbe | ||
|
|
731e52a3c1 | ||
|
|
687b6da800 | ||
|
|
2bd48f58ff | ||
|
|
f2f4f556b5 | ||
|
|
5aa15f061b | ||
|
|
63db4f0bf0 | ||
|
|
7797f5af38 | ||
|
|
f9def09d17 | ||
|
|
494934f34e | ||
|
|
caae42fdf6 | ||
|
|
8f6c4841cb | ||
|
|
64f4c097ac | ||
|
|
5d0c06e3f2 | ||
|
|
7d31a7f7ea | ||
|
|
0191e1fe46 | ||
|
|
21093067db | ||
|
|
4f4a439f8b | ||
|
|
1b10e7d4f3 | ||
|
|
2306b56ea7 | ||
|
|
5b2d3ac225 | ||
|
|
422dfcc1e6 | ||
|
|
eebb40d30d | ||
|
|
0d304393f4 | ||
|
|
766e4f2ec9 | ||
|
|
b0a584c915 | ||
|
|
57aa99e964 | ||
|
|
cfc133fcf1 | ||
|
|
8eade6be1f | ||
|
|
d5bab43ceb | ||
|
|
485ae1b324 | ||
|
|
2250664e58 | ||
|
|
75e8961109 | ||
|
|
0e9b2eda0a | ||
|
|
a900bc69fb | ||
|
|
edbfefda0a | ||
|
|
1a0b5ddc14 | ||
|
|
e8bed52b3f | ||
|
|
06e8b1d689 | ||
|
|
8a27cc8b7f | ||
|
|
9c351b28ab | ||
|
|
cbc3bb3326 | ||
|
|
0eb851ff8c | ||
|
|
038f07cb34 | ||
|
|
62adb234d1 | ||
|
|
53d8d2a41f | ||
|
|
974a69a273 | ||
|
|
7d694ee85e | ||
|
|
46e6664c3d | ||
|
|
4fd7c418e5 | ||
|
|
a3fdca7997 | ||
|
|
7f1704481e | ||
|
|
e6b962b038 | ||
|
|
668fb07ac2 | ||
|
|
f64eef02a3 | ||
|
|
239e98ccec | ||
|
|
919cfae6b2 | ||
|
|
0b9389430b | ||
|
|
1d157677d1 | ||
|
|
ef3f0cb326 | ||
|
|
fcf53f2c3e | ||
|
|
d6fda03b06 | ||
|
|
a76addd2bf | ||
|
|
b1f7731dbe | ||
|
|
e3e12dbf73 | ||
|
|
31a75029f0 | ||
|
|
e5c24cc718 | ||
|
|
2b86c8df6f | ||
|
|
4d8e6eefa1 | ||
|
|
8aeec2ba35 | ||
|
|
4eb4fd5aba | ||
|
|
56355231bd | ||
|
|
9ccb8fce31 | ||
|
|
e674420e9c | ||
|
|
8a9cbcfb99 | ||
|
|
f0d5cd3fa1 |
7
.dir-locals.el
Normal file
7
.dir-locals.el
Normal file
@@ -0,0 +1,7 @@
|
||||
;;; Directory Local Variables -*- no-byte-compile: t -*-
|
||||
;;; For more information see (info "(emacs) Directory Variables")
|
||||
|
||||
((c++-mode . ((c-file-style . "filament")
|
||||
(apheleia-inhibit . t)))
|
||||
(c-mode . ((c-file-style . "filament")
|
||||
(apheleia-inhibit . t))))
|
||||
2
.github/workflows/android-continuous.yml
vendored
2
.github/workflows/android-continuous.yml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
jobs:
|
||||
build-android:
|
||||
name: build-android
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.3.0
|
||||
|
||||
2
.github/workflows/cocopods-deploy.yml
vendored
2
.github/workflows/cocopods-deploy.yml
vendored
@@ -13,7 +13,7 @@ on:
|
||||
jobs:
|
||||
cocoapods-deploy:
|
||||
name: cocoapods-deploy
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- name: Check out iOS/CocoaPods directory
|
||||
uses: Bhacaz/checkout-files@49fc3050859046bf4f4873678d46099985640e89
|
||||
|
||||
2
.github/workflows/ios-continuous.yml
vendored
2
.github/workflows/ios-continuous.yml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
jobs:
|
||||
build-ios:
|
||||
name: build-ios
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.3.0
|
||||
|
||||
2
.github/workflows/mac-continuous.yml
vendored
2
.github/workflows/mac-continuous.yml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
jobs:
|
||||
build-mac:
|
||||
name: build-mac
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.3.0
|
||||
|
||||
2
.github/workflows/npm-deploy.yml
vendored
2
.github/workflows/npm-deploy.yml
vendored
@@ -11,7 +11,7 @@ on:
|
||||
jobs:
|
||||
npm-deploy:
|
||||
name: npm-deploy
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-14
|
||||
steps:
|
||||
- uses: actions/checkout@v3.3.0
|
||||
with:
|
||||
|
||||
8
.github/workflows/presubmit.yml
vendored
8
.github/workflows/presubmit.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-22.04]
|
||||
os: [macos-14, ubuntu-22.04]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.3.0
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
build-android:
|
||||
name: build-android
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.3.0
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
|
||||
build-ios:
|
||||
name: build-iOS
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.3.0
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
|
||||
build-web:
|
||||
name: build-web
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.3.0
|
||||
|
||||
10
.github/workflows/release.yml
vendored
10
.github/workflows/release.yml
vendored
@@ -31,7 +31,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, ubuntu-22.04]
|
||||
os: [macos-14, ubuntu-22.04]
|
||||
|
||||
steps:
|
||||
- name: Decide Git ref
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
|
||||
build-web:
|
||||
name: build-web
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-14
|
||||
if: github.event_name == 'release' || github.event.inputs.platform == 'web'
|
||||
|
||||
steps:
|
||||
@@ -98,7 +98,7 @@ jobs:
|
||||
|
||||
build-android:
|
||||
name: build-android
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-14
|
||||
if: github.event_name == 'release' || github.event.inputs.platform == 'android'
|
||||
|
||||
steps:
|
||||
@@ -129,7 +129,7 @@ jobs:
|
||||
- name: Sign sample-gltf-viewer
|
||||
run: |
|
||||
echo "${APK_KEYSTORE_BASE64}" > filament.jks.base64
|
||||
base64 --decode filament.jks.base64 > filament.jks
|
||||
base64 --decode -i filament.jks.base64 > filament.jks
|
||||
BUILD_TOOLS_VERSION=$(ls ${ANDROID_HOME}/build-tools | sort -V | tail -n 1)
|
||||
APKSIGNER=${ANDROID_HOME}/build-tools/${BUILD_TOOLS_VERSION}/apksigner
|
||||
IN_FILE="out/sample-gltf-viewer-release.apk"
|
||||
@@ -152,7 +152,7 @@ jobs:
|
||||
|
||||
build-ios:
|
||||
name: build-ios
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-14
|
||||
if: github.event_name == 'release' || github.event.inputs.platform == 'ios'
|
||||
|
||||
steps:
|
||||
|
||||
2
.github/workflows/web-continuous.yml
vendored
2
.github/workflows/web-continuous.yml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
jobs:
|
||||
build-web:
|
||||
name: build-web
|
||||
runs-on: macos-latest
|
||||
runs-on: macos-14
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3.3.0
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -16,3 +16,5 @@ settings.json
|
||||
test*.png
|
||||
test*.json
|
||||
results
|
||||
/compile_commands.json
|
||||
/.cache
|
||||
|
||||
@@ -41,6 +41,10 @@ option(FILAMENT_LINUX_IS_MOBILE "Treat Linux as Mobile" OFF)
|
||||
|
||||
option(FILAMENT_ENABLE_ASAN_UBSAN "Enable Address and Undefined Behavior Sanitizers" OFF)
|
||||
|
||||
option(FILAMENT_ENABLE_TSAN "Enable Thread Sanitizer" OFF)
|
||||
|
||||
option(FILAMENT_ENABLE_FEATURE_LEVEL_0 "Enable Feature Level 0" ON)
|
||||
|
||||
set(FILAMENT_NDK_VERSION "" CACHE STRING
|
||||
"Android NDK version or version prefix to be used when building for Android."
|
||||
)
|
||||
@@ -65,6 +69,9 @@ set(FILAMENT_METAL_HANDLE_ARENA_SIZE_IN_MB "8" CACHE STRING
|
||||
"Size of the Metal handle arena, default 8."
|
||||
)
|
||||
|
||||
# Enable exceptions by default in spirv-cross.
|
||||
set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS OFF)
|
||||
|
||||
# ==================================================================================================
|
||||
# CMake policies
|
||||
# ==================================================================================================
|
||||
@@ -225,6 +232,21 @@ if (WIN32)
|
||||
# we don't need them on CI.
|
||||
string(REPLACE "/INCREMENTAL" "/INCREMENTAL:NO" ${LinkerFlag} ${${LinkerFlag}})
|
||||
endforeach()
|
||||
|
||||
# We turn off compile-time optimizations for CI, as options that speed up the compile-time
|
||||
# (e.g. /MP) might increase memory usage, leading to instabilities on limited CI machines.
|
||||
option(FILAMENT_SHORTEN_MSVC_COMPILATION "Shorten compile-time in Visual Studio" OFF)
|
||||
else()
|
||||
option(FILAMENT_SHORTEN_MSVC_COMPILATION "Shorten compile-time in Visual Studio" ON)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
if (FILAMENT_SHORTEN_MSVC_COMPILATION)
|
||||
# enable multi-processor compilation
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
|
||||
# disable run-time STL checks to improve tools (e.g. matc) performance
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_ITERATOR_DEBUG_LEVEL=0")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -337,6 +359,7 @@ endif()
|
||||
|
||||
if (CYGWIN)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti")
|
||||
set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON)
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
@@ -373,6 +396,7 @@ endif()
|
||||
# saved by -fno-exception and 10 KiB saved by -fno-rtti).
|
||||
if (ANDROID OR IOS OR WEBGL)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-exceptions -fno-rtti")
|
||||
set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON)
|
||||
|
||||
if (ANDROID OR WEBGL)
|
||||
# Omitting unwind info prevents the generation of readable stack traces in crash reports on iOS
|
||||
@@ -384,6 +408,7 @@ endif()
|
||||
# std::visit, which is not supported on iOS 11.0 when exceptions are enabled.
|
||||
if (IOS)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-exceptions")
|
||||
set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON)
|
||||
endif()
|
||||
|
||||
# With WebGL, we disable RTTI even for debug builds because we pass emscripten::val back and forth
|
||||
@@ -403,7 +428,13 @@ endif()
|
||||
if (FILAMENT_ENABLE_ASAN_UBSAN)
|
||||
set(EXTRA_SANITIZE_OPTIONS "-fsanitize=address -fsanitize=undefined")
|
||||
endif()
|
||||
|
||||
if (FILAMENT_ENABLE_TSAN)
|
||||
set(EXTRA_SANITIZE_OPTIONS "-fsanitize=thread")
|
||||
endif()
|
||||
if (ANDROID)
|
||||
# keep STL debug infos (mimics what the NDK does)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-limit-debug-info")
|
||||
endif()
|
||||
if (NOT MSVC AND NOT WEBGL)
|
||||
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fstack-protector")
|
||||
endif()
|
||||
@@ -523,6 +554,11 @@ if (FILAMENT_SUPPORTS_METAL)
|
||||
set(MATC_API_FLAGS ${MATC_API_FLAGS} -a metal)
|
||||
endif()
|
||||
|
||||
# Disable ESSL 1.0 code generation.
|
||||
if (NOT FILAMENT_ENABLE_FEATURE_LEVEL_0)
|
||||
set(MATC_API_FLAGS ${MATC_API_FLAGS} -1)
|
||||
endif()
|
||||
|
||||
# Enable debug info (preserves names in SPIR-V)
|
||||
if (FILAMENT_ENABLE_MATDBG)
|
||||
set(MATC_OPT_FLAGS ${MATC_OPT_FLAGS} -d)
|
||||
@@ -598,9 +634,9 @@ function(combine_static_libs TARGET OUTPUT DEPS)
|
||||
# Loop through the dependent libraries and query their location on disk.
|
||||
set(DEPS_FILES )
|
||||
foreach(DEPENDENCY ${DEPS})
|
||||
if(TARGET ${DEPENDENCY})
|
||||
if (TARGET ${DEPENDENCY})
|
||||
get_property(dep_type TARGET ${DEPENDENCY} PROPERTY TYPE)
|
||||
if(dep_type STREQUAL "STATIC_LIBRARY")
|
||||
if (dep_type STREQUAL "STATIC_LIBRARY")
|
||||
list(APPEND DEPS_FILES "$<TARGET_FILE:${DEPENDENCY}>")
|
||||
endif()
|
||||
endif()
|
||||
@@ -670,7 +706,7 @@ function(get_resgen_vars ARCHIVE_DIR ARCHIVE_NAME)
|
||||
set(RESGEN_OUTPUTS "${OUTPUTS}" PARENT_SCOPE)
|
||||
set(RESGEN_FLAGS -qx ${ARCHIVE_DIR} -p ${ARCHIVE_NAME} PARENT_SCOPE)
|
||||
set(RESGEN_SOURCE "${ARCHIVE_DIR}/${ARCHIVE_NAME}${ASM_SUFFIX}.S" PARENT_SCOPE)
|
||||
set(RESGEN_SOURCE_FLAGS "-I${ARCHIVE_DIR} ${ASM_ARCH_FLAG}" PARENT_SCOPE)
|
||||
set(RESGEN_SOURCE_FLAGS "-I'${ARCHIVE_DIR}' ${ASM_ARCH_FLAG}" PARENT_SCOPE)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
@@ -685,7 +721,6 @@ add_subdirectory(${LIBRARIES}/filabridge)
|
||||
add_subdirectory(${LIBRARIES}/filaflat)
|
||||
add_subdirectory(${LIBRARIES}/filagui)
|
||||
add_subdirectory(${LIBRARIES}/filameshio)
|
||||
add_subdirectory(${LIBRARIES}/geometry)
|
||||
add_subdirectory(${LIBRARIES}/gltfio)
|
||||
add_subdirectory(${LIBRARIES}/ibl)
|
||||
add_subdirectory(${LIBRARIES}/iblprefilter)
|
||||
@@ -713,6 +748,9 @@ add_subdirectory(${EXTERNAL}/jsmn/tnt)
|
||||
add_subdirectory(${EXTERNAL}/stb/tnt)
|
||||
add_subdirectory(${EXTERNAL}/getopt)
|
||||
|
||||
# Note that this has to be placed after mikktspace in order for combine_static_libs to work.
|
||||
add_subdirectory(${LIBRARIES}/geometry)
|
||||
|
||||
if (FILAMENT_BUILD_FILAMAT OR IS_HOST_PLATFORM)
|
||||
# spirv-tools must come before filamat, as filamat relies on the presence of the
|
||||
# spirv-tools_SOURCE_DIR variable.
|
||||
@@ -730,6 +768,8 @@ endif()
|
||||
if (FILAMENT_SUPPORTS_VULKAN)
|
||||
add_subdirectory(${LIBRARIES}/bluevk)
|
||||
add_subdirectory(${EXTERNAL}/vkmemalloc/tnt)
|
||||
set(SPIRV_HEADERS_SKIP_EXAMPLES ON)
|
||||
add_subdirectory(${EXTERNAL}/spirv-headers)
|
||||
endif()
|
||||
|
||||
set(FILAMENT_SAMPLES_BINARY_DIR ${PROJECT_BINARY_DIR}/samples)
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.45.1'
|
||||
implementation 'com.google.android.filament:filament-android:1.50.5'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.45.1'
|
||||
pod 'Filament', '~> 1.50.5'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
|
||||
@@ -128,3 +128,15 @@ Navigate to [Filament's release
|
||||
workflow](https://github.com/google/filament/actions/workflows/release.yml). Hit the _Run workflow_
|
||||
dropdown. Modify _Platform to build_ and _Release tag to build_, then hit _Run workflow_. This will
|
||||
initiate a new release run.
|
||||
|
||||
## 11. Kick off the npm and CocoaPods release jobs
|
||||
|
||||
Navigate to [Filament's npm deploy
|
||||
workflow](https://github.com/google/filament/actions/workflows/npm-deploy.yml).
|
||||
Hit the _Run workflow_ dropdown. Modify _Release tag to deploy_ to the tag corresponding to this
|
||||
release (for example, v1.42.2).
|
||||
|
||||
Navigate to [Filament's CocoaPods deploy
|
||||
workflow](https://github.com/google/filament/actions/workflows/cocopods-deploy.yml).
|
||||
Hit the _Run workflow_ dropdown. Modify _Release tag to deploy_ to the tag corresponding to this
|
||||
release (for example, v1.42.2).
|
||||
|
||||
@@ -7,6 +7,69 @@ A new header is inserted each time a *tag* is created.
|
||||
Instead, if you are authoring a PR for the main branch, add your release note to
|
||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||
|
||||
## v1.50.5
|
||||
|
||||
- android: NDK 26.1.10909125 is used by default
|
||||
- android: Minimum API level on Android is now API 21 instead of API 19. This allows the use of OpenGL ES 3.1
|
||||
- rendering: New PBR Neutral tone mapper, designed to preserve materials color appearance
|
||||
- android: Change default frameRateOptions.interval to 1.0
|
||||
|
||||
## v1.50.4
|
||||
|
||||
|
||||
## v1.50.3
|
||||
|
||||
|
||||
## v1.50.2
|
||||
|
||||
|
||||
## v1.50.1
|
||||
|
||||
- Metal: fix some shader artifacts by disabling fast math optimizations.
|
||||
- backend: remove `atan2` overload which had a typo and wasn't useful. Fixes b/320856413.
|
||||
- utils: remove usages of `SpinLock`. Fixes b/321101014.
|
||||
|
||||
## v1.50.0
|
||||
- engine: TAA now supports 4x upscaling [BETA] [⚠️ **New Material Version**]
|
||||
|
||||
## v1.49.3
|
||||
|
||||
- matc: Generate stereo variants for FL0 materials [⚠️ **Recompile materials**]
|
||||
|
||||
## v1.49.2
|
||||
|
||||
|
||||
## v1.49.1
|
||||
|
||||
|
||||
## v1.49.0
|
||||
|
||||
- matc: Fix ESSL 1.0 codegen when using external samplers [⚠️ **Recompile materials**]
|
||||
|
||||
## v1.48.0
|
||||
|
||||
- matc: New option `-1` to disable generation of ESSL 1.0 code in Feature Level 0 materials
|
||||
- matc: Support optimizations for ESSL 1.0 code [⚠️ **Recompile materials**]
|
||||
|
||||
## v1.47.0
|
||||
|
||||
- engine: Support up to 4 side-by-side stereoscopic eyes, configurable at Engine creation time. See
|
||||
`Engine::Config::stereoscopicEyeCount`. [⚠️ **Recompile Materials**]
|
||||
|
||||
## v1.46.0
|
||||
|
||||
- engine: Allow instantiating Engine at a given feature level via `Engine::Builder::featureLevel`
|
||||
- matc: Enable `GL_OES_standard_derivatives` extension in ESSL 1.0 shaders
|
||||
- matc: Fix code generation of double sided and masked materials in ESSL 1.0 shaders
|
||||
- filagui: Add support for feature level 0
|
||||
- matc: Add support for post-process materials in feature level 0
|
||||
- engine: Add `Material::getFeatureLevel()`
|
||||
- engine: Add missing `Material::getReflectionMode()` method in Java
|
||||
- engine: Support basic usage of post-processing materials on feature level 0
|
||||
- engine: Fix critical GLES 2.0 bugs
|
||||
- engine: Add `FILAMENT_ENABLE_FEATURE_LEVEL_0` build-time option optionally allow building Filament
|
||||
without FL0 support.
|
||||
|
||||
## v1.45.1
|
||||
|
||||
- engine: Added parameter for configuring JobSystem thread count
|
||||
@@ -15,6 +78,7 @@ Instead, if you are authoring a PR for the main branch, add your release note to
|
||||
- engine: New tone mapper: `AgXTonemapper`.
|
||||
- matinfo: Add support for viewing ESSL 1.0 shaders
|
||||
- engine: Add `Renderer::getClearOptions()` [b/243846268]
|
||||
- engine: Fix stable shadows (again) when an IBL rotation is used
|
||||
|
||||
## v1.45.0
|
||||
|
||||
|
||||
@@ -80,15 +80,15 @@ buildscript {
|
||||
|
||||
ext.versions = [
|
||||
'jdk': 17,
|
||||
'minSdk': 19,
|
||||
'targetSdk': 33,
|
||||
'compileSdk': 33,
|
||||
'kotlin': '1.9.0',
|
||||
'kotlin_coroutines': '1.7.2',
|
||||
'minSdk': 21,
|
||||
'targetSdk': 34,
|
||||
'compileSdk': 34,
|
||||
'kotlin': '1.9.21',
|
||||
'kotlin_coroutines': '1.7.3',
|
||||
'buildTools': '34.0.0',
|
||||
'ndk': '25.1.8937393',
|
||||
'androidx_core': '1.10.1',
|
||||
'androidx_annotations': '1.6.0'
|
||||
'ndk': '26.1.10909125',
|
||||
'androidx_core': '1.12.0',
|
||||
'androidx_annotations': '1.7.0'
|
||||
]
|
||||
|
||||
ext.deps = [
|
||||
@@ -104,7 +104,7 @@ buildscript {
|
||||
]
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.1.0'
|
||||
classpath 'com.android.tools.build:gradle:8.2.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
|
||||
}
|
||||
|
||||
|
||||
@@ -384,6 +384,13 @@ Java_com_google_android_filament_Engine_nFlushAndWait(JNIEnv*, jclass,
|
||||
engine->flushAndWait();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Engine_nFlush(JNIEnv*, jclass,
|
||||
jlong nativeEngine) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
engine->flush();
|
||||
}
|
||||
|
||||
// Managers...
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
@@ -428,6 +435,13 @@ Java_com_google_android_filament_Engine_nIsAutomaticInstancingEnabled(JNIEnv*, j
|
||||
return (jboolean)engine->isAutomaticInstancingEnabled();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_Engine_nGetMaxStereoscopicEyes(JNIEnv*, jclass, jlong nativeEngine) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
return (jlong) engine->getMaxStereoscopicEyes();
|
||||
}
|
||||
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_Engine_nGetSupportedFeatureLevel(JNIEnv *, jclass,
|
||||
jlong nativeEngine) {
|
||||
@@ -470,7 +484,10 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderConfig(JNIEnv*,
|
||||
jclass, jlong nativeBuilder, jlong commandBufferSizeMB, jlong perRenderPassArenaSizeMB,
|
||||
jlong driverHandleArenaSizeMB, jlong minCommandBufferSizeMB, jlong perFrameCommandsSizeMB,
|
||||
jlong jobSystemThreadCount) {
|
||||
jlong jobSystemThreadCount,
|
||||
jlong textureUseAfterFreePoolSize, jboolean disableParallelShaderCompile,
|
||||
jint stereoscopicType, jlong stereoscopicEyeCount,
|
||||
jlong resourceAllocatorCacheSizeMB, jlong resourceAllocatorCacheMaxAge) {
|
||||
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
|
||||
Engine::Config config = {
|
||||
.commandBufferSizeMB = (uint32_t) commandBufferSizeMB,
|
||||
@@ -479,10 +496,22 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
|
||||
.minCommandBufferSizeMB = (uint32_t) minCommandBufferSizeMB,
|
||||
.perFrameCommandsSizeMB = (uint32_t) perFrameCommandsSizeMB,
|
||||
.jobSystemThreadCount = (uint32_t) jobSystemThreadCount,
|
||||
.textureUseAfterFreePoolSize = (uint32_t) textureUseAfterFreePoolSize,
|
||||
.disableParallelShaderCompile = (bool) disableParallelShaderCompile,
|
||||
.stereoscopicType = (Engine::StereoscopicType) stereoscopicType,
|
||||
.stereoscopicEyeCount = (uint8_t) stereoscopicEyeCount,
|
||||
.resourceAllocatorCacheSizeMB = (uint32_t) resourceAllocatorCacheSizeMB,
|
||||
.resourceAllocatorCacheMaxAge = (uint8_t) resourceAllocatorCacheMaxAge,
|
||||
};
|
||||
builder->config(&config);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderFeatureLevel(
|
||||
JNIEnv*, jclass, jlong nativeBuilder, jint ordinal) {
|
||||
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
|
||||
builder->featureLevel((Engine::FeatureLevel)ordinal);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderSharedContext(
|
||||
JNIEnv*, jclass, jlong nativeBuilder, jlong sharedContext) {
|
||||
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
|
||||
|
||||
@@ -38,7 +38,7 @@ Java_com_google_android_filament_EntityManager_nCreateArray(JNIEnv* env, jclass,
|
||||
// (which it is), but still.
|
||||
em->create((size_t) n, reinterpret_cast<Entity *>(entities));
|
||||
|
||||
env->ReleaseIntArrayElements(entities_, entities, 0);
|
||||
env->ReleaseIntArrayElements(entities_, entities, JNI_ABORT);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <filament/Material.h>
|
||||
|
||||
#include "common/NioUtils.h"
|
||||
#include "common/CallbackUtils.h"
|
||||
|
||||
using namespace filament;
|
||||
|
||||
@@ -105,6 +106,22 @@ Java_com_google_android_filament_Material_nGetRefractionType(JNIEnv*, jclass,
|
||||
return (jint) material->getRefractionType();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_Material_nGetReflectionMode(JNIEnv*, jclass,
|
||||
jlong nativeMaterial) {
|
||||
Material* material = (Material*) nativeMaterial;
|
||||
return (jint) material->getReflectionMode();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_Material_nGetFeatureLevel(JNIEnv*, jclass,
|
||||
jlong nativeMaterial) {
|
||||
Material* material = (Material*) nativeMaterial;
|
||||
return (jint) material->getFeatureLevel();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_Material_nGetVertexDomain(JNIEnv*, jclass,
|
||||
@@ -255,3 +272,17 @@ Java_com_google_android_filament_Material_nHasParameter(JNIEnv* env, jclass,
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
return (jboolean) hasParameter;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_Material_nCompile(JNIEnv *env, jclass clazz,
|
||||
jlong nativeMaterial, jint priority, jint variants, jobject handler, jobject runnable) {
|
||||
Material* material = (Material*) nativeMaterial;
|
||||
JniCallback* jniCallback = JniCallback::make(env, handler, runnable);
|
||||
material->compile(
|
||||
(Material::CompilerPriorityQueue) priority,
|
||||
(UserVariantFilterBit) variants,
|
||||
jniCallback->getHandler(), [jniCallback](Material*){
|
||||
JniCallback::postToJavaAndDestroy(jniCallback);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ Java_com_google_android_filament_MaterialInstance_nSetIntParameterArray(JNIEnv *
|
||||
break;
|
||||
}
|
||||
|
||||
env->ReleaseIntArrayElements(v_, v, 0);
|
||||
env->ReleaseIntArrayElements(v_, v, JNI_ABORT);
|
||||
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
@@ -98,3 +98,22 @@ Java_com_google_android_filament_Scene_nHasEntity(JNIEnv *env, jclass type, jlon
|
||||
Entity entity = Entity::import(entityId);
|
||||
return (jboolean) scene->hasEntity(entity);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_Scene_nGetEntities(JNIEnv *env, jclass ,
|
||||
jlong nativeScene, jintArray outArray, jint length) {
|
||||
Scene const* const scene = (Scene*) nativeScene;
|
||||
if (length < scene->getEntityCount()) {
|
||||
// should not happen because we already checked on the java side
|
||||
return JNI_FALSE;
|
||||
}
|
||||
jint *out = (jint *) env->GetIntArrayElements(outArray, nullptr);
|
||||
scene->forEach([out, length, i = 0](Entity entity)mutable {
|
||||
if (i < length) { // this is just paranoia here
|
||||
out[i++] = (jint) entity.getId();
|
||||
}
|
||||
});
|
||||
env->ReleaseIntArrayElements(outArray, (jint*) out, 0);
|
||||
return JNI_TRUE;
|
||||
}
|
||||
|
||||
@@ -47,6 +47,11 @@ Java_com_google_android_filament_ToneMapper_nCreateFilmicToneMapper(JNIEnv*, jcl
|
||||
return (jlong) new FilmicToneMapper();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_ToneMapper_nCreatePBRNeutralToneMapper(JNIEnv*, jclass) {
|
||||
return (jlong) new PBRNeutralToneMapper();
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_ToneMapper_nCreateAgxToneMapper(JNIEnv*, jclass, jint look) {
|
||||
return (jlong) new AgxToneMapper(AgxToneMapper::AgxLook(look));
|
||||
|
||||
@@ -480,6 +480,17 @@ Java_com_google_android_filament_View_nIsStencilBufferEnabled(JNIEnv *, jclass,
|
||||
return view->isStencilBufferEnabled();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_View_nSetStereoscopicOptions(JNIEnv *, jclass, jlong nativeView,
|
||||
jboolean enabled) {
|
||||
View* view = (View*) nativeView;
|
||||
View::StereoscopicOptions options {
|
||||
.enabled = (bool) enabled
|
||||
};
|
||||
view->setStereoscopicOptions(options);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_View_nSetGuardBandOptions(JNIEnv *, jclass,
|
||||
|
||||
@@ -111,6 +111,8 @@ public class Engine {
|
||||
|
||||
private long mNativeObject;
|
||||
|
||||
private Config mConfig;
|
||||
|
||||
@NonNull private final TransformManager mTransformManager;
|
||||
@NonNull private final LightManager mLightManager;
|
||||
@NonNull private final RenderableManager mRenderableManager;
|
||||
@@ -150,8 +152,20 @@ public class Engine {
|
||||
FEATURE_LEVEL_0,
|
||||
/** OpenGL ES 3.0 features (default) */
|
||||
FEATURE_LEVEL_1,
|
||||
/** OpenGL ES 3.1 features + 16 textures units + cubemap arrays */
|
||||
FEATURE_LEVEL_2,
|
||||
/** OpenGL ES 3.1 features + 31 textures units + cubemap arrays */
|
||||
FEATURE_LEVEL_2
|
||||
FEATURE_LEVEL_3,
|
||||
};
|
||||
|
||||
/**
|
||||
* The type of technique for stereoscopic rendering
|
||||
*/
|
||||
public enum StereoscopicType {
|
||||
/** Stereoscopic rendering is performed using instanced rendering technique. */
|
||||
INSTANCED,
|
||||
/** Stereoscopic rendering is performed using the multiview feature from the graphics backend. */
|
||||
MULTIVIEW,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -161,6 +175,7 @@ public class Engine {
|
||||
@SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"})
|
||||
private final BuilderFinalizer mFinalizer;
|
||||
private final long mNativeBuilder;
|
||||
private Config mConfig;
|
||||
|
||||
public Builder() {
|
||||
mNativeBuilder = nCreateBuilder();
|
||||
@@ -202,10 +217,25 @@ public class Engine {
|
||||
* @return A reference to this Builder for chaining calls.
|
||||
*/
|
||||
public Builder config(Config config) {
|
||||
mConfig = config;
|
||||
nSetBuilderConfig(mNativeBuilder, config.commandBufferSizeMB,
|
||||
config.perRenderPassArenaSizeMB, config.driverHandleArenaSizeMB,
|
||||
config.minCommandBufferSizeMB, config.perFrameCommandsSizeMB,
|
||||
config.jobSystemThreadCount);
|
||||
config.jobSystemThreadCount,
|
||||
config.textureUseAfterFreePoolSize, config.disableParallelShaderCompile,
|
||||
config.stereoscopicType.ordinal(), config.stereoscopicEyeCount,
|
||||
config.resourceAllocatorCacheSizeMB, config.resourceAllocatorCacheMaxAge);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the initial featureLevel for the Engine.
|
||||
*
|
||||
* @param featureLevel The feature level at which initialize Filament.
|
||||
* @return A reference to this Builder for chaining calls.
|
||||
*/
|
||||
public Builder featureLevel(FeatureLevel featureLevel) {
|
||||
nSetBuilderFeatureLevel(mNativeBuilder, featureLevel.ordinal());
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -222,7 +252,7 @@ public class Engine {
|
||||
public Engine build() {
|
||||
long nativeEngine = nBuilderBuild(mNativeBuilder);
|
||||
if (nativeEngine == 0) throw new IllegalStateException("Couldn't create Engine");
|
||||
return new Engine(nativeEngine);
|
||||
return new Engine(nativeEngine, mConfig);
|
||||
}
|
||||
|
||||
private static class BuilderFinalizer {
|
||||
@@ -330,14 +360,65 @@ public class Engine {
|
||||
* the number of threads to use.
|
||||
*/
|
||||
public long jobSystemThreadCount = 0;
|
||||
|
||||
/**
|
||||
* Number of most-recently destroyed textures to track for use-after-free.
|
||||
*
|
||||
* This will cause the backend to throw an exception when a texture is freed but still bound
|
||||
* to a SamplerGroup and used in a draw call. 0 disables completely.
|
||||
*
|
||||
* Currently only respected by the Metal backend.
|
||||
*/
|
||||
public long textureUseAfterFreePoolSize = 0;
|
||||
|
||||
/**
|
||||
* Set to `true` to forcibly disable parallel shader compilation in the backend.
|
||||
* Currently only honored by the GL backend.
|
||||
*/
|
||||
public boolean disableParallelShaderCompile = false;
|
||||
|
||||
/**
|
||||
* The type of technique for stereoscopic rendering.
|
||||
*
|
||||
* This setting determines the algorithm used when stereoscopic rendering is enabled. This
|
||||
* decision applies to the entire Engine for the lifetime of the Engine. E.g., multiple
|
||||
* Views created from the Engine must use the same stereoscopic type.
|
||||
*
|
||||
* Each view can enable stereoscopic rendering via the StereoscopicOptions::enable flag.
|
||||
*
|
||||
* @see View#setStereoscopicOptions
|
||||
*/
|
||||
public StereoscopicType stereoscopicType = StereoscopicType.INSTANCED;
|
||||
|
||||
/**
|
||||
* The number of eyes to render when stereoscopic rendering is enabled. Supported values are
|
||||
* between 1 and Engine#getMaxStereoscopicEyes() (inclusive).
|
||||
*
|
||||
* @see View#setStereoscopicOptions
|
||||
* @see Engine#getMaxStereoscopicEyes
|
||||
*/
|
||||
public long stereoscopicEyeCount = 2;
|
||||
|
||||
/*
|
||||
* Size in MiB of the frame graph texture cache. This should be adjusted based on the
|
||||
* size of used render targets (typically the screen).
|
||||
*/
|
||||
public long resourceAllocatorCacheSizeMB = 64;
|
||||
|
||||
/*
|
||||
* This value determines for how many frames are texture entries kept in the cache.
|
||||
* The default value of 30 corresponds to about half a second at 60 fps.
|
||||
*/
|
||||
public long resourceAllocatorCacheMaxAge = 30;
|
||||
}
|
||||
|
||||
private Engine(long nativeEngine) {
|
||||
private Engine(long nativeEngine, Config config) {
|
||||
mNativeObject = nativeEngine;
|
||||
mTransformManager = new TransformManager(nGetTransformManager(nativeEngine));
|
||||
mLightManager = new LightManager(nGetLightManager(nativeEngine));
|
||||
mRenderableManager = new RenderableManager(nGetRenderableManager(nativeEngine));
|
||||
mEntityManager = new EntityManager(nGetEntityManager(nativeEngine));
|
||||
mConfig = config;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -468,17 +549,23 @@ public class Engine {
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate all features of a given feature level. By default FeatureLevel::FEATURE_LEVEL_1 is
|
||||
* active. The selected feature level must not be higher than the value returned by
|
||||
* getActiveFeatureLevel() and it's not possible lower the active feature level.
|
||||
* Activate all features of a given feature level. If an explicit feature level is not specified
|
||||
* at Engine initialization time via {@link Builder#featureLevel}, the default feature level is
|
||||
* {@link FeatureLevel#FEATURE_LEVEL_0} on devices not compatible with GLES 3.0; otherwise, the
|
||||
* default is {@link FeatureLevel::FEATURE_LEVEL_1}. The selected feature level must not be
|
||||
* higher than the value returned by {@link #getActiveFeatureLevel} and it's not possible lower
|
||||
* the active feature level. Additionally, it is not possible to modify the feature level at all
|
||||
* if the Engine was initialized at {@link FeatureLevel#FEATURE_LEVEL_0}.
|
||||
*
|
||||
* @param featureLevel the feature level to activate. If featureLevel is lower than
|
||||
* getActiveFeatureLevel(), the current (higher) feature level is kept.
|
||||
* If featureLevel is higher than getSupportedFeatureLevel(), an exception
|
||||
* is thrown, or the program is terminated if exceptions are disabled.
|
||||
* @param featureLevel the feature level to activate. If featureLevel is lower than {@link
|
||||
* #getActiveFeatureLevel}, the current (higher) feature level is kept. If
|
||||
* featureLevel is higher than {@link #getSupportedFeatureLevel}, or if the
|
||||
* engine was initialized at feature level 0, an exception is thrown, or the
|
||||
* program is terminated if exceptions are disabled.
|
||||
*
|
||||
* @return the active feature level.
|
||||
*
|
||||
* @see Builder#featureLevel
|
||||
* @see #getSupportedFeatureLevel
|
||||
* @see #getActiveFeatureLevel
|
||||
*/
|
||||
@@ -524,6 +611,37 @@ public class Engine {
|
||||
return nIsAutomaticInstancingEnabled(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the configuration settings of this {@link Engine}.
|
||||
*
|
||||
* This method returns the configuration object that was supplied to the Engine's {@link
|
||||
* Builder#config} method during the creation of this Engine. If the {@link Builder::config}
|
||||
* method was not explicitly called (or called with null), this method returns the default
|
||||
* configuration settings.
|
||||
*
|
||||
* @return a {@link Config} object with this Engine's configuration
|
||||
* @see Builder#config
|
||||
*/
|
||||
@NonNull
|
||||
public Config getConfig() {
|
||||
if (mConfig == null) {
|
||||
mConfig = new Config();
|
||||
}
|
||||
return mConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of stereoscopic eyes supported by Filament. The actual number of
|
||||
* eyes rendered is set at Engine creation time with the {@link
|
||||
* Engine#Config#stereoscopicEyeCount} setting.
|
||||
*
|
||||
* @return the max number of stereoscopic eyes supported
|
||||
* @see Engine#Config#stereoscopicEyeCount
|
||||
*/
|
||||
public long getMaxStereoscopicEyes() {
|
||||
return nGetMaxStereoscopicEyes(getNativeObject());
|
||||
}
|
||||
|
||||
|
||||
// SwapChain
|
||||
|
||||
@@ -1060,6 +1178,18 @@ public class Engine {
|
||||
nFlushAndWait(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) but does not wait
|
||||
* for commands to be either executed or the hardware finished.
|
||||
*
|
||||
* <p>This is typically used after creating a lot of objects to start draining the command
|
||||
* queue which has a limited size.</p>
|
||||
*/
|
||||
public void flush() {
|
||||
nFlush(getNativeObject());
|
||||
}
|
||||
|
||||
|
||||
@UsedByReflection("TextureHelper.java")
|
||||
public long getNativeObject() {
|
||||
if (mNativeObject == 0) {
|
||||
@@ -1132,6 +1262,7 @@ public class Engine {
|
||||
private static native boolean nIsValidSwapChain(long nativeEngine, long nativeSwapChain);
|
||||
private static native void nDestroyEntity(long nativeEngine, int entity);
|
||||
private static native void nFlushAndWait(long nativeEngine);
|
||||
private static native void nFlush(long nativeEngine);
|
||||
private static native long nGetTransformManager(long nativeEngine);
|
||||
private static native long nGetLightManager(long nativeEngine);
|
||||
private static native long nGetRenderableManager(long nativeEngine);
|
||||
@@ -1139,6 +1270,7 @@ public class Engine {
|
||||
private static native long nGetEntityManager(long nativeEngine);
|
||||
private static native void nSetAutomaticInstancingEnabled(long nativeEngine, boolean enable);
|
||||
private static native boolean nIsAutomaticInstancingEnabled(long nativeEngine);
|
||||
private static native long nGetMaxStereoscopicEyes(long nativeEngine);
|
||||
private static native int nGetSupportedFeatureLevel(long nativeEngine);
|
||||
private static native int nSetActiveFeatureLevel(long nativeEngine, int ordinal);
|
||||
private static native int nGetActiveFeatureLevel(long nativeEngine);
|
||||
@@ -1148,7 +1280,11 @@ public class Engine {
|
||||
private static native void nSetBuilderBackend(long nativeBuilder, long backend);
|
||||
private static native void nSetBuilderConfig(long nativeBuilder, long commandBufferSizeMB,
|
||||
long perRenderPassArenaSizeMB, long driverHandleArenaSizeMB,
|
||||
long minCommandBufferSizeMB, long perFrameCommandsSizeMB, long jobSystemThreadCount);
|
||||
long minCommandBufferSizeMB, long perFrameCommandsSizeMB, long jobSystemThreadCount,
|
||||
long textureUseAfterFreePoolSize, boolean disableParallelShaderCompile,
|
||||
int stereoscopicType, long stereoscopicEyeCount,
|
||||
long resourceAllocatorCacheSizeMB, long resourceAllocatorCacheMaxAge);
|
||||
private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal);
|
||||
private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext);
|
||||
private static native long nBuilderBuild(long nativeBuilder);
|
||||
}
|
||||
|
||||
@@ -18,9 +18,11 @@ package com.google.android.filament;
|
||||
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.Size;
|
||||
|
||||
import com.google.android.filament.proguard.UsedByNative;
|
||||
import com.google.android.filament.Engine.FeatureLevel;
|
||||
|
||||
import java.nio.Buffer;
|
||||
import java.util.ArrayList;
|
||||
@@ -46,6 +48,8 @@ public class Material {
|
||||
static final BlendingMode[] sBlendingModeValues = BlendingMode.values();
|
||||
static final RefractionMode[] sRefractionModeValues = RefractionMode.values();
|
||||
static final RefractionType[] sRefractionTypeValues = RefractionType.values();
|
||||
static final ReflectionMode[] sReflectionModeValues = ReflectionMode.values();
|
||||
static final FeatureLevel[] sFeatureLevelValues = FeatureLevel.values();
|
||||
static final VertexDomain[] sVertexDomainValues = VertexDomain.values();
|
||||
static final CullingMode[] sCullingModeValues = CullingMode.values();
|
||||
static final VertexBuffer.VertexAttribute[] sVertexAttributeValues =
|
||||
@@ -181,6 +185,18 @@ public class Material {
|
||||
THIN
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported reflection modes
|
||||
*
|
||||
* @see
|
||||
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/lighting:reflections">
|
||||
* Lighting: reflections</a>
|
||||
*/
|
||||
public enum ReflectionMode {
|
||||
DEFAULT,
|
||||
SCREEN_SPACE
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported types of vertex domains
|
||||
*
|
||||
@@ -223,6 +239,31 @@ public class Material {
|
||||
FRONT_AND_BACK
|
||||
}
|
||||
|
||||
public enum CompilerPriorityQueue {
|
||||
HIGH,
|
||||
LOW
|
||||
}
|
||||
|
||||
public static class UserVariantFilterBit {
|
||||
/** Directional lighting */
|
||||
public static int DIRECTIONAL_LIGHTING = 0x01;
|
||||
/** Dynamic lighting */
|
||||
public static int DYNAMIC_LIGHTING = 0x02;
|
||||
/** Shadow receiver */
|
||||
public static int SHADOW_RECEIVER = 0x04;
|
||||
/** Skinning */
|
||||
public static int SKINNING = 0x08;
|
||||
/** Fog */
|
||||
public static int FOG = 0x10;
|
||||
/** Variance shadow maps */
|
||||
public static int VSM = 0x20;
|
||||
/** Screen-space reflections */
|
||||
public static int SSR = 0x40;
|
||||
/** Instanced stereo rendering */
|
||||
public static int STE = 0x80;
|
||||
public static int ALL = 0xFF;
|
||||
}
|
||||
|
||||
@UsedByNative("Material.cpp")
|
||||
public static class Parameter {
|
||||
private static final Type[] sTypeValues = Type.values();
|
||||
@@ -337,6 +378,55 @@ public class Material {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Asynchronously ensures that a subset of this Material's variants are compiled. After issuing
|
||||
* several compile() calls in a row, it is recommended to call {@link Engine#flush}
|
||||
* such that the backend can start the compilation work as soon as possible.
|
||||
* The provided callback is guaranteed to be called on the main thread after all specified
|
||||
* variants of the material are compiled. This can take hundreds of milliseconds.
|
||||
*<p>
|
||||
* If all the material's variants are already compiled, the callback will be scheduled as
|
||||
* soon as possible, but this might take a few dozen millisecond, corresponding to how
|
||||
* many previous frames are enqueued in the backend. This also varies by backend. Therefore,
|
||||
* it is recommended to only call this method once per material shortly after creation.
|
||||
*</p>
|
||||
*<p>
|
||||
* If the same variant is scheduled for compilation multiple times, the first scheduling
|
||||
* takes precedence; later scheduling are ignored.
|
||||
*</p>
|
||||
*<p>
|
||||
* caveat: A consequence is that if a variant is scheduled on the low priority queue and later
|
||||
* scheduled again on the high priority queue, the later scheduling is ignored.
|
||||
* Therefore, the second callback could be called before the variant is compiled.
|
||||
* However, the first callback, if specified, will trigger as expected.
|
||||
*</p>
|
||||
*<p>
|
||||
* The callback is guaranteed to be called. If the engine is destroyed while some material
|
||||
* variants are still compiling or in the queue, these will be discarded and the corresponding
|
||||
* callback will be called. In that case however the Material pointer passed to the callback
|
||||
* is guaranteed to be invalid (either because it's been destroyed by the user already, or,
|
||||
* because it's been cleaned-up by the Engine).
|
||||
*</p>
|
||||
*<p>
|
||||
* {@link UserVariantFilterBit#ALL} should be used with caution. Only variants that an application
|
||||
* needs should be included in the variants argument. For example, the STE variant is only used
|
||||
* for stereoscopic rendering. If an application is not planning to render in stereo, this bit
|
||||
* should be turned off to avoid unnecessary material compilations.
|
||||
*</p>
|
||||
* @param priority Which priority queue to use, LOW or HIGH.
|
||||
* @param variants Variants to include to the compile command.
|
||||
* @param handler An {@link java.util.concurrent.Executor Executor}. On Android this can also be a {@link android.os.Handler Handler}.
|
||||
* @param callback callback called on the main thread when the compilation is done on
|
||||
* by backend.
|
||||
*/
|
||||
public void compile(@NonNull CompilerPriorityQueue priority,
|
||||
int variants,
|
||||
@Nullable Object handler,
|
||||
@Nullable Runnable callback) {
|
||||
nCompile(getNativeObject(), priority.ordinal(), variants, handler, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of this material. Material instances should be freed using
|
||||
* {@link Engine#destroyMaterialInstance(MaterialInstance)}.
|
||||
@@ -437,6 +527,28 @@ public class Material {
|
||||
return EnumCache.sRefractionTypeValues[nGetRefractionType(getNativeObject())];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the reflection mode of this material.
|
||||
*
|
||||
* @see
|
||||
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/lighting:reflections">
|
||||
* Lighting: reflections</a>
|
||||
*/
|
||||
public ReflectionMode getReflectionMode() {
|
||||
return EnumCache.sReflectionModeValues[nGetReflectionMode(getNativeObject())];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum required feature level for this material.
|
||||
*
|
||||
* @see
|
||||
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/general:featurelevel">
|
||||
* General: featureLevel</a>
|
||||
*/
|
||||
public FeatureLevel getFeatureLevel() {
|
||||
return EnumCache.sFeatureLevelValues[nGetFeatureLevel(getNativeObject())];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the vertex domain of this material.
|
||||
*
|
||||
@@ -916,6 +1028,7 @@ public class Material {
|
||||
private static native long nCreateInstanceWithName(long nativeMaterial, @NonNull String name);
|
||||
private static native long nGetDefaultInstance(long nativeMaterial);
|
||||
|
||||
private static native void nCompile(long nativeMaterial, int priority, int variants, Object handler, Runnable runnable);
|
||||
private static native String nGetName(long nativeMaterial);
|
||||
private static native int nGetShading(long nativeMaterial);
|
||||
private static native int nGetInterpolation(long nativeMaterial);
|
||||
@@ -932,6 +1045,8 @@ public class Material {
|
||||
private static native float nGetSpecularAntiAliasingThreshold(long nativeMaterial);
|
||||
private static native int nGetRefractionMode(long nativeMaterial);
|
||||
private static native int nGetRefractionType(long nativeMaterial);
|
||||
private static native int nGetReflectionMode(long nativeMaterial);
|
||||
private static native int nGetFeatureLevel(long nativeMaterial);
|
||||
|
||||
|
||||
private static native int nGetParameterCount(long nativeMaterial);
|
||||
|
||||
@@ -101,7 +101,7 @@ public class Renderer {
|
||||
/**
|
||||
* Desired frame interval in unit of 1 / DisplayInfo.refreshRate.
|
||||
*/
|
||||
public float interval = 1.0f / 60.0f;
|
||||
public float interval = 1.0f;
|
||||
|
||||
/**
|
||||
* Additional headroom for the GPU as a ratio of the targetFrameTime.
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.google.android.filament;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
@@ -190,6 +191,52 @@ public class Scene {
|
||||
return mNativeObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all entities in the Scene. If outArray is provided and large enough,
|
||||
* it is used to store the list and returned, otherwise a new array is allocated and returned.
|
||||
* @param outArray an array to store the list of entities in the scene.
|
||||
* @return outArray if it was used or a newly allocated array.
|
||||
* @see #getEntityCount
|
||||
*/
|
||||
public int[] getEntities(@Nullable int[] outArray) {
|
||||
int c = getEntityCount();
|
||||
if (outArray == null || outArray.length < c) {
|
||||
outArray = new int[c];
|
||||
}
|
||||
boolean success = nGetEntities(getNativeObject(), outArray, outArray.length);
|
||||
if (!success) {
|
||||
throw new IllegalStateException("Error retriving Scene's entities");
|
||||
}
|
||||
return outArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all entities in the Scene in a newly allocated array.
|
||||
* @return an array containing the list of all entities in the scene.
|
||||
* @see #getEntityCount
|
||||
*/
|
||||
public int[] getEntities() {
|
||||
return getEntities(null);
|
||||
}
|
||||
|
||||
public interface EntityProcessor {
|
||||
void process(@Entity int entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes user functor on each entity in the scene.
|
||||
*
|
||||
* It is not allowed to add or remove an entity from the scene within the functor.
|
||||
*
|
||||
* @param entityProcessor User provided functor called for each entity in the scene
|
||||
*/
|
||||
public void forEach(@NonNull EntityProcessor entityProcessor) {
|
||||
int[] entities = getEntities(null);
|
||||
for (int entity : entities) {
|
||||
entityProcessor.process(entity);
|
||||
}
|
||||
}
|
||||
|
||||
void clearNativeObject() {
|
||||
mNativeObject = 0;
|
||||
}
|
||||
@@ -204,4 +251,5 @@ public class Scene {
|
||||
private static native int nGetRenderableCount(long nativeScene);
|
||||
private static native int nGetLightCount(long nativeScene);
|
||||
private static native boolean nHasEntity(long nativeScene, int entity);
|
||||
private static native boolean nGetEntities(long nativeScene, int[] outArray, int length);
|
||||
}
|
||||
|
||||
@@ -849,6 +849,10 @@ public class Texture {
|
||||
public static final int SAMPLEABLE = 0x10;
|
||||
/** Texture can be used as a subpass input */
|
||||
public static final int SUBPASS_INPUT = 0x20;
|
||||
/** Texture can be used the source of a blit() */
|
||||
public static final int BLIT_SRC = 0x40;
|
||||
/** Texture can be used the destination of a blit() */
|
||||
public static final int BLIT_DST = 0x80;
|
||||
/** by default textures are <code>UPLOADABLE</code> and <code>SAMPLEABLE</code>*/
|
||||
public static final int DEFAULT = UPLOADABLE | SAMPLEABLE;
|
||||
}
|
||||
|
||||
@@ -16,12 +16,14 @@ package com.google.android.filament;
|
||||
* <li>Configurable tone mapping operators</li>
|
||||
* <ul>
|
||||
* <li>GenericToneMapper</li>
|
||||
* <li>AgXToneMapper</li>
|
||||
* </ul>
|
||||
* <li>Fixed-aesthetic tone mapping operators</li>
|
||||
* <ul>
|
||||
* <li>ACESToneMapper</li>
|
||||
* <li>ACESLegacyToneMapper</li>
|
||||
* <li>FilmicToneMapper</li>
|
||||
* <li>PBRNeutralToneMapper</li>
|
||||
* </ul>
|
||||
* <li>Debug/validation tone mapping operators</li>
|
||||
* <ul>
|
||||
@@ -100,11 +102,21 @@ public class ToneMapper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Khronos PBR Neutral tone mapping operator. This tone mapper was designed
|
||||
* to preserve the appearance of materials across lighting conditions while
|
||||
* avoiding artifacts in the highlights in high dynamic range conditions.
|
||||
*/
|
||||
public static class PBRNeutralToneMapper extends ToneMapper {
|
||||
public PBRNeutralToneMapper() {
|
||||
super(nCreatePBRNeutralToneMapper());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AgX tone mapping operator.
|
||||
*/
|
||||
public static class Agx extends ToneMapper {
|
||||
|
||||
public enum AgxLook {
|
||||
/**
|
||||
* Base contrast with no look applied
|
||||
@@ -233,6 +245,7 @@ public class ToneMapper {
|
||||
private static native long nCreateACESToneMapper();
|
||||
private static native long nCreateACESLegacyToneMapper();
|
||||
private static native long nCreateFilmicToneMapper();
|
||||
private static native long nCreatePBRNeutralToneMapper();
|
||||
private static native long nCreateAgxToneMapper(int look);
|
||||
private static native long nCreateGenericToneMapper(
|
||||
float contrast, float midGrayIn, float midGrayOut, float hdrMax);
|
||||
|
||||
@@ -75,6 +75,7 @@ public class View {
|
||||
private AmbientOcclusionOptions mAmbientOcclusionOptions;
|
||||
private BloomOptions mBloomOptions;
|
||||
private FogOptions mFogOptions;
|
||||
private StereoscopicOptions mStereoscopicOptions;
|
||||
private RenderTarget mRenderTarget;
|
||||
private BlendMode mBlendMode;
|
||||
private DepthOfFieldOptions mDepthOfFieldOptions;
|
||||
@@ -1055,6 +1056,51 @@ public class View {
|
||||
return nIsStencilBufferEnabled(getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the stereoscopic rendering options for this view.
|
||||
*
|
||||
* <p>
|
||||
* Currently, only one type of stereoscopic rendering is supported: side-by-side.
|
||||
* Side-by-side stereo rendering splits the viewport into two halves: a left and right half.
|
||||
* Eye 0 will render to the left half, while Eye 1 will render into the right half.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Currently, the following features are not supported with stereoscopic rendering:
|
||||
* - post-processing
|
||||
* - shadowing
|
||||
* - punctual lights
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Stereo rendering depends on device and platform support. To check if stereo rendering is
|
||||
* supported, use {@link Engine#isStereoSupported()}. If stereo rendering is not supported, then
|
||||
* the stereoscopic options have no effect.
|
||||
* </p>
|
||||
*
|
||||
* @param options The stereoscopic options to use on this view
|
||||
* @see #getStereoscopicOptions
|
||||
*/
|
||||
public void setStereoscopicOptions(@NonNull StereoscopicOptions options) {
|
||||
mStereoscopicOptions = options;
|
||||
nSetStereoscopicOptions(getNativeObject(), options.enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stereoscopic options.
|
||||
*
|
||||
* @return options Stereoscopic options currently set.
|
||||
* @see #setStereoscopicOptions
|
||||
*/
|
||||
@NonNull
|
||||
public StereoscopicOptions getStereoscopicOptions() {
|
||||
if (mStereoscopicOptions == null) {
|
||||
mStereoscopicOptions = new StereoscopicOptions();
|
||||
}
|
||||
return mStereoscopicOptions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A class containing the result of a picking query
|
||||
*/
|
||||
@@ -1220,6 +1266,7 @@ public class View {
|
||||
private static native void nSetBloomOptions(long nativeView, long dirtNativeObject, float dirtStrength, float strength, int resolution, int levels, int blendMode, boolean threshold, boolean enabled, float highlight,
|
||||
boolean lensFlare, boolean starburst, float chromaticAberration, int ghostCount, float ghostSpacing, float ghostThreshold, float haloThickness, float haloRadius, float haloThreshold);
|
||||
private static native void nSetFogOptions(long nativeView, float distance, float maximumOpacity, float height, float heightFalloff, float cutOffDistance, float v, float v1, float v2, float density, float inScatteringStart, float inScatteringSize, boolean fogColorFromIbl, long skyColorNativeObject, boolean enabled);
|
||||
private static native void nSetStereoscopicOptions(long nativeView, boolean enabled);
|
||||
private static native void nSetBlendMode(long nativeView, int blendMode);
|
||||
private static native void nSetDepthOfFieldOptions(long nativeView, float cocScale, float maxApertureDiameter, boolean enabled, int filter,
|
||||
boolean nativeResolution, int foregroundRingCount, int backgroundRingCount, int fastGatherRingCount, int maxForegroundCOC, int maxBackgroundCOC);
|
||||
@@ -1590,6 +1637,10 @@ public class View {
|
||||
* circle of confusion scale factor (amount of blur)
|
||||
*/
|
||||
public float cocScale = 1.0f;
|
||||
/**
|
||||
* width/height aspect ratio of the circle of confusion (simulate anamorphic lenses)
|
||||
*/
|
||||
public float cocAspectRatio = 1.0f;
|
||||
/**
|
||||
* maximum aperture diameter in meters (zero to disable rotation)
|
||||
*/
|
||||
@@ -1805,7 +1856,7 @@ public class View {
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for Temporal Multi-Sample Anti-aliasing (MSAA)
|
||||
* Options for Multi-Sample Anti-aliasing (MSAA)
|
||||
* @see setMultiSampleAntiAliasingOptions()
|
||||
*/
|
||||
public static class MultiSampleAntiAliasingOptions {
|
||||
@@ -1829,21 +1880,111 @@ public class View {
|
||||
|
||||
/**
|
||||
* Options for Temporal Anti-aliasing (TAA)
|
||||
* Most TAA parameters are extremely costly to change, as they will trigger the TAA post-process
|
||||
* shaders to be recompiled. These options should be changed or set during initialization.
|
||||
* `filterWidth`, `feedback` and `jitterPattern`, however, can be changed at any time.
|
||||
*
|
||||
* `feedback` of 0.1 effectively accumulates a maximum of 19 samples in steady state.
|
||||
* see "A Survey of Temporal Antialiasing Techniques" by Lei Yang and all for more information.
|
||||
*
|
||||
* @see setTemporalAntiAliasingOptions()
|
||||
*/
|
||||
public static class TemporalAntiAliasingOptions {
|
||||
public enum BoxType {
|
||||
/**
|
||||
* use an AABB neighborhood
|
||||
*/
|
||||
AABB,
|
||||
/**
|
||||
* use the variance of the neighborhood (not recommended)
|
||||
*/
|
||||
VARIANCE,
|
||||
/**
|
||||
* use both AABB and variance
|
||||
*/
|
||||
AABB_VARIANCE,
|
||||
}
|
||||
|
||||
public enum BoxClipping {
|
||||
/**
|
||||
* Accurate box clipping
|
||||
*/
|
||||
ACCURATE,
|
||||
/**
|
||||
* clamping
|
||||
*/
|
||||
CLAMP,
|
||||
/**
|
||||
* no rejections (use for debugging)
|
||||
*/
|
||||
NONE,
|
||||
}
|
||||
|
||||
public enum JitterPattern {
|
||||
RGSS_X4,
|
||||
UNIFORM_HELIX_X4,
|
||||
HALTON_23_X8,
|
||||
HALTON_23_X16,
|
||||
HALTON_23_X32,
|
||||
}
|
||||
|
||||
/**
|
||||
* reconstruction filter width typically between 0 (sharper, aliased) and 1 (smoother)
|
||||
* reconstruction filter width typically between 0.2 (sharper, aliased) and 1.5 (smoother)
|
||||
*/
|
||||
public float filterWidth = 1.0f;
|
||||
/**
|
||||
* history feedback, between 0 (maximum temporal AA) and 1 (no temporal AA).
|
||||
*/
|
||||
public float feedback = 0.04f;
|
||||
public float feedback = 0.12f;
|
||||
/**
|
||||
* texturing lod bias (typically -1 or -2)
|
||||
*/
|
||||
public float lodBias = -1.0f;
|
||||
/**
|
||||
* post-TAA sharpen, especially useful when upscaling is true.
|
||||
*/
|
||||
public float sharpness = 0.0f;
|
||||
/**
|
||||
* enables or disables temporal anti-aliasing
|
||||
*/
|
||||
public boolean enabled = false;
|
||||
/**
|
||||
* 4x TAA upscaling. Disables Dynamic Resolution. [BETA]
|
||||
*/
|
||||
public boolean upscaling = false;
|
||||
/**
|
||||
* whether to filter the history buffer
|
||||
*/
|
||||
public boolean filterHistory = true;
|
||||
/**
|
||||
* whether to apply the reconstruction filter to the input
|
||||
*/
|
||||
public boolean filterInput = true;
|
||||
/**
|
||||
* whether to use the YcoCg color-space for history rejection
|
||||
*/
|
||||
public boolean useYCoCg = false;
|
||||
/**
|
||||
* type of color gamut box
|
||||
*/
|
||||
@NonNull
|
||||
public TemporalAntiAliasingOptions.BoxType boxType = TemporalAntiAliasingOptions.BoxType.AABB;
|
||||
/**
|
||||
* clipping algorithm
|
||||
*/
|
||||
@NonNull
|
||||
public TemporalAntiAliasingOptions.BoxClipping boxClipping = TemporalAntiAliasingOptions.BoxClipping.ACCURATE;
|
||||
@NonNull
|
||||
public TemporalAntiAliasingOptions.JitterPattern jitterPattern = TemporalAntiAliasingOptions.JitterPattern.HALTON_23_X16;
|
||||
public float varianceGamma = 1.0f;
|
||||
/**
|
||||
* adjust the feedback dynamically to reduce flickering
|
||||
*/
|
||||
public boolean preventFlickering = false;
|
||||
/**
|
||||
* whether to apply history reprojection (debug option)
|
||||
*/
|
||||
public boolean historyReprojection = true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -182,28 +182,85 @@ public class UiHelper {
|
||||
void detach();
|
||||
}
|
||||
|
||||
private static class SurfaceViewHandler implements RenderSurface {
|
||||
private final SurfaceView mSurfaceView;
|
||||
private class SurfaceViewHandler implements RenderSurface, SurfaceHolder.Callback {
|
||||
@NonNull private final SurfaceView mSurfaceView;
|
||||
|
||||
SurfaceViewHandler(SurfaceView surface) {
|
||||
mSurfaceView = surface;
|
||||
SurfaceViewHandler(@NonNull SurfaceView surfaceView) {
|
||||
mSurfaceView = surfaceView;
|
||||
|
||||
@NonNull SurfaceHolder holder = surfaceView.getHolder();
|
||||
holder.addCallback(this);
|
||||
|
||||
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
|
||||
holder.setFixedSize(mDesiredWidth, mDesiredHeight);
|
||||
}
|
||||
|
||||
// in case the SurfaceView's surface already existed
|
||||
final Surface surface = holder.getSurface();
|
||||
if (surface != null && surface.isValid()) {
|
||||
surfaceCreated(holder);
|
||||
// there is no way to retrieve the actual PixelFormat, since it is not used
|
||||
// in the callback, we can use whatever we want.
|
||||
surfaceChanged(holder, PixelFormat.RGBA_8888,
|
||||
holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height) {
|
||||
mSurfaceView.getHolder().setFixedSize(width, height);
|
||||
@NonNull SurfaceHolder holder = mSurfaceView.getHolder();
|
||||
holder.setFixedSize(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detach() {
|
||||
@NonNull SurfaceHolder holder = mSurfaceView.getHolder();
|
||||
holder.removeCallback(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(@NonNull SurfaceHolder holder) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
|
||||
createSwapChain(holder.getSurface());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(
|
||||
@NonNull SurfaceHolder holder, int format, int width, int height) {
|
||||
// Note: this is always called at least once after surfaceCreated()
|
||||
if (LOGGING) Log.d(LOG_TAG, "surfaceChanged(" + width + ", " + height + ")");
|
||||
if (mRenderCallback != null) {
|
||||
mRenderCallback.onResized(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
|
||||
destroySwapChain();
|
||||
}
|
||||
}
|
||||
|
||||
private static class SurfaceHolderHandler implements RenderSurface {
|
||||
private class SurfaceHolderHandler implements RenderSurface, SurfaceHolder.Callback {
|
||||
private final SurfaceHolder mSurfaceHolder;
|
||||
|
||||
SurfaceHolderHandler(SurfaceHolder surface) {
|
||||
mSurfaceHolder = surface;
|
||||
SurfaceHolderHandler(@NonNull SurfaceHolder holder) {
|
||||
mSurfaceHolder = holder;
|
||||
holder.addCallback(this);
|
||||
|
||||
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
|
||||
holder.setFixedSize(mDesiredWidth, mDesiredHeight);
|
||||
}
|
||||
|
||||
// in case the SurfaceHolder's surface already existed
|
||||
final Surface surface = holder.getSurface();
|
||||
if (surface != null && surface.isValid()) {
|
||||
surfaceCreated(holder);
|
||||
// there is no way to retrieve the actual PixelFormat, since it is not used
|
||||
// in the callback, we can use whatever we want.
|
||||
surfaceChanged(holder, PixelFormat.RGBA_8888,
|
||||
holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -213,30 +270,127 @@ public class UiHelper {
|
||||
|
||||
@Override
|
||||
public void detach() {
|
||||
mSurfaceHolder.removeCallback(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceCreated(@NonNull SurfaceHolder holder) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
|
||||
createSwapChain(holder.getSurface());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
|
||||
// Note: this is always called at least once after surfaceCreated()
|
||||
if (LOGGING) Log.d(LOG_TAG, "surfaceChanged(" + width + ", " + height + ")");
|
||||
if (mRenderCallback != null) {
|
||||
mRenderCallback.onResized(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
|
||||
destroySwapChain();
|
||||
}
|
||||
}
|
||||
|
||||
private class TextureViewHandler implements RenderSurface {
|
||||
private class TextureViewHandler implements RenderSurface, TextureView.SurfaceTextureListener {
|
||||
private final TextureView mTextureView;
|
||||
private Surface mSurface;
|
||||
|
||||
TextureViewHandler(TextureView surface) { mTextureView = surface; }
|
||||
TextureViewHandler(@NonNull TextureView view) {
|
||||
mTextureView = view;
|
||||
mTextureView.setSurfaceTextureListener(this);
|
||||
// in case the View's SurfaceTexture already existed
|
||||
if (view.isAvailable()) {
|
||||
SurfaceTexture surfaceTexture = view.getSurfaceTexture();
|
||||
if (surfaceTexture != null) {
|
||||
this.onSurfaceTextureAvailable(surfaceTexture,
|
||||
mDesiredWidth, mDesiredHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resize(int width, int height) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
|
||||
mTextureView.getSurfaceTexture().setDefaultBufferSize(width, height);
|
||||
final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
|
||||
if (surfaceTexture != null) {
|
||||
surfaceTexture.setDefaultBufferSize(width, height);
|
||||
}
|
||||
}
|
||||
if (mRenderCallback != null) {
|
||||
// the call above won't cause TextureView.onSurfaceTextureSizeChanged()
|
||||
mRenderCallback.onResized(width, height);
|
||||
}
|
||||
// the call above won't cause TextureView.onSurfaceTextureSizeChanged()
|
||||
mRenderCallback.onResized(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void detach() {
|
||||
setSurface(null);
|
||||
mTextureView.setSurfaceTextureListener(null);
|
||||
}
|
||||
|
||||
void setSurface(Surface surface) {
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureAvailable(
|
||||
@NonNull SurfaceTexture surfaceTexture, int width, int height) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureAvailable()");
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
|
||||
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
|
||||
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
|
||||
}
|
||||
}
|
||||
|
||||
final Surface surface = new Surface(surfaceTexture);
|
||||
setSurface(surface);
|
||||
createSwapChain(surface);
|
||||
|
||||
if (mRenderCallback != null) {
|
||||
// Call this the first time because onSurfaceTextureSizeChanged()
|
||||
// isn't called at initialization time
|
||||
mRenderCallback.onResized(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureSizeChanged(
|
||||
@NonNull SurfaceTexture surfaceTexture, int width, int height) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureSizeChanged()");
|
||||
if (mRenderCallback != null) {
|
||||
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
|
||||
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
|
||||
mRenderCallback.onResized(mDesiredWidth, mDesiredHeight);
|
||||
} else {
|
||||
mRenderCallback.onResized(width, height);
|
||||
}
|
||||
// We must recreate the SwapChain to guarantee that it sees the new size.
|
||||
// More precisely, for an EGL client, the EGLSurface must be recreated. For
|
||||
// a Vulkan client, the SwapChain must be recreated. Calling
|
||||
// onNativeWindowChanged() will accomplish that.
|
||||
// This requirement comes from SurfaceTexture.setDefaultBufferSize()
|
||||
// documentation.
|
||||
final Surface surface = getSurface();
|
||||
if (surface != null) {
|
||||
mRenderCallback.onNativeWindowChanged(surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureDestroyed()");
|
||||
setSurface(null);
|
||||
destroySwapChain();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) { }
|
||||
|
||||
|
||||
private void setSurface(@Nullable Surface surface) {
|
||||
if (surface == null) {
|
||||
if (mSurface != null) {
|
||||
mSurface.release();
|
||||
@@ -245,7 +399,7 @@ public class UiHelper {
|
||||
mSurface = surface;
|
||||
}
|
||||
|
||||
public Surface getSurface() {
|
||||
private Surface getSurface() {
|
||||
return mSurface;
|
||||
}
|
||||
}
|
||||
@@ -291,6 +445,9 @@ public class UiHelper {
|
||||
* {@link #attachTo(TextureView)}, or {@link #attachTo(SurfaceHolder)}.
|
||||
*/
|
||||
public void detach() {
|
||||
if (mRenderSurface != null) {
|
||||
mRenderSurface.detach();
|
||||
}
|
||||
destroySwapChain();
|
||||
mNativeWindow = null;
|
||||
mRenderSurface = null;
|
||||
@@ -298,7 +455,6 @@ public class UiHelper {
|
||||
|
||||
/**
|
||||
* Checks whether we are ready to render into the attached surface.
|
||||
*
|
||||
* Using OpenGL ES when this returns true, will result in drawing commands being lost,
|
||||
* HOWEVER, GLES state will be preserved. This is useful to initialize the engine.
|
||||
*
|
||||
@@ -343,7 +499,6 @@ public class UiHelper {
|
||||
/**
|
||||
* Controls whether the render target (SurfaceView or TextureView) is opaque or not.
|
||||
* The render target is considered opaque by default.
|
||||
*
|
||||
* Must be called before calling {@link #attachTo(SurfaceView)}, {@link #attachTo(TextureView)},
|
||||
* or {@link #attachTo(SurfaceHolder)}.
|
||||
*
|
||||
@@ -366,10 +521,8 @@ public class UiHelper {
|
||||
* positioned above other surfaces but below the activity's surface. This property
|
||||
* only has an effect when used in combination with {@link #setOpaque(boolean) setOpaque(false)}
|
||||
* and does not affect TextureView targets.
|
||||
*
|
||||
* Must be called before calling {@link #attachTo(SurfaceView)}
|
||||
* or {@link #attachTo(TextureView)}.
|
||||
*
|
||||
* Has no effect when using {@link #attachTo(SurfaceHolder)}.
|
||||
*
|
||||
* @param overlay Indicates whether the render target should be rendered below the activity's
|
||||
@@ -390,7 +543,6 @@ public class UiHelper {
|
||||
|
||||
/**
|
||||
* Associate UiHelper with a SurfaceView.
|
||||
*
|
||||
* As soon as SurfaceView is ready (i.e. has a Surface), we'll create the
|
||||
* EGL resources needed, and call user callbacks if needed.
|
||||
*/
|
||||
@@ -405,171 +557,32 @@ public class UiHelper {
|
||||
view.setZOrderOnTop(translucent);
|
||||
}
|
||||
|
||||
int format = isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;
|
||||
view.getHolder().setFormat(format);
|
||||
|
||||
view.getHolder().setFormat(isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT);
|
||||
mRenderSurface = new SurfaceViewHandler(view);
|
||||
|
||||
final SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder holder) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
|
||||
createSwapChain(holder.getSurface());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(
|
||||
SurfaceHolder holder, int format, int width, int height) {
|
||||
// Note: this is always called at least once after surfaceCreated()
|
||||
if (LOGGING) Log.d(LOG_TAG, "surfaceChanged(" + width + ", " + height + ")");
|
||||
mRenderCallback.onResized(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder holder) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
|
||||
destroySwapChain();
|
||||
}
|
||||
};
|
||||
|
||||
SurfaceHolder holder = view.getHolder();
|
||||
holder.addCallback(callback);
|
||||
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
|
||||
holder.setFixedSize(mDesiredWidth, mDesiredHeight);
|
||||
}
|
||||
|
||||
// in case the SurfaceView's surface already existed
|
||||
final Surface surface = holder.getSurface();
|
||||
if (surface != null && surface.isValid()) {
|
||||
callback.surfaceCreated(holder);
|
||||
callback.surfaceChanged(holder, format,
|
||||
holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate UiHelper with a TextureView.
|
||||
*
|
||||
* As soon as TextureView is ready (i.e. has a buffer), we'll create the
|
||||
* EGL resources needed, and call user callbacks if needed.
|
||||
*/
|
||||
public void attachTo(@NonNull TextureView view) {
|
||||
if (attach(view)) {
|
||||
view.setOpaque(isOpaque());
|
||||
|
||||
mRenderSurface = new TextureViewHandler(view);
|
||||
|
||||
TextureView.SurfaceTextureListener listener = new TextureView.SurfaceTextureListener() {
|
||||
@Override
|
||||
public void onSurfaceTextureAvailable(
|
||||
SurfaceTexture surfaceTexture, int width, int height) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureAvailable()");
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
|
||||
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
|
||||
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
|
||||
}
|
||||
}
|
||||
|
||||
Surface surface = new Surface(surfaceTexture);
|
||||
TextureViewHandler textureViewHandler = (TextureViewHandler) mRenderSurface;
|
||||
textureViewHandler.setSurface(surface);
|
||||
|
||||
createSwapChain(surface);
|
||||
|
||||
// Call this the first time because onSurfaceTextureSizeChanged()
|
||||
// isn't called at initialization time
|
||||
mRenderCallback.onResized(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureSizeChanged(
|
||||
SurfaceTexture surfaceTexture, int width, int height) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureSizeChanged()");
|
||||
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
|
||||
surfaceTexture.setDefaultBufferSize(mDesiredWidth, mDesiredHeight);
|
||||
mRenderCallback.onResized(mDesiredWidth, mDesiredHeight);
|
||||
} else {
|
||||
mRenderCallback.onResized(width, height);
|
||||
}
|
||||
// We must recreate the SwapChain to guarantee that it sees the new size.
|
||||
// More precisely, for an EGL client, the EGLSurface must be recreated. For
|
||||
// a Vulkan client, the SwapChain must be recreated. Calling
|
||||
// onNativeWindowChanged() will accomplish that.
|
||||
// This requirement comes from SurfaceTexture.setDefaultBufferSize()
|
||||
// documentation.
|
||||
TextureViewHandler textureViewHandler = (TextureViewHandler) mRenderSurface;
|
||||
mRenderCallback.onNativeWindowChanged(textureViewHandler.getSurface());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "onSurfaceTextureDestroyed()");
|
||||
destroySwapChain();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureUpdated(SurfaceTexture surface) { }
|
||||
};
|
||||
|
||||
view.setSurfaceTextureListener(listener);
|
||||
|
||||
// in case the View's SurfaceTexture already existed
|
||||
if (view.isAvailable()) {
|
||||
SurfaceTexture surfaceTexture = view.getSurfaceTexture();
|
||||
listener.onSurfaceTextureAvailable(surfaceTexture, mDesiredWidth, mDesiredHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate UiHelper with a SurfaceHolder.
|
||||
*
|
||||
* As soon as a Surface is created, we'll create the
|
||||
* EGL resources needed, and call user callbacks if needed.
|
||||
*/
|
||||
public void attachTo(@NonNull SurfaceHolder holder) {
|
||||
if (attach(holder)) {
|
||||
int format = isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT;
|
||||
holder.setFormat(format);
|
||||
|
||||
holder.setFormat(isOpaque() ? PixelFormat.OPAQUE : PixelFormat.TRANSLUCENT);
|
||||
mRenderSurface = new SurfaceHolderHandler(holder);
|
||||
|
||||
final SurfaceHolder.Callback callback = new SurfaceHolder.Callback() {
|
||||
@Override
|
||||
public void surfaceCreated(SurfaceHolder surfaceHolder) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "surfaceCreated()");
|
||||
createSwapChain(holder.getSurface());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
|
||||
// Note: this is always called at least once after surfaceCreated()
|
||||
if (LOGGING) Log.d(LOG_TAG, "surfaceChanged(" + width + ", " + height + ")");
|
||||
mRenderCallback.onResized(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
|
||||
if (LOGGING) Log.d(LOG_TAG, "surfaceDestroyed()");
|
||||
destroySwapChain();
|
||||
}
|
||||
};
|
||||
|
||||
holder.addCallback(callback);
|
||||
if (mDesiredWidth > 0 && mDesiredHeight > 0) {
|
||||
holder.setFixedSize(mDesiredWidth, mDesiredHeight);
|
||||
}
|
||||
|
||||
// in case the SurfaceHolder's surface already existed
|
||||
final Surface surface = holder.getSurface();
|
||||
if (surface != null && surface.isValid()) {
|
||||
callback.surfaceCreated(holder);
|
||||
callback.surfaceChanged(holder, format,
|
||||
holder.getSurfaceFrame().width(), holder.getSurfaceFrame().height());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -580,6 +593,10 @@ public class UiHelper {
|
||||
// nothing to do
|
||||
return false;
|
||||
}
|
||||
if (mRenderSurface != null) {
|
||||
mRenderSurface.detach();
|
||||
mRenderSurface = null;
|
||||
}
|
||||
destroySwapChain();
|
||||
}
|
||||
mNativeWindow = nativeWindow;
|
||||
@@ -587,15 +604,16 @@ public class UiHelper {
|
||||
}
|
||||
|
||||
private void createSwapChain(@NonNull Surface surface) {
|
||||
mRenderCallback.onNativeWindowChanged(surface);
|
||||
if (mRenderCallback != null) {
|
||||
mRenderCallback.onNativeWindowChanged(surface);
|
||||
}
|
||||
mHasSwapChain = true;
|
||||
}
|
||||
|
||||
private void destroySwapChain() {
|
||||
if (mRenderSurface != null) {
|
||||
mRenderSurface.detach();
|
||||
if (mRenderCallback != null) {
|
||||
mRenderCallback.onDetachedFromSurface();
|
||||
}
|
||||
mRenderCallback.onDetachedFromSurface();
|
||||
mHasSwapChain = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ public class FilamentInstance {
|
||||
*
|
||||
* Ignored if variantIndex is out of bounds.
|
||||
*/
|
||||
void applyMaterialVariant(@IntRange(from = 0) int variantIndex) {
|
||||
public void applyMaterialVariant(@IntRange(from = 0) int variantIndex) {
|
||||
nApplyMaterialVariant(mNativeObject, variantIndex);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.45.1
|
||||
VERSION_NAME=1.50.5
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#Wed Nov 17 10:40:18 PST 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
||||
@@ -30,7 +30,7 @@ android {
|
||||
compileSdkVersion versions.compileSdk
|
||||
defaultConfig {
|
||||
applicationId "com.google.android.filament.gltf"
|
||||
minSdkVersion 19
|
||||
minSdkVersion versions.minSdk
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import com.google.android.filament.Fence
|
||||
import com.google.android.filament.IndirectLight
|
||||
import com.google.android.filament.Material
|
||||
import com.google.android.filament.Skybox
|
||||
import com.google.android.filament.View
|
||||
import com.google.android.filament.View.OnPickCallback
|
||||
@@ -392,6 +393,36 @@ class MainActivity : Activity() {
|
||||
Log.i(TAG, "The Filament backend took $total ms to load the model geometry.")
|
||||
modelViewer.engine.destroyFence(it)
|
||||
loadStartFence = null
|
||||
|
||||
val materials = mutableSetOf<Material>()
|
||||
val rcm = modelViewer.engine.renderableManager
|
||||
modelViewer.scene.forEach {
|
||||
val entity = it
|
||||
if (rcm.hasComponent(entity)) {
|
||||
val ri = rcm.getInstance(entity)
|
||||
val c = rcm.getPrimitiveCount(ri)
|
||||
for (i in 0 until c) {
|
||||
val mi = rcm.getMaterialInstanceAt(ri, i)
|
||||
val ma = mi.material
|
||||
materials.add(ma)
|
||||
}
|
||||
}
|
||||
}
|
||||
materials.forEach {
|
||||
it.compile(
|
||||
Material.CompilerPriorityQueue.HIGH,
|
||||
Material.UserVariantFilterBit.DIRECTIONAL_LIGHTING or
|
||||
Material.UserVariantFilterBit.DYNAMIC_LIGHTING or
|
||||
Material.UserVariantFilterBit.SHADOW_RECEIVER,
|
||||
null, null)
|
||||
it.compile(
|
||||
Material.CompilerPriorityQueue.LOW,
|
||||
Material.UserVariantFilterBit.FOG or
|
||||
Material.UserVariantFilterBit.SKINNING or
|
||||
Material.UserVariantFilterBit.SSR or
|
||||
Material.UserVariantFilterBit.VSM,
|
||||
null, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ import android.animation.ValueAnimator
|
||||
import android.app.Activity
|
||||
import android.opengl.Matrix
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.Choreographer
|
||||
import android.view.Surface
|
||||
import android.view.SurfaceView
|
||||
@@ -110,7 +112,7 @@ class MainActivity : Activity() {
|
||||
}
|
||||
|
||||
private fun setupFilament() {
|
||||
engine = Engine.create()
|
||||
engine = Engine.Builder().featureLevel(Engine.FeatureLevel.FEATURE_LEVEL_0).build()
|
||||
renderer = engine.createRenderer()
|
||||
scene = engine.createScene()
|
||||
view = engine.createView()
|
||||
@@ -120,13 +122,8 @@ class MainActivity : Activity() {
|
||||
private fun setupView() {
|
||||
scene.skybox = Skybox.Builder().color(0.035f, 0.035f, 0.035f, 1.0f).build(engine)
|
||||
|
||||
if (engine.activeFeatureLevel == Engine.FeatureLevel.FEATURE_LEVEL_0) {
|
||||
// post-processing is not supported at feature level 0
|
||||
view.isPostProcessingEnabled = false
|
||||
} else {
|
||||
// NOTE: Try to disable post-processing (tone-mapping, etc.) to see the difference
|
||||
// view.isPostProcessingEnabled = false
|
||||
}
|
||||
// post-processing is not supported at feature level 0
|
||||
view.isPostProcessingEnabled = false
|
||||
|
||||
// Tell the view which camera we want to use
|
||||
view.camera = camera
|
||||
@@ -162,6 +159,14 @@ class MainActivity : Activity() {
|
||||
private fun loadMaterial() {
|
||||
readUncompressedAsset("materials/baked_color.filamat").let {
|
||||
material = Material.Builder().payload(it, it.remaining()).build(engine)
|
||||
material.compile(
|
||||
Material.CompilerPriorityQueue.HIGH,
|
||||
Material.UserVariantFilterBit.ALL,
|
||||
Handler(Looper.getMainLooper())) {
|
||||
android.util.Log.i("hellotriangle",
|
||||
"Material " + material.name + " compiled.")
|
||||
}
|
||||
engine.flush()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
9
build.sh
9
build.sh
@@ -191,6 +191,7 @@ function build_clean {
|
||||
rm -Rf android/filamat-android/build android/filamat-android/.externalNativeBuild android/filamat-android/.cxx
|
||||
rm -Rf android/gltfio-android/build android/gltfio-android/.externalNativeBuild android/gltfio-android/.cxx
|
||||
rm -Rf android/filament-utils-android/build android/filament-utils-android/.externalNativeBuild android/filament-utils-android/.cxx
|
||||
rm -f compile_commands.json
|
||||
}
|
||||
|
||||
function build_clean_aggressive {
|
||||
@@ -232,6 +233,8 @@ function build_desktop_target {
|
||||
${ASAN_UBSAN_OPTION} \
|
||||
${architectures} \
|
||||
../..
|
||||
ln -sf "out/cmake-${lc_target}/compile_commands.json" \
|
||||
../../compile_commands.json
|
||||
fi
|
||||
${BUILD_COMMAND} ${build_targets}
|
||||
|
||||
@@ -287,6 +290,8 @@ function build_webgl_with_target {
|
||||
-DCMAKE_INSTALL_PREFIX="../webgl-${lc_target}/filament" \
|
||||
-DWEBGL=1 \
|
||||
../..
|
||||
ln -sf "out/cmake-webgl-${lc_target}/compile_commands.json" \
|
||||
../../compile_commands.json
|
||||
${BUILD_COMMAND} ${BUILD_TARGETS}
|
||||
)
|
||||
fi
|
||||
@@ -359,6 +364,8 @@ function build_android_target {
|
||||
${MATOPT_OPTION} \
|
||||
${VULKAN_ANDROID_OPTION} \
|
||||
../..
|
||||
ln -sf "out/cmake-android-${lc_target}-${arch}/compile_commands.json" \
|
||||
../../compile_commands.json
|
||||
fi
|
||||
|
||||
# We must always install Android libraries to build the AAR
|
||||
@@ -591,6 +598,8 @@ function build_ios_target {
|
||||
${MATDBG_OPTION} \
|
||||
${MATOPT_OPTION} \
|
||||
../..
|
||||
ln -sf "out/cmake-ios-${lc_target}-${arch}/compile_commands.json" \
|
||||
../../compile_commands.json
|
||||
fi
|
||||
|
||||
${BUILD_COMMAND}
|
||||
|
||||
@@ -1 +1 @@
|
||||
25.1.8937393
|
||||
26.1.10909125
|
||||
@@ -50,7 +50,7 @@ function replace {
|
||||
FIND_STR="${1//\{\{VERSION\}\}/${VERSION_REGEX}}"
|
||||
REPLACE_STR="${1//\{\{VERSION\}\}/${NEW_VERSION}}"
|
||||
local FILE_NAME="$2"
|
||||
if [ IS_DARWIN ]; then
|
||||
if [ $IS_DARWIN == 1 ]; then
|
||||
sed -i '' -E "s/${FIND_STR}/${REPLACE_STR}/" "${FILE_NAME}"
|
||||
else
|
||||
sed -i -E "s/${FIND_STR}/${REPLACE_STR}/" "${FILE_NAME}"
|
||||
|
||||
@@ -125,7 +125,13 @@ if [[ "${has_universal}" == "true" ]]; then
|
||||
|
||||
arch_output="${OUTPUT_PATH%.a}_${arch}.a"
|
||||
arch_outputs+=("$arch_output")
|
||||
combine_static_libs "$arch_output" $(find "$(pwd)/${archs_temp_dir}/${arch}" -iname '*.a')
|
||||
|
||||
archives=()
|
||||
while IFS= read -r -d $'\0'; do
|
||||
archives+=("$REPLY")
|
||||
done < <(find "$(pwd)/${archs_temp_dir}/${arch}" -iname '*.a' -print0)
|
||||
|
||||
combine_static_libs "$arch_output" "$archives"
|
||||
done
|
||||
|
||||
# Finally, combine the single-architecture archives into a universal binary.
|
||||
|
||||
@@ -21,7 +21,7 @@ set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_VERSION 1)
|
||||
|
||||
# android
|
||||
set(API_LEVEL 19)
|
||||
set(API_LEVEL 21)
|
||||
|
||||
# architecture
|
||||
set(ARCH armv7a-linux-androideabi)
|
||||
|
||||
@@ -21,7 +21,7 @@ set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_VERSION 1)
|
||||
|
||||
# android
|
||||
set(API_LEVEL 19)
|
||||
set(API_LEVEL 21)
|
||||
|
||||
# architecture
|
||||
set(ARCH i686-linux-android)
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -587,6 +587,7 @@
|
||||
<li><em>overrides</em> Dictionary with one or more of the following properties: mapSize, shadowCascades, constantBias, normalBias, shadowFar, shadowNearHint, shadowFarHint, stable, polygonOffsetConstant, polygonOffsetSlope, \</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>engine.setStereoscopicOptions()</strong></li>
|
||||
<li><strong>engine.setTemporalAntiAliasingOptions()</strong></li>
|
||||
<li><strong>engine.setVignetteOptions()</strong></li>
|
||||
</ul>
|
||||
@@ -1462,6 +1463,8 @@ This defines the following functions:</p>
|
||||
<li>STENCIL_ATTACHMENT</li>
|
||||
<li>UPLOADABLE</li>
|
||||
<li>SAMPLEABLE</li>
|
||||
<li>BLIT_SRC</li>
|
||||
<li>BLIT_DST</li>
|
||||
<li>SUBPASS_INPUT</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -66,6 +66,7 @@ set(SRCS
|
||||
src/Froxelizer.cpp
|
||||
src/Frustum.cpp
|
||||
src/HwRenderPrimitiveFactory.cpp
|
||||
src/HwVertexBufferInfoFactory.cpp
|
||||
src/IndexBuffer.cpp
|
||||
src/IndirectLight.cpp
|
||||
src/InstanceBuffer.cpp
|
||||
@@ -137,6 +138,7 @@ set(SRCS
|
||||
|
||||
set(PRIVATE_HDRS
|
||||
src/Allocators.h
|
||||
src/Bimap.h
|
||||
src/BufferPoolAllocator.h
|
||||
src/ColorSpaceUtils.h
|
||||
src/Culler.h
|
||||
@@ -147,6 +149,7 @@ set(PRIVATE_HDRS
|
||||
src/FrameSkipper.h
|
||||
src/Froxelizer.h
|
||||
src/HwRenderPrimitiveFactory.h
|
||||
src/HwVertexBufferInfoFactory.h
|
||||
src/Intersections.h
|
||||
src/MaterialParser.h
|
||||
src/PerViewUniforms.h
|
||||
@@ -209,10 +212,14 @@ set(PRIVATE_HDRS
|
||||
)
|
||||
|
||||
set(MATERIAL_SRCS
|
||||
src/materials/fsr/fsr_easu.mat
|
||||
src/materials/fsr/fsr_easu_mobile.mat
|
||||
src/materials/fsr/fsr_easu_mobileF.mat
|
||||
src/materials/fsr/fsr_rcas.mat
|
||||
src/materials/antiAliasing/fxaa.mat
|
||||
src/materials/antiAliasing/taa.mat
|
||||
src/materials/blitLow.mat
|
||||
src/materials/blitArray.mat
|
||||
src/materials/bloom/bloomDownsample.mat
|
||||
src/materials/bloom/bloomDownsample2x.mat
|
||||
src/materials/bloom/bloomDownsample9.mat
|
||||
src/materials/bloom/bloomUpsample.mat
|
||||
src/materials/colorGrading/colorGrading.mat
|
||||
src/materials/colorGrading/colorGradingAsSubpass.mat
|
||||
src/materials/colorGrading/customResolveAsSubpass.mat
|
||||
@@ -220,32 +227,31 @@ set(MATERIAL_SRCS
|
||||
src/materials/defaultMaterial.mat
|
||||
src/materials/dof/dof.mat
|
||||
src/materials/dof/dofCoc.mat
|
||||
src/materials/dof/dofDownsample.mat
|
||||
src/materials/dof/dofCombine.mat
|
||||
src/materials/dof/dofDilate.mat
|
||||
src/materials/dof/dofDownsample.mat
|
||||
src/materials/dof/dofMedian.mat
|
||||
src/materials/dof/dofMipmap.mat
|
||||
src/materials/dof/dofTiles.mat
|
||||
src/materials/dof/dofTilesSwizzle.mat
|
||||
src/materials/dof/dofDilate.mat
|
||||
src/materials/dof/dofMipmap.mat
|
||||
src/materials/dof/dofMedian.mat
|
||||
src/materials/flare/flare.mat
|
||||
src/materials/blitLow.mat
|
||||
src/materials/bloom/bloomDownsample.mat
|
||||
src/materials/bloom/bloomDownsample2x.mat
|
||||
src/materials/bloom/bloomDownsample9.mat
|
||||
src/materials/bloom/bloomUpsample.mat
|
||||
src/materials/fsr/fsr_easu.mat
|
||||
src/materials/fsr/fsr_easu_mobile.mat
|
||||
src/materials/fsr/fsr_easu_mobileF.mat
|
||||
src/materials/fsr/fsr_rcas.mat
|
||||
src/materials/resolveDepth.mat
|
||||
src/materials/separableGaussianBlur.mat
|
||||
src/materials/skybox.mat
|
||||
src/materials/shadowmap.mat
|
||||
src/materials/ssao/bilateralBlur.mat
|
||||
src/materials/ssao/bilateralBlurBentNormals.mat
|
||||
src/materials/ssao/mipmapDepth.mat
|
||||
src/materials/skybox.mat
|
||||
src/materials/ssao/sao.mat
|
||||
src/materials/ssao/saoBentNormals.mat
|
||||
src/materials/separableGaussianBlur.mat
|
||||
src/materials/antiAliasing/fxaa.mat
|
||||
src/materials/antiAliasing/taa.mat
|
||||
src/materials/vsmMipmap.mat
|
||||
)
|
||||
|
||||
set(MATERIAL_ES2_SRCS
|
||||
set(MATERIAL_FL0_SRCS
|
||||
src/materials/defaultMaterial0.mat
|
||||
src/materials/skybox0.mat
|
||||
)
|
||||
@@ -274,6 +280,11 @@ if (NOT DFG_LUT_SIZE)
|
||||
endif()
|
||||
message(STATUS "DFG LUT size set to ${DFG_LUT_SIZE}x${DFG_LUT_SIZE}")
|
||||
|
||||
# Whether to include FL0 materials.
|
||||
if (FILAMENT_ENABLE_FEATURE_LEVEL_0)
|
||||
add_definitions(-DFILAMENT_ENABLE_FEATURE_LEVEL_0)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Definitions
|
||||
# ==================================================================================================
|
||||
@@ -315,15 +326,15 @@ foreach (mat_src ${MATERIAL_SRCS})
|
||||
list(APPEND MATERIAL_BINS ${output_path})
|
||||
endforeach()
|
||||
|
||||
if (IS_MOBILE_TARGET AND FILAMENT_SUPPORTS_OPENGL)
|
||||
foreach (mat_src ${MATERIAL_ES2_SRCS})
|
||||
if (FILAMENT_ENABLE_FEATURE_LEVEL_0)
|
||||
foreach (mat_src ${MATERIAL_FL0_SRCS})
|
||||
get_filename_component(localname "${mat_src}" NAME_WE)
|
||||
get_filename_component(fullname "${mat_src}" ABSOLUTE)
|
||||
set(output_path "${MATERIAL_DIR}/${localname}.filamat")
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${output_path}
|
||||
COMMAND matc -a opengl -p ${MATC_TARGET} ${MATC_OPT_FLAGS} -o ${output_path} ${fullname}
|
||||
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
|
||||
MAIN_DEPENDENCY ${fullname}
|
||||
DEPENDS matc
|
||||
COMMENT "Compiling material ${mat_src} to ${output_path}"
|
||||
|
||||
@@ -169,6 +169,8 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
||||
src/vulkan/platform/VulkanPlatform.cpp
|
||||
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
|
||||
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
|
||||
src/vulkan/spirv/VulkanSpirvUtils.cpp
|
||||
src/vulkan/spirv/VulkanSpirvUtils.h
|
||||
src/vulkan/VulkanBlitter.cpp
|
||||
src/vulkan/VulkanBlitter.h
|
||||
src/vulkan/VulkanBuffer.cpp
|
||||
@@ -313,6 +315,7 @@ endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_VULKAN)
|
||||
target_link_libraries(${TARGET} PUBLIC bluevk vkmemalloc vkshaders smol-v)
|
||||
target_link_libraries(${TARGET} PRIVATE SPIRV-Headers)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_METAL)
|
||||
@@ -397,8 +400,8 @@ install(DIRECTORY ${PUBLIC_HDR_DIR}/backend DESTINATION include)
|
||||
# ==================================================================================================
|
||||
option(INSTALL_BACKEND_TEST "Install the backend test library so it can be consumed on iOS" OFF)
|
||||
|
||||
if (APPLE)
|
||||
add_library(backend_test STATIC
|
||||
if (APPLE OR LINUX)
|
||||
set(BACKEND_TEST_SRC
|
||||
test/BackendTest.cpp
|
||||
test/ShaderGenerator.cpp
|
||||
test/TrianglePrimitive.cpp
|
||||
@@ -410,19 +413,26 @@ if (APPLE)
|
||||
test/test_BufferUpdates.cpp
|
||||
test/test_MRT.cpp
|
||||
test/test_LoadImage.cpp
|
||||
test/test_RenderExternalImage.cpp
|
||||
test/test_StencilBuffer.cpp
|
||||
test/test_Scissor.cpp
|
||||
test/test_MipLevels.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(backend_test PRIVATE
|
||||
)
|
||||
set(BACKEND_TEST_LIBS
|
||||
backend
|
||||
getopt
|
||||
gtest
|
||||
imageio
|
||||
filamat
|
||||
SPIRV
|
||||
spirv-cross-glsl)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
# TODO: we should expand this test to Linux and other platforms.
|
||||
list(APPEND BACKEND_TEST_SRC
|
||||
test/test_RenderExternalImage.cpp)
|
||||
add_library(backend_test STATIC ${BACKEND_TEST_SRC})
|
||||
target_link_libraries(backend_test PRIVATE ${BACKEND_TEST_LIBS})
|
||||
|
||||
set(BACKEND_TEST_DEPS
|
||||
OGLCompiler
|
||||
@@ -436,12 +446,11 @@ if (APPLE)
|
||||
glslang
|
||||
spirv-cross-core
|
||||
spirv-cross-glsl
|
||||
spirv-cross-msl
|
||||
)
|
||||
spirv-cross-msl)
|
||||
|
||||
if (NOT IOS)
|
||||
target_link_libraries(backend_test PRIVATE image imageio)
|
||||
list(APPEND BACKEND_TEST_DEPS image imageio)
|
||||
list(APPEND BACKEND_TEST_DEPS image)
|
||||
endif()
|
||||
|
||||
set(BACKEND_TEST_COMBINED_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/libbackendtest_combined.a")
|
||||
@@ -455,15 +464,21 @@ if (APPLE)
|
||||
endif()
|
||||
|
||||
set_target_properties(backend_test PROPERTIES FOLDER Tests)
|
||||
|
||||
if (APPLE AND NOT IOS)
|
||||
add_executable(backend_test_mac test/mac_runner.mm)
|
||||
target_link_libraries(backend_test_mac PRIVATE "-framework Metal -framework AppKit -framework QuartzCore")
|
||||
# Because each test case is a separate file, the -force_load flag is necessary to prevent the
|
||||
# linker from removing "unused" symbols.
|
||||
target_link_libraries(backend_test_mac PRIVATE -force_load backend_test)
|
||||
set_target_properties(backend_test_mac PROPERTIES FOLDER Tests)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (APPLE AND NOT IOS)
|
||||
add_executable(backend_test_mac test/mac_runner.mm)
|
||||
target_link_libraries(backend_test_mac PRIVATE "-framework Metal -framework AppKit -framework QuartzCore")
|
||||
# Because each test case is a separate file, the -force_load flag is necessary to prevent the
|
||||
# linker from removing "unused" symbols.
|
||||
target_link_libraries(backend_test_mac PRIVATE -force_load backend_test)
|
||||
set_target_properties(backend_test_mac PROPERTIES FOLDER Tests)
|
||||
if (LINUX)
|
||||
add_executable(backend_test_linux test/linux_runner.cpp ${BACKEND_TEST_SRC})
|
||||
target_link_libraries(backend_test_linux PRIVATE ${BACKEND_TEST_LIBS})
|
||||
set_target_properties(backend_test_linux PROPERTIES FOLDER Tests)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -113,7 +112,7 @@ public:
|
||||
/**
|
||||
* Helper to create a BufferDescriptor that uses a KNOWN method pointer w/ object passed
|
||||
* by pointer as the callback. e.g.:
|
||||
* auto bd = BufferDescriptor::make(buffer, size, &Foo::method, foo);
|
||||
* auto bd = BufferDescriptor::make<Foo, &Foo::method>(buffer, size, foo);
|
||||
*
|
||||
* @param buffer Memory address of the CPU buffer to reference
|
||||
* @param size Size of the CPU buffer in bytes
|
||||
@@ -121,12 +120,12 @@ public:
|
||||
* @return a new BufferDescriptor
|
||||
*/
|
||||
template<typename T, void(T::*method)(void const*, size_t)>
|
||||
static BufferDescriptor make(
|
||||
void const* buffer, size_t size, T* data, CallbackHandler* handler = nullptr) noexcept {
|
||||
static BufferDescriptor make(void const* buffer, size_t size, T* data,
|
||||
CallbackHandler* handler = nullptr) noexcept {
|
||||
return {
|
||||
buffer, size,
|
||||
handler, [](void* b, size_t s, void* u) {
|
||||
(*static_cast<T**>(u)->*method)(b, s);
|
||||
(static_cast<T*>(u)->*method)(b, s);
|
||||
}, data
|
||||
};
|
||||
}
|
||||
@@ -145,14 +144,14 @@ public:
|
||||
* @return a new BufferDescriptor
|
||||
*/
|
||||
template<typename T>
|
||||
static BufferDescriptor make(
|
||||
void const* buffer, size_t size, T&& functor, CallbackHandler* handler = nullptr) noexcept {
|
||||
static BufferDescriptor make(void const* buffer, size_t size, T&& functor,
|
||||
CallbackHandler* handler = nullptr) noexcept {
|
||||
return {
|
||||
buffer, size,
|
||||
handler, [](void* b, size_t s, void* u) {
|
||||
T& that = *static_cast<T*>(u);
|
||||
that(b, s);
|
||||
delete &that;
|
||||
T* const that = static_cast<T*>(u);
|
||||
that->operator()(b, s);
|
||||
delete that;
|
||||
},
|
||||
new T(std::forward<T>(functor))
|
||||
};
|
||||
@@ -201,7 +200,7 @@ public:
|
||||
return mUser;
|
||||
}
|
||||
|
||||
//! CPU mempry-buffer virtual address
|
||||
//! CPU memory-buffer virtual address
|
||||
void* buffer = nullptr;
|
||||
|
||||
//! CPU memory-buffer size in bytes
|
||||
|
||||
@@ -17,8 +17,6 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_CALLBACKHANDLER_H
|
||||
#define TNT_FILAMENT_BACKEND_CALLBACKHANDLER_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <array> // FIXME: STL headers are not allowed in public headers
|
||||
#include <array> // FIXME: STL headers are not allowed in public headers
|
||||
#include <type_traits> // FIXME: STL headers are not allowed in public headers
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -154,6 +155,19 @@ enum class ShaderLanguage {
|
||||
MSL = 3,
|
||||
};
|
||||
|
||||
static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) {
|
||||
switch (shaderLanguage) {
|
||||
case ShaderLanguage::ESSL1:
|
||||
return "ESSL 1.0";
|
||||
case ShaderLanguage::ESSL3:
|
||||
return "ESSL 3.0";
|
||||
case ShaderLanguage::SPIRV:
|
||||
return "SPIR-V";
|
||||
case ShaderLanguage::MSL:
|
||||
return "MSL";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bitmask for selecting render buffers
|
||||
*/
|
||||
@@ -214,6 +228,7 @@ struct Viewport {
|
||||
int32_t top() const noexcept { return bottom + int32_t(height); }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Specifies the mapping of the near and far clipping plane to window coordinates.
|
||||
*/
|
||||
@@ -645,13 +660,15 @@ enum class TextureFormat : uint16_t {
|
||||
|
||||
//! Bitmask describing the intended Texture Usage
|
||||
enum class TextureUsage : uint8_t {
|
||||
NONE = 0x0,
|
||||
COLOR_ATTACHMENT = 0x1, //!< Texture can be used as a color attachment
|
||||
DEPTH_ATTACHMENT = 0x2, //!< Texture can be used as a depth attachment
|
||||
STENCIL_ATTACHMENT = 0x4, //!< Texture can be used as a stencil attachment
|
||||
UPLOADABLE = 0x8, //!< Data can be uploaded into this texture (default)
|
||||
NONE = 0x00,
|
||||
COLOR_ATTACHMENT = 0x01, //!< Texture can be used as a color attachment
|
||||
DEPTH_ATTACHMENT = 0x02, //!< Texture can be used as a depth attachment
|
||||
STENCIL_ATTACHMENT = 0x04, //!< Texture can be used as a stencil attachment
|
||||
UPLOADABLE = 0x08, //!< Data can be uploaded into this texture (default)
|
||||
SAMPLEABLE = 0x10, //!< Texture can be sampled (default)
|
||||
SUBPASS_INPUT = 0x20, //!< Texture can be used as a subpass input
|
||||
BLIT_SRC = 0x40, //!< Texture can be used the source of a blit()
|
||||
BLIT_DST = 0x80, //!< Texture can be used the destination of a blit()
|
||||
DEFAULT = UPLOADABLE | SAMPLEABLE //!< Default texture usage
|
||||
};
|
||||
|
||||
@@ -679,6 +696,17 @@ static constexpr bool isDepthFormat(TextureFormat format) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr bool isStencilFormat(TextureFormat format) noexcept {
|
||||
switch (format) {
|
||||
case TextureFormat::STENCIL8:
|
||||
case TextureFormat::DEPTH24_STENCIL8:
|
||||
case TextureFormat::DEPTH32F_STENCIL8:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr bool isUnsignedIntFormat(TextureFormat format) {
|
||||
switch (format) {
|
||||
case TextureFormat::R8UI:
|
||||
@@ -1167,8 +1195,8 @@ using FrameScheduledCallback = void(*)(PresentCallable callable, void* user);
|
||||
enum class Workaround : uint16_t {
|
||||
// The EASU pass must split because shader compiler flattens early-exit branch
|
||||
SPLIT_EASU,
|
||||
// Backend allows feedback loop with ancillary buffers (depth/stencil) as long as they're read-only for
|
||||
// the whole render pass.
|
||||
// Backend allows feedback loop with ancillary buffers (depth/stencil) as long as they're
|
||||
// read-only for the whole render pass.
|
||||
ALLOW_READ_ONLY_ANCILLARY_FEEDBACK_LOOP,
|
||||
// for some uniform arrays, it's needed to do an initialization to avoid crash on adreno gpu
|
||||
ADRENO_UNIFORM_ARRAY_CRASH,
|
||||
@@ -1184,6 +1212,14 @@ enum class Workaround : uint16_t {
|
||||
DISABLE_THREAD_AFFINITY
|
||||
};
|
||||
|
||||
//! The type of technique for stereoscopic rendering
|
||||
enum class StereoscopicType : uint8_t {
|
||||
// Stereoscopic rendering is performed using instanced rendering technique.
|
||||
INSTANCED,
|
||||
// Stereoscopic rendering is performed using the multiview feature from the graphics backend.
|
||||
MULTIVIEW,
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
template<> struct utils::EnableBitMaskOperators<filament::backend::ShaderStageFlags>
|
||||
|
||||
@@ -17,16 +17,14 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_HANDLE_H
|
||||
#define TNT_FILAMENT_BACKEND_HANDLE_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#if !defined(NDEBUG)
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
#endif
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <type_traits> // FIXME: STL headers are not allowed in public headers
|
||||
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -41,6 +39,7 @@ struct HwStream;
|
||||
struct HwSwapChain;
|
||||
struct HwTexture;
|
||||
struct HwTimerQuery;
|
||||
struct HwVertexBufferInfo;
|
||||
struct HwVertexBuffer;
|
||||
|
||||
/*
|
||||
@@ -54,7 +53,7 @@ struct HwVertexBuffer;
|
||||
class HandleBase {
|
||||
public:
|
||||
using HandleId = uint32_t;
|
||||
static constexpr const HandleId nullid = HandleId{ std::numeric_limits<HandleId>::max() };
|
||||
static constexpr const HandleId nullid = HandleId{ UINT32_MAX };
|
||||
|
||||
constexpr HandleBase() noexcept: object(nullid) {}
|
||||
|
||||
@@ -64,14 +63,6 @@ public:
|
||||
// clear the handle, this doesn't free associated resources
|
||||
void clear() noexcept { object = nullid; }
|
||||
|
||||
// compare handles
|
||||
bool operator==(const HandleBase& rhs) const noexcept { return object == rhs.object; }
|
||||
bool operator!=(const HandleBase& rhs) const noexcept { return object != rhs.object; }
|
||||
bool operator<(const HandleBase& rhs) const noexcept { return object < rhs.object; }
|
||||
bool operator<=(const HandleBase& rhs) const noexcept { return object <= rhs.object; }
|
||||
bool operator>(const HandleBase& rhs) const noexcept { return object > rhs.object; }
|
||||
bool operator>=(const HandleBase& rhs) const noexcept { return object >= rhs.object; }
|
||||
|
||||
// get this handle's handleId
|
||||
HandleId getId() const noexcept { return object; }
|
||||
|
||||
@@ -103,6 +94,14 @@ struct Handle : public HandleBase {
|
||||
|
||||
explicit Handle(HandleId id) noexcept : HandleBase(id) { }
|
||||
|
||||
// compare handles of the same type
|
||||
bool operator==(const Handle& rhs) const noexcept { return getId() == rhs.getId(); }
|
||||
bool operator!=(const Handle& rhs) const noexcept { return getId() != rhs.getId(); }
|
||||
bool operator<(const Handle& rhs) const noexcept { return getId() < rhs.getId(); }
|
||||
bool operator<=(const Handle& rhs) const noexcept { return getId() <= rhs.getId(); }
|
||||
bool operator>(const Handle& rhs) const noexcept { return getId() > rhs.getId(); }
|
||||
bool operator>=(const Handle& rhs) const noexcept { return getId() >= rhs.getId(); }
|
||||
|
||||
// type-safe Handle cast
|
||||
template<typename B, typename = std::enable_if_t<std::is_base_of<T, B>::value> >
|
||||
Handle(Handle<B> const& base) noexcept : HandleBase(base) { } // NOLINT(hicpp-explicit-conversions,google-explicit-constructor)
|
||||
@@ -116,18 +115,19 @@ private:
|
||||
|
||||
// Types used by the command stream
|
||||
// (we use this renaming because the macro-system doesn't deal well with "<" and ">")
|
||||
using BufferObjectHandle = Handle<HwBufferObject>;
|
||||
using FenceHandle = Handle<HwFence>;
|
||||
using IndexBufferHandle = Handle<HwIndexBuffer>;
|
||||
using ProgramHandle = Handle<HwProgram>;
|
||||
using RenderPrimitiveHandle = Handle<HwRenderPrimitive>;
|
||||
using RenderTargetHandle = Handle<HwRenderTarget>;
|
||||
using SamplerGroupHandle = Handle<HwSamplerGroup>;
|
||||
using StreamHandle = Handle<HwStream>;
|
||||
using SwapChainHandle = Handle<HwSwapChain>;
|
||||
using TextureHandle = Handle<HwTexture>;
|
||||
using TimerQueryHandle = Handle<HwTimerQuery>;
|
||||
using VertexBufferHandle = Handle<HwVertexBuffer>;
|
||||
using BufferObjectHandle = Handle<HwBufferObject>;
|
||||
using FenceHandle = Handle<HwFence>;
|
||||
using IndexBufferHandle = Handle<HwIndexBuffer>;
|
||||
using ProgramHandle = Handle<HwProgram>;
|
||||
using RenderPrimitiveHandle = Handle<HwRenderPrimitive>;
|
||||
using RenderTargetHandle = Handle<HwRenderTarget>;
|
||||
using SamplerGroupHandle = Handle<HwSamplerGroup>;
|
||||
using StreamHandle = Handle<HwStream>;
|
||||
using SwapChainHandle = Handle<HwSwapChain>;
|
||||
using TextureHandle = Handle<HwTexture>;
|
||||
using TimerQueryHandle = Handle<HwTimerQuery>;
|
||||
using VertexBufferHandle = Handle<HwVertexBuffer>;
|
||||
using VertexBufferInfoHandle = Handle<HwVertexBufferInfo>;
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <limits>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -33,10 +33,6 @@ struct PipelineState {
|
||||
RasterState rasterState;
|
||||
StencilState stencilState;
|
||||
PolygonOffset polygonOffset;
|
||||
Viewport scissor{ 0, 0,
|
||||
(uint32_t)std::numeric_limits<int32_t>::max(),
|
||||
(uint32_t)std::numeric_limits<int32_t>::max()
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -141,7 +142,7 @@ public:
|
||||
CallbackHandler* handler = nullptr) noexcept {
|
||||
return { buffer, size, format, type, alignment, left, top, stride,
|
||||
handler, [](void* b, size_t s, void* u) {
|
||||
(*static_cast<T**>(u)->*method)(b, s); }, data };
|
||||
(static_cast<T*>(u)->*method)(b, s); }, data };
|
||||
}
|
||||
|
||||
template<typename T, void(T::*method)(void const*, size_t)>
|
||||
@@ -149,7 +150,7 @@ public:
|
||||
PixelDataFormat format, PixelDataType type, T* data,
|
||||
CallbackHandler* handler = nullptr) noexcept {
|
||||
return { buffer, size, format, type, handler, [](void* b, size_t s, void* u) {
|
||||
(*static_cast<T**>(u)->*method)(b, s); }, data };
|
||||
(static_cast<T*>(u)->*method)(b, s); }, data };
|
||||
}
|
||||
|
||||
template<typename T, void(T::*method)(void const*, size_t)>
|
||||
@@ -157,7 +158,7 @@ public:
|
||||
backend::CompressedPixelDataType format, uint32_t imageSize, T* data,
|
||||
CallbackHandler* handler = nullptr) noexcept {
|
||||
return { buffer, size, format, imageSize, handler, [](void* b, size_t s, void* u) {
|
||||
(*static_cast<T**>(u)->*method)(b, s); }, data
|
||||
(static_cast<T*>(u)->*method)(b, s); }, data
|
||||
};
|
||||
}
|
||||
|
||||
@@ -168,9 +169,9 @@ public:
|
||||
CallbackHandler* handler = nullptr) noexcept {
|
||||
return { buffer, size, format, type, alignment, left, top, stride,
|
||||
handler, [](void* b, size_t s, void* u) {
|
||||
T& that = *static_cast<T*>(u);
|
||||
that(b, s);
|
||||
delete &that;
|
||||
T* const that = static_cast<T*>(u);
|
||||
that->operator()(b, s);
|
||||
delete that;
|
||||
}, new T(std::forward<T>(functor))
|
||||
};
|
||||
}
|
||||
@@ -181,9 +182,9 @@ public:
|
||||
CallbackHandler* handler = nullptr) noexcept {
|
||||
return { buffer, size, format, type,
|
||||
handler, [](void* b, size_t s, void* u) {
|
||||
T& that = *static_cast<T*>(u);
|
||||
that(b, s);
|
||||
delete &that;
|
||||
T* const that = static_cast<T*>(u);
|
||||
that->operator()(b, s);
|
||||
delete that;
|
||||
}, new T(std::forward<T>(functor))
|
||||
};
|
||||
}
|
||||
@@ -194,9 +195,9 @@ public:
|
||||
CallbackHandler* handler = nullptr) noexcept {
|
||||
return { buffer, size, format, imageSize,
|
||||
handler, [](void* b, size_t s, void* u) {
|
||||
T& that = *static_cast<T*>(u);
|
||||
that(b, s);
|
||||
delete &that;
|
||||
T* const that = static_cast<T*>(u);
|
||||
that->operator()(b, s);
|
||||
delete that;
|
||||
}, new T(std::forward<T>(functor))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,11 +19,11 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_PLATFORM_H
|
||||
#define TNT_FILAMENT_BACKEND_PLATFORM_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Invocable.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class Driver;
|
||||
@@ -41,18 +41,24 @@ public:
|
||||
struct Stream {};
|
||||
|
||||
struct DriverConfig {
|
||||
/*
|
||||
* size of handle arena in bytes. Setting to 0 indicates default value is to be used.
|
||||
/**
|
||||
* Size of handle arena in bytes. Setting to 0 indicates default value is to be used.
|
||||
* Driver clamps to valid values.
|
||||
*/
|
||||
size_t handleArenaSize = 0;
|
||||
|
||||
/*
|
||||
* this number of most-recently destroyed textures will be tracked for use-after-free.
|
||||
/**
|
||||
* This number of most-recently destroyed textures will be tracked for use-after-free.
|
||||
* Throws an exception when a texture is freed but still bound to a SamplerGroup and used in
|
||||
* a draw call. 0 disables completely. Currently only respected by the Metal backend.
|
||||
*/
|
||||
size_t textureUseAfterFreePoolSize = 0;
|
||||
|
||||
/**
|
||||
* Set to `true` to forcibly disable parallel shader compilation in the backend.
|
||||
* Currently only honored by the GL backend.
|
||||
*/
|
||||
bool disableParallelShaderCompile = false;
|
||||
};
|
||||
|
||||
Platform() noexcept;
|
||||
@@ -78,7 +84,7 @@ public:
|
||||
*
|
||||
* @return nullptr on failure, or a pointer to the newly created driver.
|
||||
*/
|
||||
virtual backend::Driver* createDriver(void* sharedContext,
|
||||
virtual backend::Driver* UTILS_NULLABLE createDriver(void* UTILS_NULLABLE sharedContext,
|
||||
const DriverConfig& driverConfig) noexcept = 0;
|
||||
|
||||
/**
|
||||
@@ -96,7 +102,8 @@ public:
|
||||
* cache.
|
||||
*/
|
||||
using InsertBlobFunc = utils::Invocable<
|
||||
void(const void* key, size_t keySize, const void* value, size_t valueSize)>;
|
||||
void(const void* UTILS_NONNULL key, size_t keySize,
|
||||
const void* UTILS_NONNULL value, size_t valueSize)>;
|
||||
|
||||
/*
|
||||
* RetrieveBlobFunc is an Invocable to an application-provided function that a
|
||||
@@ -104,7 +111,8 @@ public:
|
||||
* cache.
|
||||
*/
|
||||
using RetrieveBlobFunc = utils::Invocable<
|
||||
size_t(const void* key, size_t keySize, void* value, size_t valueSize)>;
|
||||
size_t(const void* UTILS_NONNULL key, size_t keySize,
|
||||
void* UTILS_NONNULL value, size_t valueSize)>;
|
||||
|
||||
/**
|
||||
* Sets the callback functions that the backend can use to interact with caching functionality
|
||||
@@ -157,7 +165,8 @@ public:
|
||||
* @param value pointer to the beginning of the value data that is to be inserted
|
||||
* @param valueSize specifies the size in byte of the data pointed to by <value>
|
||||
*/
|
||||
void insertBlob(const void* key, size_t keySize, const void* value, size_t valueSize);
|
||||
void insertBlob(const void* UTILS_NONNULL key, size_t keySize,
|
||||
const void* UTILS_NONNULL value, size_t valueSize);
|
||||
|
||||
/**
|
||||
* To retrieve the binary value associated with a given key from the cache, a
|
||||
@@ -176,7 +185,8 @@ public:
|
||||
* @return If the cache contains a value associated with the given key then the
|
||||
* size of that binary value in bytes is returned. Otherwise 0 is returned.
|
||||
*/
|
||||
size_t retrieveBlob(const void* key, size_t keySize, void* value, size_t valueSize);
|
||||
size_t retrieveBlob(const void* UTILS_NONNULL key, size_t keySize,
|
||||
void* UTILS_NONNULL value, size_t valueSize);
|
||||
|
||||
private:
|
||||
InsertBlobFunc mInsertBlob;
|
||||
|
||||
@@ -21,8 +21,7 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
namespace filament::backend {
|
||||
|
||||
/**
|
||||
* A PresentCallable is a callable object that, when called, schedules a frame for presentation on
|
||||
@@ -98,7 +97,6 @@ private:
|
||||
*/
|
||||
using FrameFinishedCallback UTILS_DEPRECATED = void(*)(PresentCallable callable, void* user);
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_PRESENTCALLABLE
|
||||
|
||||
@@ -17,17 +17,19 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_PROGRAM_H
|
||||
#define TNT_FILAMENT_BACKEND_PRIVATE_PROGRAM_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <array>
|
||||
#include <variant>
|
||||
#include <array> // FIXME: STL headers are not allowed in public headers
|
||||
#include <utility> // FIXME: STL headers are not allowed in public headers
|
||||
#include <variant> // FIXME: STL headers are not allowed in public headers
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
|
||||
@@ -24,9 +24,6 @@
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
struct UTILS_PUBLIC SamplerDescriptor {
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_TARGETBUFFERINFO_H
|
||||
#define TNT_FILAMENT_BACKEND_TARGETBUFFERINFO_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -18,8 +18,13 @@
|
||||
#define TNT_FILAMENT_BACKEND_PRIVATE_OPENGLPLATFORM_H
|
||||
|
||||
#include <backend/AcquiredImage.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class Driver;
|
||||
@@ -38,8 +43,8 @@ protected:
|
||||
* Derived classes can use this to instantiate the default OpenGLDriver backend.
|
||||
* This is typically called from your implementation of createDriver()
|
||||
*/
|
||||
static Driver* createDefaultDriver(OpenGLPlatform* platform,
|
||||
void* sharedContext, const DriverConfig& driverConfig);
|
||||
static Driver* UTILS_NULLABLE createDefaultDriver(OpenGLPlatform* UTILS_NONNULL platform,
|
||||
void* UTILS_NULLABLE sharedContext, const DriverConfig& driverConfig);
|
||||
|
||||
~OpenGLPlatform() noexcept override;
|
||||
|
||||
@@ -66,7 +71,8 @@ public:
|
||||
* @return The driver's SwapChain object.
|
||||
*
|
||||
*/
|
||||
virtual SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) noexcept = 0;
|
||||
virtual SwapChain* UTILS_NONNULL createSwapChain(
|
||||
void* UTILS_NULLABLE nativeWindow, uint64_t flags) noexcept = 0;
|
||||
|
||||
/**
|
||||
* Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_SRGB_COLORSPACE flag.
|
||||
@@ -87,13 +93,14 @@ public:
|
||||
* TODO: we need a more generic way of passing construction parameters
|
||||
* A void* might be enough.
|
||||
*/
|
||||
virtual SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept = 0;
|
||||
virtual SwapChain* UTILS_NULLABLE createSwapChain(
|
||||
uint32_t width, uint32_t height, uint64_t flags) noexcept = 0;
|
||||
|
||||
/**
|
||||
* Called by the driver to destroys the SwapChain
|
||||
* @param swapChain SwapChain to be destroyed.
|
||||
*/
|
||||
virtual void destroySwapChain(SwapChain* swapChain) noexcept = 0;
|
||||
virtual void destroySwapChain(SwapChain* UTILS_NONNULL swapChain) noexcept = 0;
|
||||
|
||||
/**
|
||||
* Returns the set of buffers that must be preserved up to the call to commit().
|
||||
@@ -106,7 +113,7 @@ public:
|
||||
* @return buffer that must be preserved
|
||||
* @see commit()
|
||||
*/
|
||||
virtual TargetBufferFlags getPreservedFlags(SwapChain* swapChain) noexcept;
|
||||
virtual TargetBufferFlags getPreservedFlags(SwapChain* UTILS_NONNULL swapChain) noexcept;
|
||||
|
||||
/**
|
||||
* Called by the driver to establish the default FBO. The default implementation returns 0.
|
||||
@@ -120,14 +127,16 @@ public:
|
||||
* @param drawSwapChain SwapChain to draw to. It must be bound to the default FBO.
|
||||
* @param readSwapChain SwapChain to read from (for operation like `glBlitFramebuffer`)
|
||||
*/
|
||||
virtual void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept = 0;
|
||||
virtual void makeCurrent(
|
||||
SwapChain* UTILS_NONNULL drawSwapChain,
|
||||
SwapChain* UTILS_NONNULL readSwapChain) noexcept = 0;
|
||||
|
||||
/**
|
||||
* Called by the driver once the current frame finishes drawing. Typically, this should present
|
||||
* the drawSwapChain. This is for example where `eglMakeCurrent()` would be called.
|
||||
* @param swapChain the SwapChain to present.
|
||||
*/
|
||||
virtual void commit(SwapChain* swapChain) noexcept = 0;
|
||||
virtual void commit(SwapChain* UTILS_NONNULL swapChain) noexcept = 0;
|
||||
|
||||
/**
|
||||
* Set the time the next committed buffer should be presented to the user at.
|
||||
@@ -152,14 +161,14 @@ public:
|
||||
*
|
||||
* @return A Fence object. The default implementation returns nullptr.
|
||||
*/
|
||||
virtual Fence* createFence() noexcept;
|
||||
virtual Fence* UTILS_NULLABLE createFence() noexcept;
|
||||
|
||||
/**
|
||||
* Destroys a Fence object. The default implementation does nothing.
|
||||
*
|
||||
* @param fence Fence to destroy.
|
||||
*/
|
||||
virtual void destroyFence(Fence* fence) noexcept;
|
||||
virtual void destroyFence(Fence* UTILS_NONNULL fence) noexcept;
|
||||
|
||||
/**
|
||||
* Waits on a Fence.
|
||||
@@ -169,7 +178,7 @@ public:
|
||||
* @return Whether the fence signaled or timed out. See backend::FenceStatus.
|
||||
* The default implementation always return backend::FenceStatus::ERROR.
|
||||
*/
|
||||
virtual backend::FenceStatus waitFence(Fence* fence, uint64_t timeout) noexcept;
|
||||
virtual backend::FenceStatus waitFence(Fence* UTILS_NONNULL fence, uint64_t timeout) noexcept;
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
@@ -183,13 +192,13 @@ public:
|
||||
* @param nativeStream The native stream, this parameter depends on the concrete implementation.
|
||||
* @return A new Stream object.
|
||||
*/
|
||||
virtual Stream* createStream(void* nativeStream) noexcept;
|
||||
virtual Stream* UTILS_NULLABLE createStream(void* UTILS_NULLABLE nativeStream) noexcept;
|
||||
|
||||
/**
|
||||
* Destroys a Stream.
|
||||
* @param stream Stream to destroy.
|
||||
*/
|
||||
virtual void destroyStream(Stream* stream) noexcept;
|
||||
virtual void destroyStream(Stream* UTILS_NONNULL stream) noexcept;
|
||||
|
||||
/**
|
||||
* The specified stream takes ownership of the texture (tname) object
|
||||
@@ -199,20 +208,21 @@ public:
|
||||
* @param stream Stream to take ownership of the texture
|
||||
* @param tname GL texture id to "bind" to the Stream.
|
||||
*/
|
||||
virtual void attach(Stream* stream, intptr_t tname) noexcept;
|
||||
virtual void attach(Stream* UTILS_NONNULL stream, intptr_t tname) noexcept;
|
||||
|
||||
/**
|
||||
* Destroys the texture associated to the stream
|
||||
* @param stream Stream to detach from its texture
|
||||
*/
|
||||
virtual void detach(Stream* stream) noexcept;
|
||||
virtual void detach(Stream* UTILS_NONNULL stream) noexcept;
|
||||
|
||||
/**
|
||||
* Updates the content of the texture attached to the stream.
|
||||
* @param stream Stream to update
|
||||
* @param timestamp Output parameter: Timestamp of the image bound to the texture.
|
||||
*/
|
||||
virtual void updateTexImage(Stream* stream, int64_t* timestamp) noexcept;
|
||||
virtual void updateTexImage(Stream* UTILS_NONNULL stream,
|
||||
int64_t* UTILS_NONNULL timestamp) noexcept;
|
||||
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
@@ -225,13 +235,13 @@ public:
|
||||
* implementation could just return { 0, GL_TEXTURE_2D } at this point. The actual
|
||||
* values can be delayed until setExternalImage.
|
||||
*/
|
||||
virtual ExternalTexture *createExternalImageTexture() noexcept;
|
||||
virtual ExternalTexture* UTILS_NULLABLE createExternalImageTexture() noexcept;
|
||||
|
||||
/**
|
||||
* Destroys an external texture handle and associated data.
|
||||
* @param texture a pointer to the handle to destroy.
|
||||
*/
|
||||
virtual void destroyExternalImage(ExternalTexture* texture) noexcept;
|
||||
virtual void destroyExternalImage(ExternalTexture* UTILS_NONNULL texture) noexcept;
|
||||
|
||||
// called on the application thread to allow Filament to take ownership of the image
|
||||
|
||||
@@ -244,7 +254,7 @@ public:
|
||||
* @param externalImage A token representing the platform's external image.
|
||||
* @see destroyExternalImage
|
||||
*/
|
||||
virtual void retainExternalImage(void* externalImage) noexcept;
|
||||
virtual void retainExternalImage(void* UTILS_NONNULL externalImage) noexcept;
|
||||
|
||||
/**
|
||||
* Called to bind the platform-specific externalImage to an ExternalTexture.
|
||||
@@ -258,7 +268,8 @@ public:
|
||||
* @param texture an in/out pointer to ExternalTexture, id and target can be updated if necessary.
|
||||
* @return true on success, false on error.
|
||||
*/
|
||||
virtual bool setExternalImage(void* externalImage, ExternalTexture* texture) noexcept;
|
||||
virtual bool setExternalImage(void* UTILS_NONNULL externalImage,
|
||||
ExternalTexture* UTILS_NONNULL texture) noexcept;
|
||||
|
||||
/**
|
||||
* The method allows platforms to convert a user-supplied external image object into a new type
|
||||
|
||||
@@ -17,11 +17,10 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_COCOA_GL_H
|
||||
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_COCOA_GL_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ struct PlatformCocoaTouchGLImpl;
|
||||
class PlatformCocoaTouchGL : public OpenGLPlatform {
|
||||
public:
|
||||
PlatformCocoaTouchGL();
|
||||
~PlatformCocoaTouchGL() noexcept;
|
||||
~PlatformCocoaTouchGL() noexcept override;
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Platform Interface
|
||||
|
||||
@@ -17,18 +17,21 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_H
|
||||
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Platform.h>
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <EGL/eglplatform.h>
|
||||
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
/**
|
||||
@@ -148,6 +151,13 @@ protected:
|
||||
} egl;
|
||||
} ext;
|
||||
|
||||
struct SwapChainEGL : public Platform::SwapChain {
|
||||
EGLSurface sur = EGL_NO_SURFACE;
|
||||
Config attribs{};
|
||||
EGLNativeWindowType nativeWindow{};
|
||||
EGLConfig config{};
|
||||
};
|
||||
|
||||
void initializeGlExtensions() noexcept;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -17,8 +17,14 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
|
||||
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
|
||||
|
||||
#include <backend/AcquiredImage.h>
|
||||
#include <backend/Platform.h>
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
#include <backend/platforms/PlatformEGL.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class ExternalStreamManagerAndroid;
|
||||
|
||||
@@ -20,13 +20,18 @@
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/PrivateImplementation.h>
|
||||
|
||||
#include <string_view>
|
||||
#include <tuple>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using SwapChain = Platform::SwapChain;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_CIRCULARBUFFER_H
|
||||
#define TNT_FILAMENT_BACKEND_PRIVATE_CIRCULARBUFFER_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -40,28 +40,36 @@ public:
|
||||
|
||||
~CircularBuffer() noexcept;
|
||||
|
||||
// allocates 'size' bytes in the circular buffer and returns a pointer to the memory
|
||||
// return the current head and moves it forward by size bytes
|
||||
inline void* allocate(size_t size) noexcept {
|
||||
static size_t getBlockSize() noexcept { return sPageSize; }
|
||||
|
||||
// Total size of circular buffer. This is a constant.
|
||||
size_t size() const noexcept { return mSize; }
|
||||
|
||||
// Allocates `s` bytes in the circular buffer and returns a pointer to the memory. All
|
||||
// allocations must not exceed size() bytes.
|
||||
inline void* allocate(size_t s) noexcept {
|
||||
// We can never allocate more that size().
|
||||
assert_invariant(getUsed() + s <= size());
|
||||
char* const cur = static_cast<char*>(mHead);
|
||||
mHead = cur + size;
|
||||
mHead = cur + s;
|
||||
return cur;
|
||||
}
|
||||
|
||||
// Total size of circular buffer
|
||||
size_t size() const noexcept { return mSize; }
|
||||
|
||||
// returns true if the buffer is empty (e.g. after calling flush)
|
||||
// Returns true if the buffer is empty, i.e.: no allocations were made since
|
||||
// calling getBuffer();
|
||||
bool empty() const noexcept { return mTail == mHead; }
|
||||
|
||||
void* getHead() const noexcept { return mHead; }
|
||||
// Returns the size used since the last call to getBuffer()
|
||||
size_t getUsed() const noexcept { return intptr_t(mHead) - intptr_t(mTail); }
|
||||
|
||||
void* getTail() const noexcept { return mTail; }
|
||||
|
||||
// call at least once every getRequiredSize() bytes allocated from the buffer
|
||||
void circularize() noexcept;
|
||||
|
||||
static size_t getBlockSize() noexcept { return sPageSize; }
|
||||
// Retrieves the current allocated range and frees it. It is the responsibility of the caller
|
||||
// to make sure the returned range is no longer in use by the time allocate() allocates
|
||||
// (size() - getUsed()) bytes.
|
||||
struct Range {
|
||||
void* tail;
|
||||
void* head;
|
||||
};
|
||||
Range getBuffer() noexcept;
|
||||
|
||||
private:
|
||||
void* alloc(size_t size) noexcept;
|
||||
@@ -69,10 +77,10 @@ private:
|
||||
|
||||
// pointer to the beginning of the circular buffer (constant)
|
||||
void* mData = nullptr;
|
||||
int mUsesAshmem = -1;
|
||||
int mAshmemFd = -1;
|
||||
|
||||
// size of the circular buffer (constant)
|
||||
size_t mSize = 0;
|
||||
size_t const mSize;
|
||||
|
||||
// pointer to the beginning of recorded data
|
||||
void* mTail = nullptr;
|
||||
|
||||
@@ -19,19 +19,21 @@
|
||||
|
||||
#include "private/backend/CircularBuffer.h"
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Condition.h>
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
/*
|
||||
* A producer-consumer command queue that uses a CircularBuffer as main storage
|
||||
*/
|
||||
class CommandBufferQueue {
|
||||
struct Slice {
|
||||
struct Range {
|
||||
void* begin;
|
||||
void* end;
|
||||
};
|
||||
@@ -44,7 +46,7 @@ class CommandBufferQueue {
|
||||
|
||||
mutable utils::Mutex mLock;
|
||||
mutable utils::Condition mCondition;
|
||||
mutable std::vector<Slice> mCommandBuffersToExecute;
|
||||
mutable std::vector<Range> mCommandBuffersToExecute;
|
||||
size_t mFreeSpace = 0;
|
||||
size_t mHighWatermark = 0;
|
||||
uint32_t mExitRequested = 0;
|
||||
@@ -56,17 +58,20 @@ public:
|
||||
CommandBufferQueue(size_t requiredSize, size_t bufferSize);
|
||||
~CommandBufferQueue();
|
||||
|
||||
CircularBuffer& getCircularBuffer() { return mCircularBuffer; }
|
||||
CircularBuffer& getCircularBuffer() noexcept { return mCircularBuffer; }
|
||||
CircularBuffer const& getCircularBuffer() const noexcept { return mCircularBuffer; }
|
||||
|
||||
size_t getCapacity() const noexcept { return mRequiredSize; }
|
||||
|
||||
size_t getHighWatermark() const noexcept { return mHighWatermark; }
|
||||
|
||||
// wait for commands to be available and returns an array containing these commands
|
||||
std::vector<Slice> waitForCommands() const;
|
||||
std::vector<Range> waitForCommands() const;
|
||||
|
||||
// return the memory used by this command buffer to the circular buffer
|
||||
// WARNING: releaseBuffer() must be called in sequence of the Slices returned by
|
||||
// waitForCommands()
|
||||
void releaseBuffer(Slice const& buffer);
|
||||
void releaseBuffer(Range const& buffer);
|
||||
|
||||
// all commands buffers (Slices) written to this point are returned by waitForCommand(). This
|
||||
// call blocks until the CircularBuffer has at least mRequiredSize bytes available.
|
||||
|
||||
@@ -32,11 +32,13 @@
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ThreadUtils.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -152,14 +154,14 @@ struct CommandType<void (Driver::*)(ARGS...)> {
|
||||
}
|
||||
|
||||
// placement new declared as "throw" to avoid the compiler's null-check
|
||||
inline void* operator new(std::size_t size, void* ptr) {
|
||||
inline void* operator new(std::size_t, void* ptr) {
|
||||
assert_invariant(ptr);
|
||||
return ptr;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// convert an method of "class Driver" into a Command<> type
|
||||
// convert a method of "class Driver" into a Command<> type
|
||||
#define COMMAND_TYPE(method) CommandType<decltype(&Driver::method)>::Command<&Driver::method>
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
@@ -211,6 +213,8 @@ public:
|
||||
CommandStream(CommandStream const& rhs) noexcept = delete;
|
||||
CommandStream& operator=(CommandStream const& rhs) noexcept = delete;
|
||||
|
||||
CircularBuffer const& getCircularBuffer() const noexcept { return mCurrentBuffer; }
|
||||
|
||||
public:
|
||||
#define DECL_DRIVER_API(methodName, paramsDecl, params) \
|
||||
inline void methodName(paramsDecl) { \
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Command debugging off. debugging virtuals are not called.
|
||||
|
||||
@@ -167,12 +167,15 @@ DECL_DRIVER_API_0(resetState)
|
||||
* -----------------------
|
||||
*/
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::VertexBufferHandle, createVertexBuffer,
|
||||
DECL_DRIVER_API_R_N(backend::VertexBufferInfoHandle, createVertexBufferInfo,
|
||||
uint8_t, bufferCount,
|
||||
uint8_t, attributeCount,
|
||||
uint32_t, vertexCount,
|
||||
backend::AttributeArray, attributes)
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::VertexBufferHandle, createVertexBuffer,
|
||||
uint32_t, vertexCount,
|
||||
backend::VertexBufferInfoHandle, vbih)
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::IndexBufferHandle, createIndexBuffer,
|
||||
backend::ElementType, elementType,
|
||||
uint32_t, indexCount,
|
||||
@@ -224,11 +227,7 @@ DECL_DRIVER_API_R_N(backend::SamplerGroupHandle, createSamplerGroup,
|
||||
DECL_DRIVER_API_R_N(backend::RenderPrimitiveHandle, createRenderPrimitive,
|
||||
backend::VertexBufferHandle, vbh,
|
||||
backend::IndexBufferHandle, ibh,
|
||||
backend::PrimitiveType, pt,
|
||||
uint32_t, offset,
|
||||
uint32_t, minIndex,
|
||||
uint32_t, maxIndex,
|
||||
uint32_t, count)
|
||||
backend::PrimitiveType, pt)
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::ProgramHandle, createProgram,
|
||||
backend::Program&&, program)
|
||||
@@ -264,6 +263,7 @@ DECL_DRIVER_API_R_0(backend::TimerQueryHandle, createTimerQuery)
|
||||
*/
|
||||
|
||||
DECL_DRIVER_API_N(destroyVertexBuffer, backend::VertexBufferHandle, vbh)
|
||||
DECL_DRIVER_API_N(destroyVertexBufferInfo,backend::VertexBufferInfoHandle, vbih)
|
||||
DECL_DRIVER_API_N(destroyIndexBuffer, backend::IndexBufferHandle, ibh)
|
||||
DECL_DRIVER_API_N(destroyBufferObject, backend::BufferObjectHandle, ibh)
|
||||
DECL_DRIVER_API_N(destroyRenderPrimitive, backend::RenderPrimitiveHandle, rph)
|
||||
@@ -298,12 +298,12 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameBufferFetchMultiSampleSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameTimeSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isAutoDepthResolveSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isSRGBSwapChainSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isStereoSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isStereoSupported, backend::StereoscopicType, stereoscopicType)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthStencilResolveSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, canGenerateMipmaps)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage, void*, image)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, getTimerQueryValue, backend::TimerQueryHandle, query, uint64_t*, elapsedTime)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isWorkaroundNeeded, backend::Workaround, workaround)
|
||||
@@ -468,7 +468,7 @@ DECL_DRIVER_API_N(readBufferSubData,
|
||||
* --------------------
|
||||
*/
|
||||
|
||||
DECL_DRIVER_API_N(blit,
|
||||
DECL_DRIVER_API_N(blitDEPRECATED,
|
||||
backend::TargetBufferFlags, buffers,
|
||||
backend::RenderTargetHandle, dst,
|
||||
backend::Viewport, dstRect,
|
||||
@@ -476,15 +476,35 @@ DECL_DRIVER_API_N(blit,
|
||||
backend::Viewport, srcRect,
|
||||
backend::SamplerMagFilter, filter)
|
||||
|
||||
DECL_DRIVER_API_N(resolve,
|
||||
backend::TextureHandle, dst,
|
||||
uint8_t, dstLevel, uint8_t, dstLayer,
|
||||
backend::TextureHandle, src,
|
||||
uint8_t, srcLevel, uint8_t, srcLayer)
|
||||
|
||||
DECL_DRIVER_API_N(blit,
|
||||
backend::TextureHandle, dst,
|
||||
uint8_t, dstLevel, uint8_t, dstLayer,
|
||||
math::uint2, dstOrigin,
|
||||
backend::TextureHandle, src,
|
||||
uint8_t, srcLevel, uint8_t, srcLayer,
|
||||
math::uint2, srcOrigin,
|
||||
math::uint2, size)
|
||||
|
||||
DECL_DRIVER_API_N(draw,
|
||||
backend::PipelineState, state,
|
||||
backend::RenderPrimitiveHandle, rph,
|
||||
uint32_t, indexOffset,
|
||||
uint32_t, indexCount,
|
||||
uint32_t, instanceCount)
|
||||
|
||||
DECL_DRIVER_API_N(dispatchCompute,
|
||||
backend::ProgramHandle, program,
|
||||
math::uint3, workGroupCount)
|
||||
|
||||
DECL_DRIVER_API_N(scissor,
|
||||
Viewport, scissor)
|
||||
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
|
||||
@@ -22,28 +22,33 @@
|
||||
#include <utils/Allocator.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <exception>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#if !defined(NDEBUG) && UTILS_HAS_RTTI
|
||||
# define HANDLE_TYPE_SAFETY 1
|
||||
#else
|
||||
# define HANDLE_TYPE_SAFETY 0
|
||||
#endif
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define HandleAllocatorGL HandleAllocator<16, 64, 208>
|
||||
#define HandleAllocatorVK HandleAllocator<16, 64, 880>
|
||||
#define HandleAllocatorMTL HandleAllocator<16, 64, 584>
|
||||
#define HandleAllocatorGL HandleAllocator<24, 64, 136> // ~3640 / pool / MiB
|
||||
#define HandleAllocatorVK HandleAllocator<80, 176, 320> // ~1820 / pool / MiB
|
||||
#define HandleAllocatorMTL HandleAllocator<48, 160, 592> // ~1310 / pool / MiB
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
/*
|
||||
* A utility class to efficiently allocate and manage Handle<>
|
||||
*/
|
||||
template <size_t P0, size_t P1, size_t P2>
|
||||
template<size_t P0, size_t P1, size_t P2>
|
||||
class HandleAllocator {
|
||||
public:
|
||||
|
||||
HandleAllocator(const char* name, size_t size) noexcept;
|
||||
HandleAllocator(HandleAllocator const& rhs) = delete;
|
||||
HandleAllocator& operator=(HandleAllocator const& rhs) = delete;
|
||||
@@ -61,14 +66,9 @@ public:
|
||||
*/
|
||||
template<typename D, typename ... ARGS>
|
||||
Handle<D> allocateAndConstruct(ARGS&& ... args) noexcept {
|
||||
Handle<D> h{ allocateHandle<sizeof(D)>() };
|
||||
Handle<D> h{ allocateHandle<D>() };
|
||||
D* addr = handle_cast<D*>(h);
|
||||
new(addr) D(std::forward<ARGS>(args)...);
|
||||
#if HANDLE_TYPE_SAFETY
|
||||
mLock.lock();
|
||||
mHandleTypeId[addr] = typeid(D).name();
|
||||
mLock.unlock();
|
||||
#endif
|
||||
return h;
|
||||
}
|
||||
|
||||
@@ -84,13 +84,7 @@ public:
|
||||
*/
|
||||
template<typename D>
|
||||
Handle<D> allocate() noexcept {
|
||||
Handle<D> h{ allocateHandle<sizeof(D)>() };
|
||||
#if HANDLE_TYPE_SAFETY
|
||||
D* addr = handle_cast<D*>(h);
|
||||
mLock.lock();
|
||||
mHandleTypeId[addr] = typeid(D).name();
|
||||
mLock.unlock();
|
||||
#endif
|
||||
Handle<D> h{ allocateHandle<D>() };
|
||||
return h;
|
||||
}
|
||||
|
||||
@@ -107,17 +101,10 @@ public:
|
||||
assert_invariant(handle);
|
||||
D* addr = handle_cast<D*>(const_cast<Handle<B>&>(handle));
|
||||
assert_invariant(addr);
|
||||
|
||||
// currently we implement construct<> with dtor+ctor, we could use operator= also
|
||||
// but all our dtors are trivial, ~D() is actually a noop.
|
||||
addr->~D();
|
||||
new(addr) D(std::forward<ARGS>(args)...);
|
||||
|
||||
#if HANDLE_TYPE_SAFETY
|
||||
mLock.lock();
|
||||
mHandleTypeId[addr] = typeid(D).name();
|
||||
mLock.unlock();
|
||||
#endif
|
||||
return addr;
|
||||
}
|
||||
|
||||
@@ -134,12 +121,6 @@ public:
|
||||
D* addr = handle_cast<D*>(const_cast<Handle<B>&>(handle));
|
||||
assert_invariant(addr);
|
||||
new(addr) D(std::forward<ARGS>(args)...);
|
||||
|
||||
#if HANDLE_TYPE_SAFETY
|
||||
mLock.lock();
|
||||
mHandleTypeId[addr] = typeid(D).name();
|
||||
mLock.unlock();
|
||||
#endif
|
||||
return addr;
|
||||
}
|
||||
|
||||
@@ -155,19 +136,8 @@ public:
|
||||
void deallocate(Handle<B>& handle, D const* p) noexcept {
|
||||
// allow to destroy the nullptr, similarly to operator delete
|
||||
if (p) {
|
||||
#if HANDLE_TYPE_SAFETY
|
||||
mLock.lock();
|
||||
auto typeId = mHandleTypeId[p];
|
||||
mHandleTypeId.erase(p);
|
||||
mLock.unlock();
|
||||
if (UTILS_UNLIKELY(typeId != typeid(D).name())) {
|
||||
utils::slog.e << "Destroying handle " << handle.getId() << ", type " << typeid(D).name()
|
||||
<< ", but handle's actual type is " << typeId << utils::io::endl;
|
||||
std::terminate();
|
||||
}
|
||||
#endif
|
||||
p->~D();
|
||||
deallocateHandle<sizeof(D)>(handle.getId());
|
||||
deallocateHandle<D>(handle.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +165,17 @@ public:
|
||||
std::is_base_of_v<B, typename std::remove_pointer_t<Dp>>, Dp>
|
||||
handle_cast(Handle<B>& handle) noexcept {
|
||||
assert_invariant(handle);
|
||||
void* const p = handleToPointer(handle.getId());
|
||||
auto [p, tag] = handleToPointer(handle.getId());
|
||||
|
||||
if (isPoolHandle(handle.getId())) {
|
||||
// check for use after free
|
||||
uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
|
||||
auto const pNode = static_cast<typename Allocator::Node*>(p);
|
||||
uint8_t const expectedAge = pNode[-1].age;
|
||||
ASSERT_POSTCONDITION(expectedAge == age,
|
||||
"use-after-free of Handle with id=%d", handle.getId());
|
||||
}
|
||||
|
||||
return static_cast<Dp>(p);
|
||||
}
|
||||
|
||||
@@ -210,29 +190,57 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
// template <int P0, int P1, int P2>
|
||||
template<typename D>
|
||||
static constexpr size_t getBucketSize() noexcept {
|
||||
if constexpr (sizeof(D) <= P0) { return P0; }
|
||||
if constexpr (sizeof(D) <= P1) { return P1; }
|
||||
static_assert(sizeof(D) <= P2);
|
||||
return P2;
|
||||
}
|
||||
|
||||
class Allocator {
|
||||
friend class HandleAllocator;
|
||||
utils::PoolAllocator<P0, 16> mPool0;
|
||||
utils::PoolAllocator<P1, 16> mPool1;
|
||||
utils::PoolAllocator<P2, 16> mPool2;
|
||||
static constexpr size_t MIN_ALIGNMENT = alignof(std::max_align_t);
|
||||
struct Node { uint8_t age; };
|
||||
// Note: using the `extra` parameter of PoolAllocator<>, even with a 1-byte structure,
|
||||
// generally increases all pool allocations by 8-bytes because of alignment restrictions.
|
||||
template<size_t SIZE>
|
||||
using Pool = utils::PoolAllocator<SIZE, MIN_ALIGNMENT, sizeof(Node)>;
|
||||
Pool<P0> mPool0;
|
||||
Pool<P1> mPool1;
|
||||
Pool<P2> mPool2;
|
||||
UTILS_UNUSED_IN_RELEASE const utils::AreaPolicy::HeapArea& mArea;
|
||||
public:
|
||||
static constexpr size_t MIN_ALIGNMENT_SHIFT = 4;
|
||||
explicit Allocator(const utils::AreaPolicy::HeapArea& area);
|
||||
|
||||
static constexpr size_t getAlignment() noexcept { return MIN_ALIGNMENT; }
|
||||
|
||||
// this is in fact always called with a constexpr size argument
|
||||
[[nodiscard]] inline void* alloc(size_t size, size_t alignment, size_t extra) noexcept {
|
||||
[[nodiscard]] inline void* alloc(size_t size, size_t, size_t, uint8_t* outAge) noexcept {
|
||||
void* p = nullptr;
|
||||
if (size <= mPool0.getSize()) p = mPool0.alloc(size, 16, extra);
|
||||
else if (size <= mPool1.getSize()) p = mPool1.alloc(size, 16, extra);
|
||||
else if (size <= mPool2.getSize()) p = mPool2.alloc(size, 16, extra);
|
||||
if (size <= mPool0.getSize()) p = mPool0.alloc(size);
|
||||
else if (size <= mPool1.getSize()) p = mPool1.alloc(size);
|
||||
else if (size <= mPool2.getSize()) p = mPool2.alloc(size);
|
||||
if (UTILS_LIKELY(p)) {
|
||||
Node const* const pNode = static_cast<Node const*>(p);
|
||||
// we are guaranteed to have at least sizeof<Node> bytes of extra storage before
|
||||
// the allocation address.
|
||||
*outAge = pNode[-1].age;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
// this is in fact always called with a constexpr size argument
|
||||
inline void free(void* p, size_t size) noexcept {
|
||||
inline void free(void* p, size_t size, uint8_t age) noexcept {
|
||||
assert_invariant(p >= mArea.begin() && (char*)p + size <= (char*)mArea.end());
|
||||
|
||||
// check for double-free
|
||||
Node* const pNode = static_cast<Node*>(p);
|
||||
uint8_t& expectedAge = pNode[-1].age;
|
||||
ASSERT_POSTCONDITION(expectedAge == age,
|
||||
"double-free of Handle of size %d at %p", size, p);
|
||||
expectedAge = (expectedAge + 1) & 0xF; // fixme
|
||||
|
||||
if (size <= mPool0.getSize()) { mPool0.free(p); return; }
|
||||
if (size <= mPool1.getSize()) { mPool1.free(p); return; }
|
||||
if (size <= mPool2.getSize()) { mPool2.free(p); return; }
|
||||
@@ -254,24 +262,16 @@ private:
|
||||
// allocateHandle()/deallocateHandle() selects the pool to use at compile-time based on the
|
||||
// allocation size this is always inlined, because all these do is to call
|
||||
// allocateHandleInPool()/deallocateHandleFromPool() with the right pool size.
|
||||
template<size_t SIZE>
|
||||
template<typename D>
|
||||
HandleBase::HandleId allocateHandle() noexcept {
|
||||
if constexpr (SIZE <= P0) { return allocateHandleInPool<P0>(); }
|
||||
if constexpr (SIZE <= P1) { return allocateHandleInPool<P1>(); }
|
||||
static_assert(SIZE <= P2);
|
||||
return allocateHandleInPool<P2>();
|
||||
constexpr size_t BUCKET_SIZE = getBucketSize<D>();
|
||||
return allocateHandleInPool<BUCKET_SIZE>();
|
||||
}
|
||||
|
||||
template<size_t SIZE>
|
||||
template<typename D>
|
||||
void deallocateHandle(HandleBase::HandleId id) noexcept {
|
||||
if constexpr (SIZE <= P0) {
|
||||
deallocateHandleFromPool<P0>(id);
|
||||
} else if constexpr (SIZE <= P1) {
|
||||
deallocateHandleFromPool<P1>(id);
|
||||
} else {
|
||||
static_assert(SIZE <= P2);
|
||||
deallocateHandleFromPool<P2>(id);
|
||||
}
|
||||
constexpr size_t BUCKET_SIZE = getBucketSize<D>();
|
||||
deallocateHandleFromPool<BUCKET_SIZE>(id);
|
||||
}
|
||||
|
||||
// allocateHandleInPool()/deallocateHandleFromPool() is NOT inlined, which will cause three
|
||||
@@ -280,9 +280,11 @@ private:
|
||||
template<size_t SIZE>
|
||||
UTILS_NOINLINE
|
||||
HandleBase::HandleId allocateHandleInPool() noexcept {
|
||||
void* p = mHandleArena.alloc(SIZE);
|
||||
uint8_t age;
|
||||
void* p = mHandleArena.alloc(SIZE, alignof(std::max_align_t), 0, &age);
|
||||
if (UTILS_LIKELY(p)) {
|
||||
return pointerToHandle(p);
|
||||
uint32_t const tag = (uint32_t(age) << HANDLE_AGE_SHIFT) & HANDLE_AGE_MASK;
|
||||
return arenaPointerToHandle(p, tag);
|
||||
} else {
|
||||
return allocateHandleSlow(SIZE);
|
||||
}
|
||||
@@ -292,42 +294,51 @@ private:
|
||||
UTILS_NOINLINE
|
||||
void deallocateHandleFromPool(HandleBase::HandleId id) noexcept {
|
||||
if (UTILS_LIKELY(isPoolHandle(id))) {
|
||||
void* p = handleToPointer(id);
|
||||
mHandleArena.free(p, SIZE);
|
||||
auto [p, tag] = handleToPointer(id);
|
||||
uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
|
||||
mHandleArena.free(p, SIZE, age);
|
||||
} else {
|
||||
deallocateHandleSlow(id, SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr uint32_t HEAP_HANDLE_FLAG = 0x80000000u;
|
||||
// we handle a 4 bits age per address
|
||||
static constexpr uint32_t HANDLE_HEAP_FLAG = 0x80000000u; // pool vs heap handle
|
||||
static constexpr uint32_t HANDLE_AGE_MASK = 0x78000000u; // handle's age
|
||||
static constexpr uint32_t HANDLE_INDEX_MASK = 0x07FFFFFFu; // handle index
|
||||
static constexpr uint32_t HANDLE_TAG_MASK = HANDLE_AGE_MASK;
|
||||
static constexpr uint32_t HANDLE_AGE_SHIFT = 27;
|
||||
|
||||
static bool isPoolHandle(HandleBase::HandleId id) noexcept {
|
||||
return (id & HEAP_HANDLE_FLAG) == 0u;
|
||||
return (id & HANDLE_HEAP_FLAG) == 0u;
|
||||
}
|
||||
|
||||
HandleBase::HandleId allocateHandleSlow(size_t size) noexcept;
|
||||
void deallocateHandleSlow(HandleBase::HandleId id, size_t size) noexcept;
|
||||
|
||||
// We inline this because it's just 4 instructions in the fast case
|
||||
inline void* handleToPointer(HandleBase::HandleId id) const noexcept {
|
||||
inline std::pair<void*, uint32_t> handleToPointer(HandleBase::HandleId id) const noexcept {
|
||||
// note: the null handle will end-up returning nullptr b/c it'll be handled as
|
||||
// a non-pool handle.
|
||||
if (UTILS_LIKELY(isPoolHandle(id))) {
|
||||
char* const base = (char*)mHandleArena.getArea().begin();
|
||||
size_t offset = id << Allocator::MIN_ALIGNMENT_SHIFT;
|
||||
return static_cast<void*>(base + offset);
|
||||
uint32_t const tag = id & HANDLE_TAG_MASK;
|
||||
size_t const offset = (id & HANDLE_INDEX_MASK) * Allocator::getAlignment();
|
||||
return { static_cast<void*>(base + offset), tag };
|
||||
}
|
||||
return handleToPointerSlow(id);
|
||||
return { handleToPointerSlow(id), 0 };
|
||||
}
|
||||
|
||||
void* handleToPointerSlow(HandleBase::HandleId id) const noexcept;
|
||||
|
||||
// We inline this because it's just 3 instructions
|
||||
inline HandleBase::HandleId pointerToHandle(void* p) const noexcept {
|
||||
inline HandleBase::HandleId arenaPointerToHandle(void* p, uint32_t tag) const noexcept {
|
||||
char* const base = (char*)mHandleArena.getArea().begin();
|
||||
size_t offset = (char*)p - base;
|
||||
auto id = HandleBase::HandleId(offset >> Allocator::MIN_ALIGNMENT_SHIFT);
|
||||
assert_invariant((id & HEAP_HANDLE_FLAG) == 0);
|
||||
size_t const offset = (char*)p - base;
|
||||
assert_invariant((offset % Allocator::getAlignment()) == 0);
|
||||
auto id = HandleBase::HandleId(offset / Allocator::getAlignment());
|
||||
id |= tag & HANDLE_TAG_MASK;
|
||||
assert_invariant((id & HANDLE_HEAP_FLAG) == 0);
|
||||
return id;
|
||||
}
|
||||
|
||||
@@ -337,9 +348,6 @@ private:
|
||||
mutable utils::Mutex mLock;
|
||||
tsl::robin_map<HandleBase::HandleId, void*> mOverflowMap;
|
||||
HandleBase::HandleId mId = 0;
|
||||
#if HANDLE_TYPE_SAFETY
|
||||
mutable std::unordered_map<const void*, const char*> mHandleTypeId;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -19,9 +19,9 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_PLATFORM_FACTORY_H
|
||||
#define TNT_FILAMENT_BACKEND_PLATFORM_FACTORY_H
|
||||
|
||||
#include "backend/DriverEnums.h"
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include "utils/compiler.h"
|
||||
#include <utils/compiler.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
|
||||
@@ -17,15 +17,13 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_SAMPLERGROUP_H
|
||||
#define TNT_FILAMENT_BACKEND_PRIVATE_SAMPLERGROUP_H
|
||||
|
||||
#include "backend/DriverApiForward.h"
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <backend/DriverApiForward.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/SamplerDescriptor.h>
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -16,6 +16,14 @@
|
||||
|
||||
#include "private/backend/CircularBuffer.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/architecture.h>
|
||||
#include <utils/ashmem.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#if !defined(WIN32) && !defined(__EMSCRIPTEN__) && !defined(IOS)
|
||||
# include <sys/mman.h>
|
||||
# include <unistd.h>
|
||||
@@ -24,23 +32,20 @@
|
||||
# define HAS_MMAP 0
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <utils/architecture.h>
|
||||
#include <utils/ashmem.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
using namespace utils;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
size_t CircularBuffer::sPageSize = arch::getPageSize();
|
||||
|
||||
CircularBuffer::CircularBuffer(size_t size) {
|
||||
CircularBuffer::CircularBuffer(size_t size)
|
||||
: mSize(size) {
|
||||
mData = alloc(size);
|
||||
mSize = size;
|
||||
mTail = mData;
|
||||
mHead = mData;
|
||||
}
|
||||
@@ -85,7 +90,7 @@ void* CircularBuffer::alloc(size_t size) noexcept {
|
||||
MAP_PRIVATE, fd, (off_t)size);
|
||||
if (vaddr_guard != MAP_FAILED && (vaddr_guard == (char*)vaddr_shadow + size)) {
|
||||
// woo-hoo success!
|
||||
mUsesAshmem = fd;
|
||||
mAshmemFd = fd;
|
||||
data = vaddr;
|
||||
}
|
||||
}
|
||||
@@ -93,7 +98,7 @@ void* CircularBuffer::alloc(size_t size) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(mUsesAshmem < 0)) {
|
||||
if (UTILS_UNLIKELY(mAshmemFd < 0)) {
|
||||
// ashmem failed
|
||||
if (vaddr_guard != MAP_FAILED) {
|
||||
munmap(vaddr_guard, size);
|
||||
@@ -137,9 +142,9 @@ void CircularBuffer::dealloc() noexcept {
|
||||
if (mData) {
|
||||
size_t const BLOCK_SIZE = getBlockSize();
|
||||
munmap(mData, mSize * 2 + BLOCK_SIZE);
|
||||
if (mUsesAshmem >= 0) {
|
||||
close(mUsesAshmem);
|
||||
mUsesAshmem = -1;
|
||||
if (mAshmemFd >= 0) {
|
||||
close(mAshmemFd);
|
||||
mAshmemFd = -1;
|
||||
}
|
||||
}
|
||||
#else
|
||||
@@ -149,23 +154,37 @@ void CircularBuffer::dealloc() noexcept {
|
||||
}
|
||||
|
||||
|
||||
void CircularBuffer::circularize() noexcept {
|
||||
if (mUsesAshmem > 0) {
|
||||
intptr_t const overflow = intptr_t(mHead) - (intptr_t(mData) + ssize_t(mSize));
|
||||
if (overflow >= 0) {
|
||||
assert_invariant(size_t(overflow) <= mSize);
|
||||
mHead = (void *) (intptr_t(mData) + overflow);
|
||||
#ifndef NDEBUG
|
||||
memset(mData, 0xA5, size_t(overflow));
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
// Only circularize if mHead if in the second buffer.
|
||||
if (intptr_t(mHead) - intptr_t(mData) > ssize_t(mSize)) {
|
||||
CircularBuffer::Range CircularBuffer::getBuffer() noexcept {
|
||||
Range const range{ .tail = mTail, .head = mHead };
|
||||
|
||||
char* const pData = static_cast<char*>(mData);
|
||||
char const* const pEnd = pData + mSize;
|
||||
char const* const pHead = static_cast<char const*>(mHead);
|
||||
if (UTILS_UNLIKELY(pHead >= pEnd)) {
|
||||
size_t const overflow = pHead - pEnd;
|
||||
if (UTILS_LIKELY(mAshmemFd > 0)) {
|
||||
assert_invariant(overflow <= mSize);
|
||||
mHead = static_cast<void*>(pData + overflow);
|
||||
// Data Tail End Head [virtual]
|
||||
// v v v v
|
||||
// +-------------:----+-----:--------------+
|
||||
// | : | : |
|
||||
// +-----:------------+--------------------+
|
||||
// Head |<------ copy ------>| [physical]
|
||||
} else {
|
||||
// Data Tail End Head
|
||||
// v v v v
|
||||
// +-------------:----+-----:--------------+
|
||||
// | : | : |
|
||||
// +-----|------------+-----|--------------+
|
||||
// |<---------------->|
|
||||
// sliding window
|
||||
mHead = mData;
|
||||
}
|
||||
}
|
||||
mTail = mHead;
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -15,14 +15,25 @@
|
||||
*/
|
||||
|
||||
#include "private/backend/CommandBufferQueue.h"
|
||||
#include "private/backend/CircularBuffer.h"
|
||||
#include "private/backend/CommandStream.h"
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Systrace.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Systrace.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include "private/backend/BackendUtils.h"
|
||||
#include "private/backend/CommandStream.h"
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
#include <iterator>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
using namespace utils;
|
||||
|
||||
@@ -65,50 +76,53 @@ void CommandBufferQueue::flush() noexcept {
|
||||
// always guaranteed to have enough space for the NoopCommand
|
||||
new(circularBuffer.allocate(sizeof(NoopCommand))) NoopCommand(nullptr);
|
||||
|
||||
// end of this slice
|
||||
void* const head = circularBuffer.getHead();
|
||||
const size_t requiredSize = mRequiredSize;
|
||||
|
||||
// beginning of this slice
|
||||
void* const tail = circularBuffer.getTail();
|
||||
// get the current buffer
|
||||
auto const [begin, end] = circularBuffer.getBuffer();
|
||||
|
||||
// size of this slice
|
||||
uint32_t const used = uint32_t(intptr_t(head) - intptr_t(tail));
|
||||
assert_invariant(circularBuffer.empty());
|
||||
|
||||
circularBuffer.circularize();
|
||||
// size of the current buffer
|
||||
size_t const used = std::distance(
|
||||
static_cast<char const*>(begin), static_cast<char const*>(end));
|
||||
|
||||
std::unique_lock<utils::Mutex> lock(mLock);
|
||||
mCommandBuffersToExecute.push_back({ tail, head });
|
||||
mCommandBuffersToExecute.push_back({ begin, end });
|
||||
mCondition.notify_one();
|
||||
|
||||
// circular buffer is too small, we corrupted the stream
|
||||
ASSERT_POSTCONDITION(used <= mFreeSpace,
|
||||
"Backend CommandStream overflow. Commands are corrupted and unrecoverable.\n"
|
||||
"Please increase minCommandBufferSizeMB inside the Config passed to Engine::create.\n"
|
||||
"Space used at this time: %u bytes",
|
||||
(unsigned)used);
|
||||
"Space used at this time: %u bytes, overflow: %u bytes",
|
||||
(unsigned)used, unsigned(used - mFreeSpace));
|
||||
|
||||
// wait until there is enough space in the buffer
|
||||
mFreeSpace -= used;
|
||||
const size_t requiredSize = mRequiredSize;
|
||||
if (UTILS_UNLIKELY(mFreeSpace < requiredSize)) {
|
||||
|
||||
|
||||
#ifndef NDEBUG
|
||||
size_t totalUsed = circularBuffer.size() - mFreeSpace;
|
||||
mHighWatermark = std::max(mHighWatermark, totalUsed);
|
||||
if (UTILS_UNLIKELY(totalUsed > requiredSize)) {
|
||||
slog.d << "CommandStream used too much space: " << totalUsed
|
||||
<< ", out of " << requiredSize << " (will block)" << io::endl;
|
||||
}
|
||||
size_t const totalUsed = circularBuffer.size() - mFreeSpace;
|
||||
slog.d << "CommandStream used too much space (will block): "
|
||||
<< "needed space " << requiredSize << " out of " << mFreeSpace
|
||||
<< ", totalUsed=" << totalUsed << ", current=" << used
|
||||
<< ", queue size=" << mCommandBuffersToExecute.size() << " buffers"
|
||||
<< io::endl;
|
||||
|
||||
mHighWatermark = std::max(mHighWatermark, totalUsed);
|
||||
#endif
|
||||
|
||||
mCondition.notify_one();
|
||||
if (UTILS_LIKELY(mFreeSpace < requiredSize)) {
|
||||
SYSTRACE_NAME("waiting: CircularBuffer::flush()");
|
||||
mCondition.wait(lock, [this, requiredSize]() -> bool {
|
||||
// TODO: on macOS, we need to call pumpEvents from time to time
|
||||
return mFreeSpace >= requiredSize;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CommandBufferQueue::Slice> CommandBufferQueue::waitForCommands() const {
|
||||
std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() const {
|
||||
if (!UTILS_HAS_THREADING) {
|
||||
return std::move(mCommandBuffersToExecute);
|
||||
}
|
||||
@@ -123,7 +137,7 @@ std::vector<CommandBufferQueue::Slice> CommandBufferQueue::waitForCommands() con
|
||||
return std::move(mCommandBuffersToExecute);
|
||||
}
|
||||
|
||||
void CommandBufferQueue::releaseBuffer(CommandBufferQueue::Slice const& buffer) {
|
||||
void CommandBufferQueue::releaseBuffer(CommandBufferQueue::Range const& buffer) {
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
mFreeSpace += uintptr_t(buffer.end) - uintptr_t(buffer.begin);
|
||||
mCondition.notify_one();
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <backend/PixelBufferDescriptor.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
||||
@@ -21,7 +21,12 @@
|
||||
|
||||
#include <backend/AcquiredImage.h>
|
||||
#include <backend/BufferDescriptor.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <math/half.h>
|
||||
@@ -29,6 +34,13 @@
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
using namespace utils;
|
||||
using namespace filament::math;
|
||||
|
||||
@@ -119,7 +131,8 @@ void DriverBase::purge() noexcept {
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void DriverBase::scheduleDestroySlow(BufferDescriptor&& buffer) noexcept {
|
||||
scheduleCallback(buffer.getHandler(), [buffer = std::move(buffer)]() {
|
||||
auto const handler = buffer.getHandler();
|
||||
scheduleCallback(handler, [buffer = std::move(buffer)]() {
|
||||
// user callback is called when BufferDescriptor gets destroyed
|
||||
});
|
||||
}
|
||||
|
||||
@@ -49,24 +49,28 @@ struct AcquiredImage;
|
||||
struct HwBase {
|
||||
};
|
||||
|
||||
struct HwVertexBuffer : public HwBase {
|
||||
AttributeArray attributes{}; // 8 * MAX_VERTEX_ATTRIBUTE_COUNT
|
||||
uint32_t vertexCount{}; // 4
|
||||
|
||||
struct HwVertexBufferInfo : public HwBase {
|
||||
uint8_t bufferCount{}; // 1
|
||||
uint8_t attributeCount{}; // 1
|
||||
bool padding{}; // 1
|
||||
uint8_t bufferObjectsVersion{}; // 1 -> total struct is 136 bytes
|
||||
|
||||
HwVertexBuffer() noexcept = default;
|
||||
HwVertexBuffer(uint8_t bufferCount, uint8_t attributeCount, uint32_t elementCount,
|
||||
AttributeArray const& attributes) noexcept
|
||||
: attributes(attributes),
|
||||
vertexCount(elementCount),
|
||||
bufferCount(bufferCount),
|
||||
bool padding[2]{}; // 2
|
||||
HwVertexBufferInfo() noexcept = default;
|
||||
HwVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount) noexcept
|
||||
: bufferCount(bufferCount),
|
||||
attributeCount(attributeCount) {
|
||||
}
|
||||
};
|
||||
|
||||
struct HwVertexBuffer : public HwBase {
|
||||
uint32_t vertexCount{}; // 4
|
||||
uint8_t bufferObjectsVersion{0xff}; // 1
|
||||
bool padding[3]{}; // 2
|
||||
HwVertexBuffer() noexcept = default;
|
||||
explicit HwVertexBuffer(uint32_t vertextCount) noexcept
|
||||
: vertexCount(vertextCount) {
|
||||
}
|
||||
};
|
||||
|
||||
struct HwBufferObject : public HwBase {
|
||||
uint32_t byteCount{};
|
||||
|
||||
@@ -88,11 +92,6 @@ struct HwIndexBuffer : public HwBase {
|
||||
};
|
||||
|
||||
struct HwRenderPrimitive : public HwBase {
|
||||
uint32_t offset{};
|
||||
uint32_t minIndex{};
|
||||
uint32_t maxIndex{};
|
||||
uint32_t count{};
|
||||
uint32_t maxVertexCount{};
|
||||
PrimitiveType type = PrimitiveType::TRIANGLES;
|
||||
};
|
||||
|
||||
|
||||
@@ -16,9 +16,22 @@
|
||||
|
||||
#include "private/backend/HandleAllocator.h"
|
||||
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <utils/Allocator.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -28,14 +41,34 @@ template <size_t P0, size_t P1, size_t P2>
|
||||
UTILS_NOINLINE
|
||||
HandleAllocator<P0, P1, P2>::Allocator::Allocator(AreaPolicy::HeapArea const& area)
|
||||
: mArea(area) {
|
||||
// TODO: we probably need a better way to set the size of these pools
|
||||
const size_t unit = area.size() / 32;
|
||||
const size_t offsetPool1 = unit;
|
||||
const size_t offsetPool2 = 16 * unit;
|
||||
char* const p = (char*)area.begin();
|
||||
mPool0 = PoolAllocator< P0, 16>(p, p + offsetPool1);
|
||||
mPool1 = PoolAllocator< P1, 16>(p + offsetPool1, p + offsetPool2);
|
||||
mPool2 = PoolAllocator< P2, 16>(p + offsetPool2, area.end());
|
||||
|
||||
// The largest handle this allocator can generate currently depends on the architecture's
|
||||
// min alignment, typically 8 or 16 bytes.
|
||||
// e.g. On Android armv8, the alignment is 16 bytes, so for a 1 MiB heap, the largest handle
|
||||
// index will be 65536. Note that this is not the same as the number of handles (which
|
||||
// will always be less).
|
||||
// Because our maximum representable handle currently is 0x07FFFFFF, the maximum no-nonsensical
|
||||
// heap size is 2 GiB, which amounts to 7.6 millions handles per pool (in the GL case).
|
||||
size_t const maxHeapSize = std::min(area.size(), HANDLE_INDEX_MASK * getAlignment());
|
||||
|
||||
if (UTILS_UNLIKELY(maxHeapSize != area.size())) {
|
||||
slog.w << "HandleAllocator heap size reduced to "
|
||||
<< maxHeapSize << " from " << area.size() << io::endl;
|
||||
}
|
||||
|
||||
// make sure we start with a clean arena. This is needed to ensure that all blocks start
|
||||
// with an age of 0.
|
||||
memset(area.data(), 0, maxHeapSize);
|
||||
|
||||
// size the different pools so that they can all contain the same number of handles
|
||||
size_t const count = maxHeapSize / (P0 + P1 + P2);
|
||||
char* const p0 = static_cast<char*>(area.begin());
|
||||
char* const p1 = p0 + count * P0;
|
||||
char* const p2 = p1 + count * P1;
|
||||
|
||||
mPool0 = Pool<P0>(p0, count * P0);
|
||||
mPool1 = Pool<P1>(p1, count * P1);
|
||||
mPool2 = Pool<P2>(p2, count * P2);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
@@ -73,11 +106,17 @@ template <size_t P0, size_t P1, size_t P2>
|
||||
HandleBase::HandleId HandleAllocator<P0, P1, P2>::allocateHandleSlow(size_t size) noexcept {
|
||||
void* p = ::malloc(size);
|
||||
std::unique_lock lock(mLock);
|
||||
HandleBase::HandleId id = (++mId) | HEAP_HANDLE_FLAG;
|
||||
|
||||
HandleBase::HandleId id = (++mId) | HANDLE_HEAP_FLAG;
|
||||
|
||||
ASSERT_POSTCONDITION(mId < HANDLE_HEAP_FLAG,
|
||||
"No more Handle ids available! This can happen if HandleAllocator arena has been full"
|
||||
" for a while. Please increase FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB");
|
||||
|
||||
mOverflowMap.emplace(id, p);
|
||||
lock.unlock();
|
||||
|
||||
if (UTILS_UNLIKELY(id == (HEAP_HANDLE_FLAG|1u))) { // meaning id was zero
|
||||
if (UTILS_UNLIKELY(id == (HANDLE_HEAP_FLAG | 1u))) { // meaning id was zero
|
||||
PANIC_LOG("HandleAllocator arena is full, using slower system heap. Please increase "
|
||||
"the appropriate constant (e.g. FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB).");
|
||||
}
|
||||
@@ -86,7 +125,7 @@ HandleBase::HandleId HandleAllocator<P0, P1, P2>::allocateHandleSlow(size_t size
|
||||
|
||||
template <size_t P0, size_t P1, size_t P2>
|
||||
void HandleAllocator<P0, P1, P2>::deallocateHandleSlow(HandleBase::HandleId id, size_t) noexcept {
|
||||
assert_invariant(id & HEAP_HANDLE_FLAG);
|
||||
assert_invariant(id & HANDLE_HEAP_FLAG);
|
||||
void* p = nullptr;
|
||||
auto& overflowMap = mOverflowMap;
|
||||
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
#include <tsl/robin_map.h>
|
||||
#include <utils/Hash.h>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
namespace filament::backend {
|
||||
|
||||
struct MetalContext;
|
||||
|
||||
@@ -37,8 +36,7 @@ public:
|
||||
|
||||
struct BlitArgs {
|
||||
struct Attachment {
|
||||
id<MTLTexture> color = nil;
|
||||
id<MTLTexture> depth = nil;
|
||||
id<MTLTexture> texture = nil;
|
||||
MTLRegion region = {};
|
||||
uint8_t level = 0;
|
||||
uint32_t slice = 0; // must be 0 on source attachment
|
||||
@@ -46,25 +44,13 @@ public:
|
||||
|
||||
// Valid source formats: 2D, 2DArray, 2DMultisample, 3D
|
||||
// Valid destination formats: 2D, 2DArray, 3D, Cube
|
||||
Attachment source, destination;
|
||||
Attachment source;
|
||||
Attachment destination;
|
||||
SamplerMagFilter filter;
|
||||
|
||||
bool blitColor() const {
|
||||
return source.color != nil && destination.color != nil;
|
||||
}
|
||||
|
||||
bool blitDepth() const {
|
||||
return source.depth != nil && destination.depth != nil;
|
||||
}
|
||||
|
||||
bool colorDestinationIsFullAttachment() const {
|
||||
return destination.color.width == destination.region.size.width &&
|
||||
destination.color.height == destination.region.size.height;
|
||||
}
|
||||
|
||||
bool depthDestinationIsFullAttachment() const {
|
||||
return destination.depth.width == destination.region.size.width &&
|
||||
destination.depth.height == destination.region.size.height;
|
||||
bool destinationIsFullAttachment() const {
|
||||
return destination.texture.width == destination.region.size.width &&
|
||||
destination.texture.height == destination.region.size.height;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -78,48 +64,36 @@ public:
|
||||
|
||||
private:
|
||||
|
||||
static void setupColorAttachment(const BlitArgs& args, MTLRenderPassDescriptor* descriptor,
|
||||
uint32_t depthPlane);
|
||||
static void setupDepthAttachment(const BlitArgs& args, MTLRenderPassDescriptor* descriptor,
|
||||
uint32_t depthPlane);
|
||||
static void setupAttachment(MTLRenderPassAttachmentDescriptor* descriptor,
|
||||
const BlitArgs& args, uint32_t depthPlane);
|
||||
|
||||
struct BlitFunctionKey {
|
||||
bool blitColor;
|
||||
bool blitDepth;
|
||||
bool msaaColorSource;
|
||||
bool msaaDepthSource;
|
||||
bool sources3D;
|
||||
|
||||
char padding[3];
|
||||
bool msaaColorSource{};
|
||||
bool sources3D{};
|
||||
char padding[2]{};
|
||||
|
||||
bool isValid() const noexcept {
|
||||
// MSAA 3D textures do not exist.
|
||||
bool hasMsaa = msaaColorSource || msaaDepthSource;
|
||||
bool const hasMsaa = msaaColorSource;
|
||||
return !(hasMsaa && sources3D);
|
||||
}
|
||||
|
||||
bool operator==(const BlitFunctionKey& rhs) const noexcept {
|
||||
return blitColor == rhs.blitColor &&
|
||||
blitDepth == rhs.blitDepth &&
|
||||
msaaColorSource == rhs.msaaColorSource &&
|
||||
msaaDepthSource == rhs.msaaDepthSource &&
|
||||
return msaaColorSource == rhs.msaaColorSource &&
|
||||
sources3D == rhs.sources3D;
|
||||
}
|
||||
|
||||
BlitFunctionKey() {
|
||||
std::memset(this, 0, sizeof(BlitFunctionKey));
|
||||
}
|
||||
};
|
||||
|
||||
void blitFastPath(id<MTLCommandBuffer> cmdBuffer, bool& blitColor, bool& blitDepth,
|
||||
static bool blitFastPath(id<MTLCommandBuffer> cmdBuffer,
|
||||
const BlitArgs& args, const char* label);
|
||||
void blitSlowPath(id<MTLCommandBuffer> cmdBuffer, bool& blitColor, bool& blitDepth,
|
||||
|
||||
void blitSlowPath(id<MTLCommandBuffer> cmdBuffer,
|
||||
const BlitArgs& args, const char* label);
|
||||
void blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor, bool blitDepth,
|
||||
const BlitArgs& args, uint32_t depthPlaneSource, uint32_t depthPlaneDest,
|
||||
const char* label);
|
||||
id<MTLTexture> createIntermediateTexture(id<MTLTexture> t, MTLSize size);
|
||||
id<MTLFunction> compileFragmentFunction(BlitFunctionKey key);
|
||||
|
||||
void blitDepthPlane(id <MTLCommandBuffer> cmdBuffer, const BlitArgs& args,
|
||||
uint32_t depthPlaneSource, uint32_t depthPlaneDest, const char* label);
|
||||
|
||||
id<MTLFunction> compileFragmentFunction(BlitFunctionKey key) const;
|
||||
id<MTLFunction> getBlitVertexFunction();
|
||||
id<MTLFunction> getBlitFragmentFunction(BlitFunctionKey key);
|
||||
|
||||
@@ -130,10 +104,9 @@ private:
|
||||
tsl::robin_map<BlitFunctionKey, Function, HashFn> mBlitFunctions;
|
||||
|
||||
id<MTLFunction> mVertexFunction = nil;
|
||||
|
||||
};
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
} // namespace filament::backend
|
||||
|
||||
|
||||
#endif //TNT_METALBLITTER_H
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
#include "MetalUtils.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
namespace filament::backend {
|
||||
|
||||
static const char* functionLibrary = R"(
|
||||
#include <metal_stdlib>
|
||||
@@ -37,13 +37,7 @@ struct VertexOut
|
||||
|
||||
struct FragmentOut
|
||||
{
|
||||
#ifdef BLIT_COLOR
|
||||
float4 color [[color(0)]];
|
||||
#endif
|
||||
|
||||
#ifdef BLIT_DEPTH
|
||||
float depth [[depth(any)]];
|
||||
#endif
|
||||
};
|
||||
|
||||
vertex VertexOut
|
||||
@@ -67,7 +61,6 @@ fragment FragmentOut
|
||||
blitterFrag(VertexOut in [[stage_in]],
|
||||
sampler sourceSampler [[sampler(0)]],
|
||||
|
||||
#ifdef BLIT_COLOR
|
||||
#ifdef MSAA_COLOR_SOURCE
|
||||
texture2d_ms<float, access::read> sourceColor [[texture(0)]],
|
||||
#elif SOURCES_3D
|
||||
@@ -75,32 +68,18 @@ blitterFrag(VertexOut in [[stage_in]],
|
||||
#else
|
||||
texture2d<float, access::sample> sourceColor [[texture(0)]],
|
||||
#endif // MSAA_COLOR_SOURCE
|
||||
#endif // BLIT_COLOR
|
||||
|
||||
#ifdef BLIT_DEPTH
|
||||
#ifdef MSAA_DEPTH_SOURCE
|
||||
texture2d_ms<float, access::read> sourceDepth [[texture(1)]],
|
||||
#elif SOURCES_3D
|
||||
texture3d<float, access::sample> sourceDepth [[texture(1)]],
|
||||
#else
|
||||
texture2d<float, access::sample> sourceDepth [[texture(1)]],
|
||||
#endif // MSAA_DEPTH_SOURCE
|
||||
#endif // BLIT_DEPTH
|
||||
|
||||
constant FragmentArgs* args [[buffer(0)]])
|
||||
{
|
||||
FragmentOut out = {};
|
||||
|
||||
#if defined(BLIT_COLOR) || defined(BLIT_DEPTH)
|
||||
// These coordinates match the Vulkan vkCmdBlitImage spec:
|
||||
// https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/vkCmdBlitImage.html
|
||||
float2 uvbase = in.position.xy; // unnormalized coordinates at center of texel: (1.5, 2.5, etc)
|
||||
float2 uvoffset = uvbase - args->dstOffset;
|
||||
float2 uvscaled = uvoffset * args->scale;
|
||||
float2 uv = uvscaled + args->srcOffset;
|
||||
#endif
|
||||
|
||||
#ifdef BLIT_COLOR
|
||||
#ifdef MSAA_COLOR_SOURCE
|
||||
out.color = float4(0.0);
|
||||
for (uint s = 0; s < sourceColor.get_num_samples(); s++) {
|
||||
@@ -116,201 +95,106 @@ blitterFrag(VertexOut in [[stage_in]],
|
||||
float2 uvnorm = uv / float2(sourceColor.get_width(args->lod), sourceColor.get_height(args->lod));
|
||||
out.color += sourceColor.sample(sourceSampler, uvnorm, level(args->lod));
|
||||
#endif // MSAA_COLOR_SOURCE
|
||||
#endif // BLIT_COLOR
|
||||
|
||||
#ifdef BLIT_DEPTH
|
||||
#ifdef MSAA_DEPTH_SOURCE
|
||||
out.depth = 0.0;
|
||||
for (uint s = 0; s < sourceDepth.get_num_samples(); s++) {
|
||||
out.depth += sourceDepth.read(static_cast<uint2>(uv), s).r;
|
||||
}
|
||||
out.depth /= sourceDepth.get_num_samples();
|
||||
#elif SOURCES_3D
|
||||
float2 uvnormd = uv / float2(sourceDepth.get_width(args->lod), sourceDepth.get_height(args->lod));
|
||||
float3 coords = float3(uvnormd, (static_cast<float>(args->depthPlane) + 0.5) /
|
||||
sourceDepth.get_depth(args->lod));
|
||||
out.depth = sourceDepth.sample(sourceSampler, coords, level(args->lod)).r;
|
||||
#else
|
||||
float2 uvnormd = uv / float2(sourceDepth.get_width(args->lod), sourceDepth.get_height(args->lod));
|
||||
out.depth = sourceDepth.sample(sourceSampler, uvnormd, level(args->lod)).r;
|
||||
#endif // MSAA_DEPTH_SOURCE
|
||||
#endif // BLIT_DEPTH
|
||||
return out;
|
||||
}
|
||||
)";
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline bool MTLSizeEqual(T a, T b) noexcept {
|
||||
return (a.width == b.width && a.height == b.height && a.depth == b.depth);
|
||||
}
|
||||
|
||||
MetalBlitter::MetalBlitter(MetalContext& context) noexcept : mContext(context) { }
|
||||
|
||||
#define MTLSizeEqual(a, b) (a.width == b.width && a.height == b.height && a.depth == b.depth)
|
||||
|
||||
void MetalBlitter::blit(id<MTLCommandBuffer> cmdBuffer, const BlitArgs& args, const char* label) {
|
||||
bool blitColor = args.blitColor();
|
||||
bool blitDepth = args.blitDepth();
|
||||
|
||||
ASSERT_PRECONDITION(args.source.region.size.depth == args.destination.region.size.depth,
|
||||
"Blitting requires the source and destination regions to have the same depth.");
|
||||
|
||||
if (args.source.color && args.source.depth) {
|
||||
MTLTextureType colorType = args.source.color.textureType;
|
||||
MTLTextureType depthType = args.source.depth.textureType;
|
||||
|
||||
if (colorType == MTLTextureType2DMultisample) colorType = MTLTextureType2D;
|
||||
if (depthType == MTLTextureType2DMultisample) depthType = MTLTextureType2D;
|
||||
|
||||
ASSERT_PRECONDITION(colorType == depthType,
|
||||
"Blitting requires color and depth sources to be the same texture type.");
|
||||
}
|
||||
|
||||
// Determine if the blit for color or depth are eligible to use a MTLBlitCommandEncoder.
|
||||
// blitColor and / or blitDepth are set to false upon success, to indicate that no more work is
|
||||
// necessary for that attachment.
|
||||
blitFastPath(cmdBuffer, blitColor, blitDepth, args, label);
|
||||
|
||||
if (!blitColor && !blitDepth) {
|
||||
// blitFastPath returns true upon success.
|
||||
// blitFastPath fails if either the format or sampleCount don't match (i.e. resolve)
|
||||
if (blitFastPath(cmdBuffer, args, label)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the destination is MSAA and we weren't able to use the fast path, report an error, as
|
||||
// blitting to a MSAA texture isn't supported through the "slow path" yet.
|
||||
const bool colorDestinationIsMultisample =
|
||||
blitColor && args.destination.color.textureType == MTLTextureType2DMultisample;
|
||||
const bool depthDestinationIsMultisample =
|
||||
blitDepth && args.destination.depth.textureType == MTLTextureType2DMultisample;
|
||||
ASSERT_PRECONDITION(!colorDestinationIsMultisample && !depthDestinationIsMultisample,
|
||||
"Blitting between MSAA render targets with differing pixel formats and/or regions is not supported.");
|
||||
// If we end-up here, it means that either:
|
||||
// - we're resolving a color texture
|
||||
// - src/dest formats didn't match, or we're scaling -- this can only happen with the legacy
|
||||
// blit() path and implies that the format is not depth.
|
||||
// note: in the future we will support a fast-path resolve
|
||||
|
||||
// If the destination texture doesn't have the MTLTextureUsageRenderTarget flag, we have to blit
|
||||
// to an intermediate texture first to perform the format conversion. Then, we can perform a
|
||||
// "fast blit" to the final destination texture.
|
||||
UTILS_UNUSED_IN_RELEASE
|
||||
const bool destinationIsMultisample =
|
||||
args.destination.texture.textureType == MTLTextureType2DMultisample;
|
||||
|
||||
id<MTLTexture> intermediateColor = nil;
|
||||
id<MTLTexture> intermediateDepth = nil;
|
||||
BlitArgs slowBlit = args;
|
||||
BlitArgs finalBlit = args;
|
||||
|
||||
MTLRegion sourceRegionNoOffset = MTLRegionMake3D(0, 0, 0,
|
||||
args.source.region.size.width, args.source.region.size.height,
|
||||
args.source.region.size.depth);
|
||||
|
||||
if (blitColor && !(args.destination.color.usage & MTLTextureUsageRenderTarget)) {
|
||||
intermediateColor = createIntermediateTexture(args.destination.color, args.source.region.size);
|
||||
slowBlit.destination.color = finalBlit.source.color = intermediateColor;
|
||||
slowBlit.destination.level = finalBlit.source.level = 0;
|
||||
slowBlit.destination.slice = finalBlit.source.slice = 0;
|
||||
slowBlit.destination.region = finalBlit.source.region = sourceRegionNoOffset;
|
||||
}
|
||||
if (blitDepth && !(args.destination.depth.usage & MTLTextureUsageRenderTarget)) {
|
||||
intermediateDepth = createIntermediateTexture(args.destination.depth, args.source.region.size);
|
||||
slowBlit.destination.depth = finalBlit.source.depth = intermediateDepth;
|
||||
slowBlit.destination.level = finalBlit.source.level = 0;
|
||||
slowBlit.destination.slice = finalBlit.source.slice = 0;
|
||||
slowBlit.destination.region = finalBlit.source.region = sourceRegionNoOffset;
|
||||
}
|
||||
|
||||
blitSlowPath(cmdBuffer, blitColor, blitDepth, slowBlit, label);
|
||||
|
||||
bool finalBlitColor = intermediateColor != nil;
|
||||
bool finalBlitDepth = intermediateDepth != nil;
|
||||
blitFastPath(cmdBuffer, finalBlitColor, finalBlitDepth, finalBlit, label);
|
||||
assert_invariant(!destinationIsMultisample);
|
||||
assert_invariant((args.destination.texture.usage & MTLTextureUsageRenderTarget));
|
||||
blitSlowPath(cmdBuffer, args, label);
|
||||
}
|
||||
|
||||
void MetalBlitter::blitFastPath(id<MTLCommandBuffer> cmdBuffer, bool& blitColor, bool& blitDepth,
|
||||
bool MetalBlitter::blitFastPath(id<MTLCommandBuffer> cmdBuffer,
|
||||
const BlitArgs& args, const char* label) {
|
||||
if (blitColor) {
|
||||
if (args.source.color.sampleCount == args.destination.color.sampleCount &&
|
||||
args.source.color.pixelFormat == args.destination.color.pixelFormat &&
|
||||
MTLSizeEqual(args.source.region.size, args.destination.region.size)) {
|
||||
|
||||
id<MTLBlitCommandEncoder> blitEncoder = [cmdBuffer blitCommandEncoder];
|
||||
blitEncoder.label = @(label);
|
||||
[blitEncoder copyFromTexture:args.source.color
|
||||
sourceSlice:args.source.slice
|
||||
sourceLevel:args.source.level
|
||||
sourceOrigin:args.source.region.origin
|
||||
sourceSize:args.source.region.size
|
||||
toTexture:args.destination.color
|
||||
destinationSlice:args.destination.slice
|
||||
destinationLevel:args.destination.level
|
||||
destinationOrigin:args.destination.region.origin];
|
||||
[blitEncoder endEncoding];
|
||||
if (args.source.texture.sampleCount == args.destination.texture.sampleCount &&
|
||||
args.source.texture.pixelFormat == args.destination.texture.pixelFormat &&
|
||||
MTLSizeEqual(args.source.region.size, args.destination.region.size)) {
|
||||
|
||||
blitColor = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (blitDepth) {
|
||||
if (args.source.depth.sampleCount == args.destination.depth.sampleCount &&
|
||||
args.source.depth.pixelFormat == args.destination.depth.pixelFormat &&
|
||||
MTLSizeEqual(args.source.region.size, args.destination.region.size)) {
|
||||
|
||||
id<MTLBlitCommandEncoder> blitEncoder = [cmdBuffer blitCommandEncoder];
|
||||
blitEncoder.label = @(label);
|
||||
[blitEncoder copyFromTexture:args.source.depth
|
||||
sourceSlice:args.source.slice
|
||||
sourceLevel:args.source.level
|
||||
sourceOrigin:args.source.region.origin
|
||||
sourceSize:args.source.region.size
|
||||
toTexture:args.destination.depth
|
||||
destinationSlice:args.destination.slice
|
||||
destinationLevel:args.destination.level
|
||||
destinationOrigin:args.destination.region.origin];
|
||||
[blitEncoder endEncoding];
|
||||
|
||||
blitDepth = false;
|
||||
}
|
||||
id<MTLBlitCommandEncoder> const blitEncoder = [cmdBuffer blitCommandEncoder];
|
||||
blitEncoder.label = @(label);
|
||||
[blitEncoder copyFromTexture:args.source.texture
|
||||
sourceSlice:args.source.slice
|
||||
sourceLevel:args.source.level
|
||||
sourceOrigin:args.source.region.origin
|
||||
sourceSize:args.source.region.size
|
||||
toTexture:args.destination.texture
|
||||
destinationSlice:args.destination.slice
|
||||
destinationLevel:args.destination.level
|
||||
destinationOrigin:args.destination.region.origin];
|
||||
[blitEncoder endEncoding];
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MetalBlitter::blitSlowPath(id<MTLCommandBuffer> cmdBuffer, bool& blitColor, bool& blitDepth,
|
||||
void MetalBlitter::blitSlowPath(id<MTLCommandBuffer> cmdBuffer,
|
||||
const BlitArgs& args, const char* label) {
|
||||
|
||||
// scaling in any dimension is not allowed
|
||||
assert_invariant(args.source.region.size.depth == args.destination.region.size.depth);
|
||||
// we're always blitting a single plane
|
||||
uint32_t depthPlaneSource = args.source.region.origin.z;
|
||||
uint32_t depthPlaneDest = args.destination.region.origin.z;
|
||||
|
||||
assert_invariant(args.source.region.size.depth == args.destination.region.size.depth);
|
||||
uint32_t depthPlaneCount = args.source.region.size.depth;
|
||||
for (NSUInteger d = 0; d < depthPlaneCount; d++) {
|
||||
blitDepthPlane(cmdBuffer, blitColor, blitDepth, args, depthPlaneSource++, depthPlaneDest++,
|
||||
label);
|
||||
for (NSUInteger d = 0; d < args.source.region.size.depth; d++) {
|
||||
blitDepthPlane(cmdBuffer, args,
|
||||
depthPlaneSource++, depthPlaneDest++, label);
|
||||
}
|
||||
|
||||
blitColor = false;
|
||||
blitDepth = false;
|
||||
}
|
||||
|
||||
void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor, bool blitDepth,
|
||||
const BlitArgs& args, uint32_t depthPlaneSource, uint32_t depthPlaneDest,
|
||||
const char* label) {
|
||||
MTLRenderPassDescriptor* descriptor = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||
void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, const BlitArgs& args,
|
||||
uint32_t depthPlaneSource, uint32_t depthPlaneDest, const char* label) {
|
||||
|
||||
if (blitColor) {
|
||||
setupColorAttachment(args, descriptor, depthPlaneDest);
|
||||
}
|
||||
MTLRenderPassDescriptor* const descriptor = [MTLRenderPassDescriptor renderPassDescriptor];
|
||||
|
||||
if (blitDepth) {
|
||||
setupDepthAttachment(args, descriptor, depthPlaneDest);
|
||||
}
|
||||
setupAttachment(descriptor.colorAttachments[0], args, depthPlaneDest);
|
||||
|
||||
id<MTLRenderCommandEncoder> encoder = [cmdBuffer renderCommandEncoderWithDescriptor:descriptor];
|
||||
id<MTLRenderCommandEncoder> const encoder =
|
||||
[cmdBuffer renderCommandEncoderWithDescriptor:descriptor];
|
||||
encoder.label = @(label);
|
||||
|
||||
BlitFunctionKey key;
|
||||
key.blitColor = blitColor;
|
||||
key.blitDepth = blitDepth;
|
||||
key.msaaColorSource = blitColor && args.source.color.textureType == MTLTextureType2DMultisample;
|
||||
key.msaaDepthSource = blitDepth && args.source.depth.textureType == MTLTextureType2DMultisample;
|
||||
key.sources3D = blitColor && args.source.color.textureType == MTLTextureType3D;
|
||||
if (key.sources3D && blitDepth) {
|
||||
assert_invariant(args.source.depth.textureType == MTLTextureType3D);
|
||||
}
|
||||
id<MTLFunction> fragmentFunction = getBlitFragmentFunction(key);
|
||||
key.msaaColorSource = args.source.texture.textureType == MTLTextureType2DMultisample;
|
||||
key.sources3D = args.source.texture.textureType == MTLTextureType3D;
|
||||
id<MTLFunction> const fragmentFunction = getBlitFragmentFunction(key);
|
||||
|
||||
MetalPipelineState pipelineState {
|
||||
MetalPipelineState const pipelineState {
|
||||
.vertexFunction = getBlitVertexFunction(),
|
||||
.fragmentFunction = fragmentFunction,
|
||||
.vertexDescription = {},
|
||||
.colorAttachmentPixelFormat = {
|
||||
blitColor ? args.destination.color.pixelFormat : MTLPixelFormatInvalid,
|
||||
args.destination.texture.pixelFormat,
|
||||
MTLPixelFormatInvalid,
|
||||
MTLPixelFormatInvalid,
|
||||
MTLPixelFormatInvalid,
|
||||
@@ -319,31 +203,21 @@ void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor
|
||||
MTLPixelFormatInvalid,
|
||||
MTLPixelFormatInvalid
|
||||
},
|
||||
.depthAttachmentPixelFormat =
|
||||
blitDepth ? args.destination.depth.pixelFormat : MTLPixelFormatInvalid,
|
||||
.depthAttachmentPixelFormat = MTLPixelFormatInvalid,
|
||||
.sampleCount = 1,
|
||||
.blendState = {}
|
||||
};
|
||||
id<MTLRenderPipelineState> pipeline = mContext.pipelineStateCache.getOrCreateState(pipelineState);
|
||||
id<MTLRenderPipelineState> const pipeline =
|
||||
mContext.pipelineStateCache.getOrCreateState(pipelineState);
|
||||
[encoder setRenderPipelineState:pipeline];
|
||||
|
||||
// For texture arrays, create a view of the texture at the given slice (layer).
|
||||
id<MTLTexture> srcTextureColor = args.source.color;
|
||||
|
||||
id<MTLTexture> srcTextureColor = args.source.texture;
|
||||
if (srcTextureColor && srcTextureColor.textureType == MTLTextureType2DArray) {
|
||||
srcTextureColor = createTextureViewWithSingleSlice(srcTextureColor, args.source.slice);
|
||||
}
|
||||
id<MTLTexture> srcTextureDepth = args.source.depth;
|
||||
if (srcTextureDepth && srcTextureDepth.textureType == MTLTextureType2DArray) {
|
||||
srcTextureDepth = createTextureViewWithSingleSlice(srcTextureDepth, args.source.slice);
|
||||
}
|
||||
|
||||
if (blitColor) {
|
||||
[encoder setFragmentTexture:srcTextureColor atIndex:0];
|
||||
}
|
||||
|
||||
if (blitDepth) {
|
||||
[encoder setFragmentTexture:srcTextureDepth atIndex:1];
|
||||
}
|
||||
[encoder setFragmentTexture:srcTextureColor atIndex:0];
|
||||
|
||||
SamplerMinFilter filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST;
|
||||
if (args.filter == SamplerMagFilter::NEAREST) {
|
||||
@@ -352,13 +226,13 @@ void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor
|
||||
filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST;
|
||||
}
|
||||
|
||||
SamplerState s {
|
||||
SamplerState const s {
|
||||
.samplerParams = {
|
||||
.filterMag = args.filter,
|
||||
.filterMin = filterMin
|
||||
}
|
||||
};
|
||||
id<MTLSamplerState> sampler = mContext.samplerStateCache.getOrCreateState(s);
|
||||
id<MTLSamplerState> const sampler = mContext.samplerStateCache.getOrCreateState(s);
|
||||
[encoder setFragmentSamplerState:sampler atIndex:0];
|
||||
|
||||
MTLViewport viewport;
|
||||
@@ -370,11 +244,11 @@ void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor
|
||||
viewport.zfar = 1.0;
|
||||
[encoder setViewport:viewport];
|
||||
|
||||
DepthStencilState depthStencilState {
|
||||
DepthStencilState const depthStencilState {
|
||||
.depthCompare = MTLCompareFunctionAlways,
|
||||
.depthWriteEnabled = blitDepth
|
||||
.depthWriteEnabled = false
|
||||
};
|
||||
id<MTLDepthStencilState> depthStencil =
|
||||
id<MTLDepthStencilState> const depthStencil =
|
||||
mContext.depthStencilStateCache.getOrCreateState(depthStencilState);
|
||||
[encoder setDepthStencilState:depthStencil];
|
||||
|
||||
@@ -410,80 +284,42 @@ void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, bool blitColor
|
||||
[encoder endEncoding];
|
||||
}
|
||||
|
||||
id<MTLTexture> MetalBlitter::createIntermediateTexture(id<MTLTexture> t, MTLSize size) {
|
||||
MTLTextureDescriptor* descriptor = [MTLTextureDescriptor new];
|
||||
descriptor.textureType = size.depth == 1 ? MTLTextureType2D : MTLTextureType3D;
|
||||
descriptor.pixelFormat = t.pixelFormat;
|
||||
descriptor.width = size.width;
|
||||
descriptor.height = size.height;
|
||||
descriptor.depth = size.depth;
|
||||
descriptor.usage = t.usage & MTLTextureUsageRenderTarget;
|
||||
return [mContext.device newTextureWithDescriptor:descriptor];
|
||||
}
|
||||
|
||||
void MetalBlitter::shutdown() noexcept {
|
||||
mBlitFunctions.clear();
|
||||
mVertexFunction = nil;
|
||||
}
|
||||
|
||||
void MetalBlitter::setupColorAttachment(const BlitArgs& args,
|
||||
MTLRenderPassDescriptor* descriptor, uint32_t depthPlane) {
|
||||
descriptor.colorAttachments[0].texture = args.destination.color;
|
||||
descriptor.colorAttachments[0].level = args.destination.level;
|
||||
descriptor.colorAttachments[0].slice = args.destination.slice;
|
||||
descriptor.colorAttachments[0].depthPlane = depthPlane;
|
||||
|
||||
descriptor.colorAttachments[0].loadAction = MTLLoadActionLoad;
|
||||
void MetalBlitter::setupAttachment(MTLRenderPassAttachmentDescriptor* descriptor,
|
||||
const BlitArgs& args, uint32_t depthPlane) {
|
||||
descriptor.texture = args.destination.texture;
|
||||
descriptor.level = args.destination.level;
|
||||
descriptor.slice = args.destination.slice;
|
||||
descriptor.depthPlane = depthPlane;
|
||||
descriptor.loadAction = MTLLoadActionLoad;
|
||||
// We don't need to load the contents of the attachment if we're blitting over all of it.
|
||||
if (args.colorDestinationIsFullAttachment()) {
|
||||
descriptor.colorAttachments[0].loadAction = MTLLoadActionDontCare;
|
||||
if (args.destinationIsFullAttachment()) {
|
||||
descriptor.loadAction = MTLLoadActionDontCare;
|
||||
}
|
||||
|
||||
descriptor.colorAttachments[0].storeAction = MTLStoreActionStore;
|
||||
descriptor.storeAction = MTLStoreActionStore;
|
||||
}
|
||||
|
||||
void MetalBlitter::setupDepthAttachment(const BlitArgs& args, MTLRenderPassDescriptor* descriptor,
|
||||
uint32_t depthPlane) {
|
||||
descriptor.depthAttachment.texture = args.destination.depth;
|
||||
descriptor.depthAttachment.level = args.destination.level;
|
||||
descriptor.depthAttachment.slice = args.destination.slice;
|
||||
descriptor.depthAttachment.depthPlane = depthPlane;
|
||||
|
||||
descriptor.depthAttachment.loadAction = MTLLoadActionLoad;
|
||||
// We don't need to load the contents of the attachment if we're blitting over all of it.
|
||||
if (args.depthDestinationIsFullAttachment()) {
|
||||
descriptor.depthAttachment.loadAction = MTLLoadActionDontCare;
|
||||
}
|
||||
|
||||
descriptor.depthAttachment.storeAction = MTLStoreActionStore;
|
||||
}
|
||||
|
||||
id<MTLFunction> MetalBlitter::compileFragmentFunction(BlitFunctionKey key) {
|
||||
MTLCompileOptions* options = [MTLCompileOptions new];
|
||||
NSMutableDictionary* macros = [NSMutableDictionary dictionary];
|
||||
if (key.blitColor) {
|
||||
macros[@"BLIT_COLOR"] = @"1";
|
||||
}
|
||||
if (key.blitDepth) {
|
||||
macros[@"BLIT_DEPTH"] = @"1";
|
||||
}
|
||||
id<MTLFunction> MetalBlitter::compileFragmentFunction(BlitFunctionKey key) const {
|
||||
MTLCompileOptions* const options = [MTLCompileOptions new];
|
||||
NSMutableDictionary* const macros = [NSMutableDictionary dictionary];
|
||||
if (key.msaaColorSource) {
|
||||
macros[@"MSAA_COLOR_SOURCE"] = @"1";
|
||||
}
|
||||
if (key.msaaDepthSource) {
|
||||
macros[@"MSAA_DEPTH_SOURCE"] = @"1";
|
||||
}
|
||||
if (key.sources3D) {
|
||||
macros[@"SOURCES_3D"] = @"1";
|
||||
}
|
||||
options.preprocessorMacros = macros;
|
||||
NSString* objcSource = [NSString stringWithCString:functionLibrary
|
||||
encoding:NSUTF8StringEncoding];
|
||||
NSString* const objcSource = [NSString stringWithCString:functionLibrary
|
||||
encoding:NSUTF8StringEncoding];
|
||||
NSError* error = nil;
|
||||
id<MTLLibrary> library = [mContext.device newLibraryWithSource:objcSource
|
||||
options:options
|
||||
error:&error];
|
||||
id<MTLFunction> function = [library newFunctionWithName:@"blitterFrag"];
|
||||
id <MTLLibrary> const library = [mContext.device newLibraryWithSource:objcSource
|
||||
options:options
|
||||
error:&error];
|
||||
id <MTLFunction> const function = [library newFunctionWithName:@"blitterFrag"];
|
||||
|
||||
if (!library || !function) {
|
||||
if (error) {
|
||||
@@ -501,13 +337,14 @@ id<MTLFunction> MetalBlitter::getBlitVertexFunction() {
|
||||
return mVertexFunction;
|
||||
}
|
||||
|
||||
NSString* objcSource = [NSString stringWithCString:functionLibrary
|
||||
encoding:NSUTF8StringEncoding];
|
||||
NSString* const objcSource = [NSString stringWithCString:functionLibrary
|
||||
encoding:NSUTF8StringEncoding];
|
||||
NSError* error = nil;
|
||||
id<MTLLibrary> library = [mContext.device newLibraryWithSource:objcSource
|
||||
options:nil
|
||||
error:&error];
|
||||
id<MTLFunction> function = [library newFunctionWithName:@"blitterVertex"];
|
||||
id <MTLLibrary> const library = [mContext.device newLibraryWithSource:objcSource
|
||||
options:nil
|
||||
error:&error];
|
||||
|
||||
id<MTLFunction> const function = [library newFunctionWithName:@"blitterVertex"];
|
||||
|
||||
if (!library || !function) {
|
||||
if (error) {
|
||||
@@ -535,5 +372,5 @@ id<MTLFunction> MetalBlitter::getBlitFragmentFunction(BlitFunctionKey key) {
|
||||
return function;
|
||||
}
|
||||
|
||||
} // namespace backend
|
||||
} // namespace filament
|
||||
} // namespace filament::backend
|
||||
|
||||
|
||||
@@ -73,7 +73,10 @@ private:
|
||||
std::unordered_set<MetalBufferPoolEntry const*> mUsedStages;
|
||||
|
||||
// Store the current "time" (really just a frame count) and LRU eviction parameters.
|
||||
uint64_t mCurrentFrame = 0;
|
||||
// An atomic is necessary as mCurrentFrame is incremented in gc() (called on
|
||||
// the driver thread) and read from acquireBuffer() and releaseBuffer(),
|
||||
// which may be called on non-driver threads.
|
||||
std::atomic<uint64_t> mCurrentFrame = 0;
|
||||
static constexpr uint32_t TIME_BEFORE_EVICTION = 10;
|
||||
};
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "MetalContext.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/trap.h>
|
||||
|
||||
#include <thread>
|
||||
|
||||
@@ -142,7 +142,10 @@ struct MetalContext {
|
||||
// Fences, only supported on macOS 10.14 and iOS 12 and above.
|
||||
API_AVAILABLE(macos(10.14), ios(12.0))
|
||||
MTLSharedEventListener* eventListener = nil;
|
||||
uint64_t signalId = 1;
|
||||
// signalId is incremented in the MetalFence constructor, which is called on
|
||||
// both the driver (MetalTimerQueryFence::beginTimeElapsedQuery) and main
|
||||
// threads (in createFenceS), so an atomic is necessary.
|
||||
std::atomic<uint64_t> signalId = 1;
|
||||
|
||||
MetalTimerQueryInterface* timerQueryImpl;
|
||||
|
||||
|
||||
@@ -28,6 +28,10 @@
|
||||
#include <utils/Log.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
|
||||
@@ -52,13 +56,13 @@ class MetalDriver final : public DriverBase {
|
||||
|
||||
public:
|
||||
static Driver* create(MetalPlatform* platform, const Platform::DriverConfig& driverConfig);
|
||||
void runAtNextTick(const std::function<void()>& fn) noexcept;
|
||||
|
||||
private:
|
||||
|
||||
friend class MetalSwapChain;
|
||||
|
||||
MetalPlatform& mPlatform;
|
||||
|
||||
MetalContext* mContext;
|
||||
|
||||
ShaderModel getShaderModel() const noexcept final;
|
||||
@@ -66,6 +70,13 @@ private:
|
||||
// Overrides the default implementation by wrapping the call to fn in an @autoreleasepool block.
|
||||
void execute(std::function<void(void)> const& fn) noexcept final;
|
||||
|
||||
/*
|
||||
* Tasks run regularly on the driver thread.
|
||||
*/
|
||||
void executeTickOps() noexcept;
|
||||
std::vector<std::function<void()>> mTickOps;
|
||||
std::mutex mTickOpsLock;
|
||||
|
||||
/*
|
||||
* Driver interface
|
||||
*/
|
||||
@@ -122,12 +133,9 @@ private:
|
||||
mHandleAllocator.deallocate(handle, p);
|
||||
}
|
||||
|
||||
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph,
|
||||
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
|
||||
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh);
|
||||
|
||||
inline void setRenderPrimitiveRange(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
|
||||
uint32_t offset, uint32_t minIndex, uint32_t maxIndex, uint32_t count);
|
||||
|
||||
void finalizeSamplerGroup(MetalSamplerGroup* sg);
|
||||
void enumerateBoundBuffers(BufferObjectBinding bindingType,
|
||||
const std::function<void(const BufferState&, MetalBuffer*, uint32_t)>& f);
|
||||
|
||||
@@ -43,6 +43,40 @@ namespace filament {
|
||||
namespace backend {
|
||||
|
||||
Driver* MetalDriverFactory::create(MetalPlatform* const platform, const Platform::DriverConfig& driverConfig) {
|
||||
#if 0
|
||||
// this is useful for development, but too verbose even for debug builds
|
||||
// For reference on a 64-bits machine in Release mode:
|
||||
// MetalTimerQuery : 16 few
|
||||
// HwStream : 24 few
|
||||
// MetalIndexBuffer : 40 moderate
|
||||
// MetalFence : 48 few
|
||||
// MetalBufferObject : 48 many
|
||||
// -- less than or equal 48 bytes
|
||||
// MetalSamplerGroup : 112 few
|
||||
// MetalProgram : 144 moderate
|
||||
// MetalTexture : 152 moderate
|
||||
// MetalVertexBuffer : 152 moderate
|
||||
// -- less than or equal 160 bytes
|
||||
// MetalSwapChain : 184 few
|
||||
// MetalRenderTarget : 272 few
|
||||
// MetalRenderPrimitive : 584 many
|
||||
// -- less than or equal to 592 bytes
|
||||
|
||||
utils::slog.d
|
||||
<< "\nMetalSwapChain: " << sizeof(MetalSwapChain)
|
||||
<< "\nMetalBufferObject: " << sizeof(MetalBufferObject)
|
||||
<< "\nMetalVertexBuffer: " << sizeof(MetalVertexBuffer)
|
||||
<< "\nMetalIndexBuffer: " << sizeof(MetalIndexBuffer)
|
||||
<< "\nMetalSamplerGroup: " << sizeof(MetalSamplerGroup)
|
||||
<< "\nMetalRenderPrimitive: " << sizeof(MetalRenderPrimitive)
|
||||
<< "\nMetalTexture: " << sizeof(MetalTexture)
|
||||
<< "\nMetalTimerQuery: " << sizeof(MetalTimerQuery)
|
||||
<< "\nHwStream: " << sizeof(HwStream)
|
||||
<< "\nMetalRenderTarget: " << sizeof(MetalRenderTarget)
|
||||
<< "\nMetalFence: " << sizeof(MetalFence)
|
||||
<< "\nMetalProgram: " << sizeof(MetalProgram)
|
||||
<< utils::io::endl;
|
||||
#endif
|
||||
return MetalDriver::create(platform, driverConfig);
|
||||
}
|
||||
|
||||
@@ -166,6 +200,7 @@ MetalDriver::~MetalDriver() noexcept {
|
||||
}
|
||||
|
||||
void MetalDriver::tick(int) {
|
||||
executeTickOps();
|
||||
}
|
||||
|
||||
void MetalDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
|
||||
@@ -244,10 +279,16 @@ void MetalDriver::finish(int) {
|
||||
[oneOffBuffer waitUntilCompleted];
|
||||
}
|
||||
|
||||
void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint8_t bufferCount,
|
||||
uint8_t attributeCount, uint32_t vertexCount, AttributeArray attributes) {
|
||||
construct_handle<MetalVertexBuffer>(vbh, *mContext, bufferCount,
|
||||
attributeCount, vertexCount, attributes);
|
||||
void MetalDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
|
||||
uint8_t attributeCount, AttributeArray attributes) {
|
||||
construct_handle<MetalVertexBufferInfo>(vbih, *mContext,
|
||||
bufferCount, attributeCount, attributes);
|
||||
}
|
||||
|
||||
void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
|
||||
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih) {
|
||||
MetalVertexBufferInfo const* const vbi = handle_cast<const MetalVertexBufferInfo>(vbih);
|
||||
construct_handle<MetalVertexBuffer>(vbh, *mContext, vertexCount, vbi->bufferCount, vbih);
|
||||
}
|
||||
|
||||
void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
|
||||
@@ -315,11 +356,9 @@ void MetalDriver::createSamplerGroupR(
|
||||
|
||||
void MetalDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
|
||||
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
|
||||
PrimitiveType pt, uint32_t offset,
|
||||
uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
|
||||
PrimitiveType pt) {
|
||||
construct_handle<MetalRenderPrimitive>(rph);
|
||||
MetalDriver::setRenderPrimitiveBuffer(rph, vbh, ibh);
|
||||
MetalDriver::setRenderPrimitiveRange(rph, pt, offset, minIndex, maxIndex, count);
|
||||
MetalDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh);
|
||||
}
|
||||
|
||||
void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program) {
|
||||
@@ -405,6 +444,10 @@ void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
|
||||
// nothing to do, timer query was constructed in createTimerQueryS
|
||||
}
|
||||
|
||||
Handle<HwVertexBufferInfo> MetalDriver::createVertexBufferInfoS() noexcept {
|
||||
return alloc_handle<MetalVertexBufferInfo>();
|
||||
}
|
||||
|
||||
Handle<HwVertexBuffer> MetalDriver::createVertexBufferS() noexcept {
|
||||
return alloc_handle<MetalVertexBuffer>();
|
||||
}
|
||||
@@ -469,6 +512,12 @@ Handle<HwTimerQuery> MetalDriver::createTimerQueryS() noexcept {
|
||||
return alloc_and_construct_handle<MetalTimerQuery, HwTimerQuery>();
|
||||
}
|
||||
|
||||
void MetalDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
|
||||
if (vbih) {
|
||||
destruct_handle<MetalVertexBufferInfo>(vbih);
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
|
||||
if (vbh) {
|
||||
destruct_handle<MetalVertexBuffer>(vbh);
|
||||
@@ -583,6 +632,8 @@ void MetalDriver::terminate() {
|
||||
// This must be done before calling bufferPool->reset() to ensure no buffers are in flight.
|
||||
finish();
|
||||
|
||||
executeTickOps();
|
||||
|
||||
mContext->bufferPool->reset();
|
||||
mContext->commandQueue = nil;
|
||||
|
||||
@@ -719,14 +770,24 @@ bool MetalDriver::isSRGBSwapChainSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MetalDriver::isStereoSupported() {
|
||||
return true;
|
||||
bool MetalDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) {
|
||||
switch (stereoscopicType) {
|
||||
case backend::StereoscopicType::INSTANCED:
|
||||
return true;
|
||||
case backend::StereoscopicType::MULTIVIEW:
|
||||
// TODO: implement multiview feature in Metal.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool MetalDriver::isParallelShaderCompileSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MetalDriver::isDepthStencilResolveSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
|
||||
switch (workaround) {
|
||||
case Workaround::SPLIT_EASU:
|
||||
@@ -865,10 +926,6 @@ void MetalDriver::generateMipmaps(Handle<HwTexture> th) {
|
||||
tex->generateMipmaps();
|
||||
}
|
||||
|
||||
bool MetalDriver::canGenerateMipmaps() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescriptor&& data) {
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"updateSamplerGroup must be called outside of a render pass.");
|
||||
@@ -1026,23 +1083,14 @@ void MetalDriver::endRenderPass(int dummy) {
|
||||
mContext->currentRenderPassEncoder = nil;
|
||||
}
|
||||
|
||||
void MetalDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph,
|
||||
void MetalDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
|
||||
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh) {
|
||||
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
|
||||
auto vertexBuffer = handle_cast<MetalVertexBuffer>(vbh);
|
||||
auto indexBuffer = handle_cast<MetalIndexBuffer>(ibh);
|
||||
primitive->setBuffers(vertexBuffer, indexBuffer);
|
||||
}
|
||||
|
||||
void MetalDriver::setRenderPrimitiveRange(Handle<HwRenderPrimitive> rph,
|
||||
PrimitiveType pt, uint32_t offset, uint32_t minIndex, uint32_t maxIndex,
|
||||
uint32_t count) {
|
||||
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
|
||||
MetalVertexBufferInfo const* const vbi = handle_cast<MetalVertexBufferInfo>(vertexBuffer->vbih);
|
||||
primitive->setBuffers(vbi, vertexBuffer, indexBuffer);
|
||||
primitive->type = pt;
|
||||
primitive->offset = offset * primitive->indexBuffer->elementSize;
|
||||
primitive->count = count;
|
||||
primitive->minIndex = minIndex;
|
||||
primitive->maxIndex = maxIndex > minIndex ? maxIndex : primitive->maxVertexCount - 1;
|
||||
}
|
||||
|
||||
void MetalDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain> schRead) {
|
||||
@@ -1256,14 +1304,14 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
|
||||
textureDescriptor.usage = MTLTextureUsageShaderWrite | MTLTextureUsageRenderTarget;
|
||||
id<MTLTexture> readPixelsTexture = [mContext->device newTextureWithDescriptor:textureDescriptor];
|
||||
|
||||
MetalBlitter::BlitArgs args;
|
||||
MetalBlitter::BlitArgs args{};
|
||||
args.filter = SamplerMagFilter::NEAREST;
|
||||
args.source.level = miplevel;
|
||||
args.source.region = MTLRegionMake2D(0, 0, srcTexture.width >> miplevel, srcTexture.height >> miplevel);
|
||||
args.source.texture = srcTexture;
|
||||
args.destination.level = 0;
|
||||
args.destination.region = MTLRegionMake2D(0, 0, readPixelsTexture.width, readPixelsTexture.height);
|
||||
args.source.color = srcTexture;
|
||||
args.destination.color = readPixelsTexture;
|
||||
args.destination.texture = readPixelsTexture;
|
||||
|
||||
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "readPixels blit");
|
||||
|
||||
@@ -1299,24 +1347,123 @@ void MetalDriver::readBufferSubData(backend::BufferObjectHandle boh,
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void MetalDriver::blit(TargetBufferFlags buffers,
|
||||
void MetalDriver::resolve(
|
||||
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer,
|
||||
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer) {
|
||||
auto* const srcTexture = handle_cast<MetalTexture>(src);
|
||||
auto* const dstTexture = handle_cast<MetalTexture>(dst);
|
||||
assert_invariant(srcTexture);
|
||||
assert_invariant(dstTexture);
|
||||
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder == nil,
|
||||
"resolve() cannot be invoked inside a render pass.");
|
||||
|
||||
ASSERT_PRECONDITION(
|
||||
dstTexture->width == srcTexture->width && dstTexture->height == srcTexture->height,
|
||||
"invalid resolve: src and dst sizes don't match");
|
||||
|
||||
ASSERT_PRECONDITION(srcTexture->samples > 1 && dstTexture->samples == 1,
|
||||
"invalid resolve: src.samples=%u, dst.samples=%u",
|
||||
+srcTexture->samples, +dstTexture->samples);
|
||||
|
||||
ASSERT_PRECONDITION(srcTexture->format == dstTexture->format,
|
||||
"src and dst texture format don't match");
|
||||
|
||||
ASSERT_PRECONDITION(!isDepthFormat(srcTexture->format),
|
||||
"can't resolve depth formats");
|
||||
|
||||
ASSERT_PRECONDITION(!isStencilFormat(srcTexture->format),
|
||||
"can't resolve stencil formats");
|
||||
|
||||
ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
|
||||
"texture doesn't have BLIT_DST");
|
||||
|
||||
ASSERT_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC),
|
||||
"texture doesn't have BLIT_SRC");
|
||||
|
||||
// FIXME: on metal the blit() call below always take the slow path (using a shader)
|
||||
|
||||
blit( dst, dstLevel, dstLayer, {},
|
||||
src, srcLevel, srcLayer, {},
|
||||
{ dstTexture->width, dstTexture->height });
|
||||
}
|
||||
|
||||
void MetalDriver::blit(
|
||||
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer, math::uint2 dstOrigin,
|
||||
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer, math::uint2 srcOrigin,
|
||||
math::uint2 size) {
|
||||
|
||||
|
||||
auto isBlitableTextureType = [](MTLTextureType t) -> bool {
|
||||
return t == MTLTextureType2D || t == MTLTextureType2DMultisample ||
|
||||
t == MTLTextureType2DArray;
|
||||
};
|
||||
|
||||
auto* const srcTexture = handle_cast<MetalTexture>(src);
|
||||
auto* const dstTexture = handle_cast<MetalTexture>(dst);
|
||||
assert_invariant(srcTexture);
|
||||
assert_invariant(dstTexture);
|
||||
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder == nil,
|
||||
"blit() cannot be invoked inside a render pass.");
|
||||
|
||||
ASSERT_PRECONDITION(any(dstTexture->usage & TextureUsage::BLIT_DST),
|
||||
"texture doesn't have BLIT_DST");
|
||||
|
||||
ASSERT_PRECONDITION(any(srcTexture->usage & TextureUsage::BLIT_SRC),
|
||||
"texture doesn't have BLIT_SRC");
|
||||
|
||||
ASSERT_PRECONDITION(srcTexture->format == dstTexture->format,
|
||||
"src and dst texture format don't match");
|
||||
|
||||
ASSERT_PRECONDITION(isBlitableTextureType(srcTexture->getMtlTextureForRead().textureType) &&
|
||||
isBlitableTextureType(dstTexture->getMtlTextureForWrite().textureType),
|
||||
"Metal does not support blitting to/from non-2D textures.");
|
||||
|
||||
MetalBlitter::BlitArgs args{};
|
||||
args.filter = SamplerMagFilter::NEAREST;
|
||||
args.source.region = MTLRegionMake2D(
|
||||
(NSUInteger)srcOrigin.x,
|
||||
std::max(srcTexture->height - (int64_t)srcOrigin.y - size.y, (int64_t)0),
|
||||
size.x, size.y);
|
||||
args.destination.region = MTLRegionMake2D(
|
||||
(NSUInteger)dstOrigin.x,
|
||||
std::max(dstTexture->height - (int64_t)dstOrigin.y - size.y, (int64_t)0),
|
||||
size.x, size.y);
|
||||
|
||||
// FIXME: we shouldn't need to know the type here. This is an artifact of using the old API.
|
||||
|
||||
args.source.texture = srcTexture->getMtlTextureForRead();
|
||||
args.destination.texture = dstTexture->getMtlTextureForWrite();
|
||||
args.source.level = srcLevel;
|
||||
args.source.slice = srcLayer;
|
||||
args.destination.level = dstLevel;
|
||||
args.destination.slice = dstLayer;
|
||||
|
||||
// TODO: The blit() call below always take the fast path.
|
||||
|
||||
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "blit/resolve");
|
||||
|
||||
dstTexture->extendLodRangeTo(dstLevel);
|
||||
}
|
||||
|
||||
void MetalDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
Handle<HwRenderTarget> dst, Viewport dstRect,
|
||||
Handle<HwRenderTarget> src, Viewport srcRect,
|
||||
SamplerMagFilter filter) {
|
||||
// If we're the in middle of a render pass, finish it.
|
||||
// This condition should only occur during copyFrame. It's okay to end the render pass because
|
||||
// we don't issue any other rendering commands.
|
||||
if (mContext->currentRenderPassEncoder) {
|
||||
[mContext->currentRenderPassEncoder endEncoding];
|
||||
mContext->currentRenderPassEncoder = nil;
|
||||
}
|
||||
|
||||
// Note: blitDEPRECATED is only used by Renderer::copyFrame
|
||||
// It is called between beginFrame and endFrame, but should never be called in the middle of
|
||||
// a render pass.
|
||||
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder == nil,
|
||||
"blitDEPRECATED() cannot be invoked inside a render pass.");
|
||||
|
||||
auto srcTarget = handle_cast<MetalRenderTarget>(src);
|
||||
auto dstTarget = handle_cast<MetalRenderTarget>(dst);
|
||||
|
||||
ASSERT_PRECONDITION(
|
||||
!(buffers & (TargetBufferFlags::COLOR_ALL & ~TargetBufferFlags::COLOR0)),
|
||||
"Blitting only supports COLOR0");
|
||||
ASSERT_PRECONDITION(buffers == TargetBufferFlags::COLOR0,
|
||||
"blitDEPRECATED only supports COLOR0");
|
||||
|
||||
ASSERT_PRECONDITION(srcRect.left >= 0 && srcRect.bottom >= 0 &&
|
||||
dstRect.left >= 0 && dstRect.bottom >= 0,
|
||||
@@ -1327,54 +1474,28 @@ void MetalDriver::blit(TargetBufferFlags buffers,
|
||||
t == MTLTextureType2DArray;
|
||||
};
|
||||
|
||||
// MetalBlitter supports blitting color and depth simultaneously, but for simplicitly we'll blit
|
||||
// them separately. In practice, Filament only ever blits a single buffer at a time anyway.
|
||||
// We always blit from/to the COLOR0 attachment.
|
||||
MetalRenderTarget::Attachment const srcColorAttachment = srcTarget->getReadColorAttachment(0);
|
||||
MetalRenderTarget::Attachment const dstColorAttachment = dstTarget->getDrawColorAttachment(0);
|
||||
|
||||
if (any(buffers & TargetBufferFlags::COLOR_ALL)) {
|
||||
// We always blit from/to the COLOR0 attachment.
|
||||
MetalRenderTarget::Attachment srcColorAttachment = srcTarget->getReadColorAttachment(0);
|
||||
MetalRenderTarget::Attachment dstColorAttachment = dstTarget->getDrawColorAttachment(0);
|
||||
if (srcColorAttachment && dstColorAttachment) {
|
||||
ASSERT_PRECONDITION(isBlitableTextureType(srcColorAttachment.getTexture().textureType) &&
|
||||
isBlitableTextureType(dstColorAttachment.getTexture().textureType),
|
||||
"Metal does not support blitting to/from non-2D textures.");
|
||||
|
||||
if (srcColorAttachment && dstColorAttachment) {
|
||||
ASSERT_PRECONDITION(isBlitableTextureType(srcColorAttachment.getTexture().textureType) &&
|
||||
isBlitableTextureType(dstColorAttachment.getTexture().textureType),
|
||||
"Metal does not support blitting to/from non-2D textures.");
|
||||
MetalBlitter::BlitArgs args{};
|
||||
args.filter = filter;
|
||||
args.source.region = srcTarget->getRegionFromClientRect(srcRect);
|
||||
args.source.texture = srcColorAttachment.getTexture();
|
||||
args.source.level = srcColorAttachment.level;
|
||||
args.source.slice = srcColorAttachment.layer;
|
||||
|
||||
MetalBlitter::BlitArgs args;
|
||||
args.filter = filter;
|
||||
args.source.region = srcTarget->getRegionFromClientRect(srcRect);
|
||||
args.destination.region = dstTarget->getRegionFromClientRect(dstRect);
|
||||
args.source.color = srcColorAttachment.getTexture();
|
||||
args.destination.color = dstColorAttachment.getTexture();
|
||||
args.source.level = srcColorAttachment.level;
|
||||
args.destination.level = dstColorAttachment.level;
|
||||
args.source.slice = srcColorAttachment.layer;
|
||||
args.destination.slice = dstColorAttachment.layer;
|
||||
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "Color blit");
|
||||
}
|
||||
}
|
||||
args.destination.region = dstTarget->getRegionFromClientRect(dstRect);
|
||||
args.destination.texture = dstColorAttachment.getTexture();
|
||||
args.destination.level = dstColorAttachment.level;
|
||||
args.destination.slice = dstColorAttachment.layer;
|
||||
|
||||
if (any(buffers & TargetBufferFlags::DEPTH)) {
|
||||
MetalRenderTarget::Attachment srcDepthAttachment = srcTarget->getDepthAttachment();
|
||||
MetalRenderTarget::Attachment dstDepthAttachment = dstTarget->getDepthAttachment();
|
||||
|
||||
if (srcDepthAttachment && dstDepthAttachment) {
|
||||
ASSERT_PRECONDITION(isBlitableTextureType(srcDepthAttachment.getTexture().textureType) &&
|
||||
isBlitableTextureType(dstDepthAttachment.getTexture().textureType),
|
||||
"Metal does not support blitting to/from non-2D textures.");
|
||||
|
||||
MetalBlitter::BlitArgs args;
|
||||
args.filter = filter;
|
||||
args.source.region = srcTarget->getRegionFromClientRect(srcRect);
|
||||
args.destination.region = dstTarget->getRegionFromClientRect(dstRect);
|
||||
args.source.depth = srcDepthAttachment.getTexture();
|
||||
args.destination.depth = dstDepthAttachment.getTexture();
|
||||
args.source.level = srcDepthAttachment.level;
|
||||
args.destination.level = dstDepthAttachment.level;
|
||||
args.source.slice = srcDepthAttachment.layer;
|
||||
args.destination.slice = dstDepthAttachment.layer;
|
||||
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "Depth blit");
|
||||
}
|
||||
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "blitDEPRECATED");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1471,7 +1592,8 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t instanceCount) {
|
||||
void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
|
||||
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
|
||||
"Attempted to draw without a valid command encoder.");
|
||||
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
|
||||
@@ -1488,7 +1610,7 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT_PRECONDITION(bool(functions), "Attempting to draw with an invalid Metal program.");
|
||||
functions.validate();
|
||||
|
||||
auto [fragment, vertex] = functions.getRasterFunctions();
|
||||
|
||||
@@ -1512,7 +1634,7 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
stencilPixelFormat = stencilAttachment.getPixelFormat();
|
||||
assert_invariant(isMetalFormatStencil(stencilPixelFormat));
|
||||
}
|
||||
MetalPipelineState pipelineState {
|
||||
MetalPipelineState const pipelineState {
|
||||
.vertexFunction = vertex,
|
||||
.fragmentFunction = fragment,
|
||||
.vertexDescription = primitive->vertexDescription,
|
||||
@@ -1530,13 +1652,13 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
.stencilAttachmentPixelFormat = stencilPixelFormat,
|
||||
.sampleCount = mContext->currentRenderTarget->getSamples(),
|
||||
.blendState = BlendState {
|
||||
.blendingEnabled = rs.hasBlending(),
|
||||
.rgbBlendOperation = getMetalBlendOperation(rs.blendEquationRGB),
|
||||
.alphaBlendOperation = getMetalBlendOperation(rs.blendEquationAlpha),
|
||||
.sourceRGBBlendFactor = getMetalBlendFactor(rs.blendFunctionSrcRGB),
|
||||
.sourceAlphaBlendFactor = getMetalBlendFactor(rs.blendFunctionSrcAlpha),
|
||||
.rgbBlendOperation = getMetalBlendOperation(rs.blendEquationRGB),
|
||||
.destinationAlphaBlendFactor = getMetalBlendFactor(rs.blendFunctionDstAlpha),
|
||||
.destinationRGBBlendFactor = getMetalBlendFactor(rs.blendFunctionDstRGB),
|
||||
.destinationAlphaBlendFactor = getMetalBlendFactor(rs.blendFunctionDstAlpha)
|
||||
.sourceAlphaBlendFactor = getMetalBlendFactor(rs.blendFunctionSrcAlpha),
|
||||
.sourceRGBBlendFactor = getMetalBlendFactor(rs.blendFunctionSrcRGB),
|
||||
.blendingEnabled = rs.hasBlending(),
|
||||
},
|
||||
.colorWrite = rs.colorWrite
|
||||
};
|
||||
@@ -1609,36 +1731,6 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
mContext->currentPolygonOffset = ps.polygonOffset;
|
||||
}
|
||||
|
||||
// Set scissor-rectangle.
|
||||
// In order to do this, we compute the intersection between:
|
||||
// 1. the scissor rectangle
|
||||
// 2. the render target attachment dimensions (important, as the scissor can't be set larger)
|
||||
// fmax/min are used below to guard against NaN and because the MTLViewport/MTLRegion
|
||||
// coordinates are doubles.
|
||||
MTLRegion scissor = mContext->currentRenderTarget->getRegionFromClientRect(ps.scissor);
|
||||
const float sleft = scissor.origin.x, sright = scissor.origin.x + scissor.size.width;
|
||||
const float stop = scissor.origin.y, sbottom = scissor.origin.y + scissor.size.height;
|
||||
|
||||
// Attachment extent
|
||||
const auto attachmentSize = mContext->currentRenderTarget->getAttachmentSize();
|
||||
const float aleft = 0.0f, atop = 0.0f;
|
||||
const float aright = static_cast<float>(attachmentSize.x);
|
||||
const float abottom = static_cast<float>(attachmentSize.y);
|
||||
|
||||
const auto left = std::fmax(sleft, aleft);
|
||||
const auto right = std::fmin(sright, aright);
|
||||
const auto top = std::fmax(stop, atop);
|
||||
const auto bottom = std::fmin(sbottom, abottom);
|
||||
|
||||
MTLScissorRect scissorRect = {
|
||||
.x = static_cast<NSUInteger>(left),
|
||||
.y = static_cast<NSUInteger>(top),
|
||||
.width = static_cast<NSUInteger>(right - left),
|
||||
.height = static_cast<NSUInteger>(bottom - top)
|
||||
};
|
||||
|
||||
[mContext->currentRenderPassEncoder setScissorRect:scissorRect];
|
||||
|
||||
// Bind uniform buffers.
|
||||
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
|
||||
NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
|
||||
@@ -1716,10 +1808,10 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(mContext);
|
||||
id<MTLBuffer> metalIndexBuffer = indexBuffer->buffer.getGpuBufferForDraw(cmdBuffer);
|
||||
[mContext->currentRenderPassEncoder drawIndexedPrimitives:getMetalPrimitiveType(primitive->type)
|
||||
indexCount:primitive->count
|
||||
indexCount:indexCount
|
||||
indexType:getIndexType(indexBuffer->elementSize)
|
||||
indexBuffer:metalIndexBuffer
|
||||
indexBufferOffset:primitive->offset
|
||||
indexBufferOffset:indexOffset * primitive->indexBuffer->elementSize
|
||||
instanceCount:instanceCount];
|
||||
}
|
||||
|
||||
@@ -1792,6 +1884,38 @@ void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGro
|
||||
[computeEncoder endEncoding];
|
||||
}
|
||||
|
||||
void MetalDriver::scissor(Viewport scissorBox) {
|
||||
// Set scissor-rectangle.
|
||||
// In order to do this, we compute the intersection between:
|
||||
// 1. the scissor rectangle
|
||||
// 2. the render target attachment dimensions (important, as the scissor can't be set larger)
|
||||
// fmax/min are used below to guard against NaN and because the MTLViewport/MTLRegion
|
||||
// coordinates are doubles.
|
||||
MTLRegion scissor = mContext->currentRenderTarget->getRegionFromClientRect(scissorBox);
|
||||
const float sleft = scissor.origin.x, sright = scissor.origin.x + scissor.size.width;
|
||||
const float stop = scissor.origin.y, sbottom = scissor.origin.y + scissor.size.height;
|
||||
|
||||
// Attachment extent
|
||||
const auto attachmentSize = mContext->currentRenderTarget->getAttachmentSize();
|
||||
const float aleft = 0.0f, atop = 0.0f;
|
||||
const float aright = static_cast<float>(attachmentSize.x);
|
||||
const float abottom = static_cast<float>(attachmentSize.y);
|
||||
|
||||
const auto left = std::fmax(sleft, aleft);
|
||||
const auto right = std::fmin(sright, aright);
|
||||
const auto top = std::fmax(stop, atop);
|
||||
const auto bottom = std::fmin(sbottom, abottom);
|
||||
|
||||
MTLScissorRect scissorRect = {
|
||||
.x = static_cast<NSUInteger>(left),
|
||||
.y = static_cast<NSUInteger>(top),
|
||||
.width = static_cast<NSUInteger>(right - left),
|
||||
.height = static_cast<NSUInteger>(bottom - top)
|
||||
};
|
||||
|
||||
[mContext->currentRenderPassEncoder setScissorRect:scissorRect];
|
||||
}
|
||||
|
||||
void MetalDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
ASSERT_PRECONDITION(!isInRenderPass(mContext),
|
||||
"beginTimerQuery must be called outside of a render pass.");
|
||||
@@ -1835,6 +1959,21 @@ void MetalDriver::enumerateBoundBuffers(BufferObjectBinding bindingType,
|
||||
void MetalDriver::resetState(int) {
|
||||
}
|
||||
|
||||
void MetalDriver::runAtNextTick(const std::function<void()>& fn) noexcept {
|
||||
std::lock_guard<std::mutex> const lock(mTickOpsLock);
|
||||
mTickOps.push_back(fn);
|
||||
}
|
||||
|
||||
void MetalDriver::executeTickOps() noexcept {
|
||||
std::vector<std::function<void()>> ops;
|
||||
mTickOpsLock.lock();
|
||||
std::swap(ops, mTickOps);
|
||||
mTickOpsLock.unlock();
|
||||
for (const auto& f : ops) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
// explicit instantiation of the Dispatcher
|
||||
template class ConcreteDispatcher<MetalDriver>;
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "MetalUtils.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/trap.h>
|
||||
|
||||
#define NSERROR_CHECK(message) \
|
||||
|
||||
@@ -145,10 +145,18 @@ private:
|
||||
MetalBuffer buffer;
|
||||
};
|
||||
|
||||
struct MetalVertexBuffer : public HwVertexBuffer {
|
||||
MetalVertexBuffer(MetalContext& context, uint8_t bufferCount, uint8_t attributeCount,
|
||||
uint32_t vertexCount, AttributeArray const& attributes);
|
||||
struct MetalVertexBufferInfo : public HwVertexBufferInfo {
|
||||
MetalVertexBufferInfo(MetalContext& context,
|
||||
uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes);
|
||||
|
||||
AttributeArray attributes;
|
||||
};
|
||||
|
||||
struct MetalVertexBuffer : public HwVertexBuffer {
|
||||
MetalVertexBuffer(MetalContext& context,
|
||||
uint32_t vertexCount, uint32_t bufferCount, Handle<HwVertexBufferInfo> vbih);
|
||||
|
||||
Handle<HwVertexBufferInfo> vbih;
|
||||
utils::FixedCapacityVector<MetalBuffer*> buffers;
|
||||
};
|
||||
|
||||
@@ -161,7 +169,8 @@ struct MetalIndexBuffer : public HwIndexBuffer {
|
||||
|
||||
struct MetalRenderPrimitive : public HwRenderPrimitive {
|
||||
MetalRenderPrimitive();
|
||||
void setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer);
|
||||
void setBuffers(MetalVertexBufferInfo const* const vbi,
|
||||
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer);
|
||||
// The pointers to MetalVertexBuffer and MetalIndexBuffer are "weak".
|
||||
// The MetalVertexBuffer and MetalIndexBuffer must outlive the MetalRenderPrimitive.
|
||||
|
||||
|
||||
@@ -39,7 +39,15 @@ namespace filament {
|
||||
namespace backend {
|
||||
|
||||
static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) {
|
||||
NSUInteger u = 0;
|
||||
NSUInteger u = MTLTextureUsageUnknown;
|
||||
|
||||
if (any(usage & TextureUsage::SAMPLEABLE)) {
|
||||
u |= MTLTextureUsageShaderRead;
|
||||
}
|
||||
if (any(usage & TextureUsage::UPLOADABLE)) {
|
||||
// This is only needed because of the slowpath is MetalBlitter
|
||||
u |= MTLTextureUsageRenderTarget;
|
||||
}
|
||||
if (any(usage & TextureUsage::COLOR_ATTACHMENT)) {
|
||||
u |= MTLTextureUsageRenderTarget;
|
||||
}
|
||||
@@ -49,9 +57,13 @@ static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) {
|
||||
if (any(usage & TextureUsage::STENCIL_ATTACHMENT)) {
|
||||
u |= MTLTextureUsageRenderTarget;
|
||||
}
|
||||
|
||||
// All textures can be blitted from, so they must have the UsageShaderRead flag.
|
||||
u |= MTLTextureUsageShaderRead;
|
||||
if (any(usage & TextureUsage::BLIT_DST)) {
|
||||
// This is only needed because of the slowpath is MetalBlitter
|
||||
u |= MTLTextureUsageRenderTarget;
|
||||
}
|
||||
if (any(usage & TextureUsage::BLIT_SRC)) {
|
||||
u |= MTLTextureUsageShaderRead;
|
||||
}
|
||||
|
||||
return MTLTextureUsage(u);
|
||||
}
|
||||
@@ -233,13 +245,53 @@ void MetalSwapChain::present() {
|
||||
}
|
||||
}
|
||||
|
||||
void presentDrawable(bool presentFrame, void* user) {
|
||||
// CFBridgingRelease here is used to balance the CFBridgingRetain inside of acquireDrawable.
|
||||
id<CAMetalDrawable> drawable = (id<CAMetalDrawable>) CFBridgingRelease(user);
|
||||
if (presentFrame) {
|
||||
[drawable present];
|
||||
#ifndef FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD
|
||||
#define FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD 1
|
||||
#endif
|
||||
|
||||
class PresentDrawableData {
|
||||
public:
|
||||
PresentDrawableData() = delete;
|
||||
PresentDrawableData(const PresentDrawableData&) = delete;
|
||||
PresentDrawableData& operator=(const PresentDrawableData&) = delete;
|
||||
|
||||
static PresentDrawableData* create(id<CAMetalDrawable> drawable, MetalDriver* driver) {
|
||||
assert_invariant(driver);
|
||||
return new PresentDrawableData(drawable, driver);
|
||||
}
|
||||
// The drawable will be released here when the "drawable" variable goes out of scope.
|
||||
|
||||
static void maybePresentAndDestroyAsync(PresentDrawableData* that, bool shouldPresent) {
|
||||
if (shouldPresent) {
|
||||
[that->mDrawable present];
|
||||
}
|
||||
|
||||
#if FILAMENT_RELEASE_PRESENT_DRAWABLE_MAIN_THREAD == 1
|
||||
// mDrawable is acquired on the driver thread. Typically, we would release this object on
|
||||
// the same thread, but after receiving consistent crash reports from within
|
||||
// [CAMetalDrawable dealloc], we suspect this object requires releasing on the main thread.
|
||||
dispatch_async(dispatch_get_main_queue(), ^{ cleanupAndDestroy(that); });
|
||||
#else
|
||||
that->mDriver->runAtNextTick([that]() { cleanupAndDestroy(that); });
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
PresentDrawableData(id<CAMetalDrawable> drawable, MetalDriver* driver)
|
||||
: mDrawable(drawable), mDriver(driver) {}
|
||||
|
||||
static void cleanupAndDestroy(PresentDrawableData *that) {
|
||||
that->mDrawable = nil;
|
||||
that->mDriver = nullptr;
|
||||
delete that;
|
||||
}
|
||||
|
||||
id<CAMetalDrawable> mDrawable;
|
||||
MetalDriver* mDriver = nullptr;
|
||||
};
|
||||
|
||||
void presentDrawable(bool presentFrame, void* user) {
|
||||
auto* presentDrawableData = static_cast<PresentDrawableData*>(user);
|
||||
PresentDrawableData::maybePresentAndDestroyAsync(presentDrawableData, presentFrame);
|
||||
}
|
||||
|
||||
void MetalSwapChain::scheduleFrameScheduledCallback() {
|
||||
@@ -248,19 +300,16 @@ void MetalSwapChain::scheduleFrameScheduledCallback() {
|
||||
}
|
||||
|
||||
assert_invariant(drawable);
|
||||
FrameScheduledCallback callback = frameScheduledCallback;
|
||||
// This block strongly captures drawable to keep it alive until the handler executes.
|
||||
// We cannot simply reference this->drawable inside the block because the block would then only
|
||||
// capture the _this_ pointer (MetalSwapChain*) instead of the drawable.
|
||||
id<CAMetalDrawable> d = drawable;
|
||||
|
||||
// Destroy this by calling maybePresentAndDestroyAsync() later.
|
||||
auto* presentData = PresentDrawableData::create(drawable, context.driver);
|
||||
|
||||
FrameScheduledCallback userCallback = frameScheduledCallback;
|
||||
void* userData = frameScheduledUserData;
|
||||
|
||||
[getPendingCommandBuffer(&context) addScheduledHandler:^(id<MTLCommandBuffer> cb) {
|
||||
// CFBridgingRetain is used here to give the drawable a +1 retain count before
|
||||
// casting it to a void*.
|
||||
PresentCallable callable(presentDrawable, (void*) CFBridgingRetain(d));
|
||||
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
||||
callback(callable, userData);
|
||||
});
|
||||
PresentCallable callable(presentDrawable, static_cast<void*>(presentData));
|
||||
userCallback(callable, userData);
|
||||
}];
|
||||
}
|
||||
|
||||
@@ -291,9 +340,16 @@ void MetalBufferObject::updateBufferUnsynchronized(void* data, size_t size, uint
|
||||
buffer.copyIntoBufferUnsynchronized(data, size, byteOffset);
|
||||
}
|
||||
|
||||
MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, uint8_t bufferCount,
|
||||
uint8_t attributeCount, uint32_t vertexCount, AttributeArray const& attributes)
|
||||
: HwVertexBuffer(bufferCount, attributeCount, vertexCount, attributes), buffers(bufferCount, nullptr) {}
|
||||
MetalVertexBufferInfo::MetalVertexBufferInfo(MetalContext& context, uint8_t bufferCount,
|
||||
uint8_t attributeCount, AttributeArray const& attributes)
|
||||
: HwVertexBufferInfo(bufferCount, attributeCount),
|
||||
attributes(attributes) {
|
||||
}
|
||||
|
||||
MetalVertexBuffer::MetalVertexBuffer(MetalContext& context,
|
||||
uint32_t vertexCount, uint32_t bufferCount, Handle<HwVertexBufferInfo> vbih)
|
||||
: HwVertexBuffer(vertexCount), vbih(vbih), buffers(bufferCount, nullptr) {
|
||||
}
|
||||
|
||||
MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
|
||||
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
|
||||
@@ -302,12 +358,12 @@ MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uin
|
||||
MetalRenderPrimitive::MetalRenderPrimitive()
|
||||
: bufferMapping(utils::FixedCapacityVector<Entry>::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {}
|
||||
|
||||
void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer*
|
||||
indexBuffer) {
|
||||
void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
|
||||
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) {
|
||||
this->vertexBuffer = vertexBuffer;
|
||||
this->indexBuffer = indexBuffer;
|
||||
|
||||
const size_t attributeCount = vertexBuffer->attributes.size();
|
||||
const size_t attributeCount = vbi->attributes.size();
|
||||
|
||||
auto& mapping = bufferMapping;
|
||||
mapping.clear();
|
||||
@@ -348,7 +404,7 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalInde
|
||||
};
|
||||
|
||||
for (uint32_t attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) {
|
||||
const auto& attribute = vertexBuffer->attributes[attributeIndex];
|
||||
const auto& attribute = vbi->attributes[attributeIndex];
|
||||
|
||||
// If the attribute is unused, bind it to the zero buffer. It's a Metal error for a shader
|
||||
// to read from missing vertex attributes.
|
||||
@@ -756,6 +812,7 @@ void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region
|
||||
#endif
|
||||
|
||||
id<MTLTexture> stagingTexture = [context.device newTextureWithDescriptor:descriptor];
|
||||
// FIXME? Why is this not just `MTLRegion sourceRegion = region;` ?
|
||||
MTLRegion sourceRegion = MTLRegionMake3D(0, 0, 0,
|
||||
region.size.width, region.size.height, region.size.depth);
|
||||
[stagingTexture replaceRegion:sourceRegion
|
||||
@@ -782,16 +839,16 @@ void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region
|
||||
slices:NSMakeRange(0, slices)];
|
||||
}
|
||||
|
||||
MetalBlitter::BlitArgs args;
|
||||
MetalBlitter::BlitArgs args{};
|
||||
args.filter = SamplerMagFilter::NEAREST;
|
||||
args.source.level = 0;
|
||||
args.source.slice = 0;
|
||||
args.source.region = sourceRegion;
|
||||
args.source.texture = stagingTexture;
|
||||
args.destination.level = level;
|
||||
args.destination.slice = slice;
|
||||
args.destination.region = region;
|
||||
args.source.color = stagingTexture;
|
||||
args.destination.color = destinationTexture;
|
||||
args.destination.texture = destinationTexture;
|
||||
context.blitter->blit(getPendingCommandBuffer(&context), args, "Texture upload blit");
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <variant>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -41,36 +43,69 @@ class MetalShaderCompiler {
|
||||
public:
|
||||
class MetalFunctionBundle {
|
||||
public:
|
||||
MetalFunctionBundle() = default;
|
||||
MetalFunctionBundle(id<MTLFunction> fragment, id<MTLFunction> vertex)
|
||||
: functions{fragment, vertex} {
|
||||
using Raster = std::tuple<id<MTLFunction>, id<MTLFunction>>;
|
||||
using Compute = id<MTLFunction>;
|
||||
using Error = std::tuple<NSString*, NSString*>; // error message, Program name
|
||||
struct None {};
|
||||
|
||||
MetalFunctionBundle() : mPrograms{None{}} {}
|
||||
|
||||
explicit operator bool() const { return isValid(); }
|
||||
|
||||
bool isValid() const noexcept {
|
||||
return std::holds_alternative<Raster>(mPrograms) ||
|
||||
std::holds_alternative<Compute>(mPrograms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws an NSException if this MetalFunctionBundle either contains an error or is empty.
|
||||
*
|
||||
* If this MetalFunctionBundle contains an error, will throw a MetalCompilationFailure
|
||||
* NSException with the error string and program name passed to
|
||||
* MetalFunctionBundle::error(NSString*, NSString*).
|
||||
*
|
||||
* If this MetalFunctionBundle is empty, will throw a MetalEmptyFunctionBundle NSException.
|
||||
*/
|
||||
void validate() const;
|
||||
|
||||
Raster getRasterFunctions() const {
|
||||
assert_invariant(std::holds_alternative<Raster>(mPrograms));
|
||||
return std::get<Raster>(mPrograms);
|
||||
}
|
||||
|
||||
Compute getComputeFunction() const {
|
||||
assert_invariant(std::holds_alternative<Compute>(mPrograms));
|
||||
return std::get<Compute>(mPrograms);
|
||||
}
|
||||
|
||||
static MetalFunctionBundle none() {
|
||||
return MetalFunctionBundle(None{});
|
||||
}
|
||||
|
||||
static MetalFunctionBundle raster(id<MTLFunction> fragment, id<MTLFunction> vertex) {
|
||||
assert_invariant(fragment && vertex);
|
||||
assert_invariant(fragment.functionType == MTLFunctionTypeFragment);
|
||||
assert_invariant(vertex.functionType == MTLFunctionTypeVertex);
|
||||
return MetalFunctionBundle(Raster{fragment, vertex});
|
||||
}
|
||||
explicit MetalFunctionBundle(id<MTLFunction> compute) : functions{compute, nil} {
|
||||
|
||||
static MetalFunctionBundle compute(id<MTLFunction> compute) {
|
||||
assert_invariant(compute);
|
||||
assert_invariant(compute.functionType == MTLFunctionTypeKernel);
|
||||
return MetalFunctionBundle(Compute{compute});
|
||||
}
|
||||
|
||||
std::pair<id<MTLFunction>, id<MTLFunction>> getRasterFunctions() const noexcept {
|
||||
assert_invariant(functions[0].functionType == MTLFunctionTypeFragment);
|
||||
assert_invariant(functions[1].functionType == MTLFunctionTypeVertex);
|
||||
return {functions[0], functions[1]};
|
||||
static MetalFunctionBundle error(NSString* errorMessage, NSString* programName) {
|
||||
return MetalFunctionBundle(Error{errorMessage, programName});
|
||||
}
|
||||
|
||||
id<MTLFunction> getComputeFunction() const noexcept {
|
||||
assert_invariant(functions[0].functionType == MTLFunctionTypeKernel);
|
||||
return functions[0];
|
||||
}
|
||||
|
||||
explicit operator bool() const { return functions[0] != nil; }
|
||||
|
||||
private:
|
||||
// Can hold two functions, either:
|
||||
// - fragment and vertex (for rasterization pipelines)
|
||||
// - compute (for compute pipelines)
|
||||
id<MTLFunction> functions[2] = {nil, nil};
|
||||
MetalFunctionBundle(None&& t) : mPrograms(std::move(t)) {}
|
||||
MetalFunctionBundle(Raster&& t) : mPrograms(std::move(t)) {}
|
||||
MetalFunctionBundle(Compute&& t) : mPrograms(std::move(t)) {}
|
||||
MetalFunctionBundle(Error&& t) : mPrograms(std::move(t)) {}
|
||||
|
||||
std::variant<Raster, Compute, None, Error> mPrograms;
|
||||
};
|
||||
|
||||
using program_token_t = std::shared_ptr<MetalProgramToken>;
|
||||
|
||||
@@ -103,19 +103,31 @@ void MetalShaderCompiler::terminate() noexcept {
|
||||
NSString* objcSource = [[NSString alloc] initWithBytes:source.data()
|
||||
length:source.size() - 1
|
||||
encoding:NSUTF8StringEncoding];
|
||||
|
||||
// By default, Metal uses the most recent language version.
|
||||
MTLCompileOptions* options = [MTLCompileOptions new];
|
||||
|
||||
// Disable Fast Math optimizations.
|
||||
// This ensures that operations adhere to IEEE standards for floating-point arithmetic,
|
||||
// which is crucial for half precision floats in scenarios where fast math optimizations
|
||||
// lead to inaccuracies, such as in handling special values like NaN or Infinity.
|
||||
options.fastMathEnabled = NO;
|
||||
|
||||
NSError* error = nil;
|
||||
// When options is nil, Metal uses the most recent language version available.
|
||||
id<MTLLibrary> library = [device newLibraryWithSource:objcSource
|
||||
options:nil
|
||||
options:options
|
||||
error:&error];
|
||||
if (library == nil) {
|
||||
NSString* errorMessage = @"unknown error";
|
||||
if (error) {
|
||||
auto description =
|
||||
[error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
|
||||
utils::slog.w << description << utils::io::endl;
|
||||
errorMessage = error.localizedDescription;
|
||||
}
|
||||
PANIC_LOG("Failed to compile Metal program.");
|
||||
return {};
|
||||
NSString* programName = [NSString stringWithFormat:@"%s", program.getName().c_str_safe()];
|
||||
return MetalFunctionBundle::error(errorMessage, programName);
|
||||
}
|
||||
|
||||
MTLFunctionConstantValues* constants = [MTLFunctionConstantValues new];
|
||||
@@ -151,14 +163,15 @@ void MetalShaderCompiler::terminate() noexcept {
|
||||
assert_invariant(isRasterizationProgram != isComputeProgram);
|
||||
|
||||
if (isRasterizationProgram) {
|
||||
return {fragmentFunction, vertexFunction};
|
||||
return MetalFunctionBundle::raster(fragmentFunction, vertexFunction);
|
||||
}
|
||||
|
||||
if (isComputeProgram) {
|
||||
return MetalFunctionBundle{computeFunction};
|
||||
return MetalFunctionBundle::compute(computeFunction);
|
||||
}
|
||||
|
||||
return {};
|
||||
// Should never reach here.
|
||||
return MetalFunctionBundle::none();
|
||||
}
|
||||
|
||||
MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram(
|
||||
@@ -170,9 +183,6 @@ MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram(
|
||||
CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
|
||||
mCompilerThreadPool.queue(priorityQueue, token,
|
||||
[this, name, device = mDevice, program = std::move(program), token]() {
|
||||
int sleepTime = atoi(name.c_str());
|
||||
sleep(sleepTime);
|
||||
|
||||
MetalFunctionBundle compiledProgram = compileProgram(program, device);
|
||||
|
||||
token->set(compiledProgram);
|
||||
@@ -219,4 +229,24 @@ void MetalShaderCompiler::notifyWhenAllProgramsAreReady(
|
||||
mCallbackManager.setCallback(handler, callback, user);
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
void MetalShaderCompiler::MetalFunctionBundle::validate() const {
|
||||
if (UTILS_UNLIKELY(std::holds_alternative<Error>(mPrograms))) {
|
||||
auto [errorMessage, programName] = std::get<Error>(mPrograms);
|
||||
NSString* reason =
|
||||
[NSString stringWithFormat:
|
||||
@"Attempting to draw with an id<MTLFunction> that failed to compile.\n"
|
||||
@"Program: %@\n"
|
||||
@"%@", programName, errorMessage];
|
||||
[[NSException exceptionWithName:@"MetalCompilationFailure"
|
||||
reason:reason
|
||||
userInfo:nil] raise];
|
||||
} else if (UTILS_UNLIKELY(std::holds_alternative<None>(mPrograms))) {
|
||||
NSString* reason = @"Attempting to draw with an empty id<MTLFunction>.";
|
||||
[[NSException exceptionWithName:@"MetalEmptyFunctionBundle"
|
||||
reason:reason
|
||||
userInfo:nil] raise];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#include "MetalEnums.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace filament {
|
||||
namespace backend {
|
||||
|
||||
|
||||
@@ -77,6 +77,9 @@ void NoopDriver::finish(int) {
|
||||
void NoopDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
}
|
||||
|
||||
void NoopDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
|
||||
}
|
||||
|
||||
void NoopDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
|
||||
}
|
||||
|
||||
@@ -174,7 +177,7 @@ bool NoopDriver::isSRGBSwapChainSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NoopDriver::isStereoSupported() {
|
||||
bool NoopDriver::isStereoSupported(backend::StereoscopicType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -182,6 +185,10 @@ bool NoopDriver::isParallelShaderCompileSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NoopDriver::isDepthStencilResolveSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NoopDriver::isWorkaroundNeeded(Workaround) {
|
||||
return false;
|
||||
}
|
||||
@@ -252,10 +259,6 @@ void NoopDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh) {
|
||||
|
||||
void NoopDriver::generateMipmaps(Handle<HwTexture> th) { }
|
||||
|
||||
bool NoopDriver::canGenerateMipmaps() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void NoopDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
|
||||
BufferDescriptor&& data) {
|
||||
scheduleDestroy(std::move(data));
|
||||
@@ -322,19 +325,34 @@ void NoopDriver::readBufferSubData(backend::BufferObjectHandle boh,
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void NoopDriver::blit(TargetBufferFlags buffers,
|
||||
void NoopDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
Handle<HwRenderTarget> dst, Viewport dstRect,
|
||||
Handle<HwRenderTarget> src, Viewport srcRect,
|
||||
SamplerMagFilter filter) {
|
||||
}
|
||||
|
||||
void NoopDriver::resolve(
|
||||
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer,
|
||||
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer) {
|
||||
}
|
||||
|
||||
void NoopDriver::blit(
|
||||
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer, math::uint2 dstOrigin,
|
||||
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer, math::uint2 srcOrigin,
|
||||
math::uint2 size) {
|
||||
}
|
||||
|
||||
void NoopDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
|
||||
uint32_t instanceCount) {
|
||||
uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
}
|
||||
|
||||
void NoopDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
}
|
||||
|
||||
void NoopDriver::scissor(
|
||||
Viewport scissor) {
|
||||
}
|
||||
|
||||
void NoopDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
}
|
||||
|
||||
|
||||
@@ -373,16 +373,19 @@ constexpr inline GLenum getCullingMode(CullingMode mode) noexcept {
|
||||
constexpr inline std::pair<GLenum, GLenum> textureFormatToFormatAndType(
|
||||
TextureFormat format) noexcept {
|
||||
switch (format) {
|
||||
case TextureFormat::RGB8: return { GL_RGB, GL_UNSIGNED_BYTE };
|
||||
case TextureFormat::RGBA8: return { GL_RGBA, GL_UNSIGNED_BYTE };
|
||||
case TextureFormat::RGB565: return { GL_RGB, GL_UNSIGNED_SHORT_5_6_5 };
|
||||
case TextureFormat::RGB5_A1: return { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 };
|
||||
case TextureFormat::RGBA4: return { GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4 };
|
||||
case TextureFormat::DEPTH16: return { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT };
|
||||
case TextureFormat::DEPTH24: return { GL_DEPTH_COMPONENT, GL_UNSIGNED_INT };
|
||||
case TextureFormat::R8: return { 0x1909 /*GL_LUMINANCE*/, GL_UNSIGNED_BYTE };
|
||||
case TextureFormat::RGB8: return { GL_RGB, GL_UNSIGNED_BYTE };
|
||||
case TextureFormat::SRGB8: return { GL_RGB, GL_UNSIGNED_BYTE };
|
||||
case TextureFormat::RGBA8: return { GL_RGBA, GL_UNSIGNED_BYTE };
|
||||
case TextureFormat::SRGB8_A8: return { GL_RGBA, GL_UNSIGNED_BYTE };
|
||||
case TextureFormat::RGB565: return { GL_RGB, GL_UNSIGNED_SHORT_5_6_5 };
|
||||
case TextureFormat::RGB5_A1: return { GL_RGBA, GL_UNSIGNED_SHORT_5_5_5_1 };
|
||||
case TextureFormat::RGBA4: return { GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4 };
|
||||
case TextureFormat::DEPTH16: return { GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT };
|
||||
case TextureFormat::DEPTH24: return { GL_DEPTH_COMPONENT, GL_UNSIGNED_INT };
|
||||
case TextureFormat::DEPTH24_STENCIL8:
|
||||
return { GL_DEPTH24_STENCIL8, GL_UNSIGNED_INT_24_8 };
|
||||
default: return { GL_NONE, GL_NONE };
|
||||
return { GL_DEPTH24_STENCIL8, GL_UNSIGNED_INT_24_8 };
|
||||
default: return { GL_NONE, GL_NONE };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -147,17 +147,28 @@ OpenGLContext::OpenGLContext() noexcept {
|
||||
#ifndef NDEBUG
|
||||
// this is useful for development
|
||||
slog.v
|
||||
<< "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT" << gets.max_anisotropy << '\n'
|
||||
<< "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS" << gets.max_combined_texture_image_units << '\n'
|
||||
<< "GL_MAX_DRAW_BUFFERS" << gets.max_draw_buffers << '\n'
|
||||
<< "GL_MAX_RENDERBUFFER_SIZE" << gets.max_renderbuffer_size << '\n'
|
||||
<< "GL_MAX_SAMPLES" << gets.max_samples << '\n'
|
||||
<< "GL_MAX_TEXTURE_IMAGE_UNITS" << gets.max_texture_image_units << '\n'
|
||||
<< "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS" << gets.max_transform_feedback_separate_attribs << '\n'
|
||||
<< "GL_MAX_UNIFORM_BLOCK_SIZE" << gets.max_uniform_block_size << '\n'
|
||||
<< "GL_MAX_UNIFORM_BUFFER_BINDINGS" << gets.max_uniform_buffer_bindings << '\n'
|
||||
<< "GL_NUM_PROGRAM_BINARY_FORMATS" << gets.num_program_binary_formats << '\n'
|
||||
<< "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT" << gets.uniform_buffer_offset_alignment << '\n'
|
||||
<< "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = "
|
||||
<< gets.max_anisotropy << '\n'
|
||||
<< "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = "
|
||||
<< gets.max_combined_texture_image_units << '\n'
|
||||
<< "GL_MAX_DRAW_BUFFERS = "
|
||||
<< gets.max_draw_buffers << '\n'
|
||||
<< "GL_MAX_RENDERBUFFER_SIZE = "
|
||||
<< gets.max_renderbuffer_size << '\n'
|
||||
<< "GL_MAX_SAMPLES = "
|
||||
<< gets.max_samples << '\n'
|
||||
<< "GL_MAX_TEXTURE_IMAGE_UNITS = "
|
||||
<< gets.max_texture_image_units << '\n'
|
||||
<< "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS = "
|
||||
<< gets.max_transform_feedback_separate_attribs << '\n'
|
||||
<< "GL_MAX_UNIFORM_BLOCK_SIZE = "
|
||||
<< gets.max_uniform_block_size << '\n'
|
||||
<< "GL_MAX_UNIFORM_BUFFER_BINDINGS = "
|
||||
<< gets.max_uniform_buffer_bindings << '\n'
|
||||
<< "GL_NUM_PROGRAM_BINARY_FORMATS = "
|
||||
<< gets.num_program_binary_formats << '\n'
|
||||
<< "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT = "
|
||||
<< gets.uniform_buffer_offset_alignment << '\n'
|
||||
;
|
||||
flush(slog.v);
|
||||
#endif
|
||||
@@ -263,6 +274,7 @@ void OpenGLContext::setDefaultState() noexcept {
|
||||
glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_NICEST);
|
||||
#endif
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
if (ext.EXT_clip_control) {
|
||||
#if defined(BACKEND_OPENGL_VERSION_GL)
|
||||
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
|
||||
@@ -270,9 +282,11 @@ void OpenGLContext::setDefaultState() noexcept {
|
||||
glClipControlEXT(GL_LOWER_LEFT_EXT, GL_ZERO_TO_ONE_EXT);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ext.EXT_clip_cull_distance) {
|
||||
glEnable(GL_CLIP_DISTANCE0);
|
||||
glEnable(GL_CLIP_DISTANCE1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,7 +311,9 @@ void OpenGLContext::initProcs(Procs* procs,
|
||||
# ifdef BACKEND_OPENGL_VERSION_GL
|
||||
procs->getQueryObjectui64v = glGetQueryObjectui64v; // only core in GL
|
||||
# elif defined(GL_EXT_disjoint_timer_query)
|
||||
procs->getQueryObjectui64v = glGetQueryObjectui64vEXT;
|
||||
# ifndef __EMSCRIPTEN__
|
||||
procs->getQueryObjectui64v = glGetQueryObjectui64vEXT;
|
||||
# endif
|
||||
# endif // BACKEND_OPENGL_VERSION_GL
|
||||
|
||||
// core in ES 3.0 and GL 4.3
|
||||
@@ -309,6 +325,7 @@ void OpenGLContext::initProcs(Procs* procs,
|
||||
|
||||
#ifdef BACKEND_OPENGL_VERSION_GLES
|
||||
# ifndef IOS // IOS is guaranteed to have ES3.x
|
||||
# ifndef __EMSCRIPTEN__
|
||||
if (UTILS_UNLIKELY(major == 2)) {
|
||||
// Runtime OpenGL version is ES 2.x
|
||||
if (UTILS_LIKELY(ext.OES_vertex_array_object)) {
|
||||
@@ -336,6 +353,7 @@ void OpenGLContext::initProcs(Procs* procs,
|
||||
|
||||
procs->maxShaderCompilerThreadsKHR = glMaxShaderCompilerThreadsKHR;
|
||||
}
|
||||
# endif // __EMSCRIPTEN__
|
||||
# endif // IOS
|
||||
#else
|
||||
procs->maxShaderCompilerThreadsKHR = glMaxShaderCompilerThreadsARB;
|
||||
@@ -409,7 +427,6 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
|
||||
if (strstr(renderer, "Mali-T")) {
|
||||
bugs->disable_glFlush = true;
|
||||
bugs->disable_shared_context_draws = true;
|
||||
bugs->texture_external_needs_rebind = true;
|
||||
// We have not verified that timer queries work on Mali-T, so we disable to be safe.
|
||||
bugs->dont_use_timer_query = true;
|
||||
}
|
||||
@@ -464,7 +481,6 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
|
||||
// (that should be regardless of ANGLE, but we should double-check)
|
||||
bugs->split_easu = true;
|
||||
}
|
||||
// TODO: see if we could use `bugs.allow_read_only_ancillary_feedback_loop = true`
|
||||
}
|
||||
|
||||
#ifdef BACKEND_OPENGL_VERSION_GLES
|
||||
@@ -569,17 +585,23 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
|
||||
// figure out and initialize the extensions we need
|
||||
using namespace std::literals;
|
||||
ext->APPLE_color_buffer_packed_float = exts.has("GL_APPLE_color_buffer_packed_float"sv);
|
||||
#ifndef __EMSCRIPTEN__
|
||||
ext->EXT_clip_control = exts.has("GL_EXT_clip_control"sv);
|
||||
#endif
|
||||
ext->EXT_clip_cull_distance = exts.has("GL_EXT_clip_cull_distance"sv);
|
||||
ext->EXT_color_buffer_float = exts.has("GL_EXT_color_buffer_float"sv);
|
||||
ext->EXT_color_buffer_half_float = exts.has("GL_EXT_color_buffer_half_float"sv);
|
||||
#ifndef __EMSCRIPTEN__
|
||||
ext->EXT_debug_marker = exts.has("GL_EXT_debug_marker"sv);
|
||||
#endif
|
||||
ext->EXT_discard_framebuffer = exts.has("GL_EXT_discard_framebuffer"sv);
|
||||
#ifndef __EMSCRIPTEN__
|
||||
ext->EXT_disjoint_timer_query = exts.has("GL_EXT_disjoint_timer_query"sv);
|
||||
ext->EXT_multisampled_render_to_texture = exts.has("GL_EXT_multisampled_render_to_texture"sv);
|
||||
ext->EXT_multisampled_render_to_texture2 = exts.has("GL_EXT_multisampled_render_to_texture2"sv);
|
||||
#endif
|
||||
ext->EXT_shader_framebuffer_fetch = exts.has("GL_EXT_shader_framebuffer_fetch"sv);
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
#ifndef __EMSCRIPTEN__
|
||||
ext->EXT_texture_compression_etc2 = true;
|
||||
#endif
|
||||
ext->EXT_texture_compression_s3tc = exts.has("GL_EXT_texture_compression_s3tc"sv);
|
||||
@@ -600,6 +622,7 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
|
||||
ext->OES_standard_derivatives = exts.has("GL_OES_standard_derivatives"sv);
|
||||
ext->OES_texture_npot = exts.has("GL_OES_texture_npot"sv);
|
||||
ext->OES_vertex_array_object = exts.has("GL_OES_vertex_array_object"sv);
|
||||
ext->OVR_multiview2 = exts.has("GL_OVR_multiview2"sv);
|
||||
ext->WEBGL_compressed_texture_etc = exts.has("WEBGL_compressed_texture_etc"sv);
|
||||
ext->WEBGL_compressed_texture_s3tc = exts.has("WEBGL_compressed_texture_s3tc"sv);
|
||||
ext->WEBGL_compressed_texture_s3tc_srgb = exts.has("WEBGL_compressed_texture_s3tc_srgb"sv);
|
||||
@@ -664,6 +687,7 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
|
||||
ext->OES_standard_derivatives = true;
|
||||
ext->OES_texture_npot = true;
|
||||
ext->OES_vertex_array_object = true;
|
||||
ext->OVR_multiview2 = exts.has("GL_OVR_multiview2"sv);
|
||||
ext->WEBGL_compressed_texture_etc = false;
|
||||
ext->WEBGL_compressed_texture_s3tc = false;
|
||||
ext->WEBGL_compressed_texture_s3tc_srgb = false;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user