Compare commits
172
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c6b470650 | ||
|
|
68a99ef54c | ||
|
|
39fabdbc3b | ||
|
|
83967fdb1b | ||
|
|
b88c2d80a4 | ||
|
|
4a36193f04 | ||
|
|
8df19cb6b2 | ||
|
|
783c1c9c83 | ||
|
|
a92771c5ef | ||
|
|
c98a76135b | ||
|
|
7f2836bb43 | ||
|
|
b60d02919b | ||
|
|
156dde6872 | ||
|
|
dd2fbf01aa | ||
|
|
beba1f63f1 | ||
|
|
e20dd9ec93 | ||
|
|
7f80d956b6 | ||
|
|
3c941863dc | ||
|
|
57e2007aaf | ||
|
|
f88f0607c7 | ||
|
|
91b98964cc | ||
|
|
d92a90e22b | ||
|
|
79a548ecae | ||
|
|
3c4c8940b2 | ||
|
|
75d2252d33 | ||
|
|
9d3a55291d | ||
|
|
170dec0945 | ||
|
|
7967157fbb | ||
|
|
597ced13e1 | ||
|
|
c3542b135e | ||
|
|
ca3ff7e08e | ||
|
|
42c760a92f | ||
|
|
327a537bcc | ||
|
|
cfc4f34c18 | ||
|
|
6dac384bd9 | ||
|
|
d666f8a0ba | ||
|
|
d6ae3a57b5 | ||
|
|
c2155f3f98 | ||
|
|
e2a3637413 | ||
|
|
390df4e42c | ||
|
|
21dd1319df | ||
|
|
444ac8d6a6 | ||
|
|
aac1e7dc4c | ||
|
|
5b8a1e5e58 | ||
|
|
ad1b36d2b3 | ||
|
|
315c2c273f | ||
|
|
005d835dbe | ||
|
|
cade94ab2c | ||
|
|
5cd2f5626c | ||
|
|
94bbcbf1c3 | ||
|
|
52c998d2b6 | ||
|
|
527d831c15 | ||
|
|
2790f2e64c | ||
|
|
757640e850 | ||
|
|
9d9abca33a | ||
|
|
bea7a7c73f | ||
|
|
dd62f98784 | ||
|
|
395a3eda9b | ||
|
|
9d18cc35e0 | ||
|
|
2e37ebe70c | ||
|
|
63ef412be1 | ||
|
|
f0996296c1 | ||
|
|
a60d570b3b | ||
|
|
3fdb9f311a | ||
|
|
70f2cda9b6 | ||
|
|
c3e4894d49 | ||
|
|
a128d9143a | ||
|
|
2cf10999f4 | ||
|
|
e0e44da185 | ||
|
|
ce62e0ef06 | ||
|
|
826b885d0e | ||
|
|
5b9a24edf0 | ||
|
|
b651f63e02 | ||
|
|
c48273df3c | ||
|
|
8397a15914 | ||
|
|
d76567b4a8 | ||
|
|
ce62b169cb | ||
|
|
9244f8ee21 | ||
|
|
3c7a871e7e | ||
|
|
31d698e9ca | ||
|
|
769b0db858 | ||
|
|
b40064b1ab | ||
|
|
889a62d617 | ||
|
|
784b3ce565 | ||
|
|
4f77547ab3 | ||
|
|
46c89499d0 | ||
|
|
57c782685b | ||
|
|
bc799c78f6 | ||
|
|
86e688a2a3 | ||
|
|
3dead231d4 | ||
|
|
fd382ce219 | ||
|
|
46358572b7 | ||
|
|
546b986107 | ||
|
|
5245141c46 | ||
|
|
62b182fe04 | ||
|
|
87dbd59275 | ||
|
|
3c02eb9a63 | ||
|
|
0bb96f0b62 | ||
|
|
06c0667636 | ||
|
|
c873552eaf | ||
|
|
38cc77807b | ||
|
|
ded1cb1b09 | ||
|
|
653b2e859c | ||
|
|
7fd24c38d7 | ||
|
|
d4166e3a84 | ||
|
|
c9f3d40993 | ||
|
|
3cf76a837c | ||
|
|
d1781fd66c | ||
|
|
577f1f83ca | ||
|
|
7956a28c71 | ||
|
|
e264dbad98 | ||
|
|
ef14e9c90e | ||
|
|
6c27340d22 | ||
|
|
3302c1ef4b | ||
|
|
c3ae2cdc8f | ||
|
|
e52c6d8e53 | ||
|
|
fdcf82f17a | ||
|
|
60f5c4d32c | ||
|
|
42ce76e285 | ||
|
|
f903683c92 | ||
|
|
546ba48043 | ||
|
|
c81af315b2 | ||
|
|
ea64db4218 | ||
|
|
0c8df766d0 | ||
|
|
8f58743405 | ||
|
|
af079b42a6 | ||
|
|
7f7bceb970 | ||
|
|
625603d8d4 | ||
|
|
d2d5d62a20 | ||
|
|
5e9be5dd2d | ||
|
|
df897b3fb2 | ||
|
|
8c396caba0 | ||
|
|
6b91f30389 | ||
|
|
de5d0e55af | ||
|
|
6066e3a461 | ||
|
|
ea0bbad636 | ||
|
|
65861b85cf | ||
|
|
1f169d7e75 | ||
|
|
daa359717b | ||
|
|
4fbfd1b8c1 | ||
|
|
67f05c15d0 | ||
|
|
4db3cc521b | ||
|
|
9e6cf3c876 | ||
|
|
b64548267e | ||
|
|
5c811844ff | ||
|
|
e6b18b4560 | ||
|
|
1571ea846b | ||
|
|
78c6456fbe | ||
|
|
84cce8cfdf | ||
|
|
49b7c7169f | ||
|
|
dbfe2dbc5e | ||
|
|
0ec60de287 | ||
|
|
7eb756ca96 | ||
|
|
3abddc4584 | ||
|
|
35661a1974 | ||
|
|
606a1259f6 | ||
|
|
6479a5bc12 | ||
|
|
c95f9ed7e0 | ||
|
|
e3b7861aa2 | ||
|
|
84980d89af | ||
|
|
60640be5e8 | ||
|
|
5f730ac173 | ||
|
|
e12ed900d0 | ||
|
|
78487f4836 | ||
|
|
25e9802fd5 | ||
|
|
dacd03ce42 | ||
|
|
c9cfc63df0 | ||
|
|
9c69cb8484 | ||
|
|
8ea4cb50d2 | ||
|
|
4f8080ead2 | ||
|
|
34ebe45e2a | ||
|
|
bb3e3df2f4 |
@@ -0,0 +1,68 @@
|
||||
BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignConsecutiveAssignments: None
|
||||
AlignOperands: Align
|
||||
AllowAllArgumentsOnNextLine: false
|
||||
AllowAllConstructorInitializersOnNextLine: false
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: Always
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: true
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BreakBeforeBraces: Custom
|
||||
BracedInitializerIndentWidth: 4
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterUnion: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: false
|
||||
SplitEmptyRecord: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakInheritanceList: BeforeColon
|
||||
ColumnLimit: 100
|
||||
CompactNamespaces: false
|
||||
ContinuationIndentWidth: 8
|
||||
Cpp11BracedListStyle: false
|
||||
IndentCaseLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 4
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MaxEmptyLinesToKeep: 2
|
||||
NamespaceIndentation: None
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PointerAlignment: Left
|
||||
ReflowComments: true
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 0
|
||||
SpacesInAngles: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInContainerLiterals: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
PackConstructorInitializers: Never
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
name: build-android
|
||||
# We intentially use a larger runner here to enable larger disk space
|
||||
# (standard linux runner will fail on disk space and faster build time).
|
||||
runs-on: ubuntu-22.04-32core
|
||||
runs-on: 'ubuntu-24.04-32core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
jobs:
|
||||
build-linux:
|
||||
name: build-linux
|
||||
runs-on: ubuntu-22.04-16core
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
|
||||
@@ -19,6 +19,8 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Run build script
|
||||
run: |
|
||||
WORKFLOW_OS=`echo \`uname\` | sed "s/Darwin/mac/" | tr [:upper:] [:lower:]`
|
||||
@@ -40,10 +42,12 @@ jobs:
|
||||
|
||||
build-android:
|
||||
name: build-android
|
||||
runs-on: ubuntu-22.04-16core
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
@@ -60,6 +64,8 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Run build script
|
||||
run: |
|
||||
cd build/ios && printf "y" | ./build.sh presubmit
|
||||
@@ -69,7 +75,7 @@ jobs:
|
||||
|
||||
build-web:
|
||||
name: build-web
|
||||
runs-on: ubuntu-22.04-16core
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
@@ -79,7 +85,7 @@ jobs:
|
||||
|
||||
validate-docs:
|
||||
name: validate-docs
|
||||
runs-on: ubuntu-22.04-4core
|
||||
runs-on: 'ubuntu-24.04-4core'
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
@@ -92,15 +98,73 @@ jobs:
|
||||
|
||||
test-renderdiff:
|
||||
name: test-renderdiff
|
||||
runs-on: ubuntu-22.04-16core
|
||||
|
||||
runs-on: macos-14-xlarge
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: ./.github/actions/ubuntu-apt-add-src
|
||||
- name: Run script
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Cache Mesa and deps
|
||||
id: mesa-cache
|
||||
uses: actions/cache@v4 # Use a specific version
|
||||
with:
|
||||
path: |
|
||||
$HOME/Library/Caches/Homebrew
|
||||
mesa
|
||||
key: ${{ runner.os }}-mesa-deps-${{ vars.MESA_VERSION }}
|
||||
- name: Get Mesa
|
||||
id: mesa-prereq
|
||||
env:
|
||||
MESA_VERSION: ${{ vars.MESA_VERSION }}
|
||||
run: |
|
||||
source ./build/linux/ci-common.sh && bash test/renderdiff/test.sh
|
||||
bash test/utils/get_mesa.sh
|
||||
- name: Run Test
|
||||
run: |
|
||||
bash test/renderdiff/test.sh
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: presubmit-renderdiff-result
|
||||
path: ./out/renderdiff_tests
|
||||
|
||||
validate-wgsl-webgpu:
|
||||
name: validate-wgsl-webgpu
|
||||
runs-on: 'ubuntu-24.04-8core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- name: Run build script
|
||||
run: source ./build/linux/ci-common.sh && ./build.sh -W debug test_filamat filament
|
||||
- name: Run test
|
||||
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
|
||||
|
||||
code-correcteness:
|
||||
name: code-correctness
|
||||
runs-on: 'macos-14-xlarge'
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Set up Homebrew
|
||||
id: set-up-homebrew
|
||||
uses: Homebrew/actions/setup-homebrew@master
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Install prerequisites
|
||||
run: |
|
||||
pip install pyyaml
|
||||
brew install llvm
|
||||
sudo ln -s "$(brew --prefix llvm)/bin/clang-tidy" "/usr/local/bin/clang-tidy"
|
||||
- name: Run build script
|
||||
# We need to build before clang-tidy can run analysis
|
||||
run: |
|
||||
# This will build for all three desktop backends on mac
|
||||
./build.sh -p desktop debug gltf_viewer
|
||||
- name: Run test
|
||||
run: |
|
||||
bash test/code-correctness/test.sh
|
||||
|
||||
@@ -70,7 +70,7 @@ jobs:
|
||||
|
||||
build-web:
|
||||
name: build-web
|
||||
runs-on: ubuntu-22.04-16core
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
if: github.event_name == 'release' || github.event.inputs.platform == 'web'
|
||||
|
||||
steps:
|
||||
@@ -103,7 +103,7 @@ jobs:
|
||||
|
||||
build-android:
|
||||
name: build-android
|
||||
runs-on: ubuntu-22.04-16core
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
if: github.event_name == 'release' || github.event.inputs.platform == 'android'
|
||||
|
||||
steps:
|
||||
|
||||
@@ -10,7 +10,7 @@ on:
|
||||
jobs:
|
||||
build-web:
|
||||
name: build-web
|
||||
runs-on: ubuntu-22.04-16core
|
||||
runs-on: 'ubuntu-24.04-16core'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
|
||||
+24
-6
@@ -139,14 +139,14 @@ else()
|
||||
set(LINUX FALSE)
|
||||
endif()
|
||||
|
||||
if (LINUX)
|
||||
if (NOT FILAMENT_OSMESA_PATH STREQUAL "")
|
||||
if (NOT EXISTS ${FILAMENT_OSMESA_PATH}/)
|
||||
message(FATAL_ERROR "Cannot find specified OSMesa build directory: ${FILAMENT_OSMESA_PATH}")
|
||||
endif()
|
||||
set(FILAMENT_SUPPORTS_OSMESA TRUE)
|
||||
if (NOT FILAMENT_OSMESA_PATH STREQUAL "")
|
||||
if (NOT EXISTS ${FILAMENT_OSMESA_PATH}/)
|
||||
message(FATAL_ERROR "Cannot find specified OSMesa build directory: ${FILAMENT_OSMESA_PATH}")
|
||||
endif()
|
||||
set(FILAMENT_SUPPORTS_OSMESA TRUE)
|
||||
endif()
|
||||
|
||||
if (LINUX)
|
||||
if (FILAMENT_SUPPORTS_WAYLAND)
|
||||
add_definitions(-DFILAMENT_SUPPORTS_WAYLAND)
|
||||
set(FILAMENT_SUPPORTS_X11 FALSE)
|
||||
@@ -184,6 +184,12 @@ if (NOT ANDROID AND NOT WEBGL AND NOT IOS AND NOT FILAMENT_LINUX_IS_MOBILE)
|
||||
set(IS_HOST_PLATFORM TRUE)
|
||||
endif()
|
||||
|
||||
if (APPLE)
|
||||
if (FILAMENT_SUPPORTS_OSMESA)
|
||||
add_definitions(-DFILAMENT_SUPPORTS_OSMESA)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
# Link statically against c/c++ lib to avoid missing redistriburable such as
|
||||
# "VCRUNTIME140.dll not found. Try reinstalling the app.", but give users
|
||||
@@ -328,6 +334,9 @@ if (MSVC)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} /W0 /Zc:__cplusplus")
|
||||
else()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} -fstrict-aliasing -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations")
|
||||
if (APPLE)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-nullability-extension")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (FILAMENT_USE_EXTERNAL_GLES3)
|
||||
@@ -601,6 +610,14 @@ if (FILAMENT_SUPPORTS_METAL)
|
||||
set(MATC_API_FLAGS ${MATC_API_FLAGS} -a metal)
|
||||
endif()
|
||||
|
||||
# With WebGPU, push constants are not supported. Skinning uses them.
|
||||
# WebGPU has a proposal to add push constants at https://github.com/gpuweb/gpuweb/blob/main/proposals/push-constants.md
|
||||
# With WebGPU, Tint does not support ClipDistance which is used in Stereo. Mentioned in comment
|
||||
# https://github.com/google/dawn/blob/855d17b08abdf02f9142bf5a8f14d0ea088810a4/src/tint/lang/spirv/reader/ast_parser/function.cc#L4434
|
||||
if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
set(MATC_API_FLAGS ${MATC_API_FLAGS} -a webgpu --variant-filter=skinning,stereo)
|
||||
endif()
|
||||
|
||||
# Disable ESSL 1.0 code generation.
|
||||
if (NOT FILAMENT_ENABLE_FEATURE_LEVEL_0)
|
||||
set(MATC_API_FLAGS ${MATC_API_FLAGS} -1)
|
||||
@@ -784,6 +801,7 @@ add_subdirectory(${EXTERNAL}/draco/tnt)
|
||||
add_subdirectory(${EXTERNAL}/jsmn/tnt)
|
||||
add_subdirectory(${EXTERNAL}/stb/tnt)
|
||||
add_subdirectory(${EXTERNAL}/getopt)
|
||||
add_subdirectory(${EXTERNAL}/perfetto/tnt)
|
||||
|
||||
# Note that this has to be placed after mikktspace in order for combine_static_libs to work.
|
||||
add_subdirectory(${LIBRARIES}/geometry)
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.58.0'
|
||||
implementation 'com.google.android.filament:filament-android:1.59.3'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.58.0'
|
||||
pod 'Filament', '~> 1.59.3'
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -7,8 +7,32 @@ 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.59.4
|
||||
|
||||
|
||||
## v1.59.3
|
||||
|
||||
|
||||
## v1.59.2
|
||||
|
||||
- Fix build/compile errors when upgrading to MacOS 15.4
|
||||
|
||||
## v1.59.1
|
||||
|
||||
|
||||
## v1.59.0
|
||||
|
||||
- materials: five custom variables (varyings) are now available on the condition that the `color` attribute is not requested (b/404930099). [⚠️ **New Material Version**]
|
||||
|
||||
## v1.58.2
|
||||
|
||||
- engine: Generate 1D instead of 3D LUTs for color grading whenever possible.
|
||||
|
||||
## v1.58.1
|
||||
|
||||
|
||||
## v1.58.0
|
||||
|
||||
- materials: add an optional `transformName` field to sampler material parameters. [⚠️ **New Material Version**]
|
||||
|
||||
## v1.57.3
|
||||
|
||||
@@ -142,6 +142,12 @@ abstract class MaterialCompiler extends TaskWithBinary {
|
||||
if (!exclude_vulkan) {
|
||||
matcArgs += ['-a', 'vulkan']
|
||||
}
|
||||
def include_webgpu = providers
|
||||
.gradleProperty("com.google.android.filament.include-webgpu")
|
||||
.forUseAtConfigurationTime().present
|
||||
if (include_webgpu) {
|
||||
matcArgs += ['-a', 'webgpu', '--variant-filter=skinning,stereo']
|
||||
}
|
||||
|
||||
def mat_no_opt = providers
|
||||
.gradleProperty("com.google.android.filament.matnopt")
|
||||
|
||||
@@ -11,6 +11,13 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
||||
message("Library filamat ignores Vulkan settings")
|
||||
endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
# See third_party/dawn/tnt/CMakeLists.txt and libs/filamat/CMakeLists.txt
|
||||
add_library(tint STATIC IMPORTED)
|
||||
set_target_properties(tint PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libtint_combined.a)
|
||||
endif()
|
||||
|
||||
add_library(${FILAMAT_FLAVOR} STATIC IMPORTED)
|
||||
set_target_properties(${FILAMAT_FLAVOR} PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/lib${FILAMAT_FLAVOR}.a)
|
||||
@@ -19,6 +26,10 @@ add_library(utils STATIC IMPORTED)
|
||||
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
|
||||
|
||||
add_library(perfetto STATIC IMPORTED)
|
||||
set_target_properties(perfetto PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libperfetto.a)
|
||||
|
||||
add_library(filabridge STATIC IMPORTED)
|
||||
set_target_properties(filabridge PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilabridge.a)
|
||||
@@ -33,6 +44,7 @@ set_target_properties(shaders PROPERTIES IMPORTED_LOCATION
|
||||
|
||||
set(FILAMAT_INCLUDE_DIRS
|
||||
../../libs/utils/include
|
||||
../../third_party/perfetto
|
||||
)
|
||||
|
||||
include_directories(${FILAMENT_DIR}/include)
|
||||
@@ -48,6 +60,8 @@ target_link_libraries(filamat-jni
|
||||
filabridge
|
||||
shaders
|
||||
utils
|
||||
perfetto
|
||||
log
|
||||
smol-v
|
||||
$<$<STREQUAL:${FILAMENT_SUPPORTS_WEBGPU},ON>:tint>
|
||||
)
|
||||
|
||||
@@ -21,6 +21,10 @@ add_library(utils STATIC IMPORTED)
|
||||
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
|
||||
|
||||
add_library(perfetto STATIC IMPORTED)
|
||||
set_target_properties(perfetto PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libperfetto.a)
|
||||
|
||||
add_library(ibl-lite STATIC IMPORTED)
|
||||
set_target_properties(ibl-lite PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libibl-lite.a)
|
||||
@@ -29,10 +33,6 @@ add_library(filaflat STATIC IMPORTED)
|
||||
set_target_properties(filaflat PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilaflat.a)
|
||||
|
||||
add_library(filamat STATIC IMPORTED)
|
||||
set_target_properties(filamat PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilamat.a)
|
||||
|
||||
add_library(geometry STATIC IMPORTED)
|
||||
set_target_properties(geometry PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libgeometry.a)
|
||||
@@ -59,11 +59,11 @@ add_library(smol-v STATIC IMPORTED)
|
||||
set_target_properties(smol-v PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libsmol-v.a)
|
||||
|
||||
if (FILAMENT_ENABLE_FGVIEWER)
|
||||
add_library(fgviewer STATIC IMPORTED)
|
||||
set_target_properties(fgviewer PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfgviewer.a)
|
||||
endif()
|
||||
if (FILAMENT_ENABLE_FGVIEWER)
|
||||
add_library(fgviewer STATIC IMPORTED)
|
||||
set_target_properties(fgviewer PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfgviewer.a)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_ENABLE_MATDBG)
|
||||
add_library(matdbg STATIC IMPORTED)
|
||||
@@ -127,6 +127,7 @@ target_link_libraries(filament-jni
|
||||
PRIVATE android
|
||||
PRIVATE jnigraphics
|
||||
PRIVATE utils
|
||||
PRIVATE perfetto
|
||||
|
||||
# libgeometry is PUBLIC because gltfio uses it.
|
||||
PUBLIC geometry
|
||||
@@ -145,6 +146,7 @@ target_include_directories(filament-jni PRIVATE
|
||||
${FILAMENT_DIR}/include
|
||||
../../filament/backend/include
|
||||
../../third_party/robin-map
|
||||
../../third_party/perfetto
|
||||
../../libs/utils/include)
|
||||
|
||||
# Force a relink when the version script is changed:
|
||||
|
||||
@@ -68,6 +68,21 @@ Java_com_google_android_filament_Texture_nIsTextureSwizzleSupported(JNIEnv*, jcl
|
||||
return (jboolean) Texture::isTextureSwizzleSupported(*engine);
|
||||
}
|
||||
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_Texture_nGetMaxTextureSize(JNIEnv *, jclass,
|
||||
jlong nativeEngine, jint sampler) {
|
||||
Engine *engine = (Engine *) nativeEngine;
|
||||
return Texture::getMaxTextureSize(*engine, (Texture::Sampler)sampler);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_Texture_nGetMaxArrayTextureLayers(JNIEnv *, jclass,
|
||||
jlong nativeEngine) {
|
||||
Engine *engine = (Engine *) nativeEngine;
|
||||
return Texture::getMaxArrayTextureLayers(*engine);
|
||||
}
|
||||
|
||||
extern "C" JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_Texture_nValidatePixelFormatAndType(JNIEnv*, jclass,
|
||||
jint internalFormat, jint pixelDataFormat, jint pixelDataType) {
|
||||
|
||||
+10
-4
@@ -46,9 +46,8 @@ import static com.google.android.filament.Asserts.assertFloat4In;
|
||||
*
|
||||
* <h1>Performance</h1>
|
||||
*
|
||||
* Creating a new ColorGrading object may be more expensive than other Filament objects as a
|
||||
* 3D LUT may need to be generated. The generation of a 3D LUT, if necessary, may happen on
|
||||
* the CPU.
|
||||
* Creating a new ColorGrading object may be more expensive than other Filament objects as a LUT may
|
||||
* need to be generated. The generation of this LUT, if necessary, may happen on the CPU.
|
||||
*
|
||||
* <h1>Ordering</h1>
|
||||
*
|
||||
@@ -160,6 +159,10 @@ public class ColorGrading {
|
||||
* quality level will use a 32x32x32 10 bit LUT, a high quality will use a 32x32x32 16 bit
|
||||
* LUT, and a ultra quality will use a 64x64x64 16 bit LUT.
|
||||
*
|
||||
* This setting has no effect if generating a 1D LUT.
|
||||
*
|
||||
* This overrides the values set by format() and dimensions().
|
||||
*
|
||||
* The default quality is {@link QualityLevel#MEDIUM}.
|
||||
*
|
||||
* @param qualityLevel The desired quality of the color grading process
|
||||
@@ -175,6 +178,8 @@ public class ColorGrading {
|
||||
* When color grading is implemented using a 3D LUT, this sets the texture format of
|
||||
* of the LUT. This overrides the value set by quality().
|
||||
*
|
||||
* This setting has no effect if generating a 1D LUT.
|
||||
*
|
||||
* The default is INTEGER
|
||||
*
|
||||
* @param format The desired format of the 3D LUT.
|
||||
@@ -190,6 +195,8 @@ public class ColorGrading {
|
||||
* When color grading is implemented using a 3D LUT, this sets the dimension of the LUT.
|
||||
* This overrides the value set by quality().
|
||||
*
|
||||
* This setting has no effect if generating a 1D LUT.
|
||||
*
|
||||
* The default is 32
|
||||
*
|
||||
* @param dim The desired dimension of the LUT. Between 16 and 64.
|
||||
@@ -616,4 +623,3 @@ public class ColorGrading {
|
||||
|
||||
private static native long nBuilderBuild(long nativeBuilder, long nativeEngine);
|
||||
}
|
||||
|
||||
|
||||
@@ -691,6 +691,24 @@ public class Texture {
|
||||
pixelDataType.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param engine {@link Engine}
|
||||
* @param type Texture sampler type
|
||||
* @return The maximum size in texels of a texture of type \p type. At least 2048 for
|
||||
* 2D textures, 256 for 3D textures
|
||||
*/
|
||||
public static int getMaxTextureSize(@NonNull Engine engine, Sampler type) {
|
||||
return nGetMaxTextureSize(engine.getNativeObject(), type.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param engine {@link Engine}
|
||||
* @return The maximum number of layers supported by texture arrays. At least 256.
|
||||
*/
|
||||
public static int getMaxArrayTextureLayers(@NonNull Engine engine) {
|
||||
return nGetMaxArrayTextureLayers(engine.getNativeObject());
|
||||
}
|
||||
|
||||
/**
|
||||
* Use <code>Builder</code> to construct a <code>Texture</code> object instance.
|
||||
*/
|
||||
@@ -1289,6 +1307,8 @@ public class Texture {
|
||||
private static native boolean nIsTextureFormatSupported(long nativeEngine, int internalFormat);
|
||||
private static native boolean nIsTextureFormatMipmappable(long nativeEngine, int internalFormat);
|
||||
private static native boolean nIsTextureSwizzleSupported(long nativeEngine);
|
||||
private static native int nGetMaxTextureSize(long nativeObject, int ordinal);
|
||||
private static native int nGetMaxArrayTextureLayers(long nativeObject);
|
||||
|
||||
private static native boolean nValidatePixelFormatAndType(int internalFormat, int pixelDataFormat,
|
||||
int pixelDataType);
|
||||
|
||||
@@ -31,8 +31,6 @@ package com.google.android.filament;
|
||||
* <li>DisplayRangeToneMapper</li>
|
||||
* </ul>
|
||||
* </ul>
|
||||
*
|
||||
* You can create custom tone mapping operators by subclassing ToneMapper.
|
||||
*/
|
||||
public class ToneMapper {
|
||||
private final long mNativeObject;
|
||||
|
||||
@@ -35,6 +35,10 @@ add_library(utils STATIC IMPORTED)
|
||||
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
|
||||
|
||||
add_library(perfetto STATIC IMPORTED)
|
||||
set_target_properties(perfetto PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libperfetto.a)
|
||||
|
||||
add_library(uberzlib STATIC IMPORTED)
|
||||
set_target_properties(uberzlib PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libuberzlib.a)
|
||||
@@ -121,6 +125,7 @@ set(GLTFIO_INCLUDE_DIRS
|
||||
../../third_party/meshoptimizer/src
|
||||
../../third_party/robin-map
|
||||
../../third_party/stb
|
||||
../../third_party/perfetto
|
||||
../../libs/utils/include
|
||||
../../libs/ktxreader/include
|
||||
)
|
||||
@@ -129,7 +134,7 @@ add_library(gltfio-jni SHARED ${GLTFIO_SRCS})
|
||||
target_include_directories(gltfio-jni PRIVATE ${GLTFIO_INCLUDE_DIRS})
|
||||
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.symbols)
|
||||
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.map)
|
||||
target_link_libraries(gltfio-jni filament-jni utils uberzlib log stb ktxreader basis_transcoder zstd uberarchive)
|
||||
target_link_libraries(gltfio-jni filament-jni utils perfetto uberzlib log stb ktxreader basis_transcoder zstd uberarchive)
|
||||
target_link_libraries(gltfio-jni dracodec meshoptimizer)
|
||||
target_compile_definitions(gltfio-jni PUBLIC GLTFIO_DRACO_SUPPORTED=1)
|
||||
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/src)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.58.0
|
||||
VERSION_NAME=1.59.3
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -200,8 +200,9 @@ WEBGPU_ANDROID_GRADLE_OPTION=""
|
||||
EGL_ON_LINUX_OPTION="-DFILAMENT_SUPPORTS_EGL_ON_LINUX=OFF"
|
||||
|
||||
MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=OFF"
|
||||
FGVIEWER_OPTION="-DFILAMENT_ENABLE_FGVIEWER=OFF"
|
||||
MATDBG_GRADLE_OPTION=""
|
||||
FGVIEWER_OPTION="-DFILAMENT_ENABLE_FGVIEWER=OFF"
|
||||
FGVIEWER_GRADLE_OPTION=""
|
||||
|
||||
MATOPT_OPTION=""
|
||||
MATOPT_GRADLE_OPTION=""
|
||||
@@ -533,6 +534,7 @@ function build_android {
|
||||
${VULKAN_ANDROID_GRADLE_OPTION} \
|
||||
${WEBGPU_ANDROID_GRADLE_OPTION} \
|
||||
${MATDBG_GRADLE_OPTION} \
|
||||
${FGVIEWER_GRADLE_OPTION} \
|
||||
${MATOPT_GRADLE_OPTION} \
|
||||
:filament-android:assembleDebug \
|
||||
:gltfio-android:assembleDebug \
|
||||
@@ -541,6 +543,7 @@ function build_android {
|
||||
./gradlew \
|
||||
-Pcom.google.android.filament.dist-dir=../out/android-debug/filament \
|
||||
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
|
||||
${WEBGPU_ANDROID_GRADLE_OPTION} \
|
||||
:filamat-android:assembleDebug
|
||||
|
||||
if [[ "${BUILD_ANDROID_SAMPLES}" == "true" ]]; then
|
||||
@@ -583,6 +586,7 @@ function build_android {
|
||||
${VULKAN_ANDROID_GRADLE_OPTION} \
|
||||
${WEBGPU_ANDROID_GRADLE_OPTION} \
|
||||
${MATDBG_GRADLE_OPTION} \
|
||||
${FGVIEWER_GRADLE_OPTION} \
|
||||
${MATOPT_GRADLE_OPTION} \
|
||||
:filament-android:assembleRelease \
|
||||
:gltfio-android:assembleRelease \
|
||||
@@ -591,6 +595,7 @@ function build_android {
|
||||
./gradlew \
|
||||
-Pcom.google.android.filament.dist-dir=../out/android-release/filament \
|
||||
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
|
||||
${WEBGPU_ANDROID_GRADLE_OPTION} \
|
||||
:filamat-android:assembleRelease
|
||||
|
||||
if [[ "${BUILD_ANDROID_SAMPLES}" == "true" ]]; then
|
||||
|
||||
@@ -56,10 +56,16 @@ popd >/dev/null
|
||||
rm -rf out/check-headers
|
||||
mkdir -p out/check-headers
|
||||
|
||||
TMP_FILE=out/check-headers/temp.cpp
|
||||
echo "Checking that public headers compile independently..."
|
||||
for include in "${includes[@]}"; do
|
||||
rm -f ${TMP_FILE}
|
||||
echo "Checking ${include}"
|
||||
echo "#include <${include}>" >> out/check-headers/temp.cpp
|
||||
clang -std=c++17 -I "${FILAMENT_HEADERS}" out/check-headers/temp.cpp -c -o /dev/null
|
||||
if [[ "${include}" == "utils/Systrace.h" ]]; then
|
||||
# A necessary define before we can include utils/Systrace.h
|
||||
echo "#define SYSTRACE_TAG SYSTRACE_TAG_DISABLED" >> ${TMP_FILE}
|
||||
fi
|
||||
echo "#include <${include}>" >> ${TMP_FILE}
|
||||
clang -std=c++17 -I "${FILAMENT_HEADERS}" ${TMP_FILE} -c -o /dev/null
|
||||
done
|
||||
echo "Done!"
|
||||
|
||||
@@ -30,6 +30,9 @@ if [[ "$GITHUB_WORKFLOW" ]]; then
|
||||
sudo apt-get install clang-$GITHUB_CLANG_VERSION libc++-$GITHUB_CLANG_VERSION-dev libc++abi-$GITHUB_CLANG_VERSION-dev
|
||||
sudo apt-get install mesa-common-dev libxi-dev libxxf86vm-dev
|
||||
|
||||
# For dawn
|
||||
sudo apt-get install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libx11-xcb-dev
|
||||
|
||||
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${GITHUB_CLANG_VERSION} 100
|
||||
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${GITHUB_CLANG_VERSION} 100
|
||||
fi
|
||||
|
||||
+23
-15
@@ -13,7 +13,7 @@ This document is part of the [Filament project](https://github.com/google/filame
|
||||
## Authors
|
||||
|
||||
- [Romain Guy](https://github.com/romainguy), [@romainguy](https://twitter.com/romainguy)
|
||||
- [Mathias Agopian](https://github.com/pixelflinger), [@darthmoosious](https://twitter.com/darthmoosious)
|
||||
- [Mathias Agopian](https://github.com/pixelflinger), [@pixelflinger](https://bsky.app/profile/pixelflinger.bsky.social)
|
||||
|
||||
# Overview
|
||||
|
||||
@@ -78,26 +78,27 @@ in table [standardProperties].
|
||||
Property | Definition
|
||||
-----------------------:|:---------------------
|
||||
**baseColor** | Diffuse albedo for non-metallic surfaces, and specular color for metallic surfaces
|
||||
**metallic** | Whether a surface appears to be dielectric (0.0) or conductor (1.0). Often used as a binary value (0 or 1)
|
||||
**roughness** | Perceived smoothness (1.0) or roughness (0.0) of a surface. Smooth surfaces exhibit sharp reflections
|
||||
**metallic** | Whether a surface appears to be dielectric (0.0) or conductor (1.0). Often used as a binary value (0 or 1)
|
||||
**reflectance** | Fresnel reflectance at normal incidence for dielectric surfaces. This directly controls the strength of the reflections
|
||||
**sheenColor** | Strength of the sheen layer
|
||||
**sheenRoughness** | Perceived smoothness or roughness of the sheen layer
|
||||
**ambientOcclusion** | Defines how much of the ambient light is accessible to a surface point. It is a per-pixel shadowing factor between 0.0 and 1.0
|
||||
**clearCoat** | Strength of the clear coat layer
|
||||
**clearCoatRoughness** | Perceived smoothness or roughness of the clear coat layer
|
||||
**clearCoatNormal** | A detail normal used to perturb the clear coat layer using _bump mapping_ (_normal mapping_)
|
||||
**anisotropy** | Amount of anisotropy in either the tangent or bitangent direction
|
||||
**anisotropyDirection** | Local surface direction in tangent space
|
||||
**ambientOcclusion** | Defines how much of the ambient light is accessible to a surface point. It is a per-pixel shadowing factor between 0.0 and 1.0
|
||||
**normal** | A detail normal used to perturb the surface using _bump mapping_ (_normal mapping_)
|
||||
**bentNormal** | A normal pointing in the average unoccluded direction. Can be used to improve indirect lighting quality
|
||||
**clearCoatNormal** | A detail normal used to perturb the clear coat layer using _bump mapping_ (_normal mapping_)
|
||||
**emissive** | Additional diffuse albedo to simulate emissive surfaces (such as neons, etc.) This property is mostly useful in an HDR pipeline with a bloom pass
|
||||
**postLightingColor** | Additional color that can be blended with the result of the lighting computations. See `postLightingBlending`
|
||||
**ior** | Index of refraction, either for refractive objects or as an alternative to reflectance
|
||||
**transmission** | Defines how much of the diffuse light of a dielectric is transmitted through the object, in other words this defines how transparent an object is
|
||||
**absorption** | Absorption factor for refractive objects
|
||||
**microThickness** | Thickness of the thin layer of refractive objects
|
||||
**thickness** | Thickness of the solid volume of refractive objects
|
||||
**sheenColor** | Strength of the sheen layer
|
||||
**sheenRoughness** | Perceived smoothness or roughness of the sheen layer
|
||||
**emissive** | Additional diffuse albedo to simulate emissive surfaces (such as neons, etc.) This property is mostly useful in an HDR pipeline with a bloom pass
|
||||
**normal** | A detail normal used to perturb the surface using _bump mapping_ (_normal mapping_)
|
||||
**postLightingColor** | Additional color that can be blended with the result of the lighting computations. See `postLightingBlending`
|
||||
**absorption** | Absorption factor for refractive objects
|
||||
**transmission** | Defines how much of the diffuse light of a dielectric is transmitted through the object, in other words this defines how transparent an object is
|
||||
**ior** | Index of refraction, either for refractive objects or as an alternative to reflectance
|
||||
**microThickness** | Thickness of the thin layer of refractive objects
|
||||
**bentNormal** | A normal pointing in the average unoccluded direction. Can be used to improve indirect lighting quality
|
||||
**shadowStrength** | Strength factor between 0 and 1 for all shadows received by this material
|
||||
[Table [standardProperties]: Properties of the standard model]
|
||||
|
||||
The type and range of each property is described in table [standardPropertiesTypes].
|
||||
@@ -1272,6 +1273,9 @@ Description
|
||||
when selecting any shading model that is not `unlit`. See the shader sections of this document
|
||||
for more information on how to access these attributes from the shaders.
|
||||
|
||||
!!! Note: Interaction with custom variables
|
||||
When the `color` attribute is specified, only four custom variables are available instead of five.
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
|
||||
material {
|
||||
parameters : [
|
||||
@@ -1302,7 +1306,7 @@ Type
|
||||
: array of `string`
|
||||
|
||||
Value
|
||||
: Up to 4 strings, each must be a valid GLSL identifier.
|
||||
: Up to 5 strings, each must be a valid GLSL identifier.
|
||||
|
||||
Description
|
||||
: Defines custom interpolants (or variables) that are output by the material's vertex shader.
|
||||
@@ -1318,6 +1322,10 @@ Description
|
||||
particular if `default` is specified the default precision is used is the fragment shader
|
||||
(`mediump`) and in the vertex shader (`highp`).
|
||||
|
||||
!!! Warning: Interaction with required attributes
|
||||
If the `color` attribute is specified in the `required` list, then only four variables can be used
|
||||
instead of five.
|
||||
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
|
||||
material {
|
||||
name : Skybox,
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -108,8 +108,12 @@ if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3)
|
||||
list(APPEND SRCS src/opengl/platforms/PlatformCocoaTouchGL.mm)
|
||||
list(APPEND SRCS src/opengl/platforms/CocoaTouchExternalImage.mm)
|
||||
elseif (APPLE)
|
||||
list(APPEND SRCS src/opengl/platforms/PlatformCocoaGL.mm)
|
||||
list(APPEND SRCS src/opengl/platforms/CocoaExternalImage.mm)
|
||||
if (FILAMENT_SUPPORTS_OSMESA)
|
||||
list(APPEND SRCS src/opengl/platforms/PlatformOSMesa.cpp)
|
||||
else()
|
||||
list(APPEND SRCS src/opengl/platforms/PlatformCocoaGL.mm)
|
||||
list(APPEND SRCS src/opengl/platforms/CocoaExternalImage.mm)
|
||||
endif()
|
||||
elseif (WEBGL)
|
||||
list(APPEND SRCS src/opengl/platforms/PlatformWebGL.cpp)
|
||||
elseif (LINUX)
|
||||
@@ -173,27 +177,6 @@ endif()
|
||||
if (FILAMENT_SUPPORTS_VULKAN)
|
||||
list(APPEND SRCS
|
||||
include/backend/platforms/VulkanPlatform.h
|
||||
src/vulkan/caching/VulkanDescriptorSetManager.cpp
|
||||
src/vulkan/caching/VulkanDescriptorSetManager.h
|
||||
src/vulkan/caching/VulkanPipelineLayoutCache.cpp
|
||||
src/vulkan/caching/VulkanPipelineLayoutCache.h
|
||||
src/vulkan/memory/ResourceManager.cpp
|
||||
src/vulkan/memory/ResourceManager.h
|
||||
src/vulkan/memory/ResourcePointer.h
|
||||
src/vulkan/memory/Resource.cpp
|
||||
src/vulkan/memory/Resource.h
|
||||
src/vulkan/platform/VulkanPlatform.cpp
|
||||
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
|
||||
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
|
||||
src/vulkan/utils/Conversion.cpp
|
||||
src/vulkan/utils/Conversion.h
|
||||
src/vulkan/utils/Definitions.h
|
||||
src/vulkan/utils/Helper.h
|
||||
src/vulkan/utils/Image.h
|
||||
src/vulkan/utils/Image.cpp
|
||||
src/vulkan/utils/Spirv.h
|
||||
src/vulkan/utils/Spirv.cpp
|
||||
src/vulkan/utils/StaticVector.h
|
||||
src/vulkan/VulkanAsyncHandles.h
|
||||
src/vulkan/VulkanBlitter.cpp
|
||||
src/vulkan/VulkanBlitter.h
|
||||
@@ -204,46 +187,101 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
||||
src/vulkan/VulkanConstants.h
|
||||
src/vulkan/VulkanContext.cpp
|
||||
src/vulkan/VulkanContext.h
|
||||
src/vulkan/VulkanDescriptorSetCache.cpp
|
||||
src/vulkan/VulkanDescriptorSetCache.h
|
||||
src/vulkan/VulkanDescriptorSetLayoutCache.cpp
|
||||
src/vulkan/VulkanDescriptorSetLayoutCache.h
|
||||
src/vulkan/VulkanDriver.cpp
|
||||
src/vulkan/VulkanDriver.h
|
||||
src/vulkan/VulkanDriverFactory.h
|
||||
src/vulkan/VulkanExternalImageManager.cpp
|
||||
src/vulkan/VulkanExternalImageManager.h
|
||||
src/vulkan/VulkanFboCache.cpp
|
||||
src/vulkan/VulkanFboCache.h
|
||||
src/vulkan/VulkanHandles.cpp
|
||||
src/vulkan/VulkanHandles.h
|
||||
src/vulkan/VulkanMemory.h
|
||||
src/vulkan/VulkanMemory.cpp
|
||||
src/vulkan/VulkanMemory.h
|
||||
src/vulkan/VulkanPipelineCache.cpp
|
||||
src/vulkan/VulkanPipelineCache.h
|
||||
src/vulkan/VulkanPipelineLayoutCache.cpp
|
||||
src/vulkan/VulkanPipelineLayoutCache.h
|
||||
src/vulkan/VulkanQueryManager.cpp
|
||||
src/vulkan/VulkanQueryManager.h
|
||||
src/vulkan/VulkanReadPixels.cpp
|
||||
src/vulkan/VulkanReadPixels.h
|
||||
src/vulkan/VulkanSamplerCache.cpp
|
||||
src/vulkan/VulkanSamplerCache.h
|
||||
src/vulkan/VulkanStagePool.cpp
|
||||
src/vulkan/VulkanStagePool.h
|
||||
src/vulkan/VulkanSwapChain.cpp
|
||||
src/vulkan/VulkanSwapChain.h
|
||||
src/vulkan/VulkanReadPixels.cpp
|
||||
src/vulkan/VulkanReadPixels.h
|
||||
src/vulkan/VulkanTexture.cpp
|
||||
src/vulkan/VulkanTexture.h
|
||||
src/vulkan/VulkanYcbcrConversionCache.cpp
|
||||
src/vulkan/VulkanYcbcrConversionCache.h
|
||||
src/vulkan/memory/Resource.cpp
|
||||
src/vulkan/memory/Resource.h
|
||||
src/vulkan/memory/ResourceManager.cpp
|
||||
src/vulkan/memory/ResourceManager.h
|
||||
src/vulkan/memory/ResourcePointer.h
|
||||
src/vulkan/platform/VulkanPlatform.cpp
|
||||
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
|
||||
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
|
||||
src/vulkan/utils/Conversion.cpp
|
||||
src/vulkan/utils/Conversion.h
|
||||
src/vulkan/utils/Definitions.h
|
||||
src/vulkan/utils/Helper.h
|
||||
src/vulkan/utils/Image.cpp
|
||||
src/vulkan/utils/Image.h
|
||||
src/vulkan/utils/Spirv.cpp
|
||||
src/vulkan/utils/Spirv.h
|
||||
src/vulkan/utils/StaticVector.h
|
||||
)
|
||||
if (LINUX OR WIN32)
|
||||
list(APPEND SRCS src/vulkan/platform/VulkanPlatformLinuxWindows.cpp)
|
||||
elseif (APPLE OR IOS)
|
||||
list(APPEND SRCS src/vulkan/platform/VulkanPlatformApple.mm)
|
||||
elseif (ANDROID)
|
||||
list(APPEND SRCS src/vulkan/platform/VulkanPlatformAndroid.cpp)
|
||||
list(APPEND SRCS
|
||||
include/backend/platforms/VulkanPlatformAndroid.h
|
||||
src/vulkan/platform/VulkanPlatformAndroid.cpp
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
list(APPEND SRCS
|
||||
include/backend/platforms/WebGPUPlatform.h
|
||||
src/webgpu/platform/WebGPUPlatform.cpp
|
||||
src/webgpu/WebGPUConstants.h
|
||||
src/webgpu/WebGPUDriver.cpp
|
||||
src/webgpu/WebGPUDriver.h
|
||||
src/webgpu/WebGPUPlatform.cpp
|
||||
src/webgpu/WebGPUPlatform.h
|
||||
src/webgpu/WebGPUHandles.cpp
|
||||
src/webgpu/WebGPUHandles.h
|
||||
src/webgpu/WebGPUSwapChain.cpp
|
||||
src/webgpu/WebGPUSwapChain.h
|
||||
src/webgpu/WGPUProgram.cpp
|
||||
)
|
||||
if (WIN32)
|
||||
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
|
||||
elseif (LINUX)
|
||||
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformLinux.cpp)
|
||||
elseif (APPLE OR IOS)
|
||||
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformApple.mm)
|
||||
elseif (ANDROID)
|
||||
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformAndroid.cpp)
|
||||
endif()
|
||||
|
||||
if (TNT_DEV)
|
||||
set(FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING_DEFAULT ON)
|
||||
else()
|
||||
set(FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING_DEFAULT OFF)
|
||||
endif()
|
||||
|
||||
option(FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING
|
||||
"Enable immediate_error_handling for the WebGPU backend Dawn implementation"
|
||||
${FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING_DEFAULT})
|
||||
endif()
|
||||
|
||||
if (ANDROID)
|
||||
@@ -273,6 +311,11 @@ target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
|
||||
# add this subproject to the Filament folder
|
||||
set_target_properties(${TARGET} PROPERTIES FOLDER Filament)
|
||||
|
||||
# we need to export the headers properly for backend.lib on windows
|
||||
if (WIN32 AND FILAMENT_SUPPORTS_WEBGPU)
|
||||
target_compile_definitions(${TARGET} PRIVATE "WGPU_IMPLEMENTATION")
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Expose a header-only target to minimize dependencies.
|
||||
# ==================================================================================================
|
||||
@@ -393,9 +436,12 @@ set(LINUX_LINKER_OPTIMIZATION_FLAGS
|
||||
-Wl,--exclude-libs,bluegl
|
||||
)
|
||||
|
||||
if (LINUX AND FILAMENT_SUPPORTS_OSMESA)
|
||||
set(OSMESA_COMPILE_FLAGS
|
||||
-I${FILAMENT_OSMESA_PATH}/include/GL)
|
||||
if (FILAMENT_SUPPORTS_OSMESA)
|
||||
if (LINUX)
|
||||
set(OSMESA_COMPILE_FLAGS -I${FILAMENT_OSMESA_PATH}/include/GL)
|
||||
elseif (APPLE)
|
||||
set(OSMESA_COMPILE_FLAGS -I${FILAMENT_OSMESA_PATH}/include)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
@@ -430,6 +476,10 @@ if (FILAMENT_SUPPORTS_METAL)
|
||||
target_compile_definitions(${TARGET} PRIVATE $<$<BOOL:${FILAMENT_METAL_PROFILING}>:FILAMENT_METAL_PROFILING>)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
target_compile_definitions(${TARGET} PRIVATE $<$<BOOL:${FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING}>:FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING>)
|
||||
endif()
|
||||
|
||||
target_link_libraries(${TARGET} PRIVATE
|
||||
${OSMESA_LINKER_FLAGS}
|
||||
$<$<AND:$<PLATFORM_ID:Linux>,$<CONFIG:Release>>:${LINUX_LINKER_OPTIMIZATION_FLAGS}>
|
||||
@@ -458,6 +508,12 @@ if (APPLE OR LINUX)
|
||||
test/ShaderGenerator.cpp
|
||||
test/TrianglePrimitive.cpp
|
||||
test/Arguments.cpp
|
||||
test/ImageExpectations.cpp
|
||||
test/Lifetimes.cpp
|
||||
test/PlatformRunner.cpp
|
||||
test/Shader.cpp
|
||||
test/SharedShaders.cpp
|
||||
test/Skip.cpp
|
||||
test/test_FeedbackLoops.cpp
|
||||
test/test_Blit.cpp
|
||||
test/test_MissingRequiredAttributes.cpp
|
||||
@@ -473,6 +529,7 @@ if (APPLE OR LINUX)
|
||||
test/test_Handles.cpp
|
||||
)
|
||||
set(BACKEND_TEST_LIBS
|
||||
absl::str_format
|
||||
backend
|
||||
getopt
|
||||
gtest
|
||||
@@ -480,6 +537,9 @@ if (APPLE OR LINUX)
|
||||
filamat
|
||||
SPIRV
|
||||
spirv-cross-glsl)
|
||||
# Create input/output directories for test result images.
|
||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/images/actual_images)
|
||||
file(COPY test/expected_images DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/images)
|
||||
endif()
|
||||
|
||||
# TODO: Disabling IOS test due to breakage wrt glslang update
|
||||
@@ -488,7 +548,7 @@ if (APPLE AND NOT IOS)
|
||||
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})
|
||||
target_link_libraries(backend_test PUBLIC ${BACKEND_TEST_LIBS})
|
||||
|
||||
set(BACKEND_TEST_DEPS
|
||||
OSDependent
|
||||
|
||||
@@ -55,4 +55,9 @@ public:
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
utils::io::ostream& operator<<(utils::io::ostream& out,
|
||||
const filament::backend::BufferObjectStreamDescriptor& b);
|
||||
#endif
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_BUFFEROBJECTSTREAMDESCRIPTOR_H
|
||||
|
||||
@@ -126,6 +126,10 @@ static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT,
|
||||
static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 9; // This is guaranteed by OpenGL ES.
|
||||
static constexpr size_t CONFIG_SAMPLER_BINDING_COUNT = 4; // This is guaranteed by OpenGL ES.
|
||||
|
||||
static constexpr uint8_t EXTERNAL_SAMPLER_DATA_INDEX_UNUSED =
|
||||
uint8_t(-1);// Case where the descriptor set binding isnt using any external sampler state
|
||||
// and therefore doesn't have a valid entry.
|
||||
|
||||
/**
|
||||
* Defines the backend's feature levels.
|
||||
*/
|
||||
@@ -182,6 +186,7 @@ enum class ShaderLanguage {
|
||||
SPIRV = 2,
|
||||
MSL = 3,
|
||||
METAL_LIBRARY = 4,
|
||||
WGSL = 5,
|
||||
};
|
||||
|
||||
static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) {
|
||||
@@ -196,6 +201,8 @@ static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguag
|
||||
return "MSL";
|
||||
case ShaderLanguage::METAL_LIBRARY:
|
||||
return "Metal precompiled library";
|
||||
case ShaderLanguage::WGSL:
|
||||
return "WGSL";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,21 +256,21 @@ struct DescriptorSetLayoutBinding {
|
||||
DescriptorFlags flags = DescriptorFlags::NONE;
|
||||
uint16_t count = 0;
|
||||
|
||||
friend inline bool operator==(
|
||||
DescriptorSetLayoutBinding const& lhs,
|
||||
// TODO: uncomment when needed. Note that this class is used as hash key. We need to ensure
|
||||
// no uninitialized padding bytes.
|
||||
// uint8_t externalSamplerDataIndex = EXTERNAL_SAMPLER_DATA_INDEX_UNUSED;
|
||||
|
||||
friend inline bool operator==(DescriptorSetLayoutBinding const& lhs,
|
||||
DescriptorSetLayoutBinding const& rhs) noexcept {
|
||||
return lhs.type == rhs.type &&
|
||||
lhs.flags == rhs.flags &&
|
||||
lhs.count == rhs.count &&
|
||||
lhs.stageFlags == rhs.stageFlags;
|
||||
// lhs.stageFlags == rhs.stageFlags &&
|
||||
// lhs.externalSamplerDataIndex == rhs.externalSamplerDataIndex;
|
||||
}
|
||||
};
|
||||
|
||||
struct DescriptorSetLayout {
|
||||
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Bitmask for selecting render buffers
|
||||
*/
|
||||
@@ -965,8 +972,28 @@ enum class SamplerCompareFunc : uint8_t {
|
||||
N //!< Never. The depth / stencil test always fails.
|
||||
};
|
||||
|
||||
//! this API is copied from (and only applies to) the Vulkan spec.
|
||||
//! These specify YUV to RGB conversions.
|
||||
enum class SamplerYcbcrModelConversion : uint8_t {
|
||||
RGB_IDENTITY = 0,
|
||||
YCBCR_IDENTITY = 1,
|
||||
YCBCR_709 = 2,
|
||||
YCBCR_601 = 3,
|
||||
YCBCR_2020 = 4,
|
||||
};
|
||||
|
||||
enum class SamplerYcbcrRange : uint8_t {
|
||||
ITU_FULL = 0,
|
||||
ITU_NARROW = 1,
|
||||
};
|
||||
|
||||
enum class ChromaLocation : uint8_t {
|
||||
COSITED_EVEN = 0,
|
||||
MIDPOINT = 1,
|
||||
};
|
||||
|
||||
//! Sampler parameters
|
||||
struct SamplerParams { // NOLINT
|
||||
struct SamplerParams { // NOLINT
|
||||
SamplerMagFilter filterMag : 1; //!< magnification filter (NEAREST)
|
||||
SamplerMinFilter filterMin : 3; //!< minification filter (NEAREST)
|
||||
SamplerWrapMode wrapS : 2; //!< s-coordinate wrap mode (CLAMP_TO_EDGE)
|
||||
@@ -1021,6 +1048,7 @@ private:
|
||||
return SamplerParams::LessThan{}(lhs, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(SamplerParams) == 4);
|
||||
|
||||
// The limitation to 64-bits max comes from how we store a SamplerParams in our JNI code
|
||||
@@ -1028,6 +1056,95 @@ static_assert(sizeof(SamplerParams) == 4);
|
||||
static_assert(sizeof(SamplerParams) <= sizeof(uint64_t),
|
||||
"SamplerParams must be no more than 64 bits");
|
||||
|
||||
//! Sampler parameters
|
||||
struct SamplerYcbcrConversion {// NOLINT
|
||||
SamplerYcbcrModelConversion ycbcrModel : 4;
|
||||
TextureSwizzle r : 4;
|
||||
TextureSwizzle g : 4;
|
||||
TextureSwizzle b : 4;
|
||||
TextureSwizzle a : 4;
|
||||
SamplerYcbcrRange ycbcrRange : 1;
|
||||
ChromaLocation xChromaOffset : 1;
|
||||
ChromaLocation yChromaOffset : 1;
|
||||
SamplerMagFilter chromaFilter : 1;
|
||||
uint8_t padding;
|
||||
|
||||
struct Hasher {
|
||||
size_t operator()(const SamplerYcbcrConversion p) const noexcept {
|
||||
// we don't use std::hash<> here, so we don't have to include <functional>
|
||||
return *reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&p));
|
||||
}
|
||||
};
|
||||
|
||||
struct EqualTo {
|
||||
bool operator()(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) const noexcept {
|
||||
assert_invariant(lhs.padding == 0);
|
||||
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
|
||||
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
|
||||
return *pLhs == *pRhs;
|
||||
}
|
||||
};
|
||||
|
||||
struct LessThan {
|
||||
bool operator()(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) const noexcept {
|
||||
assert_invariant(lhs.padding == 0);
|
||||
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
|
||||
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
|
||||
return *pLhs < *pRhs;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
friend inline bool operator == (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
noexcept {
|
||||
return SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
|
||||
}
|
||||
friend inline bool operator != (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
noexcept {
|
||||
return !SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
|
||||
}
|
||||
friend inline bool operator < (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
noexcept {
|
||||
return SamplerYcbcrConversion::LessThan{}(lhs, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(sizeof(SamplerYcbcrConversion) == 4);
|
||||
|
||||
static_assert(sizeof(SamplerYcbcrConversion) <= sizeof(uint64_t),
|
||||
"SamplerYcbcrConversion must be no more than 64 bits");
|
||||
|
||||
struct ExternalSamplerDatum {
|
||||
ExternalSamplerDatum(SamplerYcbcrConversion ycbcr, SamplerParams spm, uint32_t extFmt)
|
||||
: YcbcrConversion(ycbcr),
|
||||
samplerParams(spm),
|
||||
externalFormat(extFmt) {}
|
||||
bool operator==(ExternalSamplerDatum const& rhs) const {
|
||||
return (YcbcrConversion == rhs.YcbcrConversion && samplerParams == rhs.samplerParams &&
|
||||
externalFormat == rhs.externalFormat);
|
||||
}
|
||||
struct EqualTo {
|
||||
bool operator()(const ExternalSamplerDatum& lhs,
|
||||
const ExternalSamplerDatum& rhs) const noexcept {
|
||||
return (lhs.YcbcrConversion == rhs.YcbcrConversion &&
|
||||
lhs.samplerParams == rhs.samplerParams &&
|
||||
lhs.externalFormat == rhs.externalFormat);
|
||||
}
|
||||
};
|
||||
SamplerYcbcrConversion YcbcrConversion;
|
||||
SamplerParams samplerParams;
|
||||
uint32_t externalFormat;
|
||||
};
|
||||
// No implicit padding allowed due to it being a hash key.
|
||||
static_assert(sizeof(ExternalSamplerDatum) == 12);
|
||||
|
||||
struct DescriptorSetLayout {
|
||||
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
|
||||
|
||||
// TODO: uncomment when needed
|
||||
// utils::FixedCapacityVector<ExternalSamplerDatum> externalSamplerData;
|
||||
};
|
||||
|
||||
//! blending equation function
|
||||
enum class BlendEquation : uint8_t {
|
||||
ADD, //!< the fragment is added to the color buffer
|
||||
|
||||
@@ -33,7 +33,7 @@ public:
|
||||
PlatformCocoaGL();
|
||||
~PlatformCocoaGL() noexcept override;
|
||||
|
||||
ExternalImageHandle createExternalImage(void* cvPixelBuffer) noexcept;
|
||||
ExternalImageHandle UTILS_PUBLIC createExternalImage(void* cvPixelBuffer) noexcept;
|
||||
|
||||
protected:
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -32,7 +32,7 @@ public:
|
||||
PlatformCocoaTouchGL();
|
||||
~PlatformCocoaTouchGL() noexcept override;
|
||||
|
||||
ExternalImageHandle createExternalImage(void* cvPixelBuffer) noexcept;
|
||||
ExternalImageHandle UTILS_PUBLIC createExternalImage(void* cvPixelBuffer) noexcept;
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Platform Interface
|
||||
|
||||
@@ -50,7 +50,7 @@ public:
|
||||
/**
|
||||
* Creates an ExternalImage from a EGLImageKHR
|
||||
*/
|
||||
ExternalImageHandle createExternalImage(EGLImageKHR eglImage) noexcept;
|
||||
ExternalImageHandle UTILS_PUBLIC createExternalImage(EGLImageKHR eglImage) noexcept;
|
||||
|
||||
protected:
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <backend/platforms/PlatformEGL.h>
|
||||
|
||||
#include <utils/android/PerformanceHintManager.h>
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
@@ -43,8 +44,21 @@ public:
|
||||
PlatformEGLAndroid() noexcept;
|
||||
~PlatformEGLAndroid() noexcept override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Creates an ExternalImage from a EGLImageKHR
|
||||
*/
|
||||
ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer, bool sRGB) noexcept;
|
||||
|
||||
struct UTILS_PUBLIC ExternalImageDescAndroid {
|
||||
uint32_t width; // Texture width
|
||||
uint32_t height; // Texture height
|
||||
TextureFormat format;// Texture format
|
||||
TextureUsage usage; // Texture usage flags
|
||||
};
|
||||
|
||||
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(ExternalImageHandle externalImage) noexcept;
|
||||
|
||||
protected:
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// Platform Interface
|
||||
|
||||
@@ -57,11 +71,6 @@ protected:
|
||||
Driver* createDriver(void* sharedContext,
|
||||
const Platform::DriverConfig& driverConfig) noexcept override;
|
||||
|
||||
/**
|
||||
* Creates an ExternalImage from a EGLImageKHR
|
||||
*/
|
||||
ExternalImageHandle createExternalImage(AHardwareBuffer const *buffer, bool sRGB) noexcept;
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
// OpenGLPlatform Interface
|
||||
|
||||
@@ -95,15 +104,26 @@ protected:
|
||||
*/
|
||||
AcquiredImage transformAcquiredImage(AcquiredImage source) noexcept override;
|
||||
|
||||
bool setExternalImage(ExternalImageHandleRef externalImage, ExternalTexture* texture) noexcept override;
|
||||
OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override;
|
||||
void destroyExternalImageTexture(ExternalTexture* texture) noexcept override;
|
||||
|
||||
struct ExternalImageEGLAndroid : public ExternalImageEGL {
|
||||
AHardwareBuffer* aHardwareBuffer = nullptr;
|
||||
uint32_t width; // Texture width
|
||||
uint32_t height; // Texture height
|
||||
TextureFormat format;// Texture format
|
||||
TextureUsage usage; // Texture usage flags
|
||||
bool sRGB = false;
|
||||
|
||||
protected:
|
||||
~ExternalImageEGLAndroid() override;
|
||||
};
|
||||
|
||||
bool setExternalImage(ExternalImageHandleRef externalImage,
|
||||
ExternalTexture* texture) noexcept override;
|
||||
bool setImage(ExternalImageEGLAndroid const* eglExternalImage,
|
||||
ExternalTexture* texture) noexcept;
|
||||
|
||||
protected:
|
||||
bool makeCurrent(ContextType type,
|
||||
SwapChain* drawSwapChain,
|
||||
|
||||
@@ -21,7 +21,12 @@
|
||||
|
||||
#include "bluegl/BlueGL.h"
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <osmesa.h>
|
||||
#elif defined(__APPLE__)
|
||||
#undef GLAPI
|
||||
#include <GL/osmesa.h>
|
||||
#endif
|
||||
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H
|
||||
|
||||
#include <backend/Platform.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
@@ -103,10 +104,6 @@ public:
|
||||
|
||||
// Semaphore to be signaled once the image is available.
|
||||
VkSemaphore imageReadySemaphore = VK_NULL_HANDLE;
|
||||
|
||||
// A function called right before vkQueueSubmit. After this call, the image must be
|
||||
// available. This pointer can be null if imageReadySemaphore is not VK_NULL_HANDLE.
|
||||
std::function<void(SwapChainPtr handle)> explicitImageReadyWait = nullptr;
|
||||
};
|
||||
|
||||
VulkanPlatform();
|
||||
@@ -297,6 +294,16 @@ public:
|
||||
VkQueue getProtectedGraphicsQueue() const noexcept;
|
||||
|
||||
struct ExternalImageMetadata {
|
||||
/**
|
||||
* The Filament texture format.
|
||||
*/
|
||||
TextureFormat filamentFormat;
|
||||
|
||||
/**
|
||||
* The Filament texture usage.
|
||||
*/
|
||||
TextureUsage filamentUsage;
|
||||
|
||||
/**
|
||||
* The width of the external image
|
||||
*/
|
||||
@@ -307,26 +314,21 @@ public:
|
||||
*/
|
||||
uint32_t height;
|
||||
|
||||
/**
|
||||
* The layerCount of the external image
|
||||
*/
|
||||
uint32_t layerCount;
|
||||
|
||||
/**
|
||||
* The layer count of the external image
|
||||
*/
|
||||
uint32_t layers;
|
||||
|
||||
/**
|
||||
* The numbers of samples per texel
|
||||
*/
|
||||
VkSampleCountFlagBits samples;
|
||||
|
||||
/**
|
||||
* The format of the external image
|
||||
*/
|
||||
VkFormat format;
|
||||
|
||||
/**
|
||||
* An external buffer can be protected. This tells you if it is.
|
||||
*/
|
||||
bool isProtected;
|
||||
|
||||
/**
|
||||
* The type of external format (opaque int) if used.
|
||||
*/
|
||||
@@ -346,23 +348,75 @@ public:
|
||||
* Heap information
|
||||
*/
|
||||
uint32_t memoryTypeBits;
|
||||
};
|
||||
virtual ExternalImageMetadata getExternalImageMetadata(void* externalImage);
|
||||
|
||||
using ImageData = std::pair<VkImage, VkDeviceMemory>;
|
||||
virtual ImageData createExternalImage(void* externalImage,
|
||||
const ExternalImageMetadata& metadata);
|
||||
/**
|
||||
* Ycbcr conversion components
|
||||
*/
|
||||
VkComponentMapping ycbcrConversionComponents;
|
||||
|
||||
/**
|
||||
* Ycbcr model
|
||||
*/
|
||||
VkSamplerYcbcrModelConversion ycbcrModel;
|
||||
|
||||
/**
|
||||
* Ycbcr range
|
||||
*/
|
||||
VkSamplerYcbcrRange ycbcrRange;
|
||||
|
||||
/**
|
||||
* Ycbcr x chroma offset
|
||||
*/
|
||||
VkChromaLocation xChromaOffset;
|
||||
|
||||
/**
|
||||
* Ycbcr y chroma offset
|
||||
*/
|
||||
VkChromaLocation yChromaOffset;
|
||||
};
|
||||
|
||||
|
||||
// Note that the image metadata might change per-frame, hence we need a method for extracting
|
||||
// it.
|
||||
virtual ExternalImageMetadata extractExternalImageMetadata(ExternalImageHandleRef image) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
struct ImageData {
|
||||
struct Bundle {
|
||||
VkImage image = VK_NULL_HANDLE;
|
||||
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||
|
||||
inline bool valid() const noexcept {
|
||||
return image != VK_NULL_HANDLE;
|
||||
}
|
||||
};
|
||||
// It's possible for the external image to also have a known VK format. We need to create an
|
||||
// image for that in case we are not looking to use an external "sampler" with this image.
|
||||
Bundle internal;
|
||||
|
||||
// If we get a externalFormat in the metadata, then we should create an image with
|
||||
// VK_FORMAT_UNDEFINED
|
||||
Bundle external;
|
||||
};
|
||||
|
||||
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ExtensionSet getSwapchainInstanceExtensions() const;
|
||||
|
||||
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
|
||||
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
|
||||
uint64_t flags) const noexcept;
|
||||
|
||||
private:
|
||||
static ExtensionSet getSwapchainInstanceExtensions();
|
||||
static ExternalImageMetadata getExternalImageMetadataImpl(void* externalImage,
|
||||
VkDevice device);
|
||||
static ImageData createExternalImageImpl(void* externalImage, VkDevice device,
|
||||
const VkAllocationCallbacks* allocator, const ExternalImageMetadata& metadata);
|
||||
// Platform dependent helper methods
|
||||
static ExtensionSet getSwapchainInstanceExtensionsImpl();
|
||||
|
||||
// Platform dependent helper methods
|
||||
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
|
||||
static SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
|
||||
static SurfaceBundle createVkSurfaceKHRImpl(void* nativeWindow, VkInstance instance,
|
||||
uint64_t flags) noexcept;
|
||||
|
||||
friend struct VulkanPlatformPrivate;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
|
||||
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
|
||||
#include <android/hardware_buffer.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class VulkanPlatformAndroid : public VulkanPlatform {
|
||||
public:
|
||||
ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer,
|
||||
bool sRGB) noexcept;
|
||||
|
||||
struct UTILS_PUBLIC ExternalImageDescAndroid {
|
||||
uint32_t width; // Texture width
|
||||
uint32_t height; // Texture height
|
||||
TextureFormat format;// Texture format
|
||||
TextureUsage usage; // Texture usage flags
|
||||
};
|
||||
|
||||
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(
|
||||
ExternalImageHandleRef externalImage) const noexcept;
|
||||
|
||||
virtual ExternalImageMetadata extractExternalImageMetadata(
|
||||
ExternalImageHandleRef image) const override;
|
||||
|
||||
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const override;
|
||||
|
||||
protected:
|
||||
virtual ExtensionSet getSwapchainInstanceExtensions() const override;
|
||||
|
||||
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
|
||||
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
|
||||
uint64_t flags) const noexcept override;
|
||||
|
||||
private:
|
||||
struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
|
||||
AHardwareBuffer* aHardwareBuffer = nullptr;
|
||||
bool sRGB = false;
|
||||
|
||||
protected:
|
||||
~ExternalImageVulkanAndroid() override;
|
||||
};
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
#endif// TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H
|
||||
#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H
|
||||
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
/**
|
||||
* A Platform interface, handling the environment-specific concerns, e.g. OS, for creating a WebGPU
|
||||
* driver (backend).
|
||||
*/
|
||||
class WebGPUPlatform final : public Platform {
|
||||
public:
|
||||
WebGPUPlatform();
|
||||
~WebGPUPlatform() override = default;
|
||||
|
||||
[[nodiscard]] int getOSVersion() const noexcept final { return 0; }
|
||||
|
||||
[[nodiscard]] wgpu::Instance& getInstance() noexcept { return mInstance; }
|
||||
|
||||
// TODO consider that this functionality is not WebGPU-specific, and thus could be
|
||||
// placed in a generic place and even reused across backends. Alternatively,
|
||||
// a 3rd party library could be considered. However, this was a simple and
|
||||
// quick change and works for now.
|
||||
// gets the size (height and width) of the surface/window
|
||||
[[nodiscard]] wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const;
|
||||
// either returns a valid surface or panics
|
||||
[[nodiscard]] wgpu::Surface createSurface(void* nativeWindow, uint64_t flags);
|
||||
// either returns a valid adapter or panics
|
||||
[[nodiscard]] wgpu::Adapter requestAdapter(wgpu::Surface const& surface);
|
||||
// either returns a valid device or panics
|
||||
[[nodiscard]] wgpu::Device requestDevice(wgpu::Adapter const& adapter);
|
||||
|
||||
protected:
|
||||
[[nodiscard]] Driver* createDriver(void* sharedContext,
|
||||
const Platform::DriverConfig& driverConfig) noexcept override;
|
||||
|
||||
private:
|
||||
// we may consider having the driver own this in the future
|
||||
wgpu::Instance mInstance;
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
#endif// TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H
|
||||
@@ -362,6 +362,8 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedTexturesSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthClampSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(size_t, getMaxTextureSize, backend::SamplerType, target)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxArrayTextureLayers)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage2, backend::Platform::ExternalImageHandleRef, image)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage, void*, image)
|
||||
|
||||
@@ -40,9 +40,11 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define HandleAllocatorGL HandleAllocator<32, 96, 184> // ~4520 / pool / MiB
|
||||
#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB
|
||||
#define HandleAllocatorMTL HandleAllocator<32, 64, 552> // ~1660 / pool / MiB
|
||||
#define HandleAllocatorGL HandleAllocator<32, 96, 184> // ~4520 / pool / MiB
|
||||
#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB
|
||||
#define HandleAllocatorMTL HandleAllocator<32, 64, 552> // ~1660 / pool / MiB
|
||||
// TODO WebGPU examine right size of handles
|
||||
#define HandleAllocatorWGPU HandleAllocator<64, 160, 552> // ~1820 / pool / MiB
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ void CommandStream::execute(void* buffer) {
|
||||
|
||||
Profiler profiler;
|
||||
|
||||
if (SYSTRACE_TAG) {
|
||||
if constexpr (SYSTRACE_TAG) {
|
||||
if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
|
||||
// we want to remove all this when tracing is completely disabled
|
||||
profiler.resetEvents(Profiler::EV_CPU_CYCLES | Profiler::EV_BPU_MISSES);
|
||||
@@ -100,7 +100,7 @@ void CommandStream::execute(void* buffer) {
|
||||
}
|
||||
});
|
||||
|
||||
if (SYSTRACE_TAG) {
|
||||
if constexpr (SYSTRACE_TAG) {
|
||||
if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
|
||||
// we want to remove all this when tracing is completely disabled
|
||||
profiler.stop();
|
||||
|
||||
@@ -211,4 +211,8 @@ template class HandleAllocatorVK;
|
||||
template class HandleAllocatorMTL;
|
||||
#endif
|
||||
|
||||
#if defined (FILAMENT_SUPPORTS_WEBGPU)
|
||||
template class HandleAllocatorWGPU;
|
||||
#endif
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
#include <utils/Systrace.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
// We need to keep this up top for the linux (X11) name collisions.
|
||||
#if defined(FILAMENT_SUPPORTS_WEBGPU)
|
||||
#include "backend/platforms/WebGPUPlatform.h"
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <sys/system_properties.h>
|
||||
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3)
|
||||
@@ -30,7 +35,11 @@
|
||||
#endif
|
||||
#elif defined(__APPLE__)
|
||||
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3)
|
||||
#include <backend/platforms/PlatformCocoaGL.h>
|
||||
#if defined(FILAMENT_SUPPORTS_OSMESA)
|
||||
#include <backend/platforms/PlatformOSMesa.h>
|
||||
#else
|
||||
#include <backend/platforms/PlatformCocoaGL.h>
|
||||
#endif
|
||||
#endif
|
||||
#elif defined(__linux__)
|
||||
#if defined(FILAMENT_SUPPORTS_X11)
|
||||
@@ -55,7 +64,11 @@
|
||||
#endif
|
||||
|
||||
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
|
||||
#include "backend/platforms/VulkanPlatform.h"
|
||||
#if defined(__ANDROID__)
|
||||
#include "backend/platforms/VulkanPlatformAndroid.h"
|
||||
#else
|
||||
#include "backend/platforms/VulkanPlatform.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined (FILAMENT_SUPPORTS_METAL)
|
||||
@@ -65,9 +78,6 @@ filament::backend::Platform* createDefaultMetalPlatform();
|
||||
#endif
|
||||
|
||||
#include "noop/PlatformNoop.h"
|
||||
#if defined(FILAMENT_SUPPORTS_WEBGPU)
|
||||
#include "webgpu/WebGPUPlatform.h"
|
||||
#endif
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -104,7 +114,11 @@ Platform* PlatformFactory::create(Backend* backend) noexcept {
|
||||
}
|
||||
if (*backend == Backend::VULKAN) {
|
||||
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
|
||||
return new VulkanPlatform();
|
||||
#if defined(__ANDROID__)
|
||||
return new VulkanPlatformAndroid();
|
||||
#else
|
||||
return new VulkanPlatform();
|
||||
#endif
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
@@ -132,7 +146,11 @@ Platform* PlatformFactory::create(Backend* backend) noexcept {
|
||||
#elif defined(FILAMENT_IOS)
|
||||
return new PlatformCocoaTouchGL();
|
||||
#elif defined(__APPLE__)
|
||||
return new PlatformCocoaGL();
|
||||
#if defined(FILAMENT_SUPPORTS_OSMESA)
|
||||
return new PlatformOSMesa();
|
||||
#else
|
||||
return new PlatformCocoaGL();
|
||||
#endif
|
||||
#elif defined(__linux__)
|
||||
#if defined(FILAMENT_SUPPORTS_X11)
|
||||
return new PlatformGLX();
|
||||
|
||||
@@ -1177,6 +1177,24 @@ size_t MetalDriver::getMaxUniformBufferSize() {
|
||||
return 256 * 1024 * 1024; // TODO: return the actual size instead of hardcoding the minspec
|
||||
}
|
||||
|
||||
size_t MetalDriver::getMaxTextureSize(SamplerType type) {
|
||||
if (type == SamplerType::SAMPLER_3D) {
|
||||
return 2048;
|
||||
}
|
||||
if (mContext->highestSupportedGpuFamily.apple >= 3 ||
|
||||
mContext->highestSupportedGpuFamily.mac >= 2) {
|
||||
return 16384;
|
||||
} else if (mContext->highestSupportedGpuFamily.apple >= 2) {
|
||||
return 8192;
|
||||
}
|
||||
return 4096;
|
||||
}
|
||||
|
||||
size_t MetalDriver::getMaxArrayTextureLayers() {
|
||||
// TODO: return the actual size instead of hardcoding the minspec
|
||||
return 256;
|
||||
}
|
||||
|
||||
void MetalDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& data,
|
||||
uint32_t byteOffset) {
|
||||
FILAMENT_CHECK_PRECONDITION(data.buffer)
|
||||
@@ -1390,8 +1408,8 @@ void MetalDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, Primit
|
||||
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
|
||||
auto vertexBuffer = handle_cast<MetalVertexBuffer>(vbh);
|
||||
auto indexBuffer = handle_cast<MetalIndexBuffer>(ibh);
|
||||
MetalVertexBufferInfo const* const vbi = handle_cast<MetalVertexBufferInfo>(vertexBuffer->vbih);
|
||||
primitive->setBuffers(vbi, vertexBuffer, indexBuffer);
|
||||
primitive->vertexBuffer = vertexBuffer;
|
||||
primitive->indexBuffer = indexBuffer;
|
||||
primitive->type = pt;
|
||||
}
|
||||
|
||||
|
||||
@@ -194,12 +194,8 @@ struct MetalIndexBuffer : public HwIndexBuffer {
|
||||
};
|
||||
|
||||
struct MetalRenderPrimitive : public HwRenderPrimitive {
|
||||
MetalRenderPrimitive();
|
||||
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.
|
||||
|
||||
MetalVertexBuffer* vertexBuffer = nullptr;
|
||||
MetalIndexBuffer* indexBuffer = nullptr;
|
||||
};
|
||||
@@ -380,7 +376,6 @@ public:
|
||||
|
||||
math::uint2 getAttachmentSize() noexcept;
|
||||
|
||||
bool isDefaultRenderTarget() const { return defaultRenderTarget; }
|
||||
uint8_t getSamples() const { return samples; }
|
||||
|
||||
Attachment getDrawColorAttachment(size_t index);
|
||||
|
||||
@@ -536,15 +536,6 @@ MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uin
|
||||
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
|
||||
buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { }
|
||||
|
||||
MetalRenderPrimitive::MetalRenderPrimitive() {
|
||||
}
|
||||
|
||||
void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
|
||||
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) {
|
||||
this->vertexBuffer = vertexBuffer;
|
||||
this->indexBuffer = indexBuffer;
|
||||
}
|
||||
|
||||
MetalProgram::MetalProgram(MetalContext& context, Program&& program) noexcept
|
||||
: HwProgram(program.getName()), mContext(context) {
|
||||
mToken = context.shaderCompiler->createProgram(program.getName(), std::move(program));
|
||||
|
||||
@@ -138,6 +138,7 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
|
||||
case ShaderLanguage::ESSL1:
|
||||
case ShaderLanguage::ESSL3:
|
||||
case ShaderLanguage::SPIRV:
|
||||
case ShaderLanguage::WGSL:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -240,6 +240,16 @@ size_t NoopDriver::getMaxUniformBufferSize() {
|
||||
return 16384u;
|
||||
}
|
||||
|
||||
size_t NoopDriver::getMaxTextureSize(SamplerType target) {
|
||||
// NoopDriver is being actively used for other purposes. This needs to be resolved before we
|
||||
// can change it to 2048. b/406832484
|
||||
return 16384u;
|
||||
}
|
||||
|
||||
size_t NoopDriver::getMaxArrayTextureLayers() {
|
||||
return 256u;
|
||||
}
|
||||
|
||||
void NoopDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
|
||||
uint32_t byteOffset) {
|
||||
scheduleDestroy(std::move(p));
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
Driver* PlatformNoop::createDriver(void* const sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
|
||||
Driver* PlatformNoop::createDriver(void* sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
|
||||
return NoopDriver::create();
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,15 @@
|
||||
|
||||
#include "GLUtils.h"
|
||||
|
||||
#include "private/backend/Driver.h"
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/trap.h>
|
||||
|
||||
#include "private/backend/Driver.h"
|
||||
#include <string_view>
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -28,38 +34,31 @@ using namespace utils;
|
||||
namespace GLUtils {
|
||||
|
||||
UTILS_NOINLINE
|
||||
const char* getGLError(GLenum error) noexcept {
|
||||
const char* string = "unknown";
|
||||
std::string_view getGLErrorString(GLenum error) noexcept {
|
||||
switch (error) {
|
||||
case GL_NO_ERROR:
|
||||
string = "GL_NO_ERROR";
|
||||
break;
|
||||
return "GL_NO_ERROR";
|
||||
case GL_INVALID_ENUM:
|
||||
string = "GL_INVALID_ENUM";
|
||||
break;
|
||||
return "GL_INVALID_ENUM";
|
||||
case GL_INVALID_VALUE:
|
||||
string = "GL_INVALID_VALUE";
|
||||
break;
|
||||
return "GL_INVALID_VALUE";
|
||||
case GL_INVALID_OPERATION:
|
||||
string = "GL_INVALID_OPERATION";
|
||||
break;
|
||||
return "GL_INVALID_OPERATION";
|
||||
case GL_INVALID_FRAMEBUFFER_OPERATION:
|
||||
string = "GL_INVALID_FRAMEBUFFER_OPERATION";
|
||||
break;
|
||||
return "GL_INVALID_FRAMEBUFFER_OPERATION";
|
||||
case GL_OUT_OF_MEMORY:
|
||||
string = "GL_OUT_OF_MEMORY";
|
||||
break;
|
||||
return "GL_OUT_OF_MEMORY";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return string;
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
GLenum checkGLError(io::ostream& out, const char* function, size_t line) noexcept {
|
||||
GLenum const error = glGetError();
|
||||
if (error != GL_NO_ERROR) {
|
||||
const char* string = getGLError(error);
|
||||
if (UTILS_VERY_UNLIKELY(error != GL_NO_ERROR)) {
|
||||
auto const string = getGLErrorString(error);
|
||||
out << "OpenGL error " << io::hex << error << " (" << string << ") in \""
|
||||
<< function << "\" at line " << io::dec << line << io::endl;
|
||||
}
|
||||
@@ -69,46 +68,39 @@ GLenum checkGLError(io::ostream& out, const char* function, size_t line) noexcep
|
||||
UTILS_NOINLINE
|
||||
void assertGLError(io::ostream& out, const char* function, size_t line) noexcept {
|
||||
GLenum const err = checkGLError(out, function, line);
|
||||
if (err != GL_NO_ERROR) {
|
||||
if (UTILS_VERY_UNLIKELY(err != GL_NO_ERROR)) {
|
||||
debug_trap();
|
||||
}
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
const char* getFramebufferStatus(GLenum status) noexcept {
|
||||
const char* string = "unknown";
|
||||
std::string_view getFramebufferStatusString(GLenum status) noexcept {
|
||||
switch (status) {
|
||||
case GL_FRAMEBUFFER_COMPLETE:
|
||||
string = "GL_FRAMEBUFFER_COMPLETE";
|
||||
break;
|
||||
return "GL_FRAMEBUFFER_COMPLETE";
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
|
||||
string = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
|
||||
break;
|
||||
return "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
|
||||
string = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
|
||||
break;
|
||||
return "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
|
||||
case GL_FRAMEBUFFER_UNSUPPORTED:
|
||||
string = "GL_FRAMEBUFFER_UNSUPPORTED";
|
||||
break;
|
||||
return "GL_FRAMEBUFFER_UNSUPPORTED";
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
case GL_FRAMEBUFFER_UNDEFINED:
|
||||
string = "GL_FRAMEBUFFER_UNDEFINED";
|
||||
break;
|
||||
return "GL_FRAMEBUFFER_UNDEFINED";
|
||||
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
|
||||
string = "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE";
|
||||
break;
|
||||
return "GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE";
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return string;
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
GLenum checkFramebufferStatus(io::ostream& out, GLenum target, const char* function, size_t line) noexcept {
|
||||
GLenum const status = glCheckFramebufferStatus(target);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
const char* string = getFramebufferStatus(status);
|
||||
if (UTILS_VERY_UNLIKELY(status != GL_FRAMEBUFFER_COMPLETE)) {
|
||||
auto const string = getFramebufferStatusString(status);
|
||||
out << "OpenGL framebuffer error " << io::hex << status << " (" << string << ") in \""
|
||||
<< function << "\" at line " << io::dec << line << io::endl;
|
||||
}
|
||||
@@ -118,7 +110,7 @@ GLenum checkFramebufferStatus(io::ostream& out, GLenum target, const char* funct
|
||||
UTILS_NOINLINE
|
||||
void assertFramebufferStatus(io::ostream& out, GLenum target, const char* function, size_t line) noexcept {
|
||||
GLenum const status = checkFramebufferStatus(out, target, function, line);
|
||||
if (status != GL_FRAMEBUFFER_COMPLETE) {
|
||||
if (UTILS_VERY_UNLIKELY(status != GL_FRAMEBUFFER_COMPLETE)) {
|
||||
debug_trap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,29 +17,26 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_OPENGL_GLUTILS_H
|
||||
#define TNT_FILAMENT_BACKEND_OPENGL_GLUTILS_H
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <string_view>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "gl_headers.h"
|
||||
|
||||
namespace filament::backend {
|
||||
namespace GLUtils {
|
||||
namespace filament::backend::GLUtils {
|
||||
|
||||
const char* getGLError(GLenum error) noexcept;
|
||||
std::string_view getGLErrorString(GLenum error) noexcept;
|
||||
GLenum checkGLError(utils::io::ostream& out, const char* function, size_t line) noexcept;
|
||||
void assertGLError(utils::io::ostream& out, const char* function, size_t line) noexcept;
|
||||
|
||||
const char* getFramebufferStatus(GLenum err) noexcept;
|
||||
std::string_view getFramebufferStatusString(GLenum err) noexcept;
|
||||
GLenum checkFramebufferStatus(utils::io::ostream& out, GLenum target, const char* function, size_t line) noexcept;
|
||||
void assertFramebufferStatus(utils::io::ostream& out, GLenum target, const char* function, size_t line) noexcept;
|
||||
|
||||
@@ -53,7 +50,7 @@ void assertFramebufferStatus(utils::io::ostream& out, GLenum target, const char*
|
||||
# define CHECK_GL_FRAMEBUFFER_STATUS(out, target) { GLUtils::checkFramebufferStatus(out, target, __func__, __LINE__); }
|
||||
#endif
|
||||
|
||||
constexpr GLuint getComponentCount(ElementType type) noexcept {
|
||||
constexpr GLuint getComponentCount(ElementType const type) noexcept {
|
||||
using ElementType = ElementType;
|
||||
switch (type) {
|
||||
case ElementType::BYTE:
|
||||
@@ -87,27 +84,29 @@ constexpr GLuint getComponentCount(ElementType type) noexcept {
|
||||
case ElementType::USHORT4:
|
||||
return 4;
|
||||
}
|
||||
// should never happen
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Our enums to GLenum conversions
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
constexpr GLbitfield getAttachmentBitfield(TargetBufferFlags flags) noexcept {
|
||||
constexpr GLbitfield getAttachmentBitfield(TargetBufferFlags const flags) noexcept {
|
||||
GLbitfield mask = 0;
|
||||
if (any(flags & TargetBufferFlags::COLOR_ALL)) {
|
||||
mask |= (GLbitfield)GL_COLOR_BUFFER_BIT;
|
||||
mask |= GLbitfield(GL_COLOR_BUFFER_BIT);
|
||||
}
|
||||
if (any(flags & TargetBufferFlags::DEPTH)) {
|
||||
mask |= (GLbitfield)GL_DEPTH_BUFFER_BIT;
|
||||
mask |= GLbitfield(GL_DEPTH_BUFFER_BIT);
|
||||
}
|
||||
if (any(flags & TargetBufferFlags::STENCIL)) {
|
||||
mask |= (GLbitfield)GL_STENCIL_BUFFER_BIT;
|
||||
mask |= GLbitfield(GL_STENCIL_BUFFER_BIT);
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
constexpr GLenum getBufferUsage(BufferUsage usage) noexcept {
|
||||
constexpr GLenum getBufferUsage(BufferUsage const usage) noexcept {
|
||||
switch (usage) {
|
||||
case BufferUsage::STATIC:
|
||||
return GL_STATIC_DRAW;
|
||||
@@ -116,7 +115,7 @@ constexpr GLenum getBufferUsage(BufferUsage usage) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
constexpr GLenum getBufferBindingType(BufferObjectBinding bindingType) noexcept {
|
||||
constexpr GLenum getBufferBindingType(BufferObjectBinding const bindingType) noexcept {
|
||||
switch (bindingType) {
|
||||
case BufferObjectBinding::VERTEX:
|
||||
return GL_ARRAY_BUFFER;
|
||||
@@ -135,13 +134,15 @@ constexpr GLenum getBufferBindingType(BufferObjectBinding bindingType) noexcept
|
||||
return 0x90D2; // just to return something
|
||||
#endif
|
||||
}
|
||||
// should never happen
|
||||
return GL_ARRAY_BUFFER;
|
||||
}
|
||||
|
||||
constexpr GLboolean getNormalization(bool normalized) noexcept {
|
||||
constexpr GLboolean getNormalization(bool const normalized) noexcept {
|
||||
return GLboolean(normalized ? GL_TRUE : GL_FALSE);
|
||||
}
|
||||
|
||||
constexpr GLenum getComponentType(ElementType type) noexcept {
|
||||
constexpr GLenum getComponentType(ElementType const type) noexcept {
|
||||
using ElementType = ElementType;
|
||||
switch (type) {
|
||||
case ElementType::BYTE:
|
||||
@@ -184,9 +185,11 @@ constexpr GLenum getComponentType(ElementType type) noexcept {
|
||||
return GL_HALF_FLOAT_OES;
|
||||
#endif
|
||||
}
|
||||
// should never happen
|
||||
return GL_INT;
|
||||
}
|
||||
|
||||
constexpr GLenum getTextureTargetNotExternal(SamplerType target) noexcept {
|
||||
constexpr GLenum getTextureTargetNotExternal(SamplerType const target) noexcept {
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
return GL_TEXTURE_2D;
|
||||
@@ -202,14 +205,16 @@ constexpr GLenum getTextureTargetNotExternal(SamplerType target) noexcept {
|
||||
// we should never be here
|
||||
return GL_TEXTURE_2D;
|
||||
}
|
||||
// should never happen
|
||||
return GL_TEXTURE_2D;
|
||||
}
|
||||
|
||||
constexpr GLenum getCubemapTarget(uint16_t layer) noexcept {
|
||||
constexpr GLenum getCubemapTarget(uint16_t const layer) noexcept {
|
||||
assert_invariant(layer <= 5);
|
||||
return GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer;
|
||||
}
|
||||
|
||||
constexpr GLenum getWrapMode(SamplerWrapMode mode) noexcept {
|
||||
constexpr GLenum getWrapMode(SamplerWrapMode const mode) noexcept {
|
||||
using SamplerWrapMode = SamplerWrapMode;
|
||||
switch (mode) {
|
||||
case SamplerWrapMode::REPEAT:
|
||||
@@ -219,6 +224,8 @@ constexpr GLenum getWrapMode(SamplerWrapMode mode) noexcept {
|
||||
case SamplerWrapMode::MIRRORED_REPEAT:
|
||||
return GL_MIRRORED_REPEAT;
|
||||
}
|
||||
// should never happen
|
||||
return GL_CLAMP_TO_EDGE;
|
||||
}
|
||||
|
||||
constexpr GLenum getTextureFilter(SamplerMinFilter filter) noexcept {
|
||||
@@ -234,6 +241,8 @@ constexpr GLenum getTextureFilter(SamplerMinFilter filter) noexcept {
|
||||
return GL_NEAREST_MIPMAP_NEAREST
|
||||
- GLenum(SamplerMinFilter::NEAREST_MIPMAP_NEAREST) + GLenum(filter);
|
||||
}
|
||||
// should never happen
|
||||
return GL_NEAREST;
|
||||
}
|
||||
|
||||
constexpr GLenum getTextureFilter(SamplerMagFilter filter) noexcept {
|
||||
@@ -241,7 +250,7 @@ constexpr GLenum getTextureFilter(SamplerMagFilter filter) noexcept {
|
||||
}
|
||||
|
||||
|
||||
constexpr GLenum getBlendEquationMode(BlendEquation mode) noexcept {
|
||||
constexpr GLenum getBlendEquationMode(BlendEquation const mode) noexcept {
|
||||
using BlendEquation = BlendEquation;
|
||||
switch (mode) {
|
||||
case BlendEquation::ADD: return GL_FUNC_ADD;
|
||||
@@ -250,9 +259,11 @@ constexpr GLenum getBlendEquationMode(BlendEquation mode) noexcept {
|
||||
case BlendEquation::MIN: return GL_MIN;
|
||||
case BlendEquation::MAX: return GL_MAX;
|
||||
}
|
||||
// should never happen
|
||||
return GL_FUNC_ADD;
|
||||
}
|
||||
|
||||
constexpr GLenum getBlendFunctionMode(BlendFunction mode) noexcept {
|
||||
constexpr GLenum getBlendFunctionMode(BlendFunction const mode) noexcept {
|
||||
using BlendFunction = BlendFunction;
|
||||
switch (mode) {
|
||||
case BlendFunction::ZERO: return GL_ZERO;
|
||||
@@ -267,9 +278,11 @@ constexpr GLenum getBlendFunctionMode(BlendFunction mode) noexcept {
|
||||
case BlendFunction::ONE_MINUS_DST_ALPHA: return GL_ONE_MINUS_DST_ALPHA;
|
||||
case BlendFunction::SRC_ALPHA_SATURATE: return GL_SRC_ALPHA_SATURATE;
|
||||
}
|
||||
// should never happen
|
||||
return GL_ONE;
|
||||
}
|
||||
|
||||
constexpr GLenum getCompareFunc(SamplerCompareFunc func) noexcept {
|
||||
constexpr GLenum getCompareFunc(SamplerCompareFunc const func) noexcept {
|
||||
switch (func) {
|
||||
case SamplerCompareFunc::LE: return GL_LEQUAL;
|
||||
case SamplerCompareFunc::GE: return GL_GEQUAL;
|
||||
@@ -280,28 +293,30 @@ constexpr GLenum getCompareFunc(SamplerCompareFunc func) noexcept {
|
||||
case SamplerCompareFunc::A: return GL_ALWAYS;
|
||||
case SamplerCompareFunc::N: return GL_NEVER;
|
||||
}
|
||||
// should never happen
|
||||
return GL_LEQUAL;
|
||||
}
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
constexpr GLenum getTextureCompareMode(SamplerCompareMode mode) noexcept {
|
||||
constexpr GLenum getTextureCompareMode(SamplerCompareMode const mode) noexcept {
|
||||
return mode == SamplerCompareMode::NONE ?
|
||||
GL_NONE : GL_COMPARE_REF_TO_TEXTURE;
|
||||
}
|
||||
|
||||
constexpr GLenum getTextureCompareFunc(SamplerCompareFunc func) noexcept {
|
||||
constexpr GLenum getTextureCompareFunc(SamplerCompareFunc const func) noexcept {
|
||||
return getCompareFunc(func);
|
||||
}
|
||||
#endif
|
||||
|
||||
constexpr GLenum getDepthFunc(SamplerCompareFunc func) noexcept {
|
||||
constexpr GLenum getDepthFunc(SamplerCompareFunc const func) noexcept {
|
||||
return getCompareFunc(func);
|
||||
}
|
||||
|
||||
constexpr GLenum getStencilFunc(SamplerCompareFunc func) noexcept {
|
||||
constexpr GLenum getStencilFunc(SamplerCompareFunc const func) noexcept {
|
||||
return getCompareFunc(func);
|
||||
}
|
||||
|
||||
constexpr GLenum getStencilOp(StencilOperation op) noexcept {
|
||||
constexpr GLenum getStencilOp(StencilOperation const op) noexcept {
|
||||
switch (op) {
|
||||
case StencilOperation::KEEP: return GL_KEEP;
|
||||
case StencilOperation::ZERO: return GL_ZERO;
|
||||
@@ -312,9 +327,11 @@ constexpr GLenum getStencilOp(StencilOperation op) noexcept {
|
||||
case StencilOperation::DECR_WRAP: return GL_DECR_WRAP;
|
||||
case StencilOperation::INVERT: return GL_INVERT;
|
||||
}
|
||||
// should never happen
|
||||
return GL_KEEP;
|
||||
}
|
||||
|
||||
constexpr GLenum getFormat(PixelDataFormat format) noexcept {
|
||||
constexpr GLenum getFormat(PixelDataFormat const format) noexcept {
|
||||
using PixelDataFormat = PixelDataFormat;
|
||||
switch (format) {
|
||||
case PixelDataFormat::RGB: return GL_RGB;
|
||||
@@ -336,9 +353,11 @@ constexpr GLenum getFormat(PixelDataFormat format) noexcept {
|
||||
default: return GL_NONE;
|
||||
#endif
|
||||
}
|
||||
// should never happen
|
||||
return GL_RGBA;
|
||||
}
|
||||
|
||||
constexpr GLenum getType(PixelDataType type) noexcept {
|
||||
constexpr GLenum getType(PixelDataType const type) noexcept {
|
||||
using PixelDataType = PixelDataType;
|
||||
switch (type) {
|
||||
case PixelDataType::UBYTE: return GL_UNSIGNED_BYTE;
|
||||
@@ -360,10 +379,12 @@ constexpr GLenum getType(PixelDataType type) noexcept {
|
||||
default: return GL_NONE;
|
||||
#endif
|
||||
}
|
||||
// should never happen
|
||||
return GL_UNSIGNED_INT;
|
||||
}
|
||||
|
||||
#if !defined(__EMSCRIPTEN__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
|
||||
constexpr GLenum getSwizzleChannel(TextureSwizzle c) noexcept {
|
||||
constexpr GLenum getSwizzleChannel(TextureSwizzle const c) noexcept {
|
||||
using TextureSwizzle = TextureSwizzle;
|
||||
switch (c) {
|
||||
case TextureSwizzle::SUBSTITUTE_ZERO: return GL_ZERO;
|
||||
@@ -373,10 +394,12 @@ constexpr GLenum getSwizzleChannel(TextureSwizzle c) noexcept {
|
||||
case TextureSwizzle::CHANNEL_2: return GL_BLUE;
|
||||
case TextureSwizzle::CHANNEL_3: return GL_ALPHA;
|
||||
}
|
||||
// should never happen
|
||||
return GL_RED;
|
||||
}
|
||||
#endif
|
||||
|
||||
constexpr GLenum getCullingMode(CullingMode mode) noexcept {
|
||||
constexpr GLenum getCullingMode(CullingMode const mode) noexcept {
|
||||
switch (mode) {
|
||||
case CullingMode::NONE:
|
||||
// should never happen
|
||||
@@ -388,11 +411,13 @@ constexpr GLenum getCullingMode(CullingMode mode) noexcept {
|
||||
case CullingMode::FRONT_AND_BACK:
|
||||
return GL_FRONT_AND_BACK;
|
||||
}
|
||||
// should never happen
|
||||
return GL_FRONT_AND_BACK;
|
||||
}
|
||||
|
||||
// ES2 supported internal formats for texturing and how they map to a format/type
|
||||
constexpr std::pair<GLenum, GLenum> textureFormatToFormatAndType(
|
||||
TextureFormat format) noexcept {
|
||||
TextureFormat const format) noexcept {
|
||||
switch (format) {
|
||||
case TextureFormat::R8: return { 0x1909 /*GL_LUMINANCE*/, GL_UNSIGNED_BYTE };
|
||||
case TextureFormat::RGB8: return { GL_RGB, GL_UNSIGNED_BYTE };
|
||||
@@ -413,7 +438,7 @@ constexpr std::pair<GLenum, GLenum> textureFormatToFormatAndType(
|
||||
// clang loses it on this one, and generates a huge jump table when
|
||||
// inlined. So we don't mark it as inline (only constexpr) which solves the problem,
|
||||
// strangely, when not inlined, clang simply generates an array lookup.
|
||||
constexpr /* inline */ GLenum getInternalFormat(TextureFormat format) noexcept {
|
||||
constexpr /* inline */ GLenum getInternalFormat(TextureFormat const format) noexcept {
|
||||
switch (format) {
|
||||
|
||||
/* Formats supported by our ES2 implementations */
|
||||
@@ -661,7 +686,7 @@ public:
|
||||
|
||||
unordered_string_set split(const char* extensions) noexcept;
|
||||
|
||||
} // namespace GLUtils
|
||||
} // namespace filament::backend
|
||||
} // namespace filament::backend::GLUtils
|
||||
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_OPENGL_GLUTILS_H
|
||||
|
||||
@@ -94,18 +94,22 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
|
||||
}
|
||||
#endif
|
||||
|
||||
OpenGLContext::initExtensions(&ext, state.major, state.minor);
|
||||
initExtensions(&ext, state.major, state.minor);
|
||||
|
||||
OpenGLContext::initProcs(&procs, ext, state.major, state.minor);
|
||||
initProcs(&procs, ext, state.major, state.minor);
|
||||
|
||||
OpenGLContext::initBugs(&bugs, ext, state.major, state.minor,
|
||||
initBugs(&bugs, ext, state.major, state.minor,
|
||||
state.vendor, state.renderer, state.version, state.shader);
|
||||
|
||||
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &gets.max_renderbuffer_size);
|
||||
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &gets.max_texture_image_units);
|
||||
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gets.max_combined_texture_image_units);
|
||||
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gets.max_texture_size);
|
||||
glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &gets.max_cubemap_texture_size);
|
||||
glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gets.max_3d_texture_size);
|
||||
glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &gets.max_array_texture_layers);
|
||||
|
||||
mFeatureLevel = OpenGLContext::resolveFeatureLevel(state.major, state.minor, ext, gets, bugs);
|
||||
mFeatureLevel = resolveFeatureLevel(state.major, state.minor, ext, gets, bugs);
|
||||
|
||||
#ifdef BACKEND_OPENGL_VERSION_GLES
|
||||
mShaderModel = ShaderModel::MOBILE;
|
||||
@@ -177,6 +181,14 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
|
||||
<< gets.max_anisotropy << '\n'
|
||||
<< "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = "
|
||||
<< gets.max_combined_texture_image_units << '\n'
|
||||
<< "GL_MAX_TEXTURE_SIZE = "
|
||||
<< gets.max_texture_size << '\n'
|
||||
<< "GL_MAX_CUBE_MAP_TEXTURE_SIZE = "
|
||||
<< gets.max_cubemap_texture_size << '\n'
|
||||
<< "GL_MAX_3D_TEXTURE_SIZE = "
|
||||
<< gets.max_3d_texture_size << '\n'
|
||||
<< "GL_MAX_ARRAY_TEXTURE_LAYERS = "
|
||||
<< gets.max_array_texture_layers << '\n'
|
||||
<< "GL_MAX_DRAW_BUFFERS = "
|
||||
<< gets.max_draw_buffers << '\n'
|
||||
<< "GL_MAX_RENDERBUFFER_SIZE = "
|
||||
@@ -554,7 +566,11 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
|
||||
} else if (strstr(renderer, "AMD") ||
|
||||
strstr(renderer, "ATI")) {
|
||||
// AMD/ATI GPU
|
||||
} else if (strstr(vendor, "Mesa")) {
|
||||
} else if (strstr(renderer, "Mozilla")) {
|
||||
bugs->disable_invalidate_framebuffer = true;
|
||||
}
|
||||
|
||||
if (strstr(vendor, "Mesa")) {
|
||||
// Seen on
|
||||
// [Mesa],
|
||||
// [llvmpipe (LLVM 17.0.6, 256 bits)],
|
||||
@@ -562,8 +578,6 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
|
||||
// [4.50]
|
||||
// not known which version are affected
|
||||
bugs->rebind_buffer_after_deletion = true;
|
||||
} else if (strstr(renderer, "Mozilla")) {
|
||||
bugs->disable_invalidate_framebuffer = true;
|
||||
}
|
||||
} else {
|
||||
// When running under ANGLE, it's a different set of workaround that we need.
|
||||
@@ -575,6 +589,19 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
|
||||
}
|
||||
}
|
||||
|
||||
if (strstr(vendor, "Mozilla")) {
|
||||
// Seen on
|
||||
// [Mozilla],
|
||||
// [GeForce GTX 980, or similar]
|
||||
// or [ANGLE (NVIDIA, NVIDIA GeForce GTX 980 Direct3D11 vs_5_0 ps_5_0), or similar]
|
||||
// or anything else,
|
||||
// [OpenGL ES 3.0 (WebGL 2.0)],
|
||||
// [OpenGL ES GLSL ES 3.00 (WebGL GLSL ES 3.00)]
|
||||
// For Mozilla, the issue appears to be observed regardless of whether the renderer is
|
||||
// ANGLE or not. (b/376125497)
|
||||
bugs->rebind_buffer_after_deletion = true;
|
||||
}
|
||||
|
||||
#ifdef BACKEND_OPENGL_VERSION_GLES
|
||||
# ifndef FILAMENT_IOS // FILAMENT_IOS is guaranteed to have ES3.x
|
||||
if (UTILS_UNLIKELY(major == 2)) {
|
||||
|
||||
@@ -203,6 +203,10 @@ public:
|
||||
GLint max_renderbuffer_size;
|
||||
GLint max_samples;
|
||||
GLint max_texture_image_units;
|
||||
GLint max_texture_size;
|
||||
GLint max_cubemap_texture_size;
|
||||
GLint max_3d_texture_size;
|
||||
GLint max_array_texture_layers;
|
||||
GLint max_transform_feedback_separate_attribs;
|
||||
GLint max_uniform_block_size;
|
||||
GLint max_uniform_buffer_bindings;
|
||||
@@ -316,8 +320,8 @@ public:
|
||||
// a glFinish. So we must delay the destruction until we know the GPU is finished.
|
||||
bool delay_fbo_destruction;
|
||||
|
||||
// Mesa sometimes clears the generic buffer binding when *another* buffer is destroyed,
|
||||
// if that other buffer is bound on an *indexed* buffer binding.
|
||||
// Mesa and Mozilla(web) sometimes clear the generic buffer binding when *another* buffer
|
||||
// is destroyed, if that other buffer is bound on an *indexed* buffer binding.
|
||||
bool rebind_buffer_after_deletion;
|
||||
|
||||
// Force feature level 0. Typically used for low end ES3 devices with significant driver
|
||||
|
||||
@@ -148,8 +148,8 @@ using namespace utils;
|
||||
namespace filament::backend {
|
||||
|
||||
Driver* OpenGLDriverFactory::create(
|
||||
OpenGLPlatform* const platform,
|
||||
void* const sharedGLContext,
|
||||
OpenGLPlatform* platform,
|
||||
void* sharedGLContext,
|
||||
const Platform::DriverConfig& driverConfig) noexcept {
|
||||
return OpenGLDriver::create(platform, sharedGLContext, driverConfig);
|
||||
}
|
||||
@@ -159,10 +159,10 @@ using namespace GLUtils;
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
UTILS_NOINLINE
|
||||
OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* const platform,
|
||||
void* const /*sharedGLContext*/, const Platform::DriverConfig& driverConfig) noexcept {
|
||||
OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* platform,
|
||||
void* /*sharedGLContext*/, const Platform::DriverConfig& driverConfig) noexcept {
|
||||
assert_invariant(platform);
|
||||
OpenGLPlatform* const ec = platform;
|
||||
OpenGLPlatform* ec = platform;
|
||||
|
||||
#if 0
|
||||
// this is useful for development, but too verbose even for debug builds
|
||||
@@ -230,7 +230,7 @@ OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* const platform,
|
||||
constexpr size_t defaultSize = FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
|
||||
Platform::DriverConfig validConfig{ driverConfig };
|
||||
validConfig.handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize);
|
||||
OpenGLDriver* const driver = new(std::nothrow) OpenGLDriver(ec, validConfig);
|
||||
OpenGLDriver* driver = new(std::nothrow) OpenGLDriver(ec, validConfig);
|
||||
return driver;
|
||||
}
|
||||
|
||||
@@ -2098,6 +2098,7 @@ void OpenGLDriver::setAcquiredImage(Handle<HwStream> sh, void* hwbuffer, const m
|
||||
|
||||
glstream->user_thread.pending = mPlatform.transformAcquiredImage({
|
||||
hwbuffer, cb, userData, handler });
|
||||
glstream->user_thread.transform = transform;
|
||||
|
||||
if (glstream->user_thread.pending.image != nullptr) {
|
||||
// If there's no pending image, do nothing. Note that GL_OES_EGL_image does not let you pass
|
||||
@@ -2460,6 +2461,26 @@ size_t OpenGLDriver::getMaxUniformBufferSize() {
|
||||
return mContext.gets.max_uniform_block_size;
|
||||
}
|
||||
|
||||
size_t OpenGLDriver::getMaxTextureSize(SamplerType target) {
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
return mContext.gets.max_texture_size;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
return mContext.gets.max_cubemap_texture_size;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
return mContext.gets.max_3d_texture_size;
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
return mContext.gets.max_cubemap_texture_size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t OpenGLDriver::getMaxArrayTextureLayers() {
|
||||
return mContext.gets.max_array_texture_layers;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// Swap chains
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -85,6 +85,7 @@ OpenGLProgram::~OpenGLProgram() noexcept {
|
||||
delete lazyInitializationData;
|
||||
|
||||
ShaderCompilerService::terminate(mToken);
|
||||
assert_invariant(!mToken);
|
||||
}
|
||||
|
||||
delete [] mUniformsRecords;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,23 +24,23 @@
|
||||
#include "OpenGLBlobCache.h"
|
||||
|
||||
#include <backend/CallbackHandler.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Program.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/JobSystem.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <deque>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class OpenGLDriver;
|
||||
@@ -57,6 +57,8 @@ class ShaderCompilerService {
|
||||
|
||||
public:
|
||||
using program_token_t = std::shared_ptr<OpenGLProgramToken>;
|
||||
using shaders_t = std::array<GLuint, Program::SHADER_TYPE_COUNT>;
|
||||
using shaders_source_t = std::array<utils::CString, Program::SHADER_TYPE_COUNT>;
|
||||
|
||||
explicit ShaderCompilerService(OpenGLDriver& driver);
|
||||
|
||||
@@ -82,6 +84,7 @@ public:
|
||||
void tick();
|
||||
|
||||
// Destroys a valid token and all associated resources. Used to "cancel" a program compilation.
|
||||
// This function is not called if `initialize(token)` is already invoked.
|
||||
static void terminate(program_token_t& token);
|
||||
|
||||
// stores a user data pointer in the token
|
||||
@@ -90,6 +93,12 @@ public:
|
||||
// retrieves the user data pointer stored in the token
|
||||
static void* getUserData(const program_token_t& token) noexcept;
|
||||
|
||||
// Issue one callback handle.
|
||||
CallbackManager::Handle issueCallbackHandle() const noexcept;
|
||||
|
||||
// Return a callback handle to the callback manager.
|
||||
void submitCallbackHandle(CallbackManager::Handle handle) noexcept;
|
||||
|
||||
// call the callback when all active programs are ready
|
||||
void notifyWhenAllProgramsAreReady(
|
||||
CallbackHandler* handler, CallbackHandler::Callback callback, void* user);
|
||||
@@ -97,7 +106,7 @@ public:
|
||||
private:
|
||||
struct Job {
|
||||
template<typename FUNC>
|
||||
Job(FUNC&& fn) : fn(std::forward<FUNC>(fn)) {}
|
||||
Job(FUNC&& fn) : fn(std::forward<FUNC>(fn)) {} // NOLINT(*-explicit-constructor)
|
||||
Job(std::function<bool(Job const& job)> fn,
|
||||
CallbackHandler* handler, void* user, CallbackHandler::Callback callback)
|
||||
: fn(std::move(fn)), handler(handler), user(user), callback(callback) {
|
||||
@@ -126,39 +135,49 @@ private:
|
||||
using ContainerType = std::tuple<CompilerPriorityQueue, program_token_t, Job>;
|
||||
std::vector<ContainerType> mRunAtNextTickOps;
|
||||
|
||||
GLuint initialize(ShaderCompilerService::program_token_t& token) noexcept;
|
||||
GLuint initialize(program_token_t& token);
|
||||
void ensureTokenIsReady(program_token_t const& token);
|
||||
|
||||
static void getProgramFromCompilerPool(program_token_t& token) noexcept;
|
||||
|
||||
static void compileShaders(
|
||||
OpenGLContext& context,
|
||||
Program::ShaderSource shadersSource,
|
||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
|
||||
bool multiview,
|
||||
std::array<GLuint, Program::SHADER_TYPE_COUNT>& outShaders,
|
||||
std::array<utils::CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept;
|
||||
|
||||
static void process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
|
||||
char* source, size_t len) noexcept;
|
||||
|
||||
static void process_OVR_multiview2(OpenGLContext& context, int32_t eyeCount,
|
||||
char* source, size_t len) noexcept;
|
||||
|
||||
static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept;
|
||||
|
||||
static std::array<std::string_view, 3> splitShaderSource(std::string_view source) noexcept;
|
||||
|
||||
static GLuint linkProgram(OpenGLContext& context,
|
||||
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders,
|
||||
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> const& attributes) noexcept;
|
||||
|
||||
static bool checkProgramStatus(program_token_t const& token) noexcept;
|
||||
|
||||
void runAtNextTick(CompilerPriorityQueue priority,
|
||||
const program_token_t& token, Job job) noexcept;
|
||||
void runAtNextTick(CompilerPriorityQueue priority, program_token_t const& token,
|
||||
Job job) noexcept;
|
||||
void executeTickOps() noexcept;
|
||||
bool cancelTickOp(program_token_t token) noexcept;
|
||||
// order of insertion is important
|
||||
bool cancelTickOp(program_token_t const& token) noexcept;
|
||||
|
||||
// Compile shaders with the given `shaderSource`. `gl.shaders` is always populated with valid
|
||||
// shader IDs after this method. But this doesn't necessarily mean the shaders are successfully
|
||||
// compiled. Errors can be checked by calling `checkCompileStatus` later.
|
||||
static void compileShaders(OpenGLContext& context, Program::ShaderSource shadersSource,
|
||||
utils::FixedCapacityVector<Program::SpecializationConstant> const&
|
||||
specializationConstants,
|
||||
bool multiview, program_token_t const& token) noexcept;
|
||||
|
||||
// Check if the shader compilation is completed. You may want to call this when the extension
|
||||
// `KHR_parallel_shader_compile` is enabled.
|
||||
static bool isCompileCompleted(program_token_t const& token) noexcept;
|
||||
|
||||
// Check compilation status of the shaders and log errors on failure.
|
||||
static void checkCompileStatus(program_token_t const& token) noexcept;
|
||||
|
||||
// Create a program by linking the compiled shaders. `gl.program` is always populated with a
|
||||
// valid program ID after this method. But this doesn't necessarily mean the program is
|
||||
// successfully linked. Errors can be checked by calling `checkLinkStatusAndCleanupShaders`
|
||||
// later.
|
||||
static void linkProgram(OpenGLContext const& context, program_token_t const& token) noexcept;
|
||||
|
||||
// Check if the program link is completed. You may want to call this when the extension
|
||||
// `KHR_parallel_shader_compile` is enabled.
|
||||
static bool isLinkCompleted(program_token_t const& token) noexcept;
|
||||
|
||||
// Check link status of the program and log errors on failure. Return the result of the link.
|
||||
// Also cleanup shaders regardless of the result.
|
||||
static bool checkLinkStatusAndCleanupShaders(program_token_t const& token) noexcept;
|
||||
|
||||
// Try caching the program if we haven't done it yet. Cache it only when the program is valid.
|
||||
static void tryCachingProgram(OpenGLBlobCache& cache, OpenGLPlatform& platform,
|
||||
program_token_t const& token) noexcept;
|
||||
|
||||
// Cleanup GL resources.
|
||||
static void cleanupProgramAndShaders(program_token_t const& token) noexcept;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -17,9 +17,11 @@
|
||||
#define COREVIDEO_SILENCE_GL_DEPRECATION
|
||||
|
||||
#include "CocoaExternalImage.h"
|
||||
#include <utils/Panic.h>
|
||||
#include "../GLUtils.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
static const char *s_vertex = R"SHADER(#version 410 core
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Log.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ PlatformCocoaTouchGL::~PlatformCocoaTouchGL() noexcept {
|
||||
delete pImpl;
|
||||
}
|
||||
|
||||
Driver* PlatformCocoaTouchGL::createDriver(void* const sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
|
||||
Driver* PlatformCocoaTouchGL::createDriver(void* sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
|
||||
EAGLSharegroup* sharegroup = (__bridge EAGLSharegroup*) sharedGLContext;
|
||||
|
||||
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 sharegroup:sharegroup];
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <backend/platforms/PlatformEGL.h>
|
||||
#include <backend/platforms/PlatformEGLAndroid.h>
|
||||
|
||||
#include <private/backend/BackendUtilsAndroid.h>
|
||||
#include <private/backend/VirtualMachineEnv.h>
|
||||
|
||||
#include "opengl/GLUtils.h"
|
||||
@@ -34,6 +35,8 @@
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
@@ -231,26 +234,133 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
|
||||
return driver;
|
||||
}
|
||||
|
||||
PlatformEGLAndroid::ExternalImageEGLAndroid::~ExternalImageEGLAndroid() = default;
|
||||
PlatformEGLAndroid::ExternalImageEGLAndroid::~ExternalImageEGLAndroid() {
|
||||
if (__builtin_available(android 26, *)) {
|
||||
if (aHardwareBuffer) {
|
||||
AHardwareBuffer_release(aHardwareBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Platform::ExternalImageHandle PlatformEGLAndroid::createExternalImage(AHardwareBuffer const* buffer, bool sRGB) noexcept {
|
||||
auto* const p = new(std::nothrow) ExternalImageEGLAndroid;
|
||||
p->aHardwareBuffer = const_cast<AHardwareBuffer*>(buffer);
|
||||
p->sRGB = sRGB;
|
||||
return ExternalImageHandle{ p };
|
||||
Platform::ExternalImageHandle PlatformEGLAndroid::createExternalImage(AHardwareBuffer const* buffer,
|
||||
bool sRGB) noexcept {
|
||||
if (__builtin_available(android 26, *)) {
|
||||
auto* const p = new (std::nothrow) ExternalImageEGLAndroid;
|
||||
auto hardwareBuffer = const_cast<AHardwareBuffer*>(buffer);
|
||||
AHardwareBuffer_acquire(hardwareBuffer);
|
||||
p->aHardwareBuffer = hardwareBuffer;
|
||||
p->sRGB = sRGB;
|
||||
AHardwareBuffer_Desc hardwareBufferDescription = {};
|
||||
AHardwareBuffer_describe(hardwareBuffer, &hardwareBufferDescription);
|
||||
p->height = hardwareBufferDescription.height;
|
||||
p->width = hardwareBufferDescription.width;
|
||||
auto textureFormat = mapToFilamentFormat(hardwareBufferDescription.format, sRGB);
|
||||
p->usage = mapToFilamentUsage(hardwareBufferDescription.usage, textureFormat);
|
||||
return ExternalImageHandle{ p };
|
||||
}
|
||||
|
||||
return Platform::ExternalImageHandle{};
|
||||
}
|
||||
|
||||
PlatformEGLAndroid::ExternalImageDescAndroid PlatformEGLAndroid::getExternalImageDesc(
|
||||
ExternalImageHandle externalImage) noexcept {
|
||||
auto const* const eglExternalImage =
|
||||
static_cast<ExternalImageEGLAndroid const*>(externalImage.get());
|
||||
ExternalImageDescAndroid metadata = {};
|
||||
if (!eglExternalImage) {
|
||||
return metadata;
|
||||
}
|
||||
metadata.height = eglExternalImage->height;
|
||||
metadata.width = eglExternalImage->width;
|
||||
metadata.format = eglExternalImage->format;
|
||||
metadata.usage = eglExternalImage->usage;
|
||||
return metadata;
|
||||
}
|
||||
|
||||
bool PlatformEGLAndroid::setExternalImage(ExternalImageHandleRef externalImage,
|
||||
UTILS_UNUSED_IN_RELEASE ExternalTexture* texture) noexcept {
|
||||
auto const* const eglExternalImage = static_cast<ExternalImageEGLAndroid const*>(externalImage.get());
|
||||
auto const* const eglExternalImage =
|
||||
static_cast<ExternalImageEGLAndroid const*>(externalImage.get());
|
||||
if (eglExternalImage->aHardwareBuffer) {
|
||||
// TODO: implement PlatformEGLAndroid::setExternalImage w/ AHardwareBuffer
|
||||
return true;
|
||||
return PlatformEGLAndroid::setImage(eglExternalImage, texture);
|
||||
}
|
||||
// not a AHardwareBuffer, fallback to the inherited version
|
||||
return PlatformEGL::setExternalImage(externalImage, texture);
|
||||
}
|
||||
|
||||
OpenGLPlatform::ExternalTexture* PlatformEGLAndroid::createExternalImageTexture() noexcept {
|
||||
ExternalTexture* outTexture = new (std::nothrow) ExternalTexture{};
|
||||
glGenTextures(1, &outTexture->id);
|
||||
return outTexture;
|
||||
}
|
||||
|
||||
void PlatformEGLAndroid::destroyExternalImageTexture(ExternalTexture* texture) noexcept {
|
||||
glDeleteTextures(1, &texture->id);
|
||||
delete texture;
|
||||
}
|
||||
|
||||
bool PlatformEGLAndroid::setImage(ExternalImageEGLAndroid const* eglExternalImage,
|
||||
UTILS_UNUSED_IN_RELEASE ExternalTexture* texture) noexcept {
|
||||
AHardwareBuffer* hardwareBuffer = eglExternalImage->aHardwareBuffer;
|
||||
|
||||
// Get the EGL client buffer from AHardwareBuffer
|
||||
EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(hardwareBuffer);
|
||||
EGLint imageAttrs[] = {
|
||||
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
|
||||
EGL_NONE, EGL_NONE, // Reserve space
|
||||
EGL_NONE, EGL_NONE, // Reserve space
|
||||
EGL_NONE // Ensure the list always ends with EGL_NONE
|
||||
};
|
||||
int attrIndex = 2;
|
||||
if (eglExternalImage->sRGB) {
|
||||
imageAttrs[attrIndex++] = EGL_GL_COLORSPACE;
|
||||
imageAttrs[attrIndex++] = EGL_GL_COLORSPACE_SRGB;
|
||||
}
|
||||
|
||||
if (static_cast<bool>(eglExternalImage->usage & TextureUsage::PROTECTED)) {
|
||||
imageAttrs[attrIndex++] = EGL_PROTECTED_CONTENT_EXT;
|
||||
imageAttrs[attrIndex++] = EGL_TRUE;
|
||||
}
|
||||
// Create an EGLImage from the client buffer
|
||||
EGLImageKHR eglImage = eglCreateImageKHR(eglGetCurrentDisplay(), EGL_NO_CONTEXT,
|
||||
EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs);
|
||||
if (eglImage == EGL_NO_IMAGE_KHR) {
|
||||
// Handle error
|
||||
slog.e << "Failed to create EGL image" << io::endl;
|
||||
glDeleteTextures(1, &texture->id);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Create and bind the OpenGL texture
|
||||
GLint prevActiveTexture, prevTexture;
|
||||
glGetIntegerv(GL_ACTIVE_TEXTURE, &prevActiveTexture);
|
||||
glGetIntegerv(GL_TEXTURE_BINDING_2D, &prevTexture);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(texture->target, texture->id);
|
||||
GLenum error = glGetError();
|
||||
if (UTILS_UNLIKELY(error != GL_NO_ERROR)) {
|
||||
slog.e << "Error after glBindTexture: " << error << io::endl;
|
||||
glDeleteTextures(1, &texture->id);
|
||||
eglDestroyImageKHR(eglGetCurrentDisplay(), eglImage);
|
||||
glActiveTexture(prevActiveTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, prevTexture);
|
||||
return false;
|
||||
}
|
||||
glEGLImageTargetTexture2DOES(texture->target, static_cast<GLeglImageOES>(eglImage));
|
||||
error = glGetError();
|
||||
if (UTILS_UNLIKELY(error != GL_NO_ERROR)) {
|
||||
slog.e << "Error after glEGLImageTargetTexture2DOES: " << error << io::endl;
|
||||
glDeleteTextures(1, &texture->id);
|
||||
eglDestroyImageKHR(eglGetCurrentDisplay(), eglImage);
|
||||
glActiveTexture(prevActiveTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, prevTexture);
|
||||
return false;
|
||||
}
|
||||
glActiveTexture(prevActiveTexture);
|
||||
glBindTexture(GL_TEXTURE_2D, prevTexture);
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlatformEGLAndroid::setPresentationTime(int64_t presentationTimeInNanosecond) noexcept {
|
||||
EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW);
|
||||
if (currentDrawSurface != EGL_NO_SURFACE) {
|
||||
|
||||
@@ -128,7 +128,7 @@ namespace filament::backend {
|
||||
|
||||
using namespace backend;
|
||||
|
||||
Driver* PlatformGLX::createDriver(void* const sharedGLContext,
|
||||
Driver* PlatformGLX::createDriver(void* sharedGLContext,
|
||||
const DriverConfig& driverConfig) noexcept {
|
||||
loadLibraries();
|
||||
// Get the display device
|
||||
|
||||
@@ -22,6 +22,12 @@
|
||||
#include <dlfcn.h>
|
||||
#include <memory>
|
||||
|
||||
#if defined(__linux__)
|
||||
// This is to ensure that linking during compilation will not fail even if
|
||||
// OSMesaGetProcAddress is not linked.
|
||||
__attribute__((weak)) OSMESAproc OSMesaGetProcAddress(char const*);
|
||||
#endif
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
using namespace backend;
|
||||
@@ -44,20 +50,27 @@ struct OSMesaSwapchain {
|
||||
|
||||
struct OSMesaAPI {
|
||||
private:
|
||||
using CreateContextFunc = OSMesaContext (*)(GLenum format, OSMesaContext);
|
||||
using CreateContextAttribsFunc = OSMesaContext (*)(const int *, OSMesaContext);
|
||||
using DestroyContextFunc = GLboolean (*)(OSMesaContext);
|
||||
using MakeCurrentFunc = GLboolean (*)(OSMesaContext ctx, void* buffer, GLenum type,
|
||||
GLsizei width, GLsizei height);
|
||||
using GetProcAddressFunc = OSMESAproc (*)(const char* funcName);
|
||||
|
||||
public:
|
||||
CreateContextFunc OSMesaCreateContext;
|
||||
DestroyContextFunc OSMesaDestroyContext;
|
||||
MakeCurrentFunc OSMesaMakeCurrent;
|
||||
GetProcAddressFunc OSMesaGetProcAddress;
|
||||
CreateContextAttribsFunc fOSMesaCreateContextAttribs;
|
||||
DestroyContextFunc fOSMesaDestroyContext;
|
||||
MakeCurrentFunc fOSMesaMakeCurrent;
|
||||
GetProcAddressFunc fOSMesaGetProcAddress;
|
||||
|
||||
OSMesaAPI() {
|
||||
constexpr char const* libraryNames[] = {"libOSMesa.so", "libosmesa.so"};
|
||||
static constexpr char const* libraryNames[] = {
|
||||
#if defined(__linux__)
|
||||
"libOSMesa.so",
|
||||
"libosmesa.so",
|
||||
#elif defined(__APPLE__)
|
||||
"libOSMesa.dylib",
|
||||
#endif
|
||||
};
|
||||
for (char const* libName: libraryNames) {
|
||||
mLib = dlopen(libName, RTLD_GLOBAL | RTLD_NOW);
|
||||
if (mLib) {
|
||||
@@ -65,18 +78,27 @@ public:
|
||||
}
|
||||
}
|
||||
if (mLib) {
|
||||
OSMesaGetProcAddress = (GetProcAddressFunc) dlsym(mLib, "OSMesaGetProcAddress");
|
||||
} else {
|
||||
OSMesaGetProcAddress = (GetProcAddressFunc) dlsym(RTLD_LOCAL, "OSMesaGetProcAddress");
|
||||
// Loading from a libosmesa.os
|
||||
fOSMesaGetProcAddress = (GetProcAddressFunc) dlsym(mLib, "OSMesaGetProcAddress");
|
||||
}
|
||||
#if defined(__linux__)
|
||||
else {
|
||||
// Filament is built into a .so
|
||||
fOSMesaGetProcAddress = (GetProcAddressFunc) dlsym(RTLD_LOCAL, "OSMesaGetProcAddress");
|
||||
}
|
||||
if (!fOSMesaGetProcAddress) {
|
||||
// Statically linking osmesa
|
||||
fOSMesaGetProcAddress = OSMesaGetProcAddress;
|
||||
}
|
||||
#endif // __linux__
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(OSMesaGetProcAddress)
|
||||
<< "Unable to against libOSMesa to create a software GL context";
|
||||
FILAMENT_CHECK_PRECONDITION(fOSMesaGetProcAddress)
|
||||
<< "Unable to link against libOSMesa to create a software GL context";
|
||||
|
||||
OSMesaCreateContext = (CreateContextFunc) OSMesaGetProcAddress("OSMesaCreateContext");
|
||||
OSMesaDestroyContext =
|
||||
(DestroyContextFunc) OSMesaGetProcAddress("OSMesaDestroyContext");
|
||||
OSMesaMakeCurrent = (MakeCurrentFunc) OSMesaGetProcAddress("OSMesaMakeCurrent");
|
||||
fOSMesaCreateContextAttribs =
|
||||
(CreateContextAttribsFunc) fOSMesaGetProcAddress("OSMesaCreateContextAttribs");
|
||||
fOSMesaDestroyContext = (DestroyContextFunc) fOSMesaGetProcAddress("OSMesaDestroyContext");
|
||||
fOSMesaMakeCurrent = (MakeCurrentFunc) fOSMesaGetProcAddress("OSMesaMakeCurrent");
|
||||
}
|
||||
|
||||
~OSMesaAPI() {
|
||||
@@ -90,14 +112,24 @@ private:
|
||||
|
||||
}// anonymous namespace
|
||||
|
||||
Driver* PlatformOSMesa::createDriver(void* const sharedGLContext,
|
||||
Driver* PlatformOSMesa::createDriver(void* sharedGLContext,
|
||||
const DriverConfig& driverConfig) noexcept {
|
||||
|
||||
OSMesaAPI* api = new OSMesaAPI();
|
||||
mOsMesaApi = api;
|
||||
|
||||
static constexpr int attribs[] = {
|
||||
OSMESA_FORMAT, GL_RGBA,
|
||||
OSMESA_DEPTH_BITS, 24,
|
||||
OSMESA_STENCIL_BITS, 8,
|
||||
OSMESA_ACCUM_BITS, 0,
|
||||
OSMESA_PROFILE, OSMESA_CORE_PROFILE,
|
||||
0,
|
||||
};
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(sharedGLContext == nullptr)
|
||||
<< "shared GL context is not supported with PlatformOSMesa";
|
||||
mContext = api->OSMesaCreateContext(GL_RGBA, NULL);
|
||||
mContext = api->fOSMesaCreateContextAttribs(attribs, NULL);
|
||||
|
||||
// We need to do a no-op makecurrent here so that the context will be in a correct state before
|
||||
// any GL calls.
|
||||
@@ -113,7 +145,7 @@ Driver* PlatformOSMesa::createDriver(void* const sharedGLContext,
|
||||
|
||||
void PlatformOSMesa::terminate() noexcept {
|
||||
OSMesaAPI* api = (OSMesaAPI*) mOsMesaApi;
|
||||
api->OSMesaDestroyContext(mContext);
|
||||
api->fOSMesaDestroyContext(mContext);
|
||||
delete api;
|
||||
mOsMesaApi = nullptr;
|
||||
|
||||
@@ -141,7 +173,7 @@ bool PlatformOSMesa::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
OSMesaAPI* api = (OSMesaAPI*) mOsMesaApi;
|
||||
OSMesaSwapchain* impl = (OSMesaSwapchain*) drawSwapChain;
|
||||
|
||||
auto result = api->OSMesaMakeCurrent(mContext, (BackingType*) impl->buffer.get(),
|
||||
auto result = api->fOSMesaMakeCurrent(mContext, (BackingType*) impl->buffer.get(),
|
||||
BACKING_GL_TYPE, impl->width, impl->height);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == GL_TRUE) << "OSMesaMakeCurrent failed!";
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ struct WGLSwapChain {
|
||||
|
||||
static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs = nullptr;
|
||||
|
||||
Driver* PlatformWGL::createDriver(void* const sharedGLContext,
|
||||
Driver* PlatformWGL::createDriver(void* sharedGLContext,
|
||||
const Platform::DriverConfig& driverConfig) noexcept {
|
||||
int result = 0;
|
||||
int pixelFormat = 0;
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace filament::backend {
|
||||
|
||||
using namespace backend;
|
||||
|
||||
Driver* PlatformWebGL::createDriver(void* const sharedGLContext,
|
||||
Driver* PlatformWebGL::createDriver(void* sharedGLContext,
|
||||
const Platform::DriverConfig& driverConfig) noexcept {
|
||||
return OpenGLPlatform::createDefaultDriver(this, sharedGLContext, driverConfig);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include <backend/BufferDescriptor.h>
|
||||
#include <backend/BufferObjectStreamDescriptor.h>
|
||||
#include <backend/DescriptorSetOffsetArray.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/PipelineState.h>
|
||||
@@ -437,6 +438,10 @@ io::ostream& operator<<(io::ostream& out, BufferDescriptor const& b) {
|
||||
<< ", user=" << b.getUser() << " }";
|
||||
}
|
||||
|
||||
io::ostream& operator<<(io::ostream& out, const BufferObjectStreamDescriptor& b) {
|
||||
return out << "BufferObjectStreamDescriptor{ streams(" << b.mStreams.size() << ")=... }";
|
||||
}
|
||||
|
||||
io::ostream& operator<<(io::ostream& out, PixelBufferDescriptor const& b) {
|
||||
BufferDescriptor const& base = static_cast<BufferDescriptor const&>(b);
|
||||
return out << "PixelBufferDescriptor{ " << base
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
#include "VulkanBlitter.h"
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanContext.h"
|
||||
#include "VulkanFboCache.h"
|
||||
#include "VulkanHandles.h"
|
||||
#include "VulkanSamplerCache.h"
|
||||
#include "VulkanTexture.h"
|
||||
#include "vulkan/utils/Image.h"
|
||||
|
||||
|
||||
@@ -24,13 +24,6 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class VulkanBuffer;
|
||||
class VulkanFboCache;
|
||||
class VulkanPipelineCache;
|
||||
class VulkanSamplerCache;
|
||||
|
||||
struct VulkanProgram;
|
||||
|
||||
class VulkanBlitter {
|
||||
public:
|
||||
VulkanBlitter(VkPhysicalDevice physicalDevice, VulkanCommands* commands) noexcept;
|
||||
|
||||
@@ -295,6 +295,8 @@ VulkanCommandBuffer& CommandBufferPool::getRecording() {
|
||||
}
|
||||
|
||||
void CommandBufferPool::gc() {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("CommandBufferPool::gc");
|
||||
ActiveBuffers reclaimed;
|
||||
mSubmitted.forEachSetBit([this,&reclaimed] (size_t index) {
|
||||
auto& buffer = mBuffers[index];
|
||||
@@ -304,6 +306,7 @@ void CommandBufferPool::gc() {
|
||||
}
|
||||
});
|
||||
mSubmitted &= ~reclaimed;
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void CommandBufferPool::update() {
|
||||
@@ -333,7 +336,9 @@ void CommandBufferPool::wait() {
|
||||
mSubmitted.forEachSetBit([this, &count, &fences] (size_t index) {
|
||||
fences[count++] = mBuffers[index]->getVkFence();
|
||||
});
|
||||
vkWaitForFences(mDevice, count, fences, VK_TRUE, UINT64_MAX);
|
||||
if (count) {
|
||||
vkWaitForFences(mDevice, count, fences, VK_TRUE, UINT64_MAX);
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
@@ -152,12 +152,12 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
|
||||
#elif FVK_ENABLED(FVK_DEBUG_SYSTRACE)
|
||||
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
|
||||
#define FVK_SYSTRACE_CONTEXT() SYSTRACE_CONTEXT()
|
||||
#define FVK_SYSTRACE_START(marker) SYSTRACE_NAME_BEGIN(marker)
|
||||
#define FVK_SYSTRACE_END() SYSTRACE_NAME_END()
|
||||
#define FVK_SYSTRACE_SCOPE() SYSTRACE_NAME(__func__)
|
||||
#define FVK_PROFILE_MARKER(marker) FVK_SYSTRACE_SCOPE()
|
||||
#define FVK_SYSTRACE_SCOPE() SYSTRACE_CALL()
|
||||
#define FVK_PROFILE_MARKER(marker) SYSTRACE_CALL()
|
||||
|
||||
#else
|
||||
#define FVK_SYSTRACE_CONTEXT()
|
||||
|
||||
@@ -56,9 +56,9 @@ VkImageView VulkanAttachment::getImageView() {
|
||||
assert_invariant(texture);
|
||||
VkImageSubresourceRange range = getSubresourceRange();
|
||||
if (range.layerCount > 1) {
|
||||
return texture->getMultiviewAttachmentView(range);
|
||||
return texture->getAttachmentView(range, VK_IMAGE_VIEW_TYPE_2D_ARRAY);
|
||||
}
|
||||
return texture->getAttachmentView(range);
|
||||
return texture->getAttachmentView(range, VK_IMAGE_VIEW_TYPE_2D);
|
||||
}
|
||||
|
||||
bool VulkanAttachment::isDepth() const {
|
||||
@@ -68,11 +68,11 @@ bool VulkanAttachment::isDepth() const {
|
||||
VkImageSubresourceRange VulkanAttachment::getSubresourceRange() const {
|
||||
assert_invariant(texture);
|
||||
return {
|
||||
.aspectMask = texture->getImageAspect(),
|
||||
.baseMipLevel = uint32_t(level),
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = uint32_t(layer),
|
||||
.layerCount = layerCount,
|
||||
.aspectMask = texture->getImageAspect(),
|
||||
.baseMipLevel = uint32_t(level),
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = uint32_t(layer),
|
||||
.layerCount = layerCount,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANCONTEXT_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANCONTEXT_H
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "vulkan/utils/Image.h"
|
||||
#include "vulkan/utils/Definitions.h"
|
||||
|
||||
@@ -30,8 +29,6 @@
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
VK_DEFINE_HANDLE(VmaAllocator)
|
||||
VK_DEFINE_HANDLE(VmaPool)
|
||||
|
||||
@@ -73,13 +70,11 @@ struct VulkanRenderPass {
|
||||
// context are stored in VulkanPlatform.
|
||||
struct VulkanContext {
|
||||
public:
|
||||
inline uint32_t selectMemoryType(uint32_t flags, VkFlags reqs) const {
|
||||
if ((reqs & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) {
|
||||
assert_invariant(isProtectedMemorySupported() == true);
|
||||
}
|
||||
static uint32_t selectMemoryType(VkPhysicalDeviceMemoryProperties const& memoryProperties,
|
||||
uint32_t flags, VkFlags reqs) {
|
||||
for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) {
|
||||
if (flags & 1) {
|
||||
if ((mMemoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) {
|
||||
if ((memoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@@ -88,6 +83,13 @@ public:
|
||||
return (uint32_t) VK_MAX_MEMORY_TYPES;
|
||||
}
|
||||
|
||||
inline uint32_t selectMemoryType(uint32_t flags, VkFlags reqs) const {
|
||||
if ((reqs & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) {
|
||||
assert_invariant(isProtectedMemorySupported());
|
||||
}
|
||||
return selectMemoryType(mMemoryProperties, flags, reqs);
|
||||
}
|
||||
|
||||
inline fvkutils::VkFormatList const& getAttachmentDepthStencilFormats() const {
|
||||
return mDepthStencilFormats;
|
||||
}
|
||||
@@ -121,7 +123,7 @@ public:
|
||||
}
|
||||
|
||||
inline bool isMultiviewEnabled() const noexcept {
|
||||
return mMultiviewEnabled;
|
||||
return mPhysicalDeviceVk11Features.multiview == VK_TRUE;
|
||||
}
|
||||
|
||||
inline bool isClipDistanceSupported() const noexcept {
|
||||
@@ -145,6 +147,9 @@ private:
|
||||
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
||||
};
|
||||
VkPhysicalDeviceVulkan11Features mPhysicalDeviceVk11Features = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES,
|
||||
};
|
||||
VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||
};
|
||||
@@ -157,7 +162,6 @@ private:
|
||||
};
|
||||
bool mDebugMarkersSupported = false;
|
||||
bool mDebugUtilsSupported = false;
|
||||
bool mMultiviewEnabled = false;
|
||||
bool mLazilyAllocatedMemorySupported = false;
|
||||
bool mProtectedMemorySupported = false;
|
||||
|
||||
|
||||
@@ -0,0 +1,429 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "VulkanDescriptorSetCache.h"
|
||||
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanHandles.h"
|
||||
#include "VulkanConstants.h"
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
using DescriptorSetLayoutArray = VulkanDescriptorSetCache::DescriptorSetLayoutArray;
|
||||
using DescriptorCount = VulkanDescriptorSetCache::DescriptorCount;
|
||||
|
||||
// We create a pool for each layout as defined by the number of descriptors of each type. For
|
||||
// example, a layout of
|
||||
// 'A' =>
|
||||
// layout(binding = 0, set = 1) uniform {};
|
||||
// layout(binding = 1, set = 1) sampler1;
|
||||
// layout(binding = 2, set = 1) sampler2;
|
||||
//
|
||||
// would be equivalent to
|
||||
// 'B' =>
|
||||
// layout(binding = 1, set = 2) uniform {};
|
||||
// layout(binding = 2, set = 2) sampler2;
|
||||
// layout(binding = 3, set = 2) sampler3;
|
||||
//
|
||||
// TODO: we might do better if we understand the types of unique layouts and can combine them in a
|
||||
// single pool without too much waste.
|
||||
class DescriptorPool {
|
||||
public:
|
||||
DescriptorPool(VkDevice device, DescriptorCount const& count, uint16_t capacity)
|
||||
: mDevice(device),
|
||||
mCount(count),
|
||||
mCapacity(capacity),
|
||||
mSize(0),
|
||||
mUnusedCount(0) {
|
||||
DescriptorCount const actual = mCount * capacity;
|
||||
VkDescriptorPoolSize sizes[4];
|
||||
uint8_t npools = 0;
|
||||
if (actual.ubo) {
|
||||
sizes[npools++] = {
|
||||
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||
.descriptorCount = actual.ubo,
|
||||
};
|
||||
}
|
||||
if (actual.dynamicUbo) {
|
||||
sizes[npools++] = {
|
||||
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
|
||||
.descriptorCount = actual.dynamicUbo,
|
||||
};
|
||||
}
|
||||
if (actual.sampler) {
|
||||
sizes[npools++] = {
|
||||
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.descriptorCount = actual.sampler,
|
||||
};
|
||||
}
|
||||
if (actual.inputAttachment) {
|
||||
sizes[npools++] = {
|
||||
.type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
|
||||
.descriptorCount = actual.inputAttachment,
|
||||
};
|
||||
}
|
||||
VkDescriptorPoolCreateInfo info{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.maxSets = capacity,
|
||||
.poolSizeCount = npools,
|
||||
.pPoolSizes = sizes,
|
||||
};
|
||||
vkCreateDescriptorPool(mDevice, &info, VKALLOC, &mPool);
|
||||
}
|
||||
|
||||
DescriptorPool(DescriptorPool const&) = delete;
|
||||
DescriptorPool& operator=(DescriptorPool const&) = delete;
|
||||
|
||||
~DescriptorPool() {
|
||||
vkDestroyDescriptorPool(mDevice, mPool, VKALLOC);
|
||||
}
|
||||
|
||||
uint16_t const& capacity() {
|
||||
return mCapacity;
|
||||
}
|
||||
|
||||
// A convenience method for checking if this pool can allocate sets for a given layout.
|
||||
inline bool canAllocate(DescriptorCount const& count) {
|
||||
return count == mCount;
|
||||
}
|
||||
|
||||
VkDescriptorSet obtainSet(VkDescriptorSetLayout vklayout) {
|
||||
auto itr = findSets(vklayout);
|
||||
if (itr != mUnused.end()) {
|
||||
// If we don't have any unused, then just return an empty handle.
|
||||
if (itr->second.empty()) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
std::vector<VkDescriptorSet>& sets = itr->second;
|
||||
auto set = sets.back();
|
||||
sets.pop_back();
|
||||
mUnusedCount--;
|
||||
return set;
|
||||
}
|
||||
if (mSize + 1 > mCapacity) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
// Creating a new set
|
||||
VkDescriptorSetLayout layouts[1] = {vklayout};
|
||||
VkDescriptorSetAllocateInfo allocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.descriptorPool = mPool,
|
||||
.descriptorSetCount = 1,
|
||||
.pSetLayouts = layouts,
|
||||
};
|
||||
VkDescriptorSet vkSet;
|
||||
UTILS_UNUSED VkResult result = vkAllocateDescriptorSets(mDevice, &allocInfo, &vkSet);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Failed to allocate descriptor set code=" << result << " size=" << mSize
|
||||
<< " capacity=" << mCapacity << " count=" << mUnusedCount;
|
||||
mSize++;
|
||||
return vkSet;
|
||||
}
|
||||
|
||||
void recycle(VkDescriptorSetLayout vklayout, VkDescriptorSet vkSet) {
|
||||
// We are recycling - release the set back into the pool. Note that the
|
||||
// vk handle has not changed, but we need to change the backend handle to allow
|
||||
// for proper refcounting of resources referenced in this set.
|
||||
auto itr = findSets(vklayout);
|
||||
if (itr != mUnused.end()) {
|
||||
itr->second.push_back(vkSet);
|
||||
} else {
|
||||
mUnused.push_back(std::make_pair(vklayout, std::vector<VkDescriptorSet> {vkSet}));
|
||||
}
|
||||
mUnusedCount++;
|
||||
}
|
||||
|
||||
private:
|
||||
using UnusedSets = std::pair<VkDescriptorSetLayout, std::vector<VkDescriptorSet>>;
|
||||
using UnusedSetMap = std::vector<UnusedSets>;
|
||||
|
||||
inline UnusedSetMap::iterator findSets(VkDescriptorSetLayout vklayout) {
|
||||
return std::find_if(mUnused.begin(), mUnused.end(), [vklayout](auto const& value) {
|
||||
return value.first == vklayout;
|
||||
});
|
||||
}
|
||||
|
||||
VkDevice mDevice;
|
||||
VkDescriptorPool mPool;
|
||||
DescriptorCount const mCount;
|
||||
uint16_t const mCapacity;
|
||||
|
||||
// Tracks the number of allocated descriptor sets.
|
||||
uint16_t mSize;
|
||||
// Tracks the number of in-use descriptor sets.
|
||||
uint16_t mUnusedCount;
|
||||
|
||||
// This maps a layout to a list of descriptor sets allocated for that layout.
|
||||
UnusedSetMap mUnused;
|
||||
};
|
||||
|
||||
template<typename Key>
|
||||
struct Equal {
|
||||
bool operator()(Key const& k1, Key const& k2) const {
|
||||
return 0 == memcmp((const void*) &k1, (const void*) &k2, sizeof(Key));
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// This is an ever-expanding pool of sets where it
|
||||
// 1. Keeps a list of smaller pools of different layout-dimensions.
|
||||
// 2. Will add a pool if existing pool are not compatible with the requested layout o runs out.
|
||||
class VulkanDescriptorSetCache::DescriptorInfinitePool {
|
||||
private:
|
||||
static constexpr uint16_t EXPECTED_SET_COUNT = 10;
|
||||
static constexpr float SET_COUNT_GROWTH_FACTOR = 1.5;
|
||||
|
||||
public:
|
||||
DescriptorInfinitePool(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
|
||||
VkDescriptorSet obtainSet(DescriptorCount const& count, VkDescriptorSetLayout vklayout) {
|
||||
DescriptorPool* sameTypePool = nullptr;
|
||||
for (auto& pool: mPools) {
|
||||
if (!pool->canAllocate(count)) {
|
||||
continue;
|
||||
}
|
||||
if (auto set = pool->obtainSet(vklayout); set != VK_NULL_HANDLE) {
|
||||
return set;
|
||||
}
|
||||
if (!sameTypePool || sameTypePool->capacity() < pool->capacity()) {
|
||||
sameTypePool = pool.get();
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t capacity = EXPECTED_SET_COUNT;
|
||||
if (sameTypePool) {
|
||||
// Exponentially increase the size of the pool to ensure we don't hit this too often.
|
||||
capacity = std::ceil(sameTypePool->capacity() * SET_COUNT_GROWTH_FACTOR);
|
||||
}
|
||||
|
||||
// We need to increase the set of pools by one.
|
||||
mPools.push_back(std::make_unique<DescriptorPool>(mDevice, count, capacity));
|
||||
auto& pool = mPools.back();
|
||||
auto ret = pool->obtainSet(vklayout);
|
||||
assert_invariant(ret != VK_NULL_HANDLE && "failed to obtain a set?");
|
||||
return ret;
|
||||
}
|
||||
|
||||
void recycle(DescriptorCount const& count, VkDescriptorSetLayout vklayout,
|
||||
VkDescriptorSet vkSet) {
|
||||
for (auto& pool: mPools) {
|
||||
if (!pool->canAllocate(count)) {
|
||||
continue;
|
||||
}
|
||||
pool->recycle(vklayout, vkSet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
std::vector<std::unique_ptr<DescriptorPool>> mPools;
|
||||
};
|
||||
|
||||
|
||||
VulkanDescriptorSetCache::VulkanDescriptorSetCache(VkDevice device,
|
||||
fvkmemory::ResourceManager* resourceManager)
|
||||
: mDevice(device),
|
||||
mResourceManager(resourceManager),
|
||||
mDescriptorPool(std::make_unique<DescriptorInfinitePool>(device)) {}
|
||||
|
||||
VulkanDescriptorSetCache::~VulkanDescriptorSetCache() = default;
|
||||
|
||||
void VulkanDescriptorSetCache::terminate() noexcept{
|
||||
mDescriptorPool.reset();
|
||||
}
|
||||
|
||||
// bind() is not really binding the set but just stashing until we have all the info
|
||||
// (pipelinelayout).
|
||||
void VulkanDescriptorSetCache::bind(uint8_t setIndex,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
backend::DescriptorSetOffsetArray&& offsets) {
|
||||
set->setOffsets(std::move(offsets));
|
||||
mStashedSets[setIndex] = set;
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetCache::unbind(uint8_t setIndex) {
|
||||
mStashedSets[setIndex] = {};
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
|
||||
VkPipelineLayout pipelineLayout, fvkutils::DescriptorSetMask const& useExternalSamplers,
|
||||
fvkutils::DescriptorSetMask const& setMask) {
|
||||
// setMask indicates the set of descriptor sets the driver wants to bind, curMask is the
|
||||
// actual set of sets that *needs* to be bound.
|
||||
fvkutils::DescriptorSetMask curMask = setMask;
|
||||
|
||||
auto const& updateSets = mStashedSets;
|
||||
curMask.forEachSetBit([&](size_t index) {
|
||||
if (!updateSets[index]) {
|
||||
curMask.unset(index);
|
||||
}
|
||||
});
|
||||
|
||||
if (mLastBoundInfo.pipelineLayout == pipelineLayout) {
|
||||
auto& lastBoundSets = mLastBoundInfo.boundSets;
|
||||
curMask.forEachSetBit([&](size_t index) {
|
||||
if (updateSets[index] == lastBoundSets[index] && !useExternalSamplers[index]) {
|
||||
curMask.unset(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
curMask.forEachSetBit([&](size_t index) {
|
||||
// This code actually binds the descriptor sets.
|
||||
auto set = updateSets[index];
|
||||
VkCommandBuffer const cmdbuffer = commands->buffer();
|
||||
VkDescriptorSet vkset = useExternalSamplers[index] ? set->getExternalSamplerVkSet() :
|
||||
set->getVkSet();
|
||||
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index,
|
||||
1, &vkset, set->uniqueDynamicUboCount, set->getOffsets()->data());
|
||||
commands->acquire(set);
|
||||
});
|
||||
|
||||
mStashedSets = {};
|
||||
|
||||
mLastBoundInfo = {
|
||||
pipelineLayout,
|
||||
setMask,
|
||||
updateSets,
|
||||
};
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
uint8_t binding, fvkmemory::resource_ptr<VulkanBufferObject> bufferObject,
|
||||
VkDeviceSize offset, VkDeviceSize size) noexcept {
|
||||
VkDescriptorBufferInfo const info = {
|
||||
.buffer = bufferObject->buffer.getGpuBuffer(),
|
||||
.offset = offset,
|
||||
.range = size,
|
||||
};
|
||||
VkDescriptorType type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
|
||||
if (set->dynamicUboMask.test(binding)) {
|
||||
type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
}
|
||||
VkWriteDescriptorSet descriptorWrite = {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = set->getVkSet(),
|
||||
.dstBinding = binding,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = type,
|
||||
.pBufferInfo = &info,
|
||||
};
|
||||
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
|
||||
|
||||
if (auto externalSamplerSet = set->getExternalSamplerVkSet();
|
||||
externalSamplerSet != VK_NULL_HANDLE) {
|
||||
descriptorWrite.dstSet = externalSamplerSet;
|
||||
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
|
||||
}
|
||||
set->acquire(bufferObject);
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetCache::updateSamplerImpl(VkDescriptorSet vkset, uint8_t binding,
|
||||
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept {
|
||||
VkImageSubresourceRange range = texture->getPrimaryViewRange();
|
||||
VkImageViewType const expectedType = texture->getViewType();
|
||||
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
|
||||
expectedType == VK_IMAGE_VIEW_TYPE_2D) {
|
||||
// If the sampler is part of a mipmapped depth texture, where one of the level *can* be
|
||||
// an attachment, then the range for this view has exactly one level and one layer.
|
||||
range.levelCount = 1;
|
||||
range.layerCount = 1;
|
||||
}
|
||||
VkDescriptorImageInfo info{
|
||||
.sampler = sampler,
|
||||
.imageView = texture->getView(range),
|
||||
.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout()),
|
||||
};
|
||||
|
||||
VkWriteDescriptorSet descriptorWrite = {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.pNext = nullptr,
|
||||
.dstSet = vkset,
|
||||
.dstBinding = binding,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.pImageInfo = &info,
|
||||
};
|
||||
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
|
||||
VkSampler sampler) noexcept {
|
||||
updateSamplerImpl(set->getVkSet(), binding, texture, sampler);
|
||||
set->acquire(texture);
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetCache::updateSamplerForExternalSamplerSet(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
|
||||
fvkmemory::resource_ptr<VulkanTexture> texture) noexcept {
|
||||
updateSamplerImpl(set->getExternalSamplerVkSet(), binding, texture, VK_NULL_HANDLE);
|
||||
set->acquire(texture);
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetCache::updateInputAttachment(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
VulkanAttachment const& attachment) noexcept {
|
||||
// TOOD: fill this in.
|
||||
}
|
||||
|
||||
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet(
|
||||
Handle<HwDescriptorSet> handle, fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
|
||||
auto const vkSet = mDescriptorPool->obtainSet(layout->count, layout->getVkLayout());
|
||||
auto const& count = layout->count;
|
||||
auto const vklayout = layout->getVkLayout();
|
||||
auto set = fvkmemory::resource_ptr<VulkanDescriptorSet>::make(
|
||||
mResourceManager, handle, layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
|
||||
[vkSet, count, vklayout, this](
|
||||
VulkanDescriptorSet*) { this->manualRecycle(count, vklayout, vkSet); },
|
||||
vkSet);
|
||||
return set;
|
||||
}
|
||||
|
||||
VkDescriptorSet VulkanDescriptorSetCache::getVkSet(DescriptorCount const& count,
|
||||
VkDescriptorSetLayout vklayout) {
|
||||
return mDescriptorPool->obtainSet(count, vklayout);
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetCache::manualRecycle(VulkanDescriptorSetLayout::Count const& count,
|
||||
VkDescriptorSetLayout vklayout, VkDescriptorSet vkSet) {
|
||||
// Note that mDescriptorPool could be gone due to terminate (when the backend shuts
|
||||
// down).
|
||||
if (mDescriptorPool) {
|
||||
mDescriptorPool->recycle(count, vklayout, vkSet);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetCache::gc() { mStashedSets = {}; }
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETCACHE_H
|
||||
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETCACHE_H
|
||||
|
||||
#include "VulkanHandles.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
#include "vulkan/utils/Definitions.h" // For DescriptorSetMask
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Program.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include <utils/bitset.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
// Abstraction over the descriptor set pool.
|
||||
class VulkanDescriptorSetCache {
|
||||
public:
|
||||
static constexpr uint8_t UNIQUE_DESCRIPTOR_SET_COUNT =
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT;
|
||||
|
||||
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
|
||||
using DescriptorSetArray =
|
||||
std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>, UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
using DescriptorCount = VulkanDescriptorSetLayout::Count;
|
||||
|
||||
VulkanDescriptorSetCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
|
||||
~VulkanDescriptorSetCache();
|
||||
|
||||
void terminate() noexcept;
|
||||
|
||||
void updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
|
||||
fvkmemory::resource_ptr<VulkanBufferObject> bufferObject, VkDeviceSize offset,
|
||||
VkDeviceSize size) noexcept;
|
||||
|
||||
void updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
|
||||
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept;
|
||||
|
||||
void updateSamplerForExternalSamplerSet(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
|
||||
fvkmemory::resource_ptr<VulkanTexture> texture) noexcept;
|
||||
|
||||
|
||||
void updateInputAttachment(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
VulkanAttachment const& attachment) noexcept;
|
||||
|
||||
void bind(uint8_t setIndex, fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
backend::DescriptorSetOffsetArray&& offsets);
|
||||
|
||||
void unbind(uint8_t setIndex);
|
||||
|
||||
void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout,
|
||||
fvkutils::DescriptorSetMask const& useExternalSamplerMask,
|
||||
fvkutils::DescriptorSetMask const& setMask);
|
||||
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> createSet(Handle<HwDescriptorSet> handle,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
||||
|
||||
// This method is meant to be used with external samplers
|
||||
VkDescriptorSet getVkSet(DescriptorCount const& count, VkDescriptorSetLayout vklayout);
|
||||
|
||||
// This method is meant to be used with external samplers
|
||||
void manualRecycle(VulkanDescriptorSetLayout::Count const& count, VkDescriptorSetLayout vklayout,
|
||||
VkDescriptorSet vkSet);
|
||||
|
||||
DescriptorSetArray const& getBoundSets() const { return mStashedSets; }
|
||||
|
||||
void gc();
|
||||
|
||||
private:
|
||||
void updateSamplerImpl(VkDescriptorSet set, uint8_t binding,
|
||||
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept;
|
||||
|
||||
class DescriptorInfinitePool;
|
||||
|
||||
VkDevice mDevice;
|
||||
fvkmemory::ResourceManager* mResourceManager;
|
||||
std::unique_ptr<DescriptorInfinitePool> mDescriptorPool;
|
||||
std::pair<VulkanAttachment, VkDescriptorImageInfo> mInputAttachment;
|
||||
DescriptorSetArray mStashedSets = {};
|
||||
|
||||
struct {
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
fvkutils::DescriptorSetMask setMask;
|
||||
DescriptorSetArray boundSets = {};
|
||||
} mLastBoundInfo;
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETCACHE_H
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "VulkanDescriptorSetLayoutCache.h"
|
||||
|
||||
#include "VulkanHandles.h"
|
||||
|
||||
#include <utils/Hash.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask;
|
||||
|
||||
template<typename Bitmask>
|
||||
uint32_t appendBindings(VkDescriptorSetLayoutBinding* toBind, VkDescriptorType type,
|
||||
Bitmask const& mask) {
|
||||
uint32_t count = 0;
|
||||
Bitmask alreadySeen;
|
||||
mask.forEachSetBit([&](size_t index) {
|
||||
VkShaderStageFlags stages = 0;
|
||||
uint32_t binding = 0;
|
||||
if (index < fvkutils::getFragmentStageShift<Bitmask>()) {
|
||||
binding = (uint32_t) index;
|
||||
stages |= VK_SHADER_STAGE_VERTEX_BIT;
|
||||
auto fragIndex = index + fvkutils::getFragmentStageShift<Bitmask>();
|
||||
if (mask.test(fragIndex)) {
|
||||
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
alreadySeen.set(fragIndex);
|
||||
}
|
||||
} else if (!alreadySeen.test(index)) {
|
||||
// We are in fragment stage bits
|
||||
binding = (uint32_t) (index - fvkutils::getFragmentStageShift<Bitmask>());
|
||||
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
}
|
||||
|
||||
if (stages) {
|
||||
toBind[count++] = {
|
||||
.binding = binding,
|
||||
.descriptorType = type,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = stages,
|
||||
};
|
||||
}
|
||||
});
|
||||
return count;
|
||||
}
|
||||
|
||||
uint32_t appendSamplerBindings(VkDescriptorSetLayoutBinding* toBind,
|
||||
fvkutils::SamplerBitmask const& mask, fvkutils::SamplerBitmask const& external,
|
||||
utils::FixedCapacityVector<VkSampler> const& immutableSamplers) {
|
||||
using Bitmask = fvkutils::SamplerBitmask;
|
||||
uint32_t count = 0;
|
||||
Bitmask alreadySeen;
|
||||
uint8_t immutableIndex = 0;
|
||||
size_t const immutableSamplerCount = immutableSamplers.size();
|
||||
mask.forEachSetBit([&](size_t index) {
|
||||
VkShaderStageFlags stages = 0;
|
||||
uint32_t binding = 0;
|
||||
if (index < fvkutils::getFragmentStageShift<Bitmask>()) {
|
||||
binding = (uint32_t) index;
|
||||
stages |= VK_SHADER_STAGE_VERTEX_BIT;
|
||||
auto fragIndex = index + fvkutils::getFragmentStageShift<Bitmask>();
|
||||
if (mask.test(fragIndex)) {
|
||||
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
alreadySeen.set(fragIndex);
|
||||
}
|
||||
} else if (!alreadySeen.test(index)) {
|
||||
// We are in fragment stage bits
|
||||
binding = (uint32_t) (index - fvkutils::getFragmentStageShift<Bitmask>());
|
||||
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
}
|
||||
|
||||
if (stages) {
|
||||
toBind[count++] = {
|
||||
.binding = binding,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = stages,
|
||||
.pImmutableSamplers = external[index] && immutableSamplerCount > immutableIndex
|
||||
? &immutableSamplers[immutableIndex++]
|
||||
: nullptr,
|
||||
};
|
||||
}
|
||||
});
|
||||
return count;
|
||||
}
|
||||
|
||||
uint64_t computeImmutableSamplerHash(utils::FixedCapacityVector<VkSampler> const& samplers) {
|
||||
size_t const size = samplers.size();
|
||||
if (size == 0) {
|
||||
return 0;
|
||||
} else if (size == 1) {
|
||||
return (uint64_t) samplers[0];
|
||||
}
|
||||
return utils::hash::murmur3((uint32_t*) samplers.data(), samplers.size() * 2, 0);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
VulkanDescriptorSetLayoutCache::VulkanDescriptorSetLayoutCache(VkDevice device,
|
||||
fvkmemory::ResourceManager* resourceManager)
|
||||
: mDevice(device),
|
||||
mResourceManager(resourceManager) {}
|
||||
|
||||
VulkanDescriptorSetLayoutCache::~VulkanDescriptorSetLayoutCache() = default;
|
||||
|
||||
void VulkanDescriptorSetLayoutCache::terminate() noexcept {
|
||||
for (auto& itr: mVkLayouts) {
|
||||
vkDestroyDescriptorSetLayout(mDevice, itr.second, VKALLOC);
|
||||
}
|
||||
}
|
||||
|
||||
VkDescriptorSetLayout VulkanDescriptorSetLayoutCache::getVkLayout(
|
||||
VulkanDescriptorSetLayout::Bitmask const& bitmasks,
|
||||
fvkutils::SamplerBitmask externalSamplers,
|
||||
utils::FixedCapacityVector<VkSampler> immutableSamplers) {
|
||||
LayoutKey key = {
|
||||
.bitmask = bitmasks,
|
||||
.immutableSamplerHash = computeImmutableSamplerHash(immutableSamplers),
|
||||
};
|
||||
if (auto itr = mVkLayouts.find(key); itr != mVkLayouts.end()) {
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayoutBinding toBind[VulkanDescriptorSetLayout::MAX_BINDINGS];
|
||||
uint32_t count = 0;
|
||||
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
|
||||
bitmasks.dynamicUbo);
|
||||
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bitmasks.ubo);
|
||||
count += appendSamplerBindings(&toBind[count], bitmasks.sampler, externalSamplers,
|
||||
immutableSamplers);
|
||||
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
|
||||
bitmasks.inputAttachment);
|
||||
|
||||
assert_invariant(count != 0 && "Need at least one binding for descriptor set layout.");
|
||||
VkDescriptorSetLayoutCreateInfo dlinfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.bindingCount = count,
|
||||
.pBindings = toBind,
|
||||
};
|
||||
VkDescriptorSetLayout vklayout;
|
||||
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout);
|
||||
mVkLayouts[key] = vklayout;
|
||||
return vklayout;
|
||||
}
|
||||
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> VulkanDescriptorSetLayoutCache::createLayout(
|
||||
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info) {
|
||||
BitmaskGroup maskGroup = VulkanDescriptorSetLayout::Bitmask::fromLayoutDescription(info);
|
||||
auto layout = fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::make(mResourceManager, handle,
|
||||
std::move(info), getVkLayout(maskGroup, maskGroup.externalSampler));
|
||||
return layout;
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETLAYOUTCACHE_H
|
||||
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETLAYOUTCACHE_H
|
||||
|
||||
#include "VulkanHandles.h"
|
||||
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Program.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include <utils/bitset.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class VulkanDescriptorSetLayoutCache {
|
||||
public:
|
||||
VulkanDescriptorSetLayoutCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
|
||||
~VulkanDescriptorSetLayoutCache();
|
||||
|
||||
void terminate() noexcept;
|
||||
|
||||
// Just a wrapper around getVkLayout()
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> createLayout(
|
||||
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info);
|
||||
|
||||
// This method is meant to be used with external samplers
|
||||
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks,
|
||||
fvkutils::SamplerBitmask externalSamplers,
|
||||
utils::FixedCapacityVector<VkSampler> immutableSamplers = {});
|
||||
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
fvkmemory::ResourceManager* mResourceManager;
|
||||
|
||||
struct LayoutKey {
|
||||
// this describes the layout using bitset.
|
||||
VulkanDescriptorSetLayout::Bitmask bitmask = {};
|
||||
// number of immutable samplers can be arbitrary; so we hash them into 64-bit.
|
||||
uint64_t immutableSamplerHash = 0;
|
||||
};
|
||||
static_assert(sizeof(LayoutKey) == 48);
|
||||
|
||||
using LayoutKeyHashFn = utils::hash::MurmurHashFn<LayoutKey>;
|
||||
struct LayoutKeyEqual {
|
||||
bool operator()(LayoutKey const& k1, LayoutKey const& k2) const {
|
||||
return k1.bitmask == k2.bitmask && k1.immutableSamplerHash == k2.immutableSamplerHash;
|
||||
}
|
||||
};
|
||||
|
||||
tsl::robin_map<LayoutKey, VkDescriptorSetLayout, LayoutKeyHashFn, LayoutKeyEqual> mVkLayouts;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETLAYOUTCACHE_H
|
||||
@@ -17,7 +17,6 @@
|
||||
#include "VulkanDriver.h"
|
||||
|
||||
#include "CommandStreamDispatcher.h"
|
||||
#include "DataReshaper.h"
|
||||
#include "SystraceProfile.h"
|
||||
#include "VulkanAsyncHandles.h"
|
||||
#include "VulkanBuffer.h"
|
||||
@@ -26,6 +25,7 @@
|
||||
#include "VulkanHandles.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "VulkanTexture.h"
|
||||
#include "vulkan/VulkanSamplerCache.h"
|
||||
#include "vulkan/memory/ResourceManager.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
#include "vulkan/utils/Conversion.h"
|
||||
@@ -35,7 +35,6 @@
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -44,8 +43,6 @@
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
using utils::FixedCapacityVector;
|
||||
|
||||
#if defined(__clang__)
|
||||
// Vulkan functions often immediately dereference pointers, so it's fine to pass in a pointer
|
||||
// to a stack-allocated variable.
|
||||
@@ -200,11 +197,9 @@ Dispatcher VulkanDriver::getDispatcher() const noexcept {
|
||||
}
|
||||
|
||||
VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& context,
|
||||
Platform::DriverConfig const& driverConfig) noexcept
|
||||
Platform::DriverConfig const& driverConfig)
|
||||
: mPlatform(platform),
|
||||
mResourceManager(
|
||||
driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck,
|
||||
mResourceManager(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck,
|
||||
driverConfig.disableHeapHandleTags),
|
||||
mAllocator(createAllocator(mPlatform->getInstance(), mPlatform->getPhysicalDevice(),
|
||||
mPlatform->getDevice())),
|
||||
@@ -216,11 +211,15 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
|
||||
mPipelineCache(mPlatform->getDevice()),
|
||||
mStagePool(mAllocator, &mCommands),
|
||||
mFramebufferCache(mPlatform->getDevice()),
|
||||
mYcbcrConversionCache(mPlatform->getDevice()),
|
||||
mSamplerCache(mPlatform->getDevice()),
|
||||
mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
|
||||
mReadPixels(mPlatform->getDevice()),
|
||||
mDescriptorSetManager(mPlatform->getDevice(), &mResourceManager),
|
||||
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager),
|
||||
mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager),
|
||||
mQueryManager(mPlatform->getDevice()),
|
||||
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache,
|
||||
&mDescriptorSetLayoutCache),
|
||||
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
|
||||
mStereoscopicType(driverConfig.stereoscopicType) {
|
||||
|
||||
@@ -251,7 +250,7 @@ VulkanDriver::~VulkanDriver() noexcept = default;
|
||||
|
||||
UTILS_NOINLINE
|
||||
Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& context,
|
||||
Platform::DriverConfig const& driverConfig) noexcept {
|
||||
Platform::DriverConfig const& 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:
|
||||
@@ -313,7 +312,7 @@ void VulkanDriver::terminate() {
|
||||
|
||||
mCurrentSwapChain = {};
|
||||
mDefaultRenderTarget = {};
|
||||
mBoundPipeline = {};
|
||||
mPipelineState = {};
|
||||
|
||||
mQueryManager.terminate();
|
||||
|
||||
@@ -325,11 +324,16 @@ void VulkanDriver::terminate() {
|
||||
|
||||
mCommands.terminate();
|
||||
|
||||
// Must come before samplerCache, ycbcrConversionCache, descriptorSetCache,
|
||||
// descriptorSetLayoutCache
|
||||
mExternalImageManager.terminate();
|
||||
|
||||
mStagePool.terminate();
|
||||
mPipelineCache.terminate();
|
||||
mFramebufferCache.reset();
|
||||
mFramebufferCache.terminate();
|
||||
mSamplerCache.terminate();
|
||||
mDescriptorSetManager.terminate();
|
||||
mDescriptorSetLayoutCache.terminate();
|
||||
mDescriptorSetCache.terminate();
|
||||
mPipelineLayoutCache.terminate();
|
||||
|
||||
// Before terminating ResourceManager, we must make sure all of the resource_ptrs have been unset.
|
||||
@@ -365,7 +369,7 @@ void VulkanDriver::collectGarbage() {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
// Command buffers need to be submitted and completed before other resources can be gc'd.
|
||||
mCommands.gc();
|
||||
mDescriptorSetManager.clearHistory();
|
||||
mDescriptorSetCache.gc();
|
||||
mStagePool.gc();
|
||||
mFramebufferCache.gc();
|
||||
mPipelineCache.gc();
|
||||
@@ -380,6 +384,10 @@ void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
int64_t refreshIntervalNs, uint32_t frameId) {
|
||||
FVK_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
|
||||
// Do nothing.
|
||||
|
||||
if (mAppState.hasExternalSamplers()) {
|
||||
mExternalImageManager.onBeginFrame();
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
|
||||
@@ -408,7 +416,7 @@ void VulkanDriver::updateDescriptorSetBuffer(
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
|
||||
auto buffer = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh);
|
||||
mDescriptorSetManager.updateBuffer(set, binding, buffer, offset, size);
|
||||
mDescriptorSetCache.updateBuffer(set, binding, buffer, offset, size);
|
||||
}
|
||||
|
||||
void VulkanDriver::updateDescriptorSetTexture(
|
||||
@@ -420,8 +428,17 @@ void VulkanDriver::updateDescriptorSetTexture(
|
||||
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
|
||||
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
|
||||
|
||||
VkSampler const vksampler = mSamplerCache.getSampler(params);
|
||||
mDescriptorSetManager.updateSampler(set, binding, texture, vksampler);
|
||||
if (mExternalImageManager.isExternallySampledTexture(texture)) {
|
||||
mExternalImageManager.bindExternallySampledTexture(set, binding, texture, params);
|
||||
mAppState.hasBoundExternalImages = true;
|
||||
} else {
|
||||
VulkanSamplerCache::Params cacheParams = {
|
||||
.sampler = params,
|
||||
};
|
||||
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
|
||||
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
|
||||
mExternalImageManager.clearTextureBinding(set, binding);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanDriver::flush(int) {
|
||||
@@ -441,6 +458,7 @@ void VulkanDriver::finish(int dummy) {
|
||||
void VulkanDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
|
||||
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
|
||||
PrimitiveType pt) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto vb = resource_ptr<VulkanVertexBuffer>::cast(&mResourceManager, vbh);
|
||||
auto ib = resource_ptr<VulkanIndexBuffer>::cast(&mResourceManager, ibh);
|
||||
auto ptr = resource_ptr<VulkanRenderPrimitive>::make(&mResourceManager, rph, pt, vb, ib);
|
||||
@@ -451,12 +469,14 @@ void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
if (!rph) {
|
||||
return;
|
||||
}
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto ptr = resource_ptr<VulkanRenderPrimitive>::cast(&mResourceManager, rph);
|
||||
ptr.dec();
|
||||
}
|
||||
|
||||
void VulkanDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
|
||||
uint8_t attributeCount, AttributeArray attributes) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto vbi = resource_ptr<VulkanVertexBufferInfo>::make(&mResourceManager, vbih, bufferCount,
|
||||
attributeCount, attributes);
|
||||
vbi.inc();
|
||||
@@ -466,12 +486,14 @@ void VulkanDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
|
||||
if (!vbih) {
|
||||
return;
|
||||
}
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto vbi = resource_ptr<VulkanVertexBufferInfo>::cast(&mResourceManager, vbih);
|
||||
vbi.dec();
|
||||
}
|
||||
|
||||
void VulkanDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint32_t vertexCount,
|
||||
Handle<HwVertexBufferInfo> vbih) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto vbi = resource_ptr<VulkanVertexBufferInfo>::cast(&mResourceManager, vbih);
|
||||
auto vb = resource_ptr<VulkanVertexBuffer>::make(&mResourceManager, vbh, mContext, mStagePool,
|
||||
vertexCount, vbi);
|
||||
@@ -482,12 +504,14 @@ void VulkanDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
|
||||
if (!vbh) {
|
||||
return;
|
||||
}
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto vb = resource_ptr<VulkanVertexBuffer>::cast(&mResourceManager, vbh);
|
||||
vb.dec();
|
||||
}
|
||||
|
||||
void VulkanDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
|
||||
uint32_t indexCount, BufferUsage usage) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto elementSize = (uint8_t) getElementTypeSize(elementType);
|
||||
auto ib = resource_ptr<VulkanIndexBuffer>::make(&mResourceManager, ibh, mAllocator, mStagePool,
|
||||
elementSize, indexCount);
|
||||
@@ -498,12 +522,14 @@ void VulkanDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
|
||||
if (!ibh) {
|
||||
return;
|
||||
}
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto ib = resource_ptr<VulkanIndexBuffer>::cast(&mResourceManager, ibh);
|
||||
ib.dec();
|
||||
}
|
||||
|
||||
void VulkanDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto bo = resource_ptr<VulkanBufferObject>::make(&mResourceManager, boh, mAllocator, mStagePool,
|
||||
byteCount, bindingType);
|
||||
bo.inc();
|
||||
@@ -513,6 +539,7 @@ void VulkanDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
|
||||
if (!boh) {
|
||||
return;
|
||||
}
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto bo = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh);
|
||||
bo.dec();
|
||||
}
|
||||
@@ -554,41 +581,68 @@ void VulkanDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwText
|
||||
texture.inc();
|
||||
}
|
||||
|
||||
void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th,
|
||||
backend::SamplerType target, backend::TextureFormat format,
|
||||
uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
Platform::ExternalImageHandleRef externalImage) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto metadata = mPlatform->extractExternalImageMetadata(externalImage);
|
||||
|
||||
// FIXME: implement createTextureExternalImage2R
|
||||
assert_invariant(width == metadata.width);
|
||||
assert_invariant(height == metadata.height);
|
||||
|
||||
// We do not check the format since AHB could return both a known format and an external format.
|
||||
// In which case, we choose one or the other, but this choice is not known to the client.
|
||||
// Therefore the following lines are commented out.
|
||||
// assert_invariant(format == metadata.filamentFormat);
|
||||
// assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
|
||||
|
||||
auto imgData = mPlatform->createVkImageFromExternal(externalImage);
|
||||
|
||||
assert_invariant(imgData.internal.valid() || imgData.external.valid());
|
||||
|
||||
VkFormat vkformat = metadata.format;
|
||||
VkImage vkimage = VK_NULL_HANDLE;
|
||||
VkDeviceMemory memory = VK_NULL_HANDLE;
|
||||
if (imgData.internal.valid()) {
|
||||
metadata.externalFormat = 0;
|
||||
vkimage = imgData.internal.image;
|
||||
memory = imgData.internal.memory;
|
||||
} else { // imgData.external.valid()
|
||||
vkformat = VK_FORMAT_UNDEFINED;
|
||||
vkimage = imgData.external.image;
|
||||
memory = imgData.external.memory;
|
||||
}
|
||||
|
||||
VkSamplerYcbcrConversion const conversion =
|
||||
mExternalImageManager.getVkSamplerYcbcrConversion(metadata);
|
||||
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mContext,
|
||||
mPlatform->getDevice(), mAllocator, &mResourceManager, &mCommands, vkimage, memory,
|
||||
vkformat, conversion, metadata.samples, metadata.width, metadata.height,
|
||||
metadata.layers, usage, mStagePool);
|
||||
auto& commands = mCommands.get();
|
||||
// Unlike uploaded textures or swapchains, we need to explicit transition this
|
||||
// texture into the read layout.
|
||||
texture->transitionLayout(&commands, texture->getPrimaryViewRange(), VulkanLayout::READ_ONLY);
|
||||
|
||||
if (imgData.external.valid()) {
|
||||
mExternalImageManager.addExternallySampledTexture(texture, externalImage);
|
||||
}
|
||||
|
||||
texture.inc();
|
||||
}
|
||||
|
||||
void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void* externalImage) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
|
||||
const auto& metadata = mPlatform->getExternalImageMetadata(externalImage);
|
||||
if (metadata.isProtected) {
|
||||
usage |= backend::TextureUsage::PROTECTED;
|
||||
}
|
||||
|
||||
assert_invariant(width == metadata.width);
|
||||
assert_invariant(height == metadata.height);
|
||||
assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
|
||||
|
||||
const auto& data = mPlatform->createExternalImage(externalImage, metadata);
|
||||
|
||||
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
|
||||
mAllocator, &mResourceManager, &mCommands, data.first, data.second, metadata.format,
|
||||
1, metadata.width, metadata.height, /*depth=*/1, usage, mStagePool);
|
||||
|
||||
texture.inc();
|
||||
assert_invariant(false && "Not supported in Vulkan backend");
|
||||
// not supported in this backend
|
||||
}
|
||||
|
||||
void VulkanDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void* image, uint32_t plane) {
|
||||
assert_invariant(false && "Not supported in Vulkan backend");
|
||||
}
|
||||
|
||||
void VulkanDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
|
||||
@@ -596,6 +650,7 @@ void VulkanDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
|
||||
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
|
||||
TextureUsage usage) {
|
||||
// not supported in this backend
|
||||
assert_invariant(false && "Not supported in Vulkan backend");
|
||||
}
|
||||
|
||||
void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
|
||||
@@ -604,6 +659,8 @@ void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
|
||||
}
|
||||
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
|
||||
texture.dec();
|
||||
|
||||
mExternalImageManager.removeExternallySampledTexture(texture);
|
||||
}
|
||||
|
||||
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
|
||||
@@ -720,6 +777,7 @@ void VulkanDriver::createFenceR(Handle<HwFence> fh, int) {
|
||||
}
|
||||
|
||||
void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
// Running gc() to guard against an edge case where the old swapchains need to have been
|
||||
// destroyed before the new swapchain can be created. Otherwise, we would fail
|
||||
// vkCreateSwapchainKHR with VK_ERROR_NATIVE_WINDOW_IN_USE_KHR.
|
||||
@@ -761,8 +819,8 @@ void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
|
||||
|
||||
void VulkanDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
|
||||
backend::DescriptorSetLayout&& info) {
|
||||
auto layout = resource_ptr<VulkanDescriptorSetLayout>::make(&mResourceManager, dslh, info);
|
||||
mDescriptorSetManager.initVkLayout(layout);
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto layout = mDescriptorSetLayoutCache.createLayout(dslh, std::move(info));
|
||||
layout.inc();
|
||||
}
|
||||
|
||||
@@ -771,8 +829,12 @@ void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout =
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
|
||||
auto set = mDescriptorSetManager.createSet(dsh, layout);
|
||||
auto set = mDescriptorSetCache.createSet(dsh, layout);
|
||||
set.inc();
|
||||
|
||||
if (layout->hasExternalSamplers()) {
|
||||
mAppState.hasExternalSamplerLayouts = true;
|
||||
}
|
||||
}
|
||||
|
||||
Handle<HwVertexBufferInfo> VulkanDriver::createVertexBufferInfoS() noexcept {
|
||||
@@ -897,6 +959,10 @@ void VulkanDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> dslh
|
||||
void VulkanDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
|
||||
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
|
||||
set.dec();
|
||||
|
||||
if (mAppState.hasExternalSamplers() && set->getExternalSamplerVkSet() != VK_NULL_HANDLE) {
|
||||
mExternalImageManager.removeDescriptorSet(set);
|
||||
}
|
||||
}
|
||||
|
||||
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream) {
|
||||
@@ -1043,7 +1109,7 @@ bool VulkanDriver::isDepthStencilBlitSupported(TextureFormat format) {
|
||||
formats.end();
|
||||
}
|
||||
|
||||
bool VulkanDriver::isProtectedTexturesSupported() { return false; }
|
||||
bool VulkanDriver::isProtectedTexturesSupported() { return isProtectedContentSupported(); }
|
||||
|
||||
bool VulkanDriver::isDepthClampSupported() {
|
||||
return mContext.isDepthClampSupported();
|
||||
@@ -1120,14 +1186,28 @@ math::float2 VulkanDriver::getClipSpaceParams() {
|
||||
}
|
||||
|
||||
uint8_t VulkanDriver::getMaxDrawBuffers() {
|
||||
return MRT::MIN_SUPPORTED_RENDER_TARGET_COUNT; // TODO: query real value
|
||||
return mContext.getPhysicalDeviceLimits().maxColorAttachments;
|
||||
}
|
||||
|
||||
size_t VulkanDriver::getMaxUniformBufferSize() {
|
||||
// TODO: return the actual size instead of hardcoded value
|
||||
// TODO: devices that return less than 32768 should be rejected. This represents only 3%
|
||||
// of android devices.
|
||||
return 32768;
|
||||
return mContext.getPhysicalDeviceLimits().maxUniformBufferRange;
|
||||
}
|
||||
|
||||
size_t VulkanDriver::getMaxTextureSize(SamplerType type) {
|
||||
switch (type) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
return mContext.getPhysicalDeviceLimits().maxImageDimension2D;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
return mContext.getPhysicalDeviceLimits().maxImageDimension3D;
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
return mContext.getPhysicalDeviceLimits().maxImageDimensionCube;
|
||||
default:
|
||||
return mContext.getPhysicalDeviceLimits().maxImageDimension1D;
|
||||
}
|
||||
}
|
||||
|
||||
size_t VulkanDriver::getMaxArrayTextureLayers() {
|
||||
return mContext.getPhysicalDeviceLimits().maxImageArrayLayers;
|
||||
}
|
||||
|
||||
void VulkanDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index,
|
||||
@@ -1266,10 +1346,9 @@ void VulkanDriver::generateMipmaps(Handle<HwTexture> th) {
|
||||
mBlitter.blit(VK_FILTER_LINEAR, dst, dstOffsets, src, srcOffsets);
|
||||
}
|
||||
|
||||
level++;
|
||||
srcw = dstw;
|
||||
srch = dsth;
|
||||
} while ((srcw > 1 || srch > 1) && level < t->levels);
|
||||
} while ((srcw > 1 || srch > 1) && ++level < t->levels - 1);
|
||||
}
|
||||
|
||||
void VulkanDriver::compilePrograms(CompilerPriorityQueue priority,
|
||||
@@ -1444,7 +1523,6 @@ void VulkanDriver::endRenderPass(int) {
|
||||
// pipeline barrier between framebuffer writes and shader reads.
|
||||
rt->emitBarriersEndRenderPass(*mCurrentRenderPass.commandBuffer);
|
||||
|
||||
mRenderPassFboInfo = {};
|
||||
mCurrentRenderPass.renderTarget = {};
|
||||
mCurrentRenderPass.renderPass = VK_NULL_HANDLE;
|
||||
|
||||
@@ -1467,7 +1545,7 @@ void VulkanDriver::nextSubpass(int) {
|
||||
|
||||
if (mCurrentRenderPass.params.subpassMask & 0x1) {
|
||||
VulkanAttachment& subpassInput = renderTarget->getColor0();
|
||||
mDescriptorSetManager.updateInputAttachment({}, subpassInput);
|
||||
mDescriptorSetCache.updateInputAttachment({}, subpassInput);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1485,7 +1563,7 @@ void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
|
||||
swapChain->acquire(resized);
|
||||
|
||||
if (resized) {
|
||||
mFramebufferCache.reset();
|
||||
mFramebufferCache.resetFramebuffers();
|
||||
}
|
||||
|
||||
if (UTILS_LIKELY(mDefaultRenderTarget)) {
|
||||
@@ -1504,10 +1582,10 @@ void VulkanDriver::commit(Handle<HwSwapChain> sch) {
|
||||
|
||||
void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
backend::PushConstantVariant value) {
|
||||
assert_invariant(mBoundPipeline.program && "Expect a program when writing to push constants");
|
||||
assert_invariant(mPipelineState.program && "Expect a program when writing to push constants");
|
||||
assert_invariant(mCurrentRenderPass.commandBuffer && "Should be called within a renderpass");
|
||||
mBoundPipeline.program->writePushConstant(mCurrentRenderPass.commandBuffer->buffer(),
|
||||
mBoundPipeline.pipelineLayout, stage, index, value);
|
||||
mPipelineState.program->writePushConstant(mCurrentRenderPass.commandBuffer->buffer(),
|
||||
mPipelineState.pipelineLayout, stage, index, value);
|
||||
}
|
||||
|
||||
void VulkanDriver::insertEventMarker(char const* string) {
|
||||
@@ -1686,16 +1764,69 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
}
|
||||
|
||||
void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
// This resets all of the pipeline states; the most relevant (needing reset) is .bindInDraw.
|
||||
mPipelineState = {};
|
||||
|
||||
auto& setLayouts = pipelineState.pipelineLayout.setLayout;
|
||||
DescriptorSetLayoutHandleList layoutHandles;
|
||||
uint8_t layoutCount = 0;
|
||||
std::transform(setLayouts.begin(), setLayouts.end(), layoutHandles.begin(),
|
||||
[&](auto const& handle) -> resource_ptr<VulkanDescriptorSetLayout> {
|
||||
if (!handle) {
|
||||
return {};
|
||||
}
|
||||
layoutCount++;
|
||||
return resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, handle);
|
||||
});
|
||||
|
||||
constexpr uint8_t descriptorSetMaskTable[4] = {0x1, 0x3, 0x7, 0xF};
|
||||
fvkutils::DescriptorSetMask const descriptorSetMask =
|
||||
fvkutils::DescriptorSetMask(descriptorSetMaskTable[layoutCount]);
|
||||
|
||||
if (mAppState.hasExternalSamplers()) {
|
||||
auto const haveExternalSamplers = [&](auto layoutHandle) {
|
||||
if (!layoutHandle) {
|
||||
return false;
|
||||
}
|
||||
return layoutHandle->hasExternalSamplers();
|
||||
};
|
||||
if (std::any_of(layoutHandles.begin(), layoutHandles.end(), haveExternalSamplers)) {
|
||||
BindInDrawBundle bundle = {
|
||||
.pipelineState = pipelineState,
|
||||
.dsLayoutHandles = layoutHandles,
|
||||
.descriptorSetMask = descriptorSetMask,
|
||||
};
|
||||
mPipelineState.bindInDraw = { true, bundle };
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// The normal, non-external sampler path
|
||||
using VkDescriptorSetLayoutArray = VulkanPipelineLayoutCache::DescriptorSetLayoutArray;
|
||||
VkDescriptorSetLayoutArray vkLayouts;
|
||||
std::transform(layoutHandles.begin(), layoutHandles.end(), vkLayouts.begin(),
|
||||
[](auto const& layout) -> VkDescriptorSetLayout {
|
||||
if (!layout) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
return layout->getVkLayout();
|
||||
});
|
||||
auto program = resource_ptr<VulkanProgram>::cast(&mResourceManager, pipelineState.program);
|
||||
auto pipelineLayout = mPipelineLayoutCache.getLayout(vkLayouts, program);
|
||||
bindPipelineImpl(pipelineState, pipelineLayout, descriptorSetMask);
|
||||
}
|
||||
|
||||
void VulkanDriver::bindPipelineImpl(PipelineState const& pipelineState,
|
||||
VkPipelineLayout pipelineLayout, fvkutils::DescriptorSetMask descriptorSetMask) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
auto commands = mCurrentRenderPass.commandBuffer;
|
||||
auto vbi = resource_ptr<VulkanVertexBufferInfo>::cast(&mResourceManager,
|
||||
pipelineState.vertexBufferInfo);
|
||||
|
||||
Handle<HwProgram> programHandle = pipelineState.program;
|
||||
RasterState const& rasterState = pipelineState.rasterState;
|
||||
PolygonOffset const& depthOffset = pipelineState.polygonOffset;
|
||||
|
||||
auto program = resource_ptr<VulkanProgram>::cast(&mResourceManager, programHandle);
|
||||
auto program = resource_ptr<VulkanProgram>::cast(&mResourceManager, pipelineState.program);
|
||||
commands->acquire(program);
|
||||
|
||||
// Update the VK raster state.
|
||||
@@ -1737,28 +1868,11 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
mPipelineCache.bindPrimitiveTopology(topology);
|
||||
mPipelineCache.bindVertexArray(attribDesc, bufferDesc, vbi->getAttributeCount());
|
||||
|
||||
auto& setLayouts = pipelineState.pipelineLayout.setLayout;
|
||||
VulkanDescriptorSetLayout::DescriptorSetLayoutArray layoutList;
|
||||
uint8_t layoutCount = 0;
|
||||
std::transform(setLayouts.begin(), setLayouts.end(), layoutList.begin(),
|
||||
[&](Handle<HwDescriptorSetLayout> handle) -> VkDescriptorSetLayout {
|
||||
if (!handle) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
auto layout =
|
||||
resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, handle);
|
||||
layoutCount++;
|
||||
return layout->getVkLayout();
|
||||
});
|
||||
auto pipelineLayout = mPipelineLayoutCache.getLayout(layoutList, program);
|
||||
|
||||
constexpr uint8_t descriptorSetMaskTable[4] = {0x1, 0x3, 0x7, 0xF};
|
||||
|
||||
mBoundPipeline = {
|
||||
.program = program,
|
||||
.pipelineLayout = pipelineLayout,
|
||||
.descriptorSetMask = fvkutils::DescriptorSetMask(descriptorSetMaskTable[layoutCount]),
|
||||
};
|
||||
// Note that we cannot reinit mPipeline because the .bindInDraw metadata that needs to carry
|
||||
// over even on bind.
|
||||
mPipelineState.program = program;
|
||||
mPipelineState.pipelineLayout = pipelineLayout;
|
||||
mPipelineState.descriptorSetMask = descriptorSetMask;
|
||||
|
||||
mPipelineCache.bindLayout(pipelineLayout);
|
||||
mPipelineCache.bindPipeline(mCurrentRenderPass.commandBuffer);
|
||||
@@ -1794,24 +1908,50 @@ void VulkanDriver::bindDescriptorSet(
|
||||
backend::DescriptorSetOffsetArray&& offsets) {
|
||||
if (dsh) {
|
||||
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
|
||||
mDescriptorSetManager.bind(setIndex, set, std::move(offsets));
|
||||
mDescriptorSetCache.bind(setIndex, set, std::move(offsets));
|
||||
} else {
|
||||
mDescriptorSetManager.unbind(setIndex);
|
||||
mDescriptorSetCache.unbind(setIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer();
|
||||
auto const& [doBindInDraw, bundle] = mPipelineState.bindInDraw;
|
||||
|
||||
mDescriptorSetManager.commit(mCurrentRenderPass.commandBuffer,
|
||||
mBoundPipeline.pipelineLayout,
|
||||
mBoundPipeline.descriptorSetMask);
|
||||
fvkutils::DescriptorSetMask setsWithExternalSamplers = {};
|
||||
if (doBindInDraw) {
|
||||
auto& layoutHandles = bundle.dsLayoutHandles;
|
||||
setsWithExternalSamplers = mExternalImageManager.prepareBindSets(layoutHandles,
|
||||
mDescriptorSetCache.getBoundSets());
|
||||
|
||||
VulkanDescriptorSetLayout::DescriptorSetLayoutArray vklayouts;
|
||||
for (size_t i = 0; i < layoutHandles.size(); i++) {
|
||||
if (!layoutHandles[i]) {
|
||||
vklayouts[i] = VK_NULL_HANDLE;
|
||||
continue;
|
||||
}
|
||||
if (setsWithExternalSamplers[i]) {
|
||||
vklayouts[i] = layoutHandles[i]->getExternalSamplerVkLayout();
|
||||
} else {
|
||||
vklayouts[i] = layoutHandles[i]->getVkLayout();
|
||||
}
|
||||
}
|
||||
auto program =
|
||||
resource_ptr<VulkanProgram>::cast(&mResourceManager, bundle.pipelineState.program);
|
||||
VkPipelineLayout const pipelineLayout = mPipelineLayoutCache.getLayout(vklayouts, program);
|
||||
if (pipelineLayout != mPipelineState.pipelineLayout) {
|
||||
bindPipelineImpl(bundle.pipelineState, pipelineLayout, bundle.descriptorSetMask);
|
||||
}
|
||||
mPipelineState.bindInDraw.first = false;
|
||||
}
|
||||
mDescriptorSetCache.commit(mCurrentRenderPass.commandBuffer, mPipelineState.pipelineLayout,
|
||||
setsWithExternalSamplers, mPipelineState.descriptorSetMask);
|
||||
|
||||
// Finally, make the actual draw call. TODO: support subranges
|
||||
const uint32_t firstIndex = indexOffset;
|
||||
const int32_t vertexOffset = 0;
|
||||
const uint32_t firstInstId = 0;
|
||||
uint32_t const firstIndex = indexOffset;
|
||||
constexpr int32_t vertexOffset = 0;
|
||||
constexpr uint32_t firstInstId = 0;
|
||||
|
||||
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
|
||||
}
|
||||
|
||||
@@ -27,8 +27,11 @@
|
||||
#include "VulkanSamplerCache.h"
|
||||
#include "VulkanStagePool.h"
|
||||
#include "VulkanQueryManager.h"
|
||||
#include "vulkan/caching/VulkanDescriptorSetManager.h"
|
||||
#include "vulkan/caching/VulkanPipelineLayoutCache.h"
|
||||
#include "VulkanYcbcrConversionCache.h"
|
||||
#include "vulkan/VulkanDescriptorSetCache.h"
|
||||
#include "vulkan/VulkanDescriptorSetLayoutCache.h"
|
||||
#include "vulkan/VulkanExternalImageManager.h"
|
||||
#include "vulkan/VulkanPipelineLayoutCache.h"
|
||||
#include "vulkan/memory/ResourceManager.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
#include "vulkan/utils/Definitions.h"
|
||||
@@ -52,7 +55,7 @@ constexpr uint8_t MAX_RENDERTARGET_ATTACHMENT_TEXTURES =
|
||||
class VulkanDriver final : public DriverBase {
|
||||
public:
|
||||
static Driver* create(VulkanPlatform* platform, VulkanContext const& context,
|
||||
Platform::DriverConfig const& driverConfig) noexcept;
|
||||
Platform::DriverConfig const& driverConfig);
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
// Encapsulates the VK_EXT_debug_utils extension. In particular, we use
|
||||
@@ -87,8 +90,8 @@ private:
|
||||
void debugCommandBegin(CommandStream* cmds, bool synchronous,
|
||||
const char* methodName) noexcept override;
|
||||
|
||||
inline VulkanDriver(VulkanPlatform* platform, VulkanContext const& context,
|
||||
Platform::DriverConfig const& driverConfig) noexcept;
|
||||
VulkanDriver(VulkanPlatform* platform, VulkanContext const& context,
|
||||
Platform::DriverConfig const& driverConfig);
|
||||
|
||||
~VulkanDriver() noexcept override;
|
||||
|
||||
@@ -117,6 +120,8 @@ private:
|
||||
|
||||
private:
|
||||
void collectGarbage();
|
||||
void bindPipelineImpl(PipelineState const& pipelineState, VkPipelineLayout pipelineLayout,
|
||||
fvkutils::DescriptorSetMask descriptorSetMask);
|
||||
|
||||
VulkanPlatform* mPlatform = nullptr;
|
||||
fvkmemory::ResourceManager mResourceManager;
|
||||
@@ -134,26 +139,46 @@ private:
|
||||
VulkanPipelineCache mPipelineCache;
|
||||
VulkanStagePool mStagePool;
|
||||
VulkanFboCache mFramebufferCache;
|
||||
VulkanYcbcrConversionCache mYcbcrConversionCache;
|
||||
VulkanSamplerCache mSamplerCache;
|
||||
VulkanBlitter mBlitter;
|
||||
VulkanReadPixels mReadPixels;
|
||||
VulkanDescriptorSetManager mDescriptorSetManager;
|
||||
VulkanDescriptorSetLayoutCache mDescriptorSetLayoutCache;
|
||||
VulkanDescriptorSetCache mDescriptorSetCache;
|
||||
VulkanQueryManager mQueryManager;
|
||||
VulkanExternalImageManager mExternalImageManager;
|
||||
|
||||
// This is necessary for us to write to push constants after binding a pipeline.
|
||||
struct {
|
||||
resource_ptr<VulkanProgram> program;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
fvkutils::DescriptorSetMask descriptorSetMask;
|
||||
} mBoundPipeline = {};
|
||||
using DescriptorSetLayoutHandleList = std::array<resource_ptr<VulkanDescriptorSetLayout>,
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
|
||||
struct BindInDrawBundle {
|
||||
PipelineState pipelineState = {};
|
||||
DescriptorSetLayoutHandleList dsLayoutHandles = {};
|
||||
fvkutils::DescriptorSetMask descriptorSetMask = {};
|
||||
resource_ptr<VulkanProgram> program = {};
|
||||
};
|
||||
|
||||
// We need to store information about a render pass to enable better barriers at the end of a
|
||||
// renderpass.
|
||||
struct {
|
||||
using AttachmentArray =
|
||||
fvkutils::StaticVector<VulkanAttachment, MAX_RENDERTARGET_ATTACHMENT_TEXTURES>;
|
||||
AttachmentArray attachments;
|
||||
} mRenderPassFboInfo = {};
|
||||
// For push constant
|
||||
resource_ptr<VulkanProgram> program = {};
|
||||
// For push commiting dynamic ubos in draw()
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
fvkutils::DescriptorSetMask descriptorSetMask = {};
|
||||
|
||||
std::pair<bool, BindInDrawBundle> bindInDraw = {false, {}};
|
||||
} mPipelineState = {};
|
||||
|
||||
struct {
|
||||
// This tracks whether the app has seen external samplers bound to a the descriptor set.
|
||||
// This will force bindPipeline to take a slow path.
|
||||
bool hasExternalSamplerLayouts = false;
|
||||
bool hasBoundExternalImages = false;
|
||||
|
||||
bool hasExternalSamplers() const noexcept {
|
||||
return hasExternalSamplerLayouts && hasBoundExternalImages;
|
||||
}
|
||||
} mAppState;
|
||||
|
||||
bool const mIsSRGBSwapChainSupported;
|
||||
backend::StereoscopicType const mStereoscopicType;
|
||||
|
||||
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "VulkanExternalImageManager.h"
|
||||
|
||||
#include "VulkanDescriptorSetCache.h"
|
||||
#include "VulkanDescriptorSetLayoutCache.h"
|
||||
#include "VulkanSamplerCache.h"
|
||||
#include "VulkanYcbcrConversionCache.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
#include "vulkan/utils/Conversion.h"
|
||||
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
using Bitmask = fvkutils::UniformBufferBitmask;
|
||||
static_assert(sizeof(Bitmask) * 8 == fvkutils::MAX_DESCRIPTOR_SET_BITMASK_BITS);
|
||||
|
||||
template<typename T>
|
||||
void erasep(std::vector<T>& v, std::function<bool(T const&)> f) {
|
||||
auto newEnd = std::remove_if(v.begin(), v.end(), f);
|
||||
v.erase(newEnd, v.end());
|
||||
}
|
||||
|
||||
using ImageData = VulkanExternalImageManager::VulkanExternalImageManager::ImageData;
|
||||
ImageData& findImage(std::vector<ImageData>& images,
|
||||
fvkmemory::resource_ptr<VulkanTexture> texture) {
|
||||
auto itr = std::find_if(images.begin(), images.end(), [&](ImageData const& data) {
|
||||
return data.image == texture;
|
||||
});
|
||||
assert_invariant(itr != images.end());
|
||||
return *itr;
|
||||
}
|
||||
|
||||
void copySet(VkDevice device, VkDescriptorSet srcSet, VkDescriptorSet dstSet, Bitmask bindings) {
|
||||
// TODO: fix the size for better memory management
|
||||
std::vector<VkCopyDescriptorSet> copies;
|
||||
bindings.forEachSetBit([&](size_t index) {
|
||||
copies.push_back({
|
||||
.sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET,
|
||||
.srcSet = srcSet,
|
||||
.srcBinding = (uint32_t) index,
|
||||
.dstSet = dstSet,
|
||||
.dstBinding = (uint32_t) index,
|
||||
.descriptorCount = 1,
|
||||
});
|
||||
});
|
||||
vkUpdateDescriptorSets(device, 0, nullptr, copies.size(), copies.data());
|
||||
}
|
||||
|
||||
Bitmask foldBitsInHalf(Bitmask bitset) {
|
||||
Bitmask outBitset;
|
||||
bitset.forEachSetBit([&](size_t index) {
|
||||
constexpr size_t BITMASK_LOWER_BITS_LEN = sizeof(outBitset) * 4;
|
||||
outBitset.set(index % BITMASK_LOWER_BITS_LEN);
|
||||
});
|
||||
return outBitset;
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
VulkanExternalImageManager::VulkanExternalImageManager(VulkanPlatform* platform,
|
||||
VulkanSamplerCache* samplerCache, VulkanYcbcrConversionCache* ycbcrConversionCache,
|
||||
VulkanDescriptorSetCache* setCache, VulkanDescriptorSetLayoutCache* layoutCache)
|
||||
: mPlatform(platform),
|
||||
mSamplerCache(samplerCache),
|
||||
mYcbcrConversionCache(ycbcrConversionCache),
|
||||
mDescriptorSetCache(setCache),
|
||||
mDescriptorSetLayoutCache(layoutCache) {
|
||||
}
|
||||
|
||||
VulkanExternalImageManager::~VulkanExternalImageManager() = default;
|
||||
|
||||
void VulkanExternalImageManager::terminate() {
|
||||
mSetBindings.clear();
|
||||
mImages.clear();
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::onBeginFrame() {
|
||||
std::for_each(mImages.begin(), mImages.end(), [](ImageData& image) {
|
||||
image.hasBeenValidated = false;
|
||||
});
|
||||
std::for_each(mSetBindings.begin(), mSetBindings.end(), [](SetBindingInfo& info) {
|
||||
info.bound = false;
|
||||
});
|
||||
}
|
||||
|
||||
fvkutils::DescriptorSetMask VulkanExternalImageManager::prepareBindSets(LayoutArray const& layouts,
|
||||
SetArray const& sets) {
|
||||
fvkutils::DescriptorSetMask shouldUseExternalSampler{};
|
||||
for (uint8_t i = 0; i < sets.size(); i++) {
|
||||
auto set = sets[i];
|
||||
auto layout = layouts[i];
|
||||
if (!set || !layout) {
|
||||
continue;
|
||||
}
|
||||
if (hasExternalSampler(set)) {
|
||||
updateSetAndLayout(set, layout);
|
||||
shouldUseExternalSampler.set(i);
|
||||
}
|
||||
}
|
||||
return shouldUseExternalSampler;
|
||||
}
|
||||
|
||||
bool VulkanExternalImageManager::hasExternalSampler(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set) {
|
||||
auto itr = std::find_if(mSetBindings.begin(), mSetBindings.end(),
|
||||
[&](SetBindingInfo const& info) { return info.set == set; });
|
||||
return itr != mSetBindings.end();
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::updateSetAndLayout(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
|
||||
utils::FixedCapacityVector<
|
||||
std::tuple<uint8_t, VkSampler, fvkmemory::resource_ptr<VulkanTexture>>>
|
||||
samplerAndBindings;
|
||||
samplerAndBindings.reserve(MAX_SAMPLER_COUNT);
|
||||
|
||||
fvkutils::SamplerBitmask actualExternalSamplers;
|
||||
for (auto& bindingInfo : mSetBindings) {
|
||||
if (bindingInfo.set != set || bindingInfo.bound) {
|
||||
continue;
|
||||
}
|
||||
auto& imageData = findImage(mImages, bindingInfo.image);
|
||||
updateImage(&imageData);
|
||||
|
||||
auto samplerParams = bindingInfo.samplerParams;
|
||||
// according to spec, these must match chromaFilter
|
||||
// https://registry.khronos.org/vulkan/specs/latest/man/html/VkSamplerCreateInfo.html#VUID-VkSamplerCreateInfo-minFilter-01645
|
||||
samplerParams.filterMag = SamplerMagFilter::NEAREST;
|
||||
samplerParams.filterMin = SamplerMinFilter::NEAREST;
|
||||
|
||||
auto sampler = mSamplerCache->getSampler({
|
||||
.sampler = samplerParams,
|
||||
.conversion = imageData.conversion,
|
||||
});
|
||||
actualExternalSamplers.set(bindingInfo.binding);
|
||||
samplerAndBindings.push_back({ bindingInfo.binding, sampler, bindingInfo.image });
|
||||
bindingInfo.bound = true;
|
||||
}
|
||||
|
||||
if (samplerAndBindings.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort by binding number
|
||||
std::sort(samplerAndBindings.begin(), samplerAndBindings.end(), [](auto const& a, auto const& b) {
|
||||
return std::get<0>(a) < std::get<0>(b);
|
||||
});
|
||||
|
||||
utils::FixedCapacityVector<VkSampler> outSamplers;
|
||||
outSamplers.reserve(MAX_SAMPLER_COUNT);
|
||||
std::for_each(samplerAndBindings.begin(), samplerAndBindings.end(),
|
||||
[&](auto const& b) { outSamplers.push_back(std::get<1>(b)); });
|
||||
|
||||
VkDescriptorSetLayout const oldLayout = layout->getExternalSamplerVkLayout();
|
||||
VkDescriptorSetLayout const newLayout = mDescriptorSetLayoutCache->getVkLayout(layout->bitmask,
|
||||
actualExternalSamplers, outSamplers);
|
||||
|
||||
// Need to copy the set
|
||||
VkDescriptorSet const oldSet = set->getExternalSamplerVkSet();
|
||||
if (oldLayout != newLayout || oldSet == VK_NULL_HANDLE) {
|
||||
// Build a new descriptor set from the new layout
|
||||
VkDescriptorSet const newSet = mDescriptorSetCache->getVkSet(layout->count, newLayout);
|
||||
auto const ubo = layout->bitmask.ubo | layout->bitmask.dynamicUbo;
|
||||
auto const samplers = layout->bitmask.sampler & (~actualExternalSamplers);
|
||||
|
||||
// Each bitmask denotes a binding index, and separated into two stages - vertex and buffer
|
||||
// We fold the two stages into just the lower half of the bits to denote a combined set of
|
||||
// bindings.
|
||||
Bitmask const copyBindings = foldBitsInHalf(ubo | samplers);
|
||||
VkDescriptorSet const srcSet = oldSet != VK_NULL_HANDLE ? oldSet : set->getVkSet();
|
||||
copySet(mPlatform->getDevice(), srcSet, newSet, copyBindings);
|
||||
|
||||
set->setExternalSamplerVkSet(newSet, [&](VulkanDescriptorSet*) {
|
||||
mDescriptorSetCache->manualRecycle(layout->count, newLayout, newSet);
|
||||
});
|
||||
if (oldLayout != newLayout) {
|
||||
layout->setExternalSamplerVkLayout(newLayout);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the external samplers in the set
|
||||
for (auto& [binding, sampler, image]: samplerAndBindings) {
|
||||
mDescriptorSetCache->updateSamplerForExternalSamplerSet(set, binding, image);
|
||||
}
|
||||
}
|
||||
|
||||
VkSamplerYcbcrConversion VulkanExternalImageManager::getVkSamplerYcbcrConversion(
|
||||
VulkanPlatform::ExternalImageMetadata const& metadata) {
|
||||
// This external image does not require external sampler (YUV conversion).
|
||||
if (metadata.externalFormat == 0 && !fvkutils::isVKYcbcrConversionFormat(metadata.format)) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
VulkanYcbcrConversionCache::Params ycbcrParams = {
|
||||
.conversion = {
|
||||
.ycbcrModel = fvkutils::getYcbcrModelConversionFilament(metadata.ycbcrModel),
|
||||
.r = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.r, 0),
|
||||
.g = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.g, 1),
|
||||
.b = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.b, 2),
|
||||
.a = fvkutils::getSwizzleFilament(metadata.ycbcrConversionComponents.a, 3),
|
||||
.ycbcrRange = fvkutils::getYcbcrRangeFilament(metadata.ycbcrRange),
|
||||
.xChromaOffset = fvkutils::getChromaLocationFilament(metadata.xChromaOffset),
|
||||
.yChromaOffset = fvkutils::getChromaLocationFilament(metadata.yChromaOffset),
|
||||
|
||||
// Unclear where to get the chromaFilter, we just assume it's nearest.
|
||||
.chromaFilter = SamplerMagFilter::NEAREST,
|
||||
},
|
||||
.format = metadata.format,
|
||||
.externalFormat = metadata.externalFormat,
|
||||
};
|
||||
return mYcbcrConversionCache->getConversion(ycbcrParams);
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::updateImage(ImageData* image) {
|
||||
if (image->hasBeenValidated) {
|
||||
return;
|
||||
}
|
||||
image->hasBeenValidated = true;
|
||||
|
||||
auto metadata = mPlatform->extractExternalImageMetadata(image->platformHandle);
|
||||
auto vkYcbcr = getVkSamplerYcbcrConversion(metadata);
|
||||
if (vkYcbcr == image->conversion) {
|
||||
return;
|
||||
}
|
||||
|
||||
image->image->setYcbcrConversion(vkYcbcr);
|
||||
image->conversion = vkYcbcr;
|
||||
return;
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::removeDescriptorSet(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> inSet) {
|
||||
erasep<SetBindingInfo>(mSetBindings,
|
||||
[&](auto const& bindingInfo) { return (bindingInfo.set == inSet); });
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::bindExternallySampledTexture(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t bindingPoint,
|
||||
fvkmemory::resource_ptr<VulkanTexture> image, SamplerParams samplerParams) {
|
||||
// Should we do duplicate validation here?
|
||||
auto& imageData = findImage(mImages, image);
|
||||
mSetBindings.push_back({ bindingPoint, imageData.image, set, samplerParams });
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::addExternallySampledTexture(
|
||||
fvkmemory::resource_ptr<VulkanTexture> image,
|
||||
Platform::ExternalImageHandleRef platformHandleRef) {
|
||||
mImages.push_back({ image, platformHandleRef, false });
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::removeExternallySampledTexture(
|
||||
fvkmemory::resource_ptr<VulkanTexture> image) {
|
||||
erasep<SetBindingInfo>(mSetBindings,
|
||||
[&](auto const& bindingInfo) { return (bindingInfo.image == image); });
|
||||
erasep<ImageData>(mImages, [&](auto const& imageData) {
|
||||
return imageData.image == image;
|
||||
});
|
||||
}
|
||||
|
||||
bool VulkanExternalImageManager::isExternallySampledTexture(
|
||||
fvkmemory::resource_ptr<VulkanTexture> image) const {
|
||||
return std::find_if(mImages.begin(), mImages.end(), [&](auto const& imageData) {
|
||||
return imageData.image == image;
|
||||
}) != mImages.end();
|
||||
}
|
||||
|
||||
void VulkanExternalImageManager::clearTextureBinding(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t bindingPoint) {
|
||||
erasep<SetBindingInfo>(mSetBindings, [&](auto const& bindingInfo) {
|
||||
return (bindingInfo.set == set && bindingInfo.binding == bindingPoint);
|
||||
});
|
||||
}
|
||||
|
||||
} // namesapce filament::backend
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
|
||||
#define TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
|
||||
|
||||
#include "VulkanHandles.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class VulkanYcbcrConversionCache;
|
||||
class VulkanSamplerCache;
|
||||
class VulkanDescriptorSetLayoutCache;
|
||||
class VulkanDescriptorSetCache;
|
||||
|
||||
// Manages the logic of external images and their quirks wrt Vulikan.
|
||||
class VulkanExternalImageManager {
|
||||
public:
|
||||
|
||||
VulkanExternalImageManager(
|
||||
VulkanPlatform* platform,
|
||||
VulkanSamplerCache* samplerCache,
|
||||
VulkanYcbcrConversionCache* ycbcrConversionCache,
|
||||
VulkanDescriptorSetCache* setCache,
|
||||
VulkanDescriptorSetLayoutCache* layoutCache);
|
||||
|
||||
~VulkanExternalImageManager();
|
||||
|
||||
void terminate();
|
||||
|
||||
void onBeginFrame();
|
||||
|
||||
using SetArray = std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>,
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
|
||||
using LayoutArray = std::array<fvkmemory::resource_ptr<VulkanDescriptorSetLayout>,
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
|
||||
using VkLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
|
||||
|
||||
// Returns bitmask to indicate whether or not to use the external sampler version of each
|
||||
// descriptor set.
|
||||
fvkutils::DescriptorSetMask prepareBindSets(LayoutArray const& layouts, SetArray const& sets);
|
||||
|
||||
void removeDescriptorSet(fvkmemory::resource_ptr<VulkanDescriptorSet> set);
|
||||
|
||||
void bindExternallySampledTexture(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
uint8_t bindingPoint, fvkmemory::resource_ptr<VulkanTexture> image,
|
||||
SamplerParams samplerParams);
|
||||
|
||||
void clearTextureBinding(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
uint8_t bindingPoint);
|
||||
|
||||
void addExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> external,
|
||||
Platform::ExternalImageHandleRef platformHandleRef);
|
||||
|
||||
void removeExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> image);
|
||||
|
||||
bool isExternallySampledTexture(fvkmemory::resource_ptr<VulkanTexture> image) const;
|
||||
|
||||
VkSamplerYcbcrConversion getVkSamplerYcbcrConversion(
|
||||
VulkanPlatform::ExternalImageMetadata const& metadata);
|
||||
|
||||
struct ImageData {
|
||||
fvkmemory::resource_ptr<VulkanTexture> image;
|
||||
Platform::ExternalImageHandle platformHandle;
|
||||
bool hasBeenValidated = false; // indicates whether the image has been validated *this frame*
|
||||
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
private:
|
||||
bool hasExternalSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set);
|
||||
|
||||
void updateSetAndLayout(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
||||
|
||||
void updateImage(ImageData* imageData);
|
||||
|
||||
VulkanPlatform* mPlatform;
|
||||
VulkanSamplerCache* mSamplerCache;
|
||||
VulkanYcbcrConversionCache* mYcbcrConversionCache;
|
||||
VulkanDescriptorSetCache* mDescriptorSetCache;
|
||||
VulkanDescriptorSetLayoutCache* mDescriptorSetLayoutCache;
|
||||
|
||||
using SetAndLayout = std::pair<fvkmemory::resource_ptr<VulkanDescriptorSet>,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>>;
|
||||
|
||||
struct SetBindingInfo {
|
||||
uint8_t binding = 0;
|
||||
fvkmemory::resource_ptr<VulkanTexture> image;
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set;
|
||||
SamplerParams samplerParams;
|
||||
bool bound = false;
|
||||
};
|
||||
|
||||
// Use vectors instead of hash maps because we only expect small number of entries.
|
||||
std::vector<SetBindingInfo> mSetBindings;
|
||||
std::vector<ImageData> mImages;
|
||||
};
|
||||
|
||||
} // filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_CACHING_VULKANEXTERNALIMAGEMANAGER_H
|
||||
@@ -330,25 +330,39 @@ VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept
|
||||
mRenderPassCache[config] = {renderPass, mCurrentTime};
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_FBO_CACHE)
|
||||
FVK_LOGD << "Created render pass " << renderPass << " with "
|
||||
<< "samples = " << int(config.samples) << ", "
|
||||
<< "depth = " << (hasDepth ? 1 : 0) << ", "
|
||||
<< "colorAttachmentCount[0] = " << subpasses[0].colorAttachmentCount
|
||||
<< utils::io::endl;
|
||||
#endif
|
||||
FVK_LOGD << "Created render pass " << renderPass << " with ";
|
||||
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; ++i) {
|
||||
FVK_LOGD << (int) config.colorFormat[i] << " ";
|
||||
}
|
||||
FVK_LOGD << ", "
|
||||
<< "depth = " << config.depthFormat << ", "
|
||||
<< "initialDepthLayout = " << (int) config.initialDepthLayout << ", "
|
||||
<< "samples = " << int(config.samples) << ", "
|
||||
<< "needsResolveMask = " << int(config.needsResolveMask) << ", "
|
||||
<< "usesLazilyAllocatedMemory = " << int(config.usesLazilyAllocatedMemory) << ", "
|
||||
<< "viewCount = " << int(config.viewCount) << ", "
|
||||
<< "colorAttachmentCount[0] = " << subpasses[0].colorAttachmentCount
|
||||
<< utils::io::endl;
|
||||
#endif
|
||||
|
||||
return renderPass;
|
||||
}
|
||||
|
||||
void VulkanFboCache::reset() noexcept {
|
||||
for (auto pair : mFramebufferCache) {
|
||||
void VulkanFboCache::resetFramebuffers() noexcept {
|
||||
for (const auto& pair: mFramebufferCache) {
|
||||
mRenderPassRefCount[pair.first.renderPass]--;
|
||||
vkDestroyFramebuffer(mDevice, pair.second.handle, VKALLOC);
|
||||
}
|
||||
mFramebufferCache.clear();
|
||||
for (auto pair : mRenderPassCache) {
|
||||
}
|
||||
|
||||
void VulkanFboCache::terminate() noexcept {
|
||||
resetFramebuffers();
|
||||
|
||||
for (const auto& pair: mRenderPassCache) {
|
||||
vkDestroyRenderPass(mDevice, pair.second.handle, VKALLOC);
|
||||
}
|
||||
mRenderPassRefCount.clear();
|
||||
mRenderPassCache.clear();
|
||||
}
|
||||
|
||||
|
||||
@@ -106,8 +106,11 @@ public:
|
||||
// Evicts old unused Vulkan objects. Call this once per frame.
|
||||
void gc() noexcept;
|
||||
|
||||
// Frees all Framebuffer objects. Call this every time a the swapchain is resized
|
||||
void resetFramebuffers() noexcept;
|
||||
|
||||
// Frees all Vulkan objects. Call this during shutdown before the device is destroyed.
|
||||
void reset() noexcept;
|
||||
void terminate() noexcept;
|
||||
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
|
||||
#include <utils/compiler.h> // UTILS_FALLTHROUGH
|
||||
#include <utils/Panic.h> // ASSERT_POSTCONDITION
|
||||
|
||||
using namespace bluevk;
|
||||
@@ -89,8 +90,9 @@ BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) {
|
||||
}
|
||||
break;
|
||||
}
|
||||
// TODO: properly handle external sampler
|
||||
case DescriptorType::SAMPLER_EXTERNAL:
|
||||
fromStageFlags(binding.stageFlags, binding.binding, mask.externalSampler);
|
||||
UTILS_FALLTHROUGH;
|
||||
case DescriptorType::SAMPLER: {
|
||||
fromStageFlags(binding.stageFlags, binding.binding, mask.sampler);
|
||||
break;
|
||||
@@ -147,11 +149,18 @@ void VulkanDescriptorSet::acquire(fvkmemory::resource_ptr<VulkanBufferObject> ob
|
||||
mResources.push_back(obj);
|
||||
}
|
||||
|
||||
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout const& layout)
|
||||
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout&& layout,
|
||||
VkDescriptorSetLayout vkLayout)
|
||||
: bitmask(fromBackendLayout(layout)),
|
||||
count(Count::fromLayoutBitmask(bitmask)) {}
|
||||
count(Count::fromLayoutBitmask(bitmask)),
|
||||
mVkLayout(vkLayout) {}
|
||||
|
||||
PushConstantDescription::PushConstantDescription(backend::Program const& program) noexcept {
|
||||
VulkanDescriptorSetLayout::Bitmask VulkanDescriptorSetLayout::Bitmask::fromLayoutDescription(
|
||||
DescriptorSetLayout const& layout) {
|
||||
return fromBackendLayout(layout);
|
||||
}
|
||||
|
||||
PushConstantDescription::PushConstantDescription(backend::Program const& program) {
|
||||
mRangeCount = 0;
|
||||
for (auto stage : { ShaderStage::VERTEX, ShaderStage::FRAGMENT, ShaderStage::COMPUTE }) {
|
||||
auto const& constants = program.getPushConstants(stage);
|
||||
|
||||
@@ -66,18 +66,23 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
|
||||
|
||||
// The bitmask representation of a set layout.
|
||||
struct Bitmask {
|
||||
// TODO: better utiltize the space below and use bitset instead.
|
||||
fvkutils::UniformBufferBitmask ubo; // 8 bytes
|
||||
fvkutils::UniformBufferBitmask dynamicUbo; // 8 bytes
|
||||
fvkutils::SamplerBitmask sampler; // 8 bytes
|
||||
fvkutils::InputAttachmentBitmask inputAttachment; // 8 bytes
|
||||
|
||||
// This is a subset of the sampler field.
|
||||
fvkutils::SamplerBitmask externalSampler; // 8 bytes
|
||||
|
||||
bool operator==(Bitmask const& right) const {
|
||||
return ubo == right.ubo && dynamicUbo == right.dynamicUbo && sampler == right.sampler &&
|
||||
inputAttachment == right.inputAttachment;
|
||||
inputAttachment == right.inputAttachment &&
|
||||
externalSampler == right.externalSampler;
|
||||
}
|
||||
|
||||
static Bitmask fromLayoutDescription(DescriptorSetLayout const& layout);
|
||||
};
|
||||
static_assert(sizeof(Bitmask) == 32);
|
||||
static_assert(sizeof(Bitmask) == 40);
|
||||
|
||||
// This is a convenience struct to quickly check layout compatibility in terms of descriptor set
|
||||
// pools.
|
||||
@@ -117,18 +122,32 @@ struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Reso
|
||||
}
|
||||
};
|
||||
|
||||
VulkanDescriptorSetLayout(DescriptorSetLayout const& layout);
|
||||
VulkanDescriptorSetLayout(DescriptorSetLayout&& layout, VkDescriptorSetLayout vkLayout);
|
||||
|
||||
// Note that we don't destroy the vklayout. This is done by the layout cache.
|
||||
~VulkanDescriptorSetLayout() = default;
|
||||
|
||||
VkDescriptorSetLayout getVkLayout() const { return mVkLayout; }
|
||||
void setVkLayout(VkDescriptorSetLayout vklayout) { mVkLayout = vklayout; }
|
||||
VkDescriptorSetLayout getVkLayout() const noexcept { return mVkLayout; }
|
||||
|
||||
VkDescriptorSetLayout getExternalSamplerVkLayout() const noexcept {
|
||||
return mExternalSamplerVkLayout;
|
||||
}
|
||||
|
||||
void setExternalSamplerVkLayout(VkDescriptorSetLayout vklayout) noexcept {
|
||||
mExternalSamplerVkLayout = vklayout;
|
||||
}
|
||||
|
||||
bool hasExternalSamplers() const noexcept { return bitmask.externalSampler.count() > 0; }
|
||||
|
||||
Bitmask const bitmask;
|
||||
Count const count;
|
||||
|
||||
private:
|
||||
VkDescriptorSetLayout mVkLayout = VK_NULL_HANDLE;
|
||||
// This is the layout without any immutable samplers.
|
||||
VkDescriptorSetLayout const mVkLayout = VK_NULL_HANDLE;
|
||||
|
||||
// This is the layout with immutable samplers, and can be updated.
|
||||
VkDescriptorSetLayout mExternalSamplerVkLayout = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
struct VulkanDescriptorSet : public HwDescriptorSet, fvkmemory::Resource {
|
||||
@@ -137,19 +156,39 @@ public:
|
||||
// can use to repackage the vk handle.
|
||||
using OnRecycle = std::function<void(VulkanDescriptorSet*)>;
|
||||
|
||||
VulkanDescriptorSet(VkDescriptorSet rawSet,
|
||||
VulkanDescriptorSet(
|
||||
fvkutils::UniformBufferBitmask const& dynamicUboMask,
|
||||
uint8_t uniqueDynamicUboCount,
|
||||
OnRecycle&& onRecycleFn)
|
||||
: vkSet(rawSet),
|
||||
dynamicUboMask(dynamicUboMask),
|
||||
OnRecycle&& onRecycleFn, VkDescriptorSet vkSet)
|
||||
: dynamicUboMask(dynamicUboMask),
|
||||
uniqueDynamicUboCount(uniqueDynamicUboCount),
|
||||
mVkSet(vkSet),
|
||||
mOnRecycleFn(std::move(onRecycleFn)) {}
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-exception-escape)
|
||||
~VulkanDescriptorSet() {
|
||||
if (mOnRecycleFn) {
|
||||
mOnRecycleFn(this);
|
||||
}
|
||||
if (mOnRecycleExternalSamplerFn) {
|
||||
mOnRecycleExternalSamplerFn(this);
|
||||
}
|
||||
}
|
||||
|
||||
VkDescriptorSet getVkSet() const noexcept {
|
||||
return mVkSet;
|
||||
}
|
||||
|
||||
VkDescriptorSet getExternalSamplerVkSet() const noexcept {
|
||||
return mExternalSamplerVkSet;
|
||||
}
|
||||
|
||||
void setExternalSamplerVkSet(VkDescriptorSet vkset, OnRecycle onRecycle) {
|
||||
mExternalSamplerVkSet = vkset;
|
||||
if (mOnRecycleExternalSamplerFn) {
|
||||
mOnRecycleExternalSamplerFn(this);
|
||||
}
|
||||
mOnRecycleExternalSamplerFn = onRecycle;
|
||||
}
|
||||
|
||||
void setOffsets(backend::DescriptorSetOffsetArray&& offsets) noexcept {
|
||||
@@ -163,21 +202,24 @@ public:
|
||||
void acquire(fvkmemory::resource_ptr<VulkanTexture> texture);
|
||||
void acquire(fvkmemory::resource_ptr<VulkanBufferObject> buffer);
|
||||
|
||||
VkDescriptorSet const vkSet;
|
||||
fvkutils::UniformBufferBitmask const dynamicUboMask;
|
||||
uint8_t const uniqueDynamicUboCount;
|
||||
|
||||
private:
|
||||
VkDescriptorSet const mVkSet;
|
||||
VkDescriptorSet mExternalSamplerVkSet = VK_NULL_HANDLE;
|
||||
|
||||
backend::DescriptorSetOffsetArray mOffsets;
|
||||
std::vector<fvkmemory::resource_ptr<fvkmemory::Resource>> mResources;
|
||||
OnRecycle mOnRecycleFn;
|
||||
OnRecycle mOnRecycleExternalSamplerFn;
|
||||
};
|
||||
|
||||
using PushConstantNameArray = utils::FixedCapacityVector<char const*>;
|
||||
using PushConstantNameByStage = std::array<PushConstantNameArray, Program::SHADER_TYPE_COUNT>;
|
||||
|
||||
struct PushConstantDescription {
|
||||
explicit PushConstantDescription(backend::Program const& program) noexcept;
|
||||
explicit PushConstantDescription(backend::Program const& program);
|
||||
|
||||
VkPushConstantRange const* getVkRanges() const noexcept { return mRanges; }
|
||||
uint32_t getVkRangeCount() const noexcept { return mRangeCount; }
|
||||
|
||||
@@ -15,15 +15,12 @@
|
||||
*/
|
||||
|
||||
#include "VulkanPipelineCache.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "caching/VulkanDescriptorSetManager.h"
|
||||
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanHandles.h"
|
||||
#include "VulkanTexture.h"
|
||||
#include "vulkan/utils/Conversion.h"
|
||||
|
||||
#if defined(__clang__)
|
||||
@@ -38,7 +35,12 @@ using namespace bluevk;
|
||||
namespace filament::backend {
|
||||
|
||||
VulkanPipelineCache::VulkanPipelineCache(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
: mDevice(device) {
|
||||
VkPipelineCacheCreateInfo createInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
|
||||
};
|
||||
bluevk::vkCreatePipelineCache(mDevice, &createInfo, VKALLOC, &mPipelineCache);
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::bindLayout(VkPipelineLayout layout) noexcept {
|
||||
mPipelineRequirements.layout = layout;
|
||||
@@ -64,8 +66,12 @@ void VulkanPipelineCache::bindPipeline(VulkanCommandBuffer* commands) {
|
||||
// If an error occurred, allow higher levels to handle it gracefully.
|
||||
assert_invariant(cacheEntry != nullptr && "Failed to create/find pipeline");
|
||||
|
||||
mBoundPipeline = mPipelineRequirements;
|
||||
vkCmdBindPipeline(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, cacheEntry->handle);
|
||||
|
||||
static PipelineEqual equal;
|
||||
if (!equal(mBoundPipeline, mPipelineRequirements)) {
|
||||
mBoundPipeline = mPipelineRequirements;
|
||||
vkCmdBindPipeline(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, cacheEntry->handle);
|
||||
}
|
||||
}
|
||||
|
||||
VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() noexcept {
|
||||
@@ -218,7 +224,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
|
||||
PipelineCacheEntry cacheEntry = {
|
||||
.lastUsed = mCurrentTime,
|
||||
};
|
||||
VkResult error = vkCreateGraphicsPipelines(mDevice, VK_NULL_HANDLE, 1, &pipelineCreateInfo,
|
||||
VkResult error = vkCreateGraphicsPipelines(mDevice, mPipelineCache, 1, &pipelineCreateInfo,
|
||||
VKALLOC, &cacheEntry.handle);
|
||||
assert_invariant(error == VK_SUCCESS);
|
||||
if (error != VK_SUCCESS) {
|
||||
@@ -274,6 +280,8 @@ void VulkanPipelineCache::terminate() noexcept {
|
||||
}
|
||||
mPipelines.clear();
|
||||
mBoundPipeline = {};
|
||||
|
||||
vkDestroyPipelineCache(mDevice, mPipelineCache, VKALLOC);
|
||||
}
|
||||
|
||||
void VulkanPipelineCache::gc() noexcept {
|
||||
|
||||
@@ -18,24 +18,18 @@
|
||||
#define TNT_FILAMENT_BACKEND_VULKANPIPELINECACHE_H
|
||||
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanMemory.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include "backend/Program.h"
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
#include <utils/bitset.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Hash.h>
|
||||
|
||||
#include <list>
|
||||
#include <tsl/robin_map.h>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -204,6 +198,10 @@ private:
|
||||
// Immutable state.
|
||||
VkDevice mDevice = VK_NULL_HANDLE;
|
||||
|
||||
// Vuklan Driver pipeline cache handle. In the cases a pipeline has been evicted by the `gc`,
|
||||
// recreating the same pipeline is cheaper, helping with frame stalling.
|
||||
VkPipelineCache mPipelineCache = VK_NULL_HANDLE;
|
||||
|
||||
// Current requirements for the pipeline layout, pipeline, and descriptor sets.
|
||||
PipelineKey mPipelineRequirements = {};
|
||||
|
||||
|
||||
+2
-1
@@ -17,7 +17,8 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H
|
||||
|
||||
#include <vulkan/VulkanHandles.h>
|
||||
#include "VulkanHandles.h"
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
#include <utils/Hash.h>
|
||||
@@ -335,7 +335,7 @@ void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget
|
||||
mTaskHandler->post(std::move(waitFenceFunc), std::move(cleanPbdFunc));
|
||||
}
|
||||
|
||||
void VulkanReadPixels::runUntilComplete() noexcept {
|
||||
void VulkanReadPixels::runUntilComplete() {
|
||||
if (!mTaskHandler) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ public:
|
||||
OnReadCompleteFunction const& readCompleteFunc);
|
||||
|
||||
// This method will block until all of the in-flight requests are complete.
|
||||
void runUntilComplete() noexcept;
|
||||
void runUntilComplete();
|
||||
|
||||
private:
|
||||
VkDevice mDevice = VK_NULL_HANDLE;
|
||||
|
||||
@@ -14,7 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "vulkan/VulkanSamplerCache.h"
|
||||
#include "VulkanSamplerCache.h"
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "vulkan/utils/Conversion.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
@@ -23,117 +25,51 @@ using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
constexpr inline VkSamplerAddressMode getWrapMode(SamplerWrapMode mode) noexcept {
|
||||
switch (mode) {
|
||||
case SamplerWrapMode::REPEAT:
|
||||
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||
case SamplerWrapMode::CLAMP_TO_EDGE:
|
||||
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
case SamplerWrapMode::MIRRORED_REPEAT:
|
||||
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline VkFilter getFilter(SamplerMinFilter filter) noexcept {
|
||||
switch (filter) {
|
||||
case SamplerMinFilter::NEAREST:
|
||||
return VK_FILTER_NEAREST;
|
||||
case SamplerMinFilter::LINEAR:
|
||||
return VK_FILTER_LINEAR;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
|
||||
return VK_FILTER_NEAREST;
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
|
||||
return VK_FILTER_LINEAR;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
|
||||
return VK_FILTER_NEAREST;
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
|
||||
return VK_FILTER_LINEAR;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline VkFilter getFilter(SamplerMagFilter filter) noexcept {
|
||||
switch (filter) {
|
||||
case SamplerMagFilter::NEAREST:
|
||||
return VK_FILTER_NEAREST;
|
||||
case SamplerMagFilter::LINEAR:
|
||||
return VK_FILTER_LINEAR;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline VkSamplerMipmapMode getMipmapMode(SamplerMinFilter filter) noexcept {
|
||||
switch (filter) {
|
||||
case SamplerMinFilter::NEAREST:
|
||||
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
case SamplerMinFilter::LINEAR:
|
||||
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
|
||||
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
|
||||
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
|
||||
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
|
||||
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline float getMaxLod(SamplerMinFilter filter) noexcept {
|
||||
switch (filter) {
|
||||
case SamplerMinFilter::NEAREST:
|
||||
case SamplerMinFilter::LINEAR:
|
||||
// The Vulkan spec recommends a max LOD of 0.25 to "disable" mipmapping.
|
||||
// See "Mapping of OpenGL to Vulkan filter modes" in the VK Spec.
|
||||
return 0.25f;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
|
||||
return VK_LOD_CLAMP_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr inline VkBool32 getCompareEnable(SamplerCompareMode mode) noexcept {
|
||||
return mode == SamplerCompareMode::NONE ? VK_FALSE : VK_TRUE;
|
||||
}
|
||||
|
||||
VulkanSamplerCache::VulkanSamplerCache(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
|
||||
VkSampler VulkanSamplerCache::getSampler(SamplerParams params) noexcept {
|
||||
VkSampler VulkanSamplerCache::getSampler(Params params) {
|
||||
auto iter = mCache.find(params);
|
||||
if (UTILS_LIKELY(iter != mCache.end())) {
|
||||
return iter->second;
|
||||
}
|
||||
VkSamplerCreateInfo samplerInfo {
|
||||
VkSamplerYcbcrConversionInfo ycbcrConversion = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
||||
.conversion = params.conversion,
|
||||
};
|
||||
|
||||
auto const& samplerParams = params.sampler;
|
||||
VkSamplerCreateInfo samplerInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.magFilter = getFilter(params.filterMag),
|
||||
.minFilter = getFilter(params.filterMin),
|
||||
.mipmapMode = getMipmapMode(params.filterMin),
|
||||
.addressModeU = getWrapMode(params.wrapS),
|
||||
.addressModeV = getWrapMode(params.wrapT),
|
||||
.addressModeW = getWrapMode(params.wrapR),
|
||||
.anisotropyEnable = params.anisotropyLog2 == 0 ? 0u : 1u,
|
||||
.maxAnisotropy = (float)(1u << params.anisotropyLog2),
|
||||
.compareEnable = getCompareEnable(params.compareMode),
|
||||
.compareOp = fvkutils::getCompareOp(params.compareFunc),
|
||||
.pNext = params.conversion != VK_NULL_HANDLE ? &ycbcrConversion : VK_NULL_HANDLE,
|
||||
.magFilter = fvkutils::getFilter(samplerParams.filterMag),
|
||||
.minFilter = fvkutils::getFilter(samplerParams.filterMin),
|
||||
.mipmapMode = fvkutils::getMipmapMode(samplerParams.filterMin),
|
||||
.addressModeU = fvkutils::getWrapMode(samplerParams.wrapS),
|
||||
.addressModeV = fvkutils::getWrapMode(samplerParams.wrapT),
|
||||
.addressModeW = fvkutils::getWrapMode(samplerParams.wrapR),
|
||||
.anisotropyEnable = samplerParams.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
|
||||
.maxAnisotropy = (float) (1u << samplerParams.anisotropyLog2),
|
||||
.compareEnable = fvkutils::getCompareEnable(samplerParams.compareMode),
|
||||
.compareOp = fvkutils::getCompareOp(samplerParams.compareFunc),
|
||||
.minLod = 0.0f,
|
||||
.maxLod = getMaxLod(params.filterMin),
|
||||
.maxLod = fvkutils::getMaxLod(samplerParams.filterMin),
|
||||
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
|
||||
.unnormalizedCoordinates = VK_FALSE
|
||||
.unnormalizedCoordinates = VK_FALSE,
|
||||
};
|
||||
VkSampler sampler;
|
||||
VkResult result = vkCreateSampler(mDevice, &samplerInfo, VKALLOC, &sampler);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create sampler."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
mCache.insert({params, sampler});
|
||||
mCache.insert({ params, sampler });
|
||||
return sampler;
|
||||
}
|
||||
|
||||
void VulkanSamplerCache::terminate() noexcept {
|
||||
for (auto pair : mCache) {
|
||||
for (auto pair: mCache) {
|
||||
vkDestroySampler(mDevice, pair.second, VKALLOC);
|
||||
}
|
||||
mCache.clear();
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
}// namespace filament::backend
|
||||
|
||||
@@ -17,8 +17,11 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANSAMPLERCACHE_H
|
||||
|
||||
#include "VulkanContext.h"
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/Hash.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
namespace filament::backend {
|
||||
@@ -26,12 +29,28 @@ namespace filament::backend {
|
||||
// Simple manager for VkSampler objects.
|
||||
class VulkanSamplerCache {
|
||||
public:
|
||||
struct Params {
|
||||
SamplerParams sampler = {};
|
||||
uint32_t padding = 0;
|
||||
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Params) == 16);
|
||||
|
||||
explicit VulkanSamplerCache(VkDevice device);
|
||||
VkSampler getSampler(SamplerParams params) noexcept;
|
||||
VkSampler getSampler(Params params);
|
||||
void terminate() noexcept;
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
tsl::robin_map<SamplerParams, VkSampler, SamplerParams::Hasher, SamplerParams::EqualTo> mCache;
|
||||
|
||||
struct SamplerEqualTo {
|
||||
bool operator()(Params lhs, Params rhs) const noexcept {
|
||||
SamplerParams::EqualTo equal;
|
||||
return equal(lhs.sampler, rhs.sampler) && lhs.conversion == rhs.conversion;
|
||||
}
|
||||
};
|
||||
using SamplerHashFn = utils::hash::MurmurHashFn<Params>;
|
||||
tsl::robin_map<Params, VkSampler, SamplerHashFn, SamplerEqualTo> mCache;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
static constexpr uint32_t TIME_BEFORE_EVICTION = FVK_MAX_COMMAND_BUFFERS;
|
||||
static constexpr uint32_t TIME_BEFORE_EVICTION = 3;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -39,7 +39,7 @@ VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
|
||||
auto stage = iter->second;
|
||||
mFreeStages.erase(iter);
|
||||
stage->lastAccessed = mCurrentFrame;
|
||||
mUsedStages.insert(stage);
|
||||
mUsedStages.push_back(stage);
|
||||
return stage;
|
||||
}
|
||||
// We were not able to find a sufficiently large stage, so create a new one.
|
||||
@@ -51,7 +51,7 @@ VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
|
||||
});
|
||||
|
||||
// Create the VkBuffer.
|
||||
mUsedStages.insert(stage);
|
||||
mUsedStages.push_back(stage);
|
||||
VkBufferCreateInfo bufferInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
|
||||
.size = numBytes,
|
||||
@@ -77,7 +77,7 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi
|
||||
if (image->format == vkformat && image->width == width && image->height == height) {
|
||||
mFreeImages.erase(image);
|
||||
image->lastAccessed = mCurrentFrame;
|
||||
mUsedImages.insert(image);
|
||||
mUsedImages.push_back(image);
|
||||
return image;
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi
|
||||
.lastAccessed = mCurrentFrame,
|
||||
});
|
||||
|
||||
mUsedImages.insert(image);
|
||||
mUsedImages.push_back(image);
|
||||
|
||||
const VkImageCreateInfo imageInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
@@ -161,7 +161,7 @@ void VulkanStagePool::gc() noexcept {
|
||||
stage->lastAccessed = mCurrentFrame;
|
||||
mFreeStages.insert(std::make_pair(stage->capacity, stage));
|
||||
} else {
|
||||
mUsedStages.insert(stage);
|
||||
mUsedStages.push_back(stage);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ void VulkanStagePool::gc() noexcept {
|
||||
image->lastAccessed = mCurrentFrame;
|
||||
mFreeImages.insert(image);
|
||||
} else {
|
||||
mUsedImages.insert(image);
|
||||
mUsedImages.push_back(image);
|
||||
}
|
||||
}
|
||||
FVK_SYSTRACE_END();
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -73,10 +74,10 @@ private:
|
||||
std::multimap<uint32_t, VulkanStage const*> mFreeStages;
|
||||
|
||||
// Simple unordered set for stashing a list of in-use stages that can be reclaimed later.
|
||||
std::unordered_set<VulkanStage const*> mUsedStages;
|
||||
std::vector<VulkanStage const*> mUsedStages;
|
||||
|
||||
std::unordered_set<VulkanStageImage const*> mFreeImages;
|
||||
std::unordered_set<VulkanStageImage const*> mUsedImages;
|
||||
std::vector<VulkanStageImage const*> mUsedImages;
|
||||
|
||||
// Store the current "time" (really just a frame count) and LRU eviction parameters.
|
||||
uint64_t mCurrentFrame = 0;
|
||||
|
||||
@@ -32,6 +32,7 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
|
||||
VulkanCommands* commands, VulkanStagePool& stagePool, void* nativeWindow, uint64_t flags,
|
||||
VkExtent2D extent)
|
||||
: mPlatform(platform),
|
||||
mContext(context),
|
||||
mResourceManager(resourceManager),
|
||||
mCommands(commands),
|
||||
mAllocator(allocator),
|
||||
@@ -77,16 +78,16 @@ void VulkanSwapChain::update() {
|
||||
}
|
||||
for (auto const color: bundle.colors) {
|
||||
auto colorTexture = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager,
|
||||
device, mAllocator, mResourceManager, mCommands, color, VK_NULL_HANDLE,
|
||||
bundle.colorFormat, 1, bundle.extent.width, bundle.extent.height, bundle.layerCount, colorUsage,
|
||||
mStagePool);
|
||||
mContext, device, mAllocator, mResourceManager, mCommands, color, VK_NULL_HANDLE,
|
||||
bundle.colorFormat, VK_NULL_HANDLE /*ycrcb */, 1, bundle.extent.width,
|
||||
bundle.extent.height, bundle.layerCount, colorUsage, mStagePool);
|
||||
mColors.push_back(colorTexture);
|
||||
}
|
||||
|
||||
mDepth = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager, device,
|
||||
mAllocator, mResourceManager, mCommands, bundle.depth, VK_NULL_HANDLE,
|
||||
bundle.depthFormat, 1, bundle.extent.width, bundle.extent.height, bundle.layerCount, depthUsage,
|
||||
mStagePool);
|
||||
mDepth = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager, mContext, device,
|
||||
mAllocator, mResourceManager, mCommands, bundle.depth, VK_NULL_HANDLE,
|
||||
bundle.depthFormat, VK_NULL_HANDLE /*ycrcb */, 1, bundle.extent.width,
|
||||
bundle.extent.height, bundle.layerCount, depthUsage, mStagePool);
|
||||
|
||||
mExtent = bundle.extent;
|
||||
mLayerCount = bundle.layerCount;
|
||||
@@ -107,11 +108,6 @@ void VulkanSwapChain::present() {
|
||||
|
||||
mCommands->flush();
|
||||
|
||||
// call the image ready wait function
|
||||
if (mExplicitImageReadyWait != nullptr) {
|
||||
mExplicitImageReadyWait(swapChain);
|
||||
}
|
||||
|
||||
// We only present if it is not headless. No-op for headless.
|
||||
if (!mHeadless) {
|
||||
VkSemaphore const finishedDrawing = mCommands->acquireFinishedSignal();
|
||||
@@ -145,7 +141,6 @@ void VulkanSwapChain::acquire(bool& resized) {
|
||||
VulkanPlatform::ImageSyncData imageSyncData;
|
||||
VkResult const result = mPlatform->acquire(swapChain, &imageSyncData);
|
||||
mCurrentSwapIndex = imageSyncData.imageIndex;
|
||||
mExplicitImageReadyWait = imageSyncData.explicitImageReadyWait;
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
|
||||
<< "Cannot acquire in swapchain. error=" << static_cast<int32_t>(result);
|
||||
if (imageSyncData.imageReadySemaphore != VK_NULL_HANDLE) {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanContext.h"
|
||||
#include "VulkanTexture.h"
|
||||
#include "vulkan/memory/Resource.h"
|
||||
@@ -28,7 +29,6 @@
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
@@ -49,7 +49,9 @@ struct VulkanSwapChain : public HwSwapChain, fvkmemory::Resource {
|
||||
|
||||
void present();
|
||||
|
||||
void acquire(bool& reized);
|
||||
// Acquire a new image from the swapchain. If the image is not available it would wait until it
|
||||
// is.
|
||||
void acquire(bool& resized);
|
||||
|
||||
fvkmemory::resource_ptr<VulkanTexture> getCurrentColor() const noexcept {
|
||||
uint32_t const imageIndex = mCurrentSwapIndex;
|
||||
@@ -83,6 +85,7 @@ private:
|
||||
void update();
|
||||
|
||||
VulkanPlatform* mPlatform;
|
||||
VulkanContext const& mContext;
|
||||
fvkmemory::ResourceManager* mResourceManager;
|
||||
VulkanCommands* mCommands;
|
||||
VmaAllocator mAllocator;
|
||||
@@ -98,7 +101,6 @@ private:
|
||||
VkExtent2D mExtent;
|
||||
uint32_t mLayerCount;
|
||||
uint32_t mCurrentSwapIndex;
|
||||
std::function<void(Platform::SwapChain* handle)> mExplicitImageReadyWait = nullptr;
|
||||
bool mAcquired;
|
||||
bool mIsFirstRenderPass;
|
||||
};
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanContext.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "VulkanTexture.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
@@ -54,64 +55,61 @@ VkComponentMapping composeSwizzle(VkComponentMapping const& prev, VkComponentMap
|
||||
VK_COMPONENT_SWIZZLE_B,
|
||||
VK_COMPONENT_SWIZZLE_A,
|
||||
};
|
||||
|
||||
auto const compose = [](VkComponentSwizzle out, VkComponentMapping const& prev,
|
||||
uint8_t channelIndex) {
|
||||
// We need to first change all identities to its equivalent channel.
|
||||
if (out == VK_COMPONENT_SWIZZLE_IDENTITY) {
|
||||
out = IDENTITY[channelIndex];
|
||||
auto const compose = [](VkComponentMapping const& prev,
|
||||
VkComponentMapping const& next) -> VkComponentMapping {
|
||||
VkComponentSwizzle vals[4] = { next.r, next.g, next.b, next.a };
|
||||
for (auto& out: vals) {
|
||||
switch (out) {
|
||||
case VK_COMPONENT_SWIZZLE_R:
|
||||
out = prev.r;
|
||||
break;
|
||||
case VK_COMPONENT_SWIZZLE_G:
|
||||
out = prev.g;
|
||||
break;
|
||||
case VK_COMPONENT_SWIZZLE_B:
|
||||
out = prev.b;
|
||||
break;
|
||||
case VK_COMPONENT_SWIZZLE_A:
|
||||
out = prev.a;
|
||||
break;
|
||||
// Do not modify the value
|
||||
case VK_COMPONENT_SWIZZLE_IDENTITY:
|
||||
case VK_COMPONENT_SWIZZLE_ZERO:
|
||||
case VK_COMPONENT_SWIZZLE_ONE:
|
||||
// Below is not exposed in Vulkan's API, but needs to be there for compilation.
|
||||
case VK_COMPONENT_SWIZZLE_MAX_ENUM:
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (out) {
|
||||
case VK_COMPONENT_SWIZZLE_R:
|
||||
out = prev.r;
|
||||
break;
|
||||
case VK_COMPONENT_SWIZZLE_G:
|
||||
out = prev.g;
|
||||
break;
|
||||
case VK_COMPONENT_SWIZZLE_B:
|
||||
out = prev.b;
|
||||
break;
|
||||
case VK_COMPONENT_SWIZZLE_A:
|
||||
out = prev.a;
|
||||
break;
|
||||
case VK_COMPONENT_SWIZZLE_IDENTITY:
|
||||
case VK_COMPONENT_SWIZZLE_ZERO:
|
||||
case VK_COMPONENT_SWIZZLE_ONE:
|
||||
return out;
|
||||
// Below is not exposed in Vulkan's API, but needs to be there for compilation.
|
||||
case VK_COMPONENT_SWIZZLE_MAX_ENUM:
|
||||
break;
|
||||
}
|
||||
// If the result correctly corresponds to the identity, just return identity.
|
||||
if (IDENTITY[channelIndex] == out) {
|
||||
return VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
}
|
||||
return out;
|
||||
return { vals[0], vals[1], vals[2], vals[3] };
|
||||
};
|
||||
|
||||
auto const identityToChannel = [](VkComponentSwizzle val, uint8_t channelIndex) {
|
||||
if (val != VK_COMPONENT_SWIZZLE_IDENTITY) {
|
||||
return val;
|
||||
auto const identityToChannel = [](VkComponentMapping const& mapping) -> VkComponentMapping {
|
||||
VkComponentSwizzle vals[4] = { mapping.r, mapping.g, mapping.b, mapping.a };
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
if (vals[i] != VK_COMPONENT_SWIZZLE_IDENTITY) {
|
||||
continue;
|
||||
}
|
||||
vals[i] = IDENTITY[i];
|
||||
}
|
||||
return IDENTITY[channelIndex];
|
||||
return { vals[0], vals[1], vals[2], vals[3] };
|
||||
};
|
||||
auto const channelToIdentity = [](VkComponentMapping const& mapping) -> VkComponentMapping {
|
||||
VkComponentSwizzle vals[4] = { mapping.r, mapping.g, mapping.b, mapping.a };
|
||||
for (uint8_t i = 0; i < 4; i++) {
|
||||
if (IDENTITY[i] != vals[i]) {
|
||||
continue;
|
||||
}
|
||||
vals[i] = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||
}
|
||||
return { vals[0], vals[1], vals[2], vals[3] };
|
||||
};
|
||||
|
||||
// We make sure all all identities are mapped into respective channels so that actual channel
|
||||
// mapping will be passed onto the output.
|
||||
VkComponentMapping const prevExplicit = {
|
||||
identityToChannel(prev.r, 0),
|
||||
identityToChannel(prev.g, 1),
|
||||
identityToChannel(prev.b, 2),
|
||||
identityToChannel(prev.a, 3),
|
||||
};
|
||||
|
||||
// Note that the channel index corresponds to the VkComponentMapping struct layout.
|
||||
return {
|
||||
compose(next.r, prevExplicit, 0),
|
||||
compose(next.g, prevExplicit, 1),
|
||||
compose(next.b, prevExplicit, 2),
|
||||
compose(next.a, prevExplicit, 3),
|
||||
};
|
||||
VkComponentMapping const prevExplicit = identityToChannel(prev);
|
||||
VkComponentMapping const nextExplicit = identityToChannel(next);
|
||||
return channelToIdentity(compose(prevExplicit, nextExplicit));
|
||||
}
|
||||
|
||||
inline VulkanLayout getDefaultLayoutImpl(TextureUsage usage) {
|
||||
@@ -153,36 +151,150 @@ uint8_t getLayerCountFromDepth(uint32_t const depth) {
|
||||
return getLayerCount(getSamplerTypeFromDepth(depth), depth);
|
||||
}
|
||||
|
||||
VkImageUsageFlags getUsage(VulkanContext const& context, uint8_t samples,
|
||||
VkPhysicalDevice physicalDevice, VkFormat vkFormat, TextureUsage tusage) {
|
||||
VkImageUsageFlags usage = {};
|
||||
if (any(tusage & TextureUsage::BLIT_SRC)) {
|
||||
usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
if (any(tusage & TextureUsage::BLIT_DST)) {
|
||||
usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
|
||||
// Determine if we can use the transient usage flag combined with lazily allocated memory.
|
||||
const bool useTransientAttachment =
|
||||
// Lazily allocated memory is available.
|
||||
context.isLazilyAllocatedMemorySupported() &&
|
||||
// Usage consists of attachment flags only.
|
||||
none(tusage & ~TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Usage contains at least one attachment flag.
|
||||
any(tusage & TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Depth resolve cannot use transient attachment because it uses a custom shader.
|
||||
// TODO: see VulkanDriver::isDepthStencilResolveSupported() to know when to remove this
|
||||
// restriction.
|
||||
// Note that the custom shader does not resolve stencil. We do need to move to vk 1.2
|
||||
// and above to be able to support stencil resolve (along with depth).
|
||||
!(any(tusage & TextureUsage::DEPTH_ATTACHMENT) && samples > 1);
|
||||
|
||||
const VkImageUsageFlags transientFlag =
|
||||
useTransientAttachment ? VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT : 0U;
|
||||
|
||||
if (any(tusage & TextureUsage::SAMPLEABLE)) {
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
|
||||
if (physicalDevice != VK_NULL_HANDLE) {
|
||||
// Validate that the format is actually sampleable.
|
||||
VkFormatProperties props;
|
||||
vkGetPhysicalDeviceFormatProperties(physicalDevice, vkFormat, &props);
|
||||
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
|
||||
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << mState->mVkFormat << " is not "
|
||||
"sampleable with optimal tiling." << utils::io::endl;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
}
|
||||
if (any(tusage & TextureUsage::COLOR_ATTACHMENT)) {
|
||||
usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | transientFlag;
|
||||
if (any(tusage & TextureUsage::SUBPASS_INPUT)) {
|
||||
usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
|
||||
}
|
||||
}
|
||||
if (any(tusage & TextureUsage::STENCIL_ATTACHMENT)) {
|
||||
usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transientFlag;
|
||||
}
|
||||
if (any(tusage & TextureUsage::UPLOADABLE)) {
|
||||
usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
if (any(tusage & TextureUsage::DEPTH_ATTACHMENT)) {
|
||||
usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transientFlag;
|
||||
|
||||
// Depth resolves uses a custom shader and therefore needs to be sampleable.
|
||||
if (samples > 1) {
|
||||
usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
}
|
||||
}
|
||||
return usage;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator,
|
||||
VulkanCommands* commands, VulkanStagePool& stagePool, VkFormat format,
|
||||
VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VulkanLayout defaultLayout,
|
||||
bool isProtected)
|
||||
: mVkFormat(format),
|
||||
mViewType(viewType),
|
||||
mFullViewRange{fvkutils::getImageAspect(format), 0, levels, 0, layerCount},
|
||||
mDefaultLayout(defaultLayout),
|
||||
mIsProtected(isProtected),
|
||||
mStagePool(stagePool),
|
||||
mDevice(device),
|
||||
mAllocator(allocator),
|
||||
VulkanTextureState::VulkanTextureState(VulkanStagePool& stagePool, VulkanCommands* commands,
|
||||
VmaAllocator allocator, VkDevice device, VkImage image, VkDeviceMemory deviceMemory,
|
||||
VkFormat format, VkImageViewType viewType, uint8_t levels, uint8_t layerCount,
|
||||
VkSamplerYcbcrConversion ycbcrConversion, VkImageUsageFlags usage, bool isProtected)
|
||||
: mStagePool(stagePool),
|
||||
mCommands(commands),
|
||||
mIsTransientAttachment(false) {}
|
||||
mAllocator(allocator),
|
||||
mDevice(device),
|
||||
mTextureImage(image),
|
||||
mTextureImageMemory(deviceMemory),
|
||||
mVkFormat(format),
|
||||
mViewType(viewType),
|
||||
mFullViewRange{ fvkutils::getImageAspect(format), 0, levels, 0, layerCount },
|
||||
mYcbcr{ ycbcrConversion },
|
||||
mDefaultLayout(getDefaultLayoutImpl(usage)),
|
||||
mUsage(usage),
|
||||
mIsProtected(isProtected) {}
|
||||
|
||||
// Constructor for internally passed VkImage
|
||||
VulkanTexture::VulkanTexture(VkDevice device, VmaAllocator allocator,
|
||||
VulkanTextureState::~VulkanTextureState() {
|
||||
clearCachedImageViews();
|
||||
if (mTextureImageMemory != VK_NULL_HANDLE) {
|
||||
vkDestroyImage(mDevice, mTextureImage, VKALLOC);
|
||||
vkFreeMemory(mDevice, mTextureImageMemory, VKALLOC);
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanTextureState::clearCachedImageViews() noexcept {
|
||||
for (auto entry: mCachedImageViews) {
|
||||
vkDestroyImageView(mDevice, entry.second, VKALLOC);
|
||||
}
|
||||
mCachedImageViews.clear();
|
||||
}
|
||||
|
||||
VkImageView VulkanTextureState::getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
|
||||
VkComponentMapping swizzle) {
|
||||
ImageViewKey const key{ range, viewType, swizzle };
|
||||
if (auto iter = mCachedImageViews.find(key); iter != mCachedImageViews.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
VkSamplerYcbcrConversionInfo conversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
||||
.conversion = mYcbcr.conversion,
|
||||
};
|
||||
|
||||
VkImageViewCreateInfo viewInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = mYcbcr.conversion != VK_NULL_HANDLE ? &conversionInfo : nullptr,
|
||||
.flags = 0,
|
||||
.image = mTextureImage,
|
||||
.viewType = viewType,
|
||||
.format = mYcbcr.conversion != VK_NULL_HANDLE ? VK_FORMAT_UNDEFINED : mVkFormat,
|
||||
.components = swizzle,
|
||||
.subresourceRange = range,
|
||||
};
|
||||
VkImageView imageView;
|
||||
vkCreateImageView(mDevice, &viewInfo, VKALLOC, &imageView);
|
||||
mCachedImageViews.emplace(key, imageView);
|
||||
return imageView;
|
||||
}
|
||||
|
||||
// Constructor for internally passed VkImage - including swapchain images and external images.
|
||||
VulkanTexture::VulkanTexture(VulkanContext const& context, VkDevice device, VmaAllocator allocator,
|
||||
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image,
|
||||
VkDeviceMemory memory, VkFormat format, uint8_t samples, uint32_t width,
|
||||
uint32_t height, uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool)
|
||||
: HwTexture(getSamplerTypeFromDepth(depth), 1, samples, width, height, depth, TextureFormat::UNUSED,
|
||||
tusage),
|
||||
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, device,
|
||||
allocator, commands, stagePool, format, fvkutils::getViewType(SamplerType::SAMPLER_2D),
|
||||
/*mipLevels=*/1, getLayerCountFromDepth(depth), getDefaultLayoutImpl(tusage), any(usage & TextureUsage::PROTECTED))) {
|
||||
mState->mTextureImage = image;
|
||||
mState->mTextureImageMemory = memory;
|
||||
VkDeviceMemory memory, VkFormat format, VkSamplerYcbcrConversion conversion,
|
||||
uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage tusage,
|
||||
VulkanStagePool& stagePool)
|
||||
: HwTexture(getSamplerTypeFromDepth(depth), 1, samples, width, height, depth,
|
||||
TextureFormat::UNUSED, tusage),
|
||||
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, stagePool,
|
||||
commands, allocator, device, image, memory, format,
|
||||
fvkutils::getViewType(SamplerType::SAMPLER_2D),
|
||||
/*mipLevels=*/1, getLayerCountFromDepth(depth), conversion,
|
||||
getUsage(context, samples, VK_NULL_HANDLE, format, tusage),
|
||||
any(usage & TextureUsage::PROTECTED))) {
|
||||
mPrimaryViewRange = mState->mFullViewRange;
|
||||
}
|
||||
|
||||
@@ -192,21 +304,19 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, SamplerType target,
|
||||
uint8_t levels, TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h,
|
||||
uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool)
|
||||
: HwTexture(target, levels, samples, w, h, depth, tformat, tusage),
|
||||
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, device,
|
||||
allocator, commands, stagePool, fvkutils::getVkFormat(tformat),
|
||||
fvkutils::getViewType(target), levels, getLayerCount(target, depth),
|
||||
VulkanLayout::UNDEFINED, any(usage & TextureUsage::PROTECTED))) {
|
||||
: HwTexture(target, levels, samples, w, h, depth, tformat, tusage) {
|
||||
// Create an appropriately-sized device-only VkImage, but do not fill it yet.
|
||||
VkFormat const vkFormat = fvkutils::getVkFormat(tformat);
|
||||
bool const isProtected = any(tusage & TextureUsage::PROTECTED);
|
||||
VkImageCreateInfo imageInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.imageType = target == SamplerType::SAMPLER_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D,
|
||||
.format = mState->mVkFormat,
|
||||
.format = vkFormat,
|
||||
.extent = {w, h, depth},
|
||||
.mipLevels = levels,
|
||||
.arrayLayers = 1,
|
||||
.tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
.usage = 0,
|
||||
.usage = getUsage(context, samples, physicalDevice, vkFormat, tusage),
|
||||
};
|
||||
if (target == SamplerType::SAMPLER_3D && any(tusage & TextureUsage::ALL_ATTACHMENTS)) {
|
||||
if (context.isImageView2DOn3DImageSupported()) {
|
||||
@@ -236,72 +346,17 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
imageInfo.arrayLayers = depth * 6;
|
||||
imageInfo.extent.depth = 1;
|
||||
}
|
||||
if (any(usage & TextureUsage::PROTECTED)) {
|
||||
if (isProtected) {
|
||||
imageInfo.flags |= VK_IMAGE_CREATE_PROTECTED_BIT;
|
||||
}
|
||||
|
||||
if (any(usage & TextureUsage::BLIT_SRC)) {
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
if (any(usage & TextureUsage::BLIT_DST)) {
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
|
||||
// Determine if we can use the transient usage flag combined with lazily allocated memory.
|
||||
const bool useTransientAttachment =
|
||||
// Lazily allocated memory is available.
|
||||
context.isLazilyAllocatedMemorySupported() &&
|
||||
// Usage consists of attachment flags only.
|
||||
none(tusage & ~TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Usage contains at least one attachment flag.
|
||||
any(tusage & TextureUsage::ALL_ATTACHMENTS);
|
||||
mState->mIsTransientAttachment = useTransientAttachment;
|
||||
|
||||
const VkImageUsageFlags transientFlag =
|
||||
useTransientAttachment ? VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT : 0U;
|
||||
|
||||
if (any(usage & TextureUsage::SAMPLEABLE)) {
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
|
||||
// Validate that the format is actually sampleable.
|
||||
VkFormatProperties props;
|
||||
vkGetPhysicalDeviceFormatProperties(physicalDevice, mState->mVkFormat, &props);
|
||||
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
|
||||
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << mState->mVkFormat << " is not "
|
||||
"sampleable with optimal tiling." << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
}
|
||||
if (any(usage & TextureUsage::COLOR_ATTACHMENT)) {
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | transientFlag;
|
||||
if (any(usage & TextureUsage::SUBPASS_INPUT)) {
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT;
|
||||
}
|
||||
}
|
||||
if (any(usage & TextureUsage::STENCIL_ATTACHMENT)) {
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transientFlag;
|
||||
}
|
||||
if (any(usage & TextureUsage::UPLOADABLE)) {
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
if (any(usage & TextureUsage::DEPTH_ATTACHMENT)) {
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | transientFlag;
|
||||
|
||||
// Depth resolves uses a custom shader and therefore needs to be sampleable.
|
||||
if (samples > 1) {
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
}
|
||||
}
|
||||
|
||||
// Constrain the sample count according to the sample count masks in VkPhysicalDeviceProperties.
|
||||
// Note that VulkanRenderTarget holds a single MSAA count, so we play it safe if this is used as
|
||||
// any kind of attachment (color or depth).
|
||||
const auto& limits = context.getPhysicalDeviceLimits();
|
||||
auto const& limits = context.getPhysicalDeviceLimits();
|
||||
if (imageInfo.usage & VK_IMAGE_USAGE_SAMPLED_BIT) {
|
||||
samples = fvkutils::reduceSampleCount(samples,
|
||||
fvkutils::isVkDepthFormat(mState->mVkFormat)
|
||||
fvkutils::isVkDepthFormat(vkFormat)
|
||||
? limits.sampledImageDepthSampleCounts
|
||||
: limits.sampledImageColorSampleCounts);
|
||||
}
|
||||
@@ -315,33 +370,36 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
this->samples = samples;
|
||||
imageInfo.samples = (VkSampleCountFlagBits) samples;
|
||||
|
||||
VkResult result = vkCreateImage(mState->mDevice, &imageInfo, VKALLOC, &mState->mTextureImage);
|
||||
VkImage textureImage;
|
||||
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &textureImage);
|
||||
if (result != VK_SUCCESS || FVK_ENABLED(FVK_DEBUG_TEXTURE)) {
|
||||
FVK_LOGD << "vkCreateImage: "
|
||||
<< "image = " << mState->mTextureImage << ", "
|
||||
<< "image = " << textureImage << ", "
|
||||
<< "result = " << result << ", "
|
||||
<< "handle = " << utils::io::hex << mState->mTextureImage << utils::io::dec << ", "
|
||||
<< "handle = " << utils::io::hex << textureImage << utils::io::dec << ", "
|
||||
<< "extent = " << w << "x" << h << "x"<< depth << ", "
|
||||
<< "mipLevels = " << int(levels) << ", "
|
||||
<< "TextureUsage = " << static_cast<int>(usage) << ", "
|
||||
<< "TextureUsage = " << static_cast<int>(tusage) << ", "
|
||||
<< "usage = " << imageInfo.usage << ", "
|
||||
<< "samples = " << imageInfo.samples << ", "
|
||||
<< "type = " << imageInfo.imageType << ", "
|
||||
<< "flags = " << imageInfo.flags << ", "
|
||||
<< "target = " << static_cast<int>(target) <<", "
|
||||
<< "format = " << mState->mVkFormat << utils::io::endl;
|
||||
<< "format = " << vkFormat << utils::io::endl;
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create image."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
// Allocate memory for the VkImage and bind it.
|
||||
VkMemoryRequirements memReqs = {};
|
||||
vkGetImageMemoryRequirements(mState->mDevice, mState->mTextureImage, &memReqs);
|
||||
VkMemoryRequirements memReqs;
|
||||
vkGetImageMemoryRequirements(device, textureImage, &memReqs);
|
||||
|
||||
const VkFlags requiredMemoryFlags =
|
||||
bool const useTransientAttachment = imageInfo.usage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
|
||||
|
||||
VkFlags const requiredMemoryFlags =
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
|
||||
(useTransientAttachment ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0U) |
|
||||
(mState->mIsProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
|
||||
(isProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
|
||||
uint32_t memoryTypeIndex
|
||||
= context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags);
|
||||
|
||||
@@ -353,21 +411,21 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
.allocationSize = memReqs.size,
|
||||
.memoryTypeIndex = memoryTypeIndex,
|
||||
};
|
||||
result = vkAllocateMemory(mState->mDevice, &allocInfo, nullptr, &mState->mTextureImageMemory);
|
||||
VkDeviceMemory textureImageMemory;
|
||||
result = vkAllocateMemory(device, &allocInfo, nullptr, &textureImageMemory);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to allocate image memory."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
result = vkBindImageMemory(mState->mDevice, mState->mTextureImage, mState->mTextureImageMemory,
|
||||
0);
|
||||
result = vkBindImageMemory(device, textureImage, textureImageMemory, 0);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to bind image."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
mState = fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, stagePool,
|
||||
commands, allocator, device, textureImage, textureImageMemory, vkFormat,
|
||||
fvkutils::getViewType(target), levels, getLayerCount(target, depth),
|
||||
VK_NULL_HANDLE /* ycbcrConversion */, imageInfo.usage, isProtected);
|
||||
|
||||
// Spec out the "primary" VkImageView that shaders use to sample from the image.
|
||||
mPrimaryViewRange = mState->mFullViewRange;
|
||||
|
||||
// Go ahead and create the primary image view.
|
||||
getImageView(mPrimaryViewRange, mState->mViewType, mSwizzle);
|
||||
|
||||
mState->mDefaultLayout = getDefaultLayoutImpl(imageInfo.usage);
|
||||
}
|
||||
|
||||
// Constructor for creating a texture view
|
||||
@@ -376,8 +434,8 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
fvkmemory::resource_ptr<VulkanTexture> src, uint8_t baseLevel,
|
||||
uint8_t levelCount)
|
||||
: HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth,
|
||||
src->format, src->usage) {
|
||||
mState = src->mState;
|
||||
src->format, src->usage),
|
||||
mState(src->mState) {
|
||||
mPrimaryViewRange = src->mPrimaryViewRange;
|
||||
mPrimaryViewRange.baseMipLevel = src->mPrimaryViewRange.baseMipLevel + baseLevel;
|
||||
mPrimaryViewRange.levelCount = levelCount;
|
||||
@@ -388,21 +446,10 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
|
||||
fvkmemory::resource_ptr<VulkanTexture> src, VkComponentMapping swizzle)
|
||||
: HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth,
|
||||
src->format, src->usage) {
|
||||
mState = src->mState;
|
||||
mPrimaryViewRange = src->mPrimaryViewRange;
|
||||
mSwizzle = composeSwizzle(src->mSwizzle, swizzle);
|
||||
}
|
||||
|
||||
VulkanTextureState::~VulkanTextureState() {
|
||||
if (mTextureImageMemory != VK_NULL_HANDLE) {
|
||||
vkDestroyImage(mDevice, mTextureImage, VKALLOC);
|
||||
vkFreeMemory(mDevice, mTextureImageMemory, VKALLOC);
|
||||
}
|
||||
for (auto entry: mCachedImageViews) {
|
||||
vkDestroyImageView(mDevice, entry.second, VKALLOC);
|
||||
}
|
||||
}
|
||||
src->format, src->usage),
|
||||
mState(src->mState),
|
||||
mPrimaryViewRange(src->mPrimaryViewRange),
|
||||
mSwizzle(composeSwizzle(src->mSwizzle, swizzle)) {}
|
||||
|
||||
void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height,
|
||||
uint32_t depth, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t miplevel) {
|
||||
@@ -509,20 +556,20 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, u
|
||||
commands.acquire(fvkmemory::resource_ptr<VulkanTexture>::cast(this));
|
||||
|
||||
// TODO: support blit-based format conversion for 3D images and cubemaps.
|
||||
const int layer = 0;
|
||||
constexpr int layer = 0;
|
||||
|
||||
const VkOffset3D rect[2] { {0, 0, 0}, {int32_t(width), int32_t(height), 1} };
|
||||
VkOffset3D const rect[2] { {0, 0, 0}, {int32_t(width), int32_t(height), 1} };
|
||||
|
||||
const VkImageAspectFlags aspect = getImageAspect();
|
||||
VkImageAspectFlags const aspect = getImageAspect();
|
||||
|
||||
const VkImageBlit blitRegions[1] = {{
|
||||
VkImageBlit const blitRegions[1] = {{
|
||||
.srcSubresource = { aspect, 0, 0, 1 },
|
||||
.srcOffsets = { rect[0], rect[1] },
|
||||
.dstSubresource = { aspect, uint32_t(miplevel), layer, 1 },
|
||||
.dstOffsets = { rect[0], rect[1] }
|
||||
}};
|
||||
|
||||
const VkImageSubresourceRange range = { aspect, miplevel, 1, layer, 1 };
|
||||
VkImageSubresourceRange const range = { aspect, miplevel, 1, layer, 1 };
|
||||
|
||||
VulkanLayout const newLayout = VulkanLayout::TRANSFER_DST;
|
||||
VulkanLayout const oldLayout = getLayout(layer, miplevel);
|
||||
@@ -538,41 +585,21 @@ VulkanLayout VulkanTexture::getDefaultLayout() const {
|
||||
return mState->mDefaultLayout;
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange range) {
|
||||
range.levelCount = 1;
|
||||
range.layerCount = 1;
|
||||
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D, {});
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getMultiviewAttachmentView(VkImageSubresourceRange range) {
|
||||
return getImageView(range, VK_IMAGE_VIEW_TYPE_2D_ARRAY, {});
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getViewForType(VkImageSubresourceRange const& range, VkImageViewType type) {
|
||||
return getImageView(range, type, mSwizzle);
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
|
||||
VkComponentMapping swizzle) {
|
||||
VulkanTextureState::ImageViewKey const key{ range, viewType, swizzle };
|
||||
auto iter = mState->mCachedImageViews.find(key);
|
||||
if (iter != mState->mCachedImageViews.end()) {
|
||||
return iter->second;
|
||||
VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange const& range, VkImageViewType type) {
|
||||
assert_invariant(mState->mYcbcr.conversion == VK_NULL_HANDLE &&
|
||||
"We are not yet supporting external image as attachments.");
|
||||
if (type == VK_IMAGE_VIEW_TYPE_2D) {
|
||||
VkImageSubresourceRange copy = range;
|
||||
copy.levelCount = 1;
|
||||
copy.layerCount = 1;
|
||||
return mState->getImageView(copy, type, {});
|
||||
} else {
|
||||
return mState->getImageView(range, type, {});
|
||||
}
|
||||
VkImageViewCreateInfo viewInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.image = mState->mTextureImage,
|
||||
.viewType = viewType,
|
||||
.format = mState->mVkFormat,
|
||||
.components = swizzle,
|
||||
.subresourceRange = range,
|
||||
};
|
||||
VkImageView imageView;
|
||||
vkCreateImageView(mState->mDevice, &viewInfo, VKALLOC, &imageView);
|
||||
mState->mCachedImageViews.emplace(key, imageView);
|
||||
return imageView;
|
||||
}
|
||||
|
||||
VkImageView VulkanTexture::getView(VkImageSubresourceRange const& range) {
|
||||
return mState->getImageView(range, mState->mViewType, mSwizzle);
|
||||
}
|
||||
|
||||
VkImageAspectFlags VulkanTexture::getImageAspect() const {
|
||||
@@ -734,6 +761,19 @@ void VulkanTexture::setLayout(VkImageSubresourceRange const& range, VulkanLayout
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanTexture::setYcbcrConversion(VkSamplerYcbcrConversion conversion) {
|
||||
// Note that this comparison is valid because we only ever create VkSamplerYcbcrConversion from
|
||||
// a cache. So for each set of parameters, there is exactly one conversion (similar to
|
||||
// samplers).
|
||||
VulkanTextureState::Ycbcr ycbcr = {
|
||||
.conversion = conversion,
|
||||
};
|
||||
if (mState->mYcbcr != ycbcr) {
|
||||
mState->mYcbcr = ycbcr;
|
||||
mState->clearCachedImageViews();
|
||||
}
|
||||
}
|
||||
|
||||
VulkanLayout VulkanTexture::getLayout(uint32_t layer, uint32_t level) const {
|
||||
assert_invariant(level <= 0xffff && layer <= 0xffff);
|
||||
const uint32_t key = (layer << 16) | level;
|
||||
|
||||
@@ -19,7 +19,10 @@
|
||||
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include "VulkanBuffer.h"
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "VulkanStagePool.h"
|
||||
#include "vulkan/memory/Resource.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
#include "vulkan/utils/Image.h"
|
||||
@@ -31,10 +34,13 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
struct VulkanTexture;
|
||||
|
||||
struct VulkanTextureState : public fvkmemory::Resource {
|
||||
VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
|
||||
VulkanStagePool& stagePool, VkFormat format, VkImageViewType viewType, uint8_t levels,
|
||||
uint8_t layerCount, VulkanLayout defaultLayout, bool isProtected);
|
||||
VulkanTextureState(VulkanStagePool& stagePool, VulkanCommands* commands, VmaAllocator allocator,
|
||||
VkDevice device, VkImage image, VkDeviceMemory deviceMemory, VkFormat format,
|
||||
VkImageViewType viewType, uint8_t levels, uint8_t layerCount,
|
||||
VkSamplerYcbcrConversion ycbcrConversion, VkImageUsageFlags usage, bool isProtected);
|
||||
|
||||
~VulkanTextureState();
|
||||
|
||||
@@ -57,29 +63,49 @@ struct VulkanTextureState : public fvkmemory::Resource {
|
||||
// No implicit padding allowed due to it being a hash key.
|
||||
static_assert(sizeof(ImageViewKey) == 40);
|
||||
|
||||
using ImageViewHash = utils::hash::MurmurHashFn<ImageViewKey>;
|
||||
VkImageView getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
|
||||
VkComponentMapping swizzle);
|
||||
private:
|
||||
void clearCachedImageViews() noexcept;
|
||||
VulkanStagePool& mStagePool;
|
||||
VulkanCommands* const mCommands;
|
||||
VmaAllocator const mAllocator;
|
||||
VkDevice const mDevice;
|
||||
|
||||
// The texture with the sidecar owns the sidecar.
|
||||
fvkmemory::resource_ptr<VulkanTexture> mSidecarMSAA;
|
||||
VkDeviceMemory mTextureImageMemory = VK_NULL_HANDLE;
|
||||
|
||||
VkImage const mTextureImage;
|
||||
VkDeviceMemory const mTextureImageMemory;
|
||||
VkFormat const mVkFormat;
|
||||
VkImageViewType const mViewType;
|
||||
VkImageSubresourceRange const mFullViewRange;
|
||||
VkImage mTextureImage = VK_NULL_HANDLE;
|
||||
VulkanLayout mDefaultLayout;
|
||||
|
||||
bool mIsProtected = false;
|
||||
// Note that this parameter is not constant due to the fact that AHB can force a change in the
|
||||
// conversion matrix per-frame.
|
||||
struct Ycbcr {
|
||||
VkSamplerYcbcrConversion conversion;
|
||||
|
||||
bool operator==(Ycbcr const& other) const {
|
||||
return conversion == other.conversion;
|
||||
}
|
||||
|
||||
bool operator!=(Ycbcr const& other) const {
|
||||
return !((*this) == other);
|
||||
}
|
||||
|
||||
} mYcbcr;
|
||||
|
||||
VulkanLayout const mDefaultLayout;
|
||||
VkImageUsageFlags const mUsage;
|
||||
bool const mIsProtected;
|
||||
|
||||
// Track the image layout of each subresource using a sparse range map.
|
||||
utils::RangeMap<uint32_t, VulkanLayout> mSubresourceLayouts;
|
||||
|
||||
using ImageViewHash = utils::hash::MurmurHashFn<ImageViewKey>;
|
||||
std::unordered_map<ImageViewKey, VkImageView, ImageViewHash> mCachedImageViews;
|
||||
VulkanStagePool& mStagePool;
|
||||
VkDevice mDevice;
|
||||
VmaAllocator mAllocator;
|
||||
VulkanCommands* mCommands;
|
||||
bool mIsTransientAttachment;
|
||||
|
||||
friend struct VulkanTexture;
|
||||
};
|
||||
|
||||
struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
||||
@@ -91,10 +117,10 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
||||
VulkanStagePool& stagePool);
|
||||
|
||||
// Specialized constructor for internally created textures (e.g. from a swap chain)
|
||||
// The texture will never destroy the given VkImage, but it does manages its subresources.
|
||||
VulkanTexture(VkDevice device, VmaAllocator allocator,
|
||||
VulkanTexture(VulkanContext const& context, VkDevice device, VmaAllocator allocator,
|
||||
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image,
|
||||
VkDeviceMemory memory, VkFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth,
|
||||
VkDeviceMemory memory, VkFormat format, VkSamplerYcbcrConversion conversion,
|
||||
uint8_t samples, uint32_t width, uint32_t height, uint32_t depth,
|
||||
TextureUsage tusage, VulkanStagePool& stagePool);
|
||||
|
||||
// Constructor for creating a texture view for wrt specific mip range
|
||||
@@ -113,11 +139,6 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
||||
void updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height,
|
||||
uint32_t depth, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset, uint32_t miplevel);
|
||||
|
||||
// Returns the primary image view, which is used for shader sampling.
|
||||
VkImageView getPrimaryImageView() {
|
||||
return getImageView(mPrimaryViewRange, mState->mViewType, mSwizzle);
|
||||
}
|
||||
|
||||
VkImageViewType getViewType() const {
|
||||
return mState->mViewType;
|
||||
}
|
||||
@@ -132,20 +153,11 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
||||
// time of the call.
|
||||
VulkanLayout getDefaultLayout() const;
|
||||
|
||||
// Gets or creates a cached VkImageView for a single subresource that can be used as a render
|
||||
// target attachment. Unlike the primary image view, this always has type VK_IMAGE_VIEW_TYPE_2D
|
||||
// and the identity swizzle.
|
||||
VkImageView getAttachmentView(VkImageSubresourceRange range);
|
||||
// Gets or creates a cached VkImageView for a subresource that can be used as a render
|
||||
// target attachment. Unlike the primary image view, this always the identity swizzle.
|
||||
VkImageView getAttachmentView(VkImageSubresourceRange const& range, VkImageViewType type);
|
||||
|
||||
// Gets or creates a cached VkImageView for a single subresource that can be used as a render
|
||||
// target attachment when rendering with multiview.
|
||||
VkImageView getMultiviewAttachmentView(VkImageSubresourceRange range);
|
||||
|
||||
// This is a workaround for the first few frames where we're waiting for the texture to actually
|
||||
// be uploaded. In that case, we bind the sampler to an empty texture, but the corresponding
|
||||
// imageView needs to be of the right type. Hence, we provide an option to indicate the
|
||||
// view type. Swizzle option does not matter in this case.
|
||||
VkImageView getViewForType(VkImageSubresourceRange const& range, VkImageViewType type);
|
||||
VkImageView getView(VkImageSubresourceRange const& range);
|
||||
|
||||
VkFormat getVkFormat() const {
|
||||
return mState->mVkFormat;
|
||||
@@ -165,7 +177,7 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
||||
}
|
||||
|
||||
bool isTransientAttachment() const {
|
||||
return mState->mIsTransientAttachment;
|
||||
return mState->mUsage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
|
||||
}
|
||||
|
||||
bool getIsProtected() const {
|
||||
@@ -192,6 +204,11 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
||||
// manually (outside of calls to transitionLayout).
|
||||
void setLayout(VkImageSubresourceRange const& range, VulkanLayout newLayout);
|
||||
|
||||
// This is used in the case of external images and external samplers. AHB might update the
|
||||
// conversion per-frame. This implies that we need to invalidate the view cache when that
|
||||
// happens.
|
||||
void setYcbcrConversion(VkSamplerYcbcrConversion conversion);
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
|
||||
void print() const;
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "VulkanYcbcrConversionCache.h"
|
||||
|
||||
#include "vulkan/VulkanConstants.h"
|
||||
#include "vulkan/utils/Conversion.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <vulkan/vulkan_android.h> // for VkExternalFormatANDROID
|
||||
#endif
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
VulkanYcbcrConversionCache::VulkanYcbcrConversionCache(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
|
||||
VkSamplerYcbcrConversion VulkanYcbcrConversionCache::getConversion(
|
||||
VulkanYcbcrConversionCache::Params params) {
|
||||
auto iter = mCache.find(params);
|
||||
if (UTILS_LIKELY(iter != mCache.end())) {
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
auto const& chroma = params.conversion;
|
||||
TextureSwizzle const swizzleArray[] = { chroma.r, chroma.g, chroma.b, chroma.a };
|
||||
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
|
||||
.format = params.format,
|
||||
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
|
||||
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
|
||||
.components = fvkutils::getSwizzleMap(swizzleArray),
|
||||
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
|
||||
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
|
||||
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
|
||||
};
|
||||
|
||||
// We could put this in the platform class, but that seems like a bit of an overkill
|
||||
#if defined(__ANDROID__)
|
||||
VkExternalFormatANDROID externalFormat = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||
.externalFormat = params.externalFormat,
|
||||
};
|
||||
if (params.externalFormat) {
|
||||
conversionInfo.pNext = &externalFormat;
|
||||
conversionInfo.format = VK_FORMAT_UNDEFINED;
|
||||
}
|
||||
#endif
|
||||
|
||||
VkSamplerYcbcrConversion conversion;
|
||||
VkResult result =
|
||||
vkCreateSamplerYcbcrConversion(mDevice, &conversionInfo, nullptr, &conversion);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to create Ycbcr Conversion."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
mCache.insert({ params, conversion });
|
||||
return conversion;
|
||||
}
|
||||
|
||||
void VulkanYcbcrConversionCache::terminate() noexcept {
|
||||
for (auto& [param, conv]: mCache) {
|
||||
vkDestroySamplerYcbcrConversion(mDevice, conv, VKALLOC);
|
||||
}
|
||||
mCache.clear();
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/Hash.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
// Simple manager for VkSamplerYcbcrConversion objects.
|
||||
class VulkanYcbcrConversionCache {
|
||||
public:
|
||||
struct Params {
|
||||
SamplerYcbcrConversion conversion = {}; // 4
|
||||
VkFormat format; // 4
|
||||
uint64_t externalFormat = 0; // 8
|
||||
};
|
||||
static_assert(sizeof(Params) == 16);
|
||||
|
||||
explicit VulkanYcbcrConversionCache(VkDevice device);
|
||||
VkSamplerYcbcrConversion getConversion(Params params);
|
||||
void terminate() noexcept;
|
||||
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
|
||||
struct ConversionEqualTo {
|
||||
bool operator()(Params lhs, Params rhs) const noexcept {
|
||||
SamplerYcbcrConversion::EqualTo equal;
|
||||
return equal(lhs.conversion, rhs.conversion) &&
|
||||
lhs.externalFormat == rhs.externalFormat &&
|
||||
lhs.format == rhs.format;
|
||||
}
|
||||
};
|
||||
using ConversionHashFn = utils::hash::MurmurHashFn<Params>;
|
||||
tsl::robin_map<Params, VkSamplerYcbcrConversion, ConversionHashFn, ConversionEqualTo> mCache;
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
#endif// TNT_FILAMENT_BACKEND_VULKANYCBCRCONVERSIONCACHE_H
|
||||
@@ -1,504 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "VulkanDescriptorSetManager.h"
|
||||
|
||||
#include "vulkan/VulkanCommands.h"
|
||||
#include "vulkan/VulkanHandles.h"
|
||||
#include "vulkan/VulkanConstants.h"
|
||||
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask;
|
||||
using DescriptorCount = VulkanDescriptorSetLayout::Count;
|
||||
using DescriptorSetLayoutArray = VulkanDescriptorSetManager::DescriptorSetLayoutArray;
|
||||
using BitmaskGroupHashFn = utils::hash::MurmurHashFn<BitmaskGroup>;
|
||||
struct BitmaskGroupEqual {
|
||||
bool operator()(BitmaskGroup const& k1, BitmaskGroup const& k2) const {
|
||||
return k1 == k2;
|
||||
}
|
||||
};
|
||||
|
||||
// We create a pool for each layout as defined by the number of descriptors of each type. For
|
||||
// example, a layout of
|
||||
// 'A' =>
|
||||
// layout(binding = 0, set = 1) uniform {};
|
||||
// layout(binding = 1, set = 1) sampler1;
|
||||
// layout(binding = 2, set = 1) sampler2;
|
||||
//
|
||||
// would be equivalent to
|
||||
// 'B' =>
|
||||
// layout(binding = 1, set = 2) uniform {};
|
||||
// layout(binding = 2, set = 2) sampler2;
|
||||
// layout(binding = 3, set = 2) sampler3;
|
||||
//
|
||||
// TODO: we might do better if we understand the types of unique layouts and can combine them in a
|
||||
// single pool without too much waste.
|
||||
class DescriptorPool {
|
||||
public:
|
||||
DescriptorPool(VkDevice device, DescriptorCount const& count, uint16_t capacity)
|
||||
: mDevice(device),
|
||||
mCount(count),
|
||||
mCapacity(capacity),
|
||||
mSize(0),
|
||||
mUnusedCount(0) {
|
||||
DescriptorCount const actual = mCount * capacity;
|
||||
VkDescriptorPoolSize sizes[4];
|
||||
uint8_t npools = 0;
|
||||
if (actual.ubo) {
|
||||
sizes[npools++] = {
|
||||
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
|
||||
.descriptorCount = actual.ubo,
|
||||
};
|
||||
}
|
||||
if (actual.dynamicUbo) {
|
||||
sizes[npools++] = {
|
||||
.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
|
||||
.descriptorCount = actual.dynamicUbo,
|
||||
};
|
||||
}
|
||||
if (actual.sampler) {
|
||||
sizes[npools++] = {
|
||||
.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.descriptorCount = actual.sampler,
|
||||
};
|
||||
}
|
||||
if (actual.inputAttachment) {
|
||||
sizes[npools++] = {
|
||||
.type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
|
||||
.descriptorCount = actual.inputAttachment,
|
||||
};
|
||||
}
|
||||
VkDescriptorPoolCreateInfo info{
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.flags = 0,
|
||||
.maxSets = capacity,
|
||||
.poolSizeCount = npools,
|
||||
.pPoolSizes = sizes,
|
||||
};
|
||||
vkCreateDescriptorPool(mDevice, &info, VKALLOC, &mPool);
|
||||
}
|
||||
|
||||
DescriptorPool(DescriptorPool const&) = delete;
|
||||
DescriptorPool& operator=(DescriptorPool const&) = delete;
|
||||
|
||||
~DescriptorPool() {
|
||||
vkDestroyDescriptorPool(mDevice, mPool, VKALLOC);
|
||||
}
|
||||
|
||||
uint16_t const& capacity() {
|
||||
return mCapacity;
|
||||
}
|
||||
|
||||
// A convenience method for checking if this pool can allocate sets for a given layout.
|
||||
inline bool canAllocate(DescriptorCount const& count) {
|
||||
return count == mCount;
|
||||
}
|
||||
|
||||
VkDescriptorSet obtainSet(VkDescriptorSetLayout vklayout) {
|
||||
auto itr = findSets(vklayout);
|
||||
if (itr != mUnused.end()) {
|
||||
// If we don't have any unused, then just return an empty handle.
|
||||
if (itr->second.empty()) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
std::vector<VkDescriptorSet>& sets = itr->second;
|
||||
auto set = sets.back();
|
||||
sets.pop_back();
|
||||
mUnusedCount--;
|
||||
return set;
|
||||
}
|
||||
if (mSize + 1 > mCapacity) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
// Creating a new set
|
||||
VkDescriptorSetLayout layouts[1] = {vklayout};
|
||||
VkDescriptorSetAllocateInfo allocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.descriptorPool = mPool,
|
||||
.descriptorSetCount = 1,
|
||||
.pSetLayouts = layouts,
|
||||
};
|
||||
VkDescriptorSet vkSet;
|
||||
UTILS_UNUSED VkResult result = vkAllocateDescriptorSets(mDevice, &allocInfo, &vkSet);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Failed to allocate descriptor set code=" << result << " size=" << mSize
|
||||
<< " capacity=" << mCapacity << " count=" << mUnusedCount;
|
||||
mSize++;
|
||||
return vkSet;
|
||||
}
|
||||
|
||||
void recycle(VkDescriptorSetLayout vklayout, VkDescriptorSet vkSet) {
|
||||
// We are recycling - release the set back into the pool. Note that the
|
||||
// vk handle has not changed, but we need to change the backend handle to allow
|
||||
// for proper refcounting of resources referenced in this set.
|
||||
auto itr = findSets(vklayout);
|
||||
if (itr != mUnused.end()) {
|
||||
itr->second.push_back(vkSet);
|
||||
} else {
|
||||
mUnused.push_back(std::make_pair(vklayout, std::vector<VkDescriptorSet> {vkSet}));
|
||||
}
|
||||
mUnusedCount++;
|
||||
}
|
||||
|
||||
private:
|
||||
using UnusedSets = std::pair<VkDescriptorSetLayout, std::vector<VkDescriptorSet>>;
|
||||
using UnusedSetMap = std::vector<UnusedSets>;
|
||||
|
||||
inline UnusedSetMap::iterator findSets(VkDescriptorSetLayout vklayout) {
|
||||
return std::find_if(mUnused.begin(), mUnused.end(), [vklayout](auto const& value) {
|
||||
return value.first == vklayout;
|
||||
});
|
||||
}
|
||||
|
||||
VkDevice mDevice;
|
||||
VkDescriptorPool mPool;
|
||||
DescriptorCount const mCount;
|
||||
uint16_t const mCapacity;
|
||||
|
||||
// Tracks the number of allocated descriptor sets.
|
||||
uint16_t mSize;
|
||||
// Tracks the number of in-use descriptor sets.
|
||||
uint16_t mUnusedCount;
|
||||
|
||||
// This maps a layout to a list of descriptor sets allocated for that layout.
|
||||
UnusedSetMap mUnused;
|
||||
};
|
||||
|
||||
template<typename Key>
|
||||
struct Equal {
|
||||
bool operator()(Key const& k1, Key const& k2) const {
|
||||
return 0 == memcmp((const void*) &k1, (const void*) &k2, sizeof(Key));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Bitmask>
|
||||
uint32_t createBindings(VkDescriptorSetLayoutBinding* toBind, uint32_t count, VkDescriptorType type,
|
||||
Bitmask const& mask) {
|
||||
Bitmask alreadySeen;
|
||||
mask.forEachSetBit([&](size_t index) {
|
||||
VkShaderStageFlags stages = 0;
|
||||
uint32_t binding = 0;
|
||||
if (index < fvkutils::getFragmentStageShift<Bitmask>()) {
|
||||
binding = (uint32_t) index;
|
||||
stages |= VK_SHADER_STAGE_VERTEX_BIT;
|
||||
auto fragIndex = index + fvkutils::getFragmentStageShift<Bitmask>();
|
||||
if (mask.test(fragIndex)) {
|
||||
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
alreadySeen.set(fragIndex);
|
||||
}
|
||||
} else if (!alreadySeen.test(index)) {
|
||||
// We are in fragment stage bits
|
||||
binding = (uint32_t) (index - fvkutils::getFragmentStageShift<Bitmask>());
|
||||
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||
}
|
||||
|
||||
if (stages) {
|
||||
toBind[count++] = {
|
||||
.binding = binding,
|
||||
.descriptorType = type,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = stages,
|
||||
};
|
||||
}
|
||||
});
|
||||
return count;
|
||||
}
|
||||
|
||||
inline VkDescriptorSetLayout createLayout(VkDevice device, BitmaskGroup const& bitmaskGroup) {
|
||||
// Note that the following *needs* to be static so that VkDescriptorSetLayoutCreateInfo will not
|
||||
// refer to stack memory.
|
||||
VkDescriptorSetLayoutBinding toBind[VulkanDescriptorSetLayout::MAX_BINDINGS];
|
||||
uint32_t count = 0;
|
||||
|
||||
count = createBindings(toBind, count, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
|
||||
bitmaskGroup.dynamicUbo);
|
||||
count = createBindings(toBind, count, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bitmaskGroup.ubo);
|
||||
count = createBindings(toBind, count, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
bitmaskGroup.sampler);
|
||||
count = createBindings(toBind, count, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
|
||||
bitmaskGroup.inputAttachment);
|
||||
|
||||
assert_invariant(count != 0 && "Need at least one binding for descriptor set layout.");
|
||||
VkDescriptorSetLayoutCreateInfo dlinfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.pNext = nullptr,
|
||||
.bindingCount = count,
|
||||
.pBindings = toBind,
|
||||
};
|
||||
|
||||
VkDescriptorSetLayout layout;
|
||||
vkCreateDescriptorSetLayout(device, &dlinfo, VKALLOC, &layout);
|
||||
return layout;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
// This is an ever-expanding pool of sets where it
|
||||
// 1. Keeps a list of smaller pools of different layout-dimensions.
|
||||
// 2. Will add a pool if existing pool are not compatible with the requested layout o runs out.
|
||||
class VulkanDescriptorSetManager::DescriptorInfinitePool {
|
||||
private:
|
||||
static constexpr uint16_t EXPECTED_SET_COUNT = 10;
|
||||
static constexpr float SET_COUNT_GROWTH_FACTOR = 1.5;
|
||||
|
||||
public:
|
||||
DescriptorInfinitePool(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
|
||||
VkDescriptorSet obtainSet(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
|
||||
auto const vklayout = layout->getVkLayout();
|
||||
DescriptorPool* sameTypePool = nullptr;
|
||||
for (auto& pool: mPools) {
|
||||
if (!pool->canAllocate(layout->count)) {
|
||||
continue;
|
||||
}
|
||||
if (auto set = pool->obtainSet(vklayout); set != VK_NULL_HANDLE) {
|
||||
return set;
|
||||
}
|
||||
if (!sameTypePool || sameTypePool->capacity() < pool->capacity()) {
|
||||
sameTypePool = pool.get();
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t capacity = EXPECTED_SET_COUNT;
|
||||
if (sameTypePool) {
|
||||
// Exponentially increase the size of the pool to ensure we don't hit this too often.
|
||||
capacity = std::ceil(sameTypePool->capacity() * SET_COUNT_GROWTH_FACTOR);
|
||||
}
|
||||
|
||||
// We need to increase the set of pools by one.
|
||||
mPools.push_back(std::make_unique<DescriptorPool>(mDevice,
|
||||
DescriptorCount::fromLayoutBitmask(layout->bitmask), capacity));
|
||||
auto& pool = mPools.back();
|
||||
auto ret = pool->obtainSet(vklayout);
|
||||
assert_invariant(ret != VK_NULL_HANDLE && "failed to obtain a set?");
|
||||
return ret;
|
||||
}
|
||||
|
||||
void recycle(DescriptorCount const& count, VkDescriptorSetLayout vklayout,
|
||||
VkDescriptorSet vkSet) {
|
||||
for (auto& pool: mPools) {
|
||||
if (!pool->canAllocate(count)) {
|
||||
continue;
|
||||
}
|
||||
pool->recycle(vklayout, vkSet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
std::vector<std::unique_ptr<DescriptorPool>> mPools;
|
||||
};
|
||||
|
||||
class VulkanDescriptorSetManager::DescriptorSetLayoutManager {
|
||||
public:
|
||||
DescriptorSetLayoutManager(VkDevice device)
|
||||
: mDevice(device) {}
|
||||
|
||||
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks) {
|
||||
if (auto itr = mVkLayouts.find(bitmasks); itr != mVkLayouts.end()) {
|
||||
return itr->second;
|
||||
}
|
||||
auto vklayout = createLayout(mDevice, bitmasks);
|
||||
mVkLayouts[bitmasks] = vklayout;
|
||||
return vklayout;
|
||||
}
|
||||
|
||||
~DescriptorSetLayoutManager() {
|
||||
for (auto& itr: mVkLayouts) {
|
||||
vkDestroyDescriptorSetLayout(mDevice, itr.second, VKALLOC);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
VkDevice mDevice;
|
||||
tsl::robin_map<BitmaskGroup, VkDescriptorSetLayout, BitmaskGroupHashFn, BitmaskGroupEqual>
|
||||
mVkLayouts;
|
||||
};
|
||||
|
||||
VulkanDescriptorSetManager::VulkanDescriptorSetManager(VkDevice device,
|
||||
fvkmemory::ResourceManager* resourceManager)
|
||||
: mDevice(device),
|
||||
mResourceManager(resourceManager),
|
||||
mLayoutManager(std::make_unique<DescriptorSetLayoutManager>(device)),
|
||||
mDescriptorPool(std::make_unique<DescriptorInfinitePool>(device)) {}
|
||||
|
||||
VulkanDescriptorSetManager::~VulkanDescriptorSetManager() = default;
|
||||
|
||||
void VulkanDescriptorSetManager::terminate() noexcept{
|
||||
mLayoutManager.reset();
|
||||
mDescriptorPool.reset();
|
||||
clearHistory();
|
||||
}
|
||||
|
||||
// bind() is not really binding the set but just stashing until we have all the info
|
||||
// (pipelinelayout).
|
||||
void VulkanDescriptorSetManager::bind(uint8_t setIndex,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
backend::DescriptorSetOffsetArray&& offsets) {
|
||||
set->setOffsets(std::move(offsets));
|
||||
mStashedSets[setIndex] = set;
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::unbind(uint8_t setIndex) {
|
||||
mStashedSets[setIndex] = {};
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands,
|
||||
VkPipelineLayout pipelineLayout, fvkutils::DescriptorSetMask const& setMask) {
|
||||
// setMask indicates the set of descriptor sets the driver wants to bind, curMask is the
|
||||
// actual set of sets that *needs* to be bound.
|
||||
fvkutils::DescriptorSetMask curMask = setMask;
|
||||
|
||||
auto& updateSets = mStashedSets;
|
||||
auto& lastBoundSets = mLastBoundInfo.boundSets;
|
||||
|
||||
setMask.forEachSetBit([&](size_t index) {
|
||||
if (!updateSets[index] || updateSets[index] == lastBoundSets[index]) {
|
||||
curMask.unset(index);
|
||||
}
|
||||
});
|
||||
|
||||
if (curMask.none() &&
|
||||
(mLastBoundInfo.pipelineLayout == pipelineLayout && mLastBoundInfo.setMask == setMask &&
|
||||
mLastBoundInfo.boundSets == updateSets)) {
|
||||
return;
|
||||
}
|
||||
|
||||
curMask.forEachSetBit([&updateSets, commands, pipelineLayout](size_t index) {
|
||||
// This code actually binds the descriptor sets.
|
||||
auto set = updateSets[index];
|
||||
VkCommandBuffer const cmdbuffer = commands->buffer();
|
||||
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index,
|
||||
1, &set->vkSet, set->uniqueDynamicUboCount, set->getOffsets()->data());
|
||||
commands->acquire(set);
|
||||
});
|
||||
|
||||
mStashedSets = {};
|
||||
|
||||
mLastBoundInfo = {
|
||||
pipelineLayout,
|
||||
setMask,
|
||||
updateSets,
|
||||
};
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
uint8_t binding, fvkmemory::resource_ptr<VulkanBufferObject> bufferObject,
|
||||
VkDeviceSize offset, VkDeviceSize size) noexcept {
|
||||
VkDescriptorBufferInfo const info = {
|
||||
.buffer = bufferObject->buffer.getGpuBuffer(),
|
||||
.offset = offset,
|
||||
.range = size,
|
||||
};
|
||||
VkDescriptorType type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
|
||||
if (set->dynamicUboMask.test(binding)) {
|
||||
type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
}
|
||||
VkWriteDescriptorSet const descriptorWrite = {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.pNext = nullptr,
|
||||
.dstSet = set->vkSet,
|
||||
.dstBinding = binding,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = type,
|
||||
.pBufferInfo = &info,
|
||||
};
|
||||
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
|
||||
set->acquire(bufferObject);
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
|
||||
VkSampler sampler) noexcept {
|
||||
VkDescriptorImageInfo info{
|
||||
.sampler = sampler,
|
||||
};
|
||||
VkImageSubresourceRange const range = texture->getPrimaryViewRange();
|
||||
VkImageViewType const expectedType = texture->getViewType();
|
||||
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
|
||||
expectedType == VK_IMAGE_VIEW_TYPE_2D) {
|
||||
// If the sampler is part of a mipmapped depth texture, where one of the level *can* be
|
||||
// an attachment, then the sampler for this texture has the same view properties as a
|
||||
// view for an attachment. Therefore, we can use getAttachmentView to get a
|
||||
// corresponding VkImageView.
|
||||
info.imageView = texture->getAttachmentView(range);
|
||||
} else {
|
||||
info.imageView = texture->getViewForType(range, expectedType);
|
||||
}
|
||||
info.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout());
|
||||
VkWriteDescriptorSet const descriptorWrite = {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.pNext = nullptr,
|
||||
.dstSet = set->vkSet,
|
||||
.dstBinding = binding,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
|
||||
.pImageInfo = &info,
|
||||
};
|
||||
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
|
||||
set->acquire(texture);
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::updateInputAttachment(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
VulkanAttachment const& attachment) noexcept {
|
||||
// TOOD: fill-in this region
|
||||
}
|
||||
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetManager::createSet(
|
||||
Handle<HwDescriptorSet> handle, fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
|
||||
auto const vkSet = mDescriptorPool->obtainSet(layout);
|
||||
auto const& count = layout->count;
|
||||
auto const vklayout = layout->getVkLayout();
|
||||
return fvkmemory::resource_ptr<VulkanDescriptorSet>::make(mResourceManager, handle, vkSet,
|
||||
layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
|
||||
[vkSet, count, vklayout, this](VulkanDescriptorSet*) {
|
||||
// Note that mDescriptorPool could be gone due to terminate (when the backend shuts
|
||||
// down).
|
||||
if (mDescriptorPool) {
|
||||
mDescriptorPool->recycle(count, vklayout, vkSet);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::initVkLayout(
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
|
||||
layout->setVkLayout(mLayoutManager->getVkLayout(layout->bitmask));
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::clearHistory() {
|
||||
mStashedSets = {};
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -1,102 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
|
||||
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
|
||||
|
||||
#include "vulkan/VulkanHandles.h"
|
||||
#include "vulkan/memory/ResourcePointer.h"
|
||||
#include "vulkan/utils/Definitions.h" // For DescriptorSetMask
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Program.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include <utils/bitset.h>
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
// [GDSR]: Great-Descriptor-Set-Refactor: As of 03/20/24, the Filament frontend is planning to
|
||||
// introduce descriptor set. This PR will arrive before that change is complete. As such, some of
|
||||
// the methods introduced here will be obsolete, and certain logic will be generalized.
|
||||
|
||||
// Abstraction over the pool and the layout cache.
|
||||
class VulkanDescriptorSetManager {
|
||||
public:
|
||||
static constexpr uint8_t UNIQUE_DESCRIPTOR_SET_COUNT =
|
||||
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT;
|
||||
|
||||
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
|
||||
|
||||
VulkanDescriptorSetManager(VkDevice device, fvkmemory::ResourceManager* resourceManager);
|
||||
~VulkanDescriptorSetManager();
|
||||
|
||||
void terminate() noexcept;
|
||||
|
||||
void updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
|
||||
fvkmemory::resource_ptr<VulkanBufferObject> bufferObject, VkDeviceSize offset,
|
||||
VkDeviceSize size) noexcept;
|
||||
|
||||
void updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
|
||||
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept;
|
||||
|
||||
void updateInputAttachment(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
VulkanAttachment const& attachment) noexcept;
|
||||
|
||||
void bind(uint8_t setIndex, fvkmemory::resource_ptr<VulkanDescriptorSet> set,
|
||||
backend::DescriptorSetOffsetArray&& offsets);
|
||||
|
||||
void unbind(uint8_t setIndex);
|
||||
|
||||
void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout,
|
||||
fvkutils::DescriptorSetMask const& setMask);
|
||||
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSet> createSet(Handle<HwDescriptorSet> handle,
|
||||
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
||||
|
||||
void initVkLayout(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
|
||||
|
||||
void clearHistory();
|
||||
|
||||
private:
|
||||
class DescriptorSetLayoutManager;
|
||||
class DescriptorInfinitePool;
|
||||
|
||||
using DescriptorSetArray =
|
||||
std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>, UNIQUE_DESCRIPTOR_SET_COUNT>;
|
||||
|
||||
VkDevice mDevice;
|
||||
fvkmemory::ResourceManager* mResourceManager;
|
||||
std::unique_ptr<DescriptorSetLayoutManager> mLayoutManager;
|
||||
std::unique_ptr<DescriptorInfinitePool> mDescriptorPool;
|
||||
std::pair<VulkanAttachment, VkDescriptorImageInfo> mInputAttachment;
|
||||
DescriptorSetArray mStashedSets = {};
|
||||
|
||||
struct {
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
fvkutils::DescriptorSetMask setMask;
|
||||
DescriptorSetArray boundSets = {};
|
||||
} mLastBoundInfo;
|
||||
};
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
|
||||
@@ -31,6 +31,8 @@ ResourceManager::ResourceManager(size_t arenaSize, bool disableUseAfterFreeCheck
|
||||
: mHandleAllocatorImpl("Handles", arenaSize, disableUseAfterFreeCheck, disablePoolHandleTags) {}
|
||||
|
||||
void ResourceManager::gc() noexcept {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("ResourceManager::gc");
|
||||
auto destroyAll = [this](GcList& list) {
|
||||
for (auto const& [type, id]: list) {
|
||||
destroyWithType(type, id);
|
||||
@@ -49,6 +51,7 @@ void ResourceManager::gc() noexcept {
|
||||
GcList gcs;
|
||||
std::swap(gcs, mGcList);
|
||||
destroyAll(gcs);
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void ResourceManager::terminate() noexcept {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user