Compare commits
49
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94cdc79ce3 | ||
|
|
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 | ||
|
|
0bcb45613e | ||
|
|
0a0d4ed23a | ||
|
|
b9a39bc066 | ||
|
|
e825f43935 | ||
|
|
ee68872a47 | ||
|
|
2f4e48b6b9 | ||
|
|
5091b3112d | ||
|
|
7199961248 | ||
|
|
d0a00803c1 | ||
|
|
0e3ef4bd63 | ||
|
|
c24535a6e4 | ||
|
|
74b09c708a | ||
|
|
80e0dbaa8a | ||
|
|
31e966f63f | ||
|
|
02223dcaab | ||
|
|
47112f136e | ||
|
|
30bb4ad6eb | ||
|
|
d9e30460dc | ||
|
|
075743ef59 |
@@ -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
|
||||
@@ -92,15 +92,30 @@ jobs:
|
||||
|
||||
test-renderdiff:
|
||||
name: test-renderdiff
|
||||
runs-on: ubuntu-22.04-16core
|
||||
runs-on: macos-14-xlarge
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- 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 python prereqs
|
||||
run: pip install mako setuptools pyyaml
|
||||
- name: Run script
|
||||
run: |
|
||||
echo "$(brew --version)"
|
||||
bash test/renderdiff/test.sh
|
||||
|
||||
validate-wgsl-pipeline:
|
||||
name: validate-wgsl-pipeline
|
||||
runs-on: ubuntu-22.04-4core
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4.1.6
|
||||
- uses: ./.github/actions/ubuntu-apt-add-src
|
||||
- name: Run script
|
||||
run: |
|
||||
source ./build/linux/ci-common.sh && bash test/renderdiff/test.sh
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: presubmit-renderdiff-result
|
||||
path: ./out/renderdiff_tests
|
||||
- name: Run build script
|
||||
run: source ./build/linux/ci-common.sh && ./build.sh -W debug test_filamat
|
||||
- name: Run test
|
||||
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
|
||||
|
||||
@@ -9,6 +9,11 @@ name: Release
|
||||
env:
|
||||
RELEASE_TAG: ${{ github.event.inputs.release_tag }}
|
||||
|
||||
# The default GITHUB_TOKEN does not have write permissions, which is needed to upload release
|
||||
# assets.
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
|
||||
+20
-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
|
||||
@@ -601,6 +607,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)
|
||||
|
||||
@@ -7,4 +7,3 @@ for next branch cut* header.
|
||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
- materials: add an optional `transformName` field to sampler material parameters. [⚠️ **New Material Version**]
|
||||
|
||||
@@ -7,6 +7,13 @@ 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.58.1
|
||||
|
||||
|
||||
## v1.58.0
|
||||
|
||||
- materials: add an optional `transformName` field to sampler material parameters. [⚠️ **New Material Version**]
|
||||
|
||||
## v1.57.3
|
||||
|
||||
- android: breaking changes to API KTX1Loader::createIndirectLight and KTX1Loader::createSkybox
|
||||
|
||||
@@ -71,10 +71,9 @@ buildscript {
|
||||
.gradleProperty("com.google.android.filament.include-webgpu")
|
||||
.isPresent()
|
||||
|
||||
// TODO: Uncomment below when fgviewer is ready
|
||||
// def fgviewer = providers
|
||||
// .gradleProperty("com.google.android.filament.fgviewer")
|
||||
// .isPresent()
|
||||
def fgviewer = providers
|
||||
.gradleProperty("com.google.android.filament.fgviewer")
|
||||
.isPresent()
|
||||
|
||||
def matdbg = providers
|
||||
.gradleProperty("com.google.android.filament.matdbg")
|
||||
@@ -131,8 +130,7 @@ buildscript {
|
||||
"-DFILAMENT_DIST_DIR=${filamentPath}".toString(),
|
||||
"-DFILAMENT_SUPPORTS_VULKAN=${excludeVulkan ? 'OFF' : 'ON'}".toString(),
|
||||
"-DFILAMENT_SUPPORTS_WEBGPU=${includeWebGPU ? 'ON' : 'OFF'}".toString(),
|
||||
// TODO: Uncomment below when fgviewer is ready
|
||||
// "-DFILAMENT_ENABLE_FGVIEWER=${fgviewer ? 'ON' : 'OFF'}".toString(),
|
||||
"-DFILAMENT_ENABLE_FGVIEWER=${fgviewer ? 'ON' : 'OFF'}".toString(),
|
||||
"-DFILAMENT_ENABLE_MATDBG=${matdbg ? 'ON' : 'OFF'}".toString(),
|
||||
"-DFILAMENT_DISABLE_MATOPT=${matnopt ? 'ON' : 'OFF'}".toString()
|
||||
]
|
||||
|
||||
@@ -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)
|
||||
@@ -50,4 +57,5 @@ target_link_libraries(filamat-jni
|
||||
utils
|
||||
log
|
||||
smol-v
|
||||
$<$<STREQUAL:${FILAMENT_SUPPORTS_WEBGPU},ON>:tint>
|
||||
)
|
||||
|
||||
@@ -2,8 +2,7 @@ cmake_minimum_required(VERSION 3.19)
|
||||
project(filament-android)
|
||||
|
||||
option(FILAMENT_SUPPORTS_VULKAN "Enables Vulkan on Android" OFF)
|
||||
# TODO: Uncomment below when fgviewer is ready
|
||||
# option(FILAMENT_ENABLE_FGVIEWER "Enables Frame Graph Viewer" OFF)
|
||||
option(FILAMENT_ENABLE_FGVIEWER "Enables Frame Graph Viewer" OFF)
|
||||
option(FILAMENT_ENABLE_MATDBG "Enables Material debugger" OFF)
|
||||
option(FILAMENT_DISABLE_MATOPT "Disables material optimizations" OFF)
|
||||
option(FILAMENT_SUPPORTS_WEBGPU "Enables WebGPU on Android" OFF)
|
||||
@@ -30,10 +29,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)
|
||||
@@ -60,12 +55,11 @@ add_library(smol-v STATIC IMPORTED)
|
||||
set_target_properties(smol-v PROPERTIES IMPORTED_LOCATION
|
||||
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libsmol-v.a)
|
||||
|
||||
# TODO: Uncomment below when fgviewer is ready
|
||||
# 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)
|
||||
@@ -133,8 +127,7 @@ target_link_libraries(filament-jni
|
||||
# libgeometry is PUBLIC because gltfio uses it.
|
||||
PUBLIC geometry
|
||||
|
||||
# TODO: Uncomment below when fgviewer is ready
|
||||
# $<$<STREQUAL:${FILAMENT_ENABLE_FGVIEWER},ON>:fgviewer>
|
||||
$<$<STREQUAL:${FILAMENT_ENABLE_FGVIEWER},ON>:fgviewer>
|
||||
$<$<STREQUAL:${FILAMENT_ENABLE_MATDBG},ON>:matdbg>
|
||||
$<$<STREQUAL:${FILAMENT_ENABLE_MATDBG},ON>:filamat>
|
||||
$<$<STREQUAL:${FILAMENT_SUPPORTS_VULKAN},ON>:bluevk>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -30,4 +30,5 @@ com.google.android.filament.abis=all
|
||||
#com.google.android.filament.include-webgpu
|
||||
|
||||
#com.google.android.filament.matdbg
|
||||
#com.google.android.filament.fgviewer
|
||||
#com.google.android.filament.matnopt
|
||||
|
||||
@@ -541,6 +541,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
|
||||
@@ -591,6 +592,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
|
||||
|
||||
@@ -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
|
||||
|
||||
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
|
||||
|
||||
@@ -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)
|
||||
@@ -233,17 +237,44 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
||||
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
|
||||
)
|
||||
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)
|
||||
list(APPEND SRCS src/BackendUtilsAndroid.cpp)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
@@ -389,9 +420,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)
|
||||
@@ -426,6 +460,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}>
|
||||
@@ -454,6 +492,7 @@ if (APPLE OR LINUX)
|
||||
test/ShaderGenerator.cpp
|
||||
test/TrianglePrimitive.cpp
|
||||
test/Arguments.cpp
|
||||
test/Lifetimes.cpp
|
||||
test/test_FeedbackLoops.cpp
|
||||
test/test_Blit.cpp
|
||||
test/test_MissingRequiredAttributes.cpp
|
||||
|
||||
@@ -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,19 @@ struct DescriptorSetLayoutBinding {
|
||||
DescriptorFlags flags = DescriptorFlags::NONE;
|
||||
uint16_t count = 0;
|
||||
|
||||
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
|
||||
*/
|
||||
@@ -807,6 +812,33 @@ static constexpr bool isStencilFormat(TextureFormat format) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
inline constexpr bool isColorFormat(TextureFormat format) noexcept {
|
||||
switch (format) {
|
||||
// Standard color formats
|
||||
case TextureFormat::R8:
|
||||
case TextureFormat::RG8:
|
||||
case TextureFormat::RGBA8:
|
||||
case TextureFormat::R16F:
|
||||
case TextureFormat::RG16F:
|
||||
case TextureFormat::RGBA16F:
|
||||
case TextureFormat::R32F:
|
||||
case TextureFormat::RG32F:
|
||||
case TextureFormat::RGBA32F:
|
||||
case TextureFormat::RGB10_A2:
|
||||
case TextureFormat::R11F_G11F_B10F:
|
||||
case TextureFormat::SRGB8:
|
||||
case TextureFormat::SRGB8_A8:
|
||||
case TextureFormat::RGB8:
|
||||
case TextureFormat::RGB565:
|
||||
case TextureFormat::RGB5_A1:
|
||||
case TextureFormat::RGBA4:
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static constexpr bool isUnsignedIntFormat(TextureFormat format) {
|
||||
switch (format) {
|
||||
case TextureFormat::R8UI:
|
||||
@@ -938,8 +970,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)
|
||||
@@ -994,6 +1046,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
|
||||
@@ -1001,6 +1054,93 @@ 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;
|
||||
utils::FixedCapacityVector<ExternalSamplerDatum> externalSamplerData;
|
||||
};
|
||||
|
||||
//! blending equation function
|
||||
enum class BlendEquation : uint8_t {
|
||||
ADD, //!< the fragment is added to the color buffer
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -317,6 +318,11 @@ public:
|
||||
*/
|
||||
uint32_t layers;
|
||||
|
||||
/**
|
||||
* The numbers of samples per texel
|
||||
*/
|
||||
VkSampleCountFlagBits samples;
|
||||
|
||||
/**
|
||||
* The format of the external image
|
||||
*/
|
||||
@@ -347,18 +353,37 @@ public:
|
||||
*/
|
||||
uint32_t memoryTypeBits;
|
||||
};
|
||||
virtual ExternalImageMetadata getExternalImageMetadata(void* externalImage);
|
||||
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage);
|
||||
|
||||
using ImageData = std::pair<VkImage, VkDeviceMemory>;
|
||||
virtual ImageData createExternalImage(void* externalImage,
|
||||
const ExternalImageMetadata& metadata);
|
||||
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
|
||||
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
|
||||
VkImageUsageFlags usage);
|
||||
|
||||
virtual VkSampler createExternalSampler(SamplerYcbcrConversion chroma,
|
||||
SamplerParams sampler,
|
||||
uint32_t internalFormat);
|
||||
|
||||
virtual VkImageView createExternalImageView(SamplerYcbcrConversion chroma,
|
||||
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||
VkImageViewType viewType, VkComponentMapping swizzle);
|
||||
|
||||
private:
|
||||
static ExtensionSet getSwapchainInstanceExtensions();
|
||||
static ExternalImageMetadata getExternalImageMetadataImpl(void* externalImage,
|
||||
|
||||
static ExternalImageMetadata getExternalImageMetadataImpl(ExternalImageHandleRef externalImage,
|
||||
VkDevice device);
|
||||
static ImageData createExternalImageImpl(void* externalImage, VkDevice device,
|
||||
const VkAllocationCallbacks* allocator, const ExternalImageMetadata& metadata);
|
||||
|
||||
static ImageData createExternalImageDataImpl(ExternalImageHandleRef externalImage,
|
||||
VkDevice device, const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
|
||||
VkImageUsageFlags usage);
|
||||
static VkSampler createExternalSamplerImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma, SamplerParams sampler,
|
||||
uint32_t internalFormat);
|
||||
static VkImageView createExternalImageViewImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
|
||||
VkImageSubresourceRange range, VkImageViewType viewType,
|
||||
VkComponentMapping swizzle);
|
||||
|
||||
// Platform dependent helper methods
|
||||
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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/Platform.h>
|
||||
|
||||
#include <android/hardware_buffer.h>
|
||||
|
||||
namespace filament::backend::fvkandroid {
|
||||
|
||||
struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
|
||||
AHardwareBuffer* aHardwareBuffer = nullptr;
|
||||
bool sRGB = false;
|
||||
unsigned int width; // Texture width
|
||||
unsigned int height; // Texture height
|
||||
TextureFormat format; // Texture format
|
||||
TextureUsage usage; // Texture usage flags
|
||||
|
||||
protected:
|
||||
~ExternalImageVulkanAndroid() override;
|
||||
};
|
||||
|
||||
Platform::ExternalImageHandle createExternalImage(AHardwareBuffer const* buffer,
|
||||
bool sRGB) noexcept;
|
||||
|
||||
} // namespace filament::backend::fvkandroid
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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; }
|
||||
|
||||
// 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
|
||||
+11
-17
@@ -14,29 +14,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_WEBGPUPLATFORM_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUPLATFORM_H
|
||||
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_BACKENDUTILSANDROID_H
|
||||
#define TNT_FILAMENT_BACKEND_PRIVATE_BACKENDUTILSANDROID_H
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#include "webgpu/webgpu_cpp.h"
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class WebGPUPlatform
|
||||
final : public Platform {
|
||||
public:
|
||||
/**
|
||||
* Maps AHardwareBuffer format to TextureFormat.
|
||||
*/
|
||||
TextureFormat mapToFilamentFormat(unsigned int format, bool isSrgbTransfer) noexcept;
|
||||
|
||||
int getOSVersion() const noexcept final { return 0; }
|
||||
|
||||
~WebGPUPlatform() noexcept override = default;
|
||||
|
||||
protected:
|
||||
|
||||
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
|
||||
};
|
||||
/**
|
||||
* Maps AHardwareBuffer usage to TextureUsage.
|
||||
*/
|
||||
TextureUsage mapToFilamentUsage(unsigned int usage, TextureFormat format) noexcept;
|
||||
|
||||
} // namespace filament
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_WEBGPUPLATFORM_H
|
||||
#endif // TNT_FILAMENT_BACKEND_PRIVATE_BACKENDUTILSANDROID_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)
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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 "private/backend/BackendUtilsAndroid.h"
|
||||
|
||||
#include <android/hardware_buffer.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
TextureFormat mapToFilamentFormat(unsigned int format, bool isSrgbTransfer) noexcept {
|
||||
if (isSrgbTransfer) {
|
||||
switch (format) {
|
||||
case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
|
||||
return TextureFormat::SRGB8;
|
||||
default:
|
||||
return TextureFormat::SRGB8_A8;
|
||||
}
|
||||
}
|
||||
switch (format) {
|
||||
case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
|
||||
case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
|
||||
return TextureFormat::RGBA8;
|
||||
case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
|
||||
case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
|
||||
return TextureFormat::RGB8;
|
||||
case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
|
||||
return TextureFormat::RGB565;
|
||||
case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
|
||||
return TextureFormat::RGBA16F;
|
||||
case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
|
||||
return TextureFormat::RGB10_A2;
|
||||
case AHARDWAREBUFFER_FORMAT_D16_UNORM:
|
||||
return TextureFormat::DEPTH16;
|
||||
case AHARDWAREBUFFER_FORMAT_D24_UNORM:
|
||||
return TextureFormat::DEPTH24;
|
||||
case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
|
||||
return TextureFormat::DEPTH24_STENCIL8;
|
||||
case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
|
||||
return TextureFormat::DEPTH32F;
|
||||
case AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT:
|
||||
return TextureFormat::DEPTH32F_STENCIL8;
|
||||
case AHARDWAREBUFFER_FORMAT_S8_UINT:
|
||||
return TextureFormat::STENCIL8;
|
||||
case AHARDWAREBUFFER_FORMAT_R8_UNORM:
|
||||
return TextureFormat::R8;
|
||||
default:
|
||||
return TextureFormat::UNUSED;
|
||||
}
|
||||
}
|
||||
|
||||
TextureUsage mapToFilamentUsage(unsigned int usage, TextureFormat format) noexcept {
|
||||
TextureUsage usageFlags = TextureUsage::NONE;
|
||||
|
||||
if (usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
|
||||
usageFlags |= TextureUsage::SAMPLEABLE;
|
||||
}
|
||||
|
||||
if (usage & AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER) {
|
||||
if (isDepthFormat(format)) {
|
||||
usageFlags |= TextureUsage::DEPTH_ATTACHMENT;
|
||||
}
|
||||
if (isStencilFormat(format)) {
|
||||
usageFlags |= TextureUsage::STENCIL_ATTACHMENT;
|
||||
}
|
||||
if (isColorFormat(format)) {
|
||||
usageFlags |= TextureUsage::COLOR_ATTACHMENT;
|
||||
}
|
||||
}
|
||||
|
||||
if (usage & AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER) {
|
||||
usageFlags |= TextureUsage::UPLOADABLE;
|
||||
}
|
||||
|
||||
if (usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
|
||||
usageFlags |= TextureUsage::PROTECTED;
|
||||
}
|
||||
|
||||
if (usageFlags == TextureUsage::NONE) {
|
||||
usageFlags = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
|
||||
}
|
||||
|
||||
return usageFlags;
|
||||
}
|
||||
|
||||
} // namespace backend::filament
|
||||
@@ -30,7 +30,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)
|
||||
@@ -66,7 +70,7 @@ filament::backend::Platform* createDefaultMetalPlatform();
|
||||
|
||||
#include "noop/PlatformNoop.h"
|
||||
#if defined(FILAMENT_SUPPORTS_WEBGPU)
|
||||
#include "webgpu/WebGPUPlatform.h"
|
||||
#include "backend/platforms/WebGPUPlatform.h"
|
||||
#endif
|
||||
|
||||
namespace filament::backend {
|
||||
@@ -132,7 +136,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,16 @@ size_t MetalDriver::getMaxUniformBufferSize() {
|
||||
return 256 * 1024 * 1024; // TODO: return the actual size instead of hardcoding the minspec
|
||||
}
|
||||
|
||||
size_t MetalDriver::getMaxTextureSize(SamplerType) {
|
||||
// TODO: return the actual size instead of hardcoding the minspec
|
||||
return 2048;
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
@@ -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,14 @@ size_t NoopDriver::getMaxUniformBufferSize() {
|
||||
return 16384u;
|
||||
}
|
||||
|
||||
size_t NoopDriver::getMaxTextureSize(SamplerType target) {
|
||||
return 2048u;
|
||||
}
|
||||
|
||||
size_t NoopDriver::getMaxArrayTextureLayers() {
|
||||
return 256u;
|
||||
}
|
||||
|
||||
void NoopDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
|
||||
uint32_t byteOffset) {
|
||||
scheduleDestroy(std::move(p));
|
||||
|
||||
@@ -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 = "
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -2460,6 +2460,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
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -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() {
|
||||
@@ -92,12 +114,22 @@ private:
|
||||
|
||||
Driver* PlatformOSMesa::createDriver(void* const 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!";
|
||||
|
||||
|
||||
@@ -554,13 +554,45 @@ 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();
|
||||
const auto& metadata = mPlatform->getExternalImageMetadata(externalImage);
|
||||
if (metadata.isProtected) {
|
||||
usage |= backend::TextureUsage::PROTECTED;
|
||||
}
|
||||
|
||||
// FIXME: implement createTextureExternalImage2R
|
||||
VkImageUsageFlags vkUsage = metadata.usage;
|
||||
if (any(usage & TextureUsage::BLIT_SRC)) {
|
||||
vkUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
}
|
||||
|
||||
if (any(usage & (TextureUsage::BLIT_DST & TextureUsage::UPLOADABLE))) {
|
||||
vkUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
|
||||
}
|
||||
|
||||
assert_invariant(width == metadata.width);
|
||||
assert_invariant(height == metadata.height);
|
||||
assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
|
||||
|
||||
VkMemoryPropertyFlags const requiredMemoryFlags = any(usage & TextureUsage::UPLOADABLE)
|
||||
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
|
||||
: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
|
||||
uint32_t const memoryTypeIndex =
|
||||
mContext.selectMemoryType(metadata.memoryTypeBits, requiredMemoryFlags);
|
||||
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex != VK_MAX_MEMORY_TYPES)
|
||||
<< "failed to find a valid memory type for external image memory.";
|
||||
|
||||
const auto& data =
|
||||
mPlatform->createExternalImageData(externalImage, metadata, memoryTypeIndex, vkUsage);
|
||||
|
||||
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
|
||||
mAllocator, &mResourceManager, &mCommands, data.first, data.second, metadata.format,
|
||||
metadata.samples, metadata.width, metadata.height, metadata.layerCount, usage,
|
||||
mStagePool);
|
||||
|
||||
texture.inc();
|
||||
}
|
||||
|
||||
void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
|
||||
@@ -568,22 +600,7 @@ void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::Sa
|
||||
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();
|
||||
// not supported in this backend
|
||||
}
|
||||
|
||||
void VulkanDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
|
||||
@@ -1130,6 +1147,16 @@ size_t VulkanDriver::getMaxUniformBufferSize() {
|
||||
return 32768;
|
||||
}
|
||||
|
||||
size_t VulkanDriver::getMaxTextureSize(SamplerType) {
|
||||
// TODO: return the actual size instead of hardcoded value
|
||||
return 2048;
|
||||
}
|
||||
|
||||
size_t VulkanDriver::getMaxArrayTextureLayers() {
|
||||
// TODO: return the actual size instead of hardcoded value
|
||||
return 256;
|
||||
}
|
||||
|
||||
void VulkanDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index,
|
||||
Handle<HwBufferObject> boh) {
|
||||
auto vb = resource_ptr<VulkanVertexBuffer>::cast(&mResourceManager, vbh);
|
||||
@@ -1266,10 +1293,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,
|
||||
|
||||
@@ -23,79 +23,6 @@ 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) {}
|
||||
|
||||
@@ -106,18 +33,18 @@ VkSampler VulkanSamplerCache::getSampler(SamplerParams params) noexcept {
|
||||
}
|
||||
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,
|
||||
.magFilter = fvkutils::getFilter(params.filterMag),
|
||||
.minFilter = fvkutils::getFilter(params.filterMin),
|
||||
.mipmapMode = fvkutils::getMipmapMode(params.filterMin),
|
||||
.addressModeU = fvkutils::getWrapMode(params.wrapS),
|
||||
.addressModeV = fvkutils::getWrapMode(params.wrapT),
|
||||
.addressModeW = fvkutils::getWrapMode(params.wrapR),
|
||||
.anisotropyEnable = params.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
|
||||
.maxAnisotropy = (float)(1u << params.anisotropyLog2),
|
||||
.compareEnable = getCompareEnable(params.compareMode),
|
||||
.compareEnable = fvkutils::getCompareEnable(params.compareMode),
|
||||
.compareOp = fvkutils::getCompareOp(params.compareFunc),
|
||||
.minLod = 0.0f,
|
||||
.maxLod = getMaxLod(params.filterMin),
|
||||
.maxLod = fvkutils::getMaxLod(params.filterMin),
|
||||
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
|
||||
.unnormalizedCoordinates = VK_FALSE
|
||||
};
|
||||
|
||||
@@ -55,63 +55,61 @@ VkComponentMapping composeSwizzle(VkComponentMapping const& prev, VkComponentMap
|
||||
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,7 +151,6 @@ uint8_t getLayerCountFromDepth(uint32_t const depth) {
|
||||
return getLayerCount(getSamplerTypeFromDepth(depth), depth);
|
||||
}
|
||||
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator,
|
||||
@@ -249,12 +246,19 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
|
||||
// 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);
|
||||
// Lazily allocated memory is available.
|
||||
context.isLazilyAllocatedMemorySupported() &&
|
||||
// Usage consists of attachment flags only.
|
||||
none(tusage & ~TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Usage contains at least one attachment flag.
|
||||
any(tusage & TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Depth resolve cannot use transient attachment because it uses a custom shader.
|
||||
// TODO: see VulkanDriver::isDepthStencilResolveSupported() to know when to remove this
|
||||
// restriction.
|
||||
// Note that the custom shader does not resolve stencil. We do need to move to vk 1.2
|
||||
// and above to be able to support stencil resolve (along with depth).
|
||||
!(any(usage & TextureUsage::DEPTH_ATTACHMENT) && samples > 1);
|
||||
|
||||
mState->mIsTransientAttachment = useTransientAttachment;
|
||||
|
||||
const VkImageUsageFlags transientFlag =
|
||||
|
||||
@@ -978,15 +978,28 @@ VkQueue VulkanPlatform::getProtectedGraphicsQueue() const noexcept {
|
||||
}
|
||||
|
||||
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadata(
|
||||
void* externalImage) {
|
||||
ExternalImageHandleRef externalImage) {
|
||||
return getExternalImageMetadataImpl(externalImage, mImpl->mDevice);
|
||||
}
|
||||
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImage(void* externalImage,
|
||||
const ExternalImageMetadata& metadata) {
|
||||
return createExternalImageImpl(externalImage, mImpl->mDevice, nullptr, metadata);
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageData(
|
||||
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata,
|
||||
uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||
return createExternalImageDataImpl(externalImage, mImpl->mDevice, metadata, memoryTypeIndex,
|
||||
usage);
|
||||
}
|
||||
|
||||
VkSampler VulkanPlatform::createExternalSampler(SamplerYcbcrConversion chroma,
|
||||
SamplerParams sampler, uint32_t internalFormat) {
|
||||
return createExternalSamplerImpl(mImpl->mDevice, chroma, sampler, internalFormat);
|
||||
}
|
||||
|
||||
VkImageView VulkanPlatform::createExternalImageView(SamplerYcbcrConversion chroma,
|
||||
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||
VkImageViewType viewType, VkComponentMapping swizzle) {
|
||||
return createExternalImageViewImpl(mImpl->mDevice, chroma, internalFormat, image, range,
|
||||
viewType, swizzle);
|
||||
}
|
||||
#undef SWAPCHAIN_RET_FUNC
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
@@ -15,26 +15,59 @@
|
||||
*/
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/platforms/VulkanPlatformAndroid.h>
|
||||
#include <private/backend/BackendUtilsAndroid.h>
|
||||
|
||||
#include "vulkan/VulkanConstants.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include "vulkan/utils/Image.h"
|
||||
#include "vulkan/utils/Conversion.h"
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
#include <android/hardware_buffer.h>
|
||||
#include <android/native_window.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
using namespace bluevk;
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
void getVKFormatAndUsage(const AHardwareBuffer_Desc& desc, VkFormat& format,
|
||||
VkImageUsageFlags& usage, bool& isProtected) {
|
||||
|
||||
VkFormat transformVkFormat(VkFormat format, bool sRGB) {
|
||||
if (!sRGB) {
|
||||
return format;
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case VK_FORMAT_R8G8B8A8_UNORM:
|
||||
format = VK_FORMAT_R8G8B8A8_SRGB;
|
||||
break;
|
||||
case VK_FORMAT_R8G8B8_UNORM:
|
||||
format = VK_FORMAT_R8G8B8_SRGB;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return format;
|
||||
}
|
||||
|
||||
bool isProtectedFromUsage(uint64_t usage) {
|
||||
return (usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) ? true : false;
|
||||
}
|
||||
|
||||
std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer_Desc& desc,
|
||||
bool sRGB) {
|
||||
VkFormat format = VK_FORMAT_UNDEFINED;
|
||||
VkImageUsageFlags usage = 0;
|
||||
// Refer to "11.2.17. External Memory Handle Types" in the spec, and
|
||||
// Tables 13/14 for how the following derivation works.
|
||||
bool isDepthFormat = false;
|
||||
isProtected = false;
|
||||
switch (desc.format) {
|
||||
case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
|
||||
format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
@@ -82,6 +115,8 @@ void getVKFormatAndUsage(const AHardwareBuffer_Desc& desc, VkFormat& format,
|
||||
format = VK_FORMAT_UNDEFINED;
|
||||
}
|
||||
|
||||
format = transformVkFormat(format, sRGB);
|
||||
|
||||
// The following only concern usage flags derived from Table 14.
|
||||
usage = 0;
|
||||
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
|
||||
@@ -98,16 +133,14 @@ void getVKFormatAndUsage(const AHardwareBuffer_Desc& desc, VkFormat& format,
|
||||
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER) {
|
||||
usage = VK_IMAGE_USAGE_STORAGE_BIT;
|
||||
}
|
||||
if (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
|
||||
isProtected = true;
|
||||
}
|
||||
|
||||
return { format, usage };
|
||||
}
|
||||
|
||||
VulkanPlatform::ImageData allocateExternalImage(void* externalBuffer,
|
||||
VkDevice device, const VkAllocationCallbacks* allocator,
|
||||
VulkanPlatform::ExternalImageMetadata const& metadata) {
|
||||
VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevice device,
|
||||
VulkanPlatform::ExternalImageMetadata const& metadata, uint32_t memoryTypeIndex,
|
||||
VkImageUsageFlags usage) {
|
||||
VulkanPlatform::ImageData data;
|
||||
AHardwareBuffer* buffer = static_cast<AHardwareBuffer*>(externalBuffer);
|
||||
|
||||
// if external format we need to specifiy it in the allocation
|
||||
const bool useExternalFormat = metadata.format == VK_FORMAT_UNDEFINED;
|
||||
@@ -115,8 +148,8 @@ VulkanPlatform::ImageData allocateExternalImage(void* externalBuffer,
|
||||
const VkExternalFormatANDROID externalFormat = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||
.pNext = nullptr,
|
||||
.externalFormat = metadata
|
||||
.externalFormat,// pass down the format (external means we don't have it VK defined)
|
||||
// pass down the format (external means we don't have it VK defined)
|
||||
.externalFormat = metadata.externalFormat,
|
||||
};
|
||||
const VkExternalMemoryImageCreateInfo externalCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
|
||||
@@ -135,12 +168,10 @@ VulkanPlatform::ImageData allocateExternalImage(void* externalBuffer,
|
||||
};
|
||||
imageInfo.mipLevels = 1;
|
||||
imageInfo.arrayLayers = metadata.layers;
|
||||
imageInfo.usage = metadata.usage;
|
||||
// In the unprotected case add R/W capabilities
|
||||
if (metadata.isProtected == false)
|
||||
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
imageInfo.samples = metadata.samples;
|
||||
imageInfo.usage = usage;
|
||||
|
||||
VkResult result = vkCreateImage(device, &imageInfo, allocator, &data.first);
|
||||
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &data.first);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkCreateImage failed with error=" << static_cast<int32_t>(result);
|
||||
|
||||
@@ -156,36 +187,66 @@ VulkanPlatform::ImageData allocateExternalImage(void* externalBuffer,
|
||||
.image = data.first,
|
||||
.buffer = VK_NULL_HANDLE,
|
||||
};
|
||||
VkMemoryAllocateInfo allocInfo = {.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
VkMemoryAllocateInfo allocInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.pNext = &memoryDedicatedAllocateInfo,
|
||||
.allocationSize = metadata.allocationSize,
|
||||
.memoryTypeIndex = metadata.memoryTypeBits};
|
||||
result = vkAllocateMemory(device, &allocInfo, allocator, &data.second);
|
||||
.memoryTypeIndex = memoryTypeIndex,
|
||||
};
|
||||
result = vkAllocateMemory(device, &allocInfo, VKALLOC, &data.second);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkAllocateMemory failed with error=" << static_cast<int32_t>(result);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
}// namespace
|
||||
} // namespace
|
||||
|
||||
namespace fvkandroid {
|
||||
|
||||
ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() = default;
|
||||
|
||||
Platform::ExternalImageHandle createExternalImage(AHardwareBuffer const* buffer,
|
||||
bool sRGB) noexcept {
|
||||
if (__builtin_available(android 26, *)) {
|
||||
AHardwareBuffer_Desc hardwareBufferDescription = {};
|
||||
AHardwareBuffer_describe(buffer, &hardwareBufferDescription);
|
||||
|
||||
auto* const p = new (std::nothrow) ExternalImageVulkanAndroid;
|
||||
p->aHardwareBuffer = const_cast<AHardwareBuffer*>(buffer);
|
||||
p->sRGB = sRGB;
|
||||
p->height = hardwareBufferDescription.height;
|
||||
p->width = hardwareBufferDescription.width;
|
||||
TextureFormat textureFormat = mapToFilamentFormat(hardwareBufferDescription.format, sRGB);
|
||||
p->format = textureFormat;
|
||||
p->usage = mapToFilamentUsage(hardwareBufferDescription.usage, textureFormat);
|
||||
return Platform::ExternalImageHandle{ p };
|
||||
}
|
||||
|
||||
return Platform::ExternalImageHandle{};
|
||||
}
|
||||
|
||||
} // namespace fvkandroid
|
||||
|
||||
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device) {
|
||||
auto const* fvkExternalImage =
|
||||
static_cast<fvkandroid::ExternalImageVulkanAndroid const*>(externalImage.get());
|
||||
|
||||
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(void* externalImage,
|
||||
VkDevice device) {
|
||||
ExternalImageMetadata metadata;
|
||||
AHardwareBuffer* buffer = static_cast<AHardwareBuffer*>(externalImage);
|
||||
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
|
||||
if (__builtin_available(android 26, *)) {
|
||||
AHardwareBuffer_Desc bufferDesc;
|
||||
AHardwareBuffer_describe(buffer, &bufferDesc);
|
||||
metadata.width = bufferDesc.width;
|
||||
metadata.height = bufferDesc.height;
|
||||
metadata.layers = bufferDesc.layers;
|
||||
|
||||
getVKFormatAndUsage(bufferDesc, metadata.format, metadata.usage, metadata.isProtected);
|
||||
}
|
||||
// In the unprotected case add R/W capabilities
|
||||
if (metadata.isProtected == false) {
|
||||
metadata.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
|
||||
metadata.isProtected = isProtectedFromUsage(bufferDesc.usage);
|
||||
std::tie(metadata.format, metadata.usage) =
|
||||
getVKFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
|
||||
}
|
||||
|
||||
metadata.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
|
||||
VkAndroidHardwareBufferFormatPropertiesANDROID formatInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID,
|
||||
@@ -197,26 +258,145 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataIm
|
||||
};
|
||||
VkResult result = vkGetAndroidHardwareBufferPropertiesANDROID(device, buffer, &properties);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkGetAndroidHardwareBufferProperties failed with error="
|
||||
<< static_cast<int32_t>(result);
|
||||
FILAMENT_CHECK_POSTCONDITION(metadata.format == formatInfo.format)
|
||||
<< "mismatched image format for external image (AHB)";
|
||||
<< "vkGetAndroidHardwareBufferProperties failed with error="
|
||||
<< static_cast<int32_t>(result);
|
||||
|
||||
VkFormat bufferPropertiesFormat = transformVkFormat(formatInfo.format, fvkExternalImage->sRGB);
|
||||
FILAMENT_CHECK_POSTCONDITION(metadata.format == bufferPropertiesFormat)
|
||||
<< "mismatched image format( " << metadata.format << ") and queried format("
|
||||
<< bufferPropertiesFormat << ") for external image (AHB)";
|
||||
metadata.externalFormat = formatInfo.externalFormat;
|
||||
metadata.allocationSize = properties.allocationSize;
|
||||
metadata.memoryTypeBits = properties.memoryTypeBits;
|
||||
return metadata;
|
||||
}
|
||||
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageImpl(void* externalImage,
|
||||
VkDevice device, const VkAllocationCallbacks* allocator,
|
||||
const ExternalImageMetadata& metadata) {
|
||||
ImageData data = allocateExternalImage(externalImage, device, allocator, metadata);
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device,
|
||||
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||
auto const* fvkExternalImage =
|
||||
static_cast<fvkandroid::ExternalImageVulkanAndroid const*>(externalImage.get());
|
||||
ImageData data = allocateExternalImage(fvkExternalImage->aHardwareBuffer, device, metadata,
|
||||
memoryTypeIndex, usage);
|
||||
VkResult result = vkBindImageMemory(device, data.first, data.second, 0);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
|
||||
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
|
||||
return data;
|
||||
}
|
||||
|
||||
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device, SamplerYcbcrConversion chroma,
|
||||
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
|
||||
VkImageViewType viewType, VkComponentMapping swizzle){
|
||||
VkExternalFormatANDROID externalFormat = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||
.pNext = nullptr,
|
||||
.externalFormat = internalFormat,
|
||||
};
|
||||
|
||||
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
|
||||
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
|
||||
.pNext = &externalFormat,
|
||||
.format = VK_FORMAT_UNDEFINED,
|
||||
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
|
||||
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
|
||||
.components = fvkutils::getSwizzleMap(swizzleArray),
|
||||
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
|
||||
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
|
||||
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
|
||||
};
|
||||
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
||||
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
|
||||
nullptr, &conversion);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create Ycbcr Conversion."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
||||
.pNext = nullptr,
|
||||
.conversion = conversion,
|
||||
};
|
||||
|
||||
VkImageViewCreateInfo viewInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.pNext = &samplerYcbcrConversionInfo,
|
||||
.flags = 0,
|
||||
.image = image,
|
||||
.viewType = viewType,
|
||||
.format = VK_FORMAT_UNDEFINED,
|
||||
.components = swizzle,
|
||||
.subresourceRange = range,
|
||||
};
|
||||
VkImageView imageView;
|
||||
result = vkCreateImageView(device, &viewInfo, VKALLOC, &imageView);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create VkImageView."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
return imageView;
|
||||
}
|
||||
|
||||
VkSampler VulkanPlatform::createExternalSamplerImpl(
|
||||
VkDevice device, SamplerYcbcrConversion chroma, SamplerParams params,
|
||||
uint32_t internalFormat) {
|
||||
VkExternalFormatANDROID externalFormat = {
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
|
||||
.pNext = nullptr,
|
||||
.externalFormat = internalFormat,
|
||||
};
|
||||
|
||||
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
|
||||
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
|
||||
.pNext = &externalFormat,
|
||||
.format = VK_FORMAT_UNDEFINED,
|
||||
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
|
||||
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
|
||||
.components = fvkutils::getSwizzleMap(swizzleArray),
|
||||
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
|
||||
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
|
||||
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
|
||||
};
|
||||
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
|
||||
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
|
||||
nullptr, &conversion);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create Ycbcr Conversion."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
|
||||
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
|
||||
.pNext = nullptr,
|
||||
.conversion = conversion,
|
||||
};
|
||||
|
||||
VkSamplerCreateInfo samplerInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
|
||||
.pNext = &samplerYcbcrConversionInfo,
|
||||
.magFilter = fvkutils::getFilter(params.filterMag),
|
||||
.minFilter = fvkutils::getFilter(params.filterMin),
|
||||
.mipmapMode = fvkutils::getMipmapMode(params.filterMin),
|
||||
.addressModeU = fvkutils::getWrapMode(params.wrapS),
|
||||
.addressModeV = fvkutils::getWrapMode(params.wrapT),
|
||||
.addressModeW = fvkutils::getWrapMode(params.wrapR),
|
||||
.anisotropyEnable = params.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
|
||||
.maxAnisotropy = (float)(1u << params.anisotropyLog2),
|
||||
.compareEnable = fvkutils::getCompareEnable(params.compareMode),
|
||||
.compareOp = fvkutils::getCompareOp(params.compareFunc),
|
||||
.minLod = 0.0f,
|
||||
.maxLod = fvkutils::getMaxLod(params.filterMin),
|
||||
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
|
||||
.unnormalizedCoordinates = VK_FALSE,
|
||||
};
|
||||
VkSampler sampler;
|
||||
result = vkCreateSampler(device, &samplerInfo, VKALLOC, &sampler);
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create sampler."
|
||||
<< " error=" << static_cast<int32_t>(result);
|
||||
return sampler;
|
||||
}
|
||||
|
||||
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
|
||||
return {
|
||||
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
|
||||
|
||||
@@ -64,16 +64,29 @@ VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
|
||||
}
|
||||
|
||||
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
|
||||
void* externalImage, VkDevice device) {
|
||||
ExternalImageHandleRef externalImage, VkDevice device) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageImpl(void* externalImage,
|
||||
VkDevice device, const VkAllocationCallbacks* allocator,
|
||||
const ExternalImageMetadata& metadata) {
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device,
|
||||
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma,
|
||||
SamplerParams sampler,
|
||||
uint32_t internalFormat) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
|
||||
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWindow,
|
||||
VkInstance instance, uint64_t flags) noexcept {
|
||||
VkSurfaceKHR surface;
|
||||
|
||||
@@ -85,16 +85,29 @@ using namespace bluevk;
|
||||
namespace filament::backend {
|
||||
|
||||
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
|
||||
void* externalImage, VkDevice device) {
|
||||
ExternalImageHandleRef externalImage, VkDevice device) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageImpl(void* externalImage,
|
||||
VkDevice device, const VkAllocationCallbacks* allocator,
|
||||
const ExternalImageMetadata& metadata) {
|
||||
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
|
||||
ExternalImageHandleRef externalImage, VkDevice device,
|
||||
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
|
||||
return {};
|
||||
}
|
||||
|
||||
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma,
|
||||
SamplerParams sampler,
|
||||
uint32_t internalFormat) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
|
||||
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
|
||||
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
|
||||
VulkanPlatform::ExtensionSet const ret = {
|
||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
|
||||
@@ -588,6 +588,79 @@ VkComponentMapping getSwizzleMap(TextureSwizzle const swizzle[4]) {
|
||||
return map;
|
||||
}
|
||||
|
||||
VkFilter getFilter(SamplerMinFilter filter) {
|
||||
switch (filter) {
|
||||
case SamplerMinFilter::NEAREST:
|
||||
return VK_FILTER_NEAREST;
|
||||
case SamplerMinFilter::LINEAR:
|
||||
return VK_FILTER_LINEAR;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
|
||||
return VK_FILTER_NEAREST;
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
|
||||
return VK_FILTER_LINEAR;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
|
||||
return VK_FILTER_NEAREST;
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
|
||||
return VK_FILTER_LINEAR;
|
||||
}
|
||||
}
|
||||
|
||||
VkFilter getFilter(SamplerMagFilter filter) {
|
||||
switch (filter) {
|
||||
case SamplerMagFilter::NEAREST:
|
||||
return VK_FILTER_NEAREST;
|
||||
case SamplerMagFilter::LINEAR:
|
||||
return VK_FILTER_LINEAR;
|
||||
}
|
||||
}
|
||||
|
||||
VkSamplerMipmapMode getMipmapMode(SamplerMinFilter filter) {
|
||||
switch (filter) {
|
||||
case SamplerMinFilter::NEAREST:
|
||||
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
case SamplerMinFilter::LINEAR:
|
||||
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
|
||||
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
|
||||
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
|
||||
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
|
||||
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
|
||||
}
|
||||
}
|
||||
|
||||
VkSamplerAddressMode getWrapMode(SamplerWrapMode mode) {
|
||||
switch (mode) {
|
||||
case SamplerWrapMode::REPEAT:
|
||||
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
|
||||
case SamplerWrapMode::CLAMP_TO_EDGE:
|
||||
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
|
||||
case SamplerWrapMode::MIRRORED_REPEAT:
|
||||
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
|
||||
}
|
||||
}
|
||||
|
||||
VkBool32 getCompareEnable(SamplerCompareMode mode) {
|
||||
return mode == SamplerCompareMode::NONE ? VK_FALSE : VK_TRUE;
|
||||
}
|
||||
|
||||
float getMaxLod(SamplerMinFilter filter) {
|
||||
switch (filter) {
|
||||
case SamplerMinFilter::NEAREST:
|
||||
case SamplerMinFilter::LINEAR:
|
||||
// The Vulkan spec recommends a max LOD of 0.25 to "disable" mipmapping.
|
||||
// See "Mapping of OpenGL to Vulkan filter modes" in the VK Spec.
|
||||
return 0.25f;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
|
||||
return VK_LOD_CLAMP_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags) {
|
||||
VkShaderStageFlags flags = 0x0;
|
||||
if (any(stageFlags & ShaderStageFlags::VERTEX)) flags |= VK_SHADER_STAGE_VERTEX_BIT;
|
||||
|
||||
@@ -56,6 +56,19 @@ uint32_t getComponentCount(VkFormat format);
|
||||
VkComponentMapping getSwizzleMap(TextureSwizzle const swizzle[4]);
|
||||
VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags);
|
||||
|
||||
// Needed by the Platform for external sampler creation
|
||||
VkFilter getFilter(SamplerMinFilter filter);
|
||||
VkFilter getFilter(SamplerMagFilter filter);
|
||||
VkSamplerMipmapMode getMipmapMode(SamplerMinFilter filter);
|
||||
VkSamplerAddressMode getWrapMode(SamplerWrapMode mode);
|
||||
VkBool32 getCompareEnable(SamplerCompareMode mode);
|
||||
float getMaxLod(SamplerMinFilter filter);
|
||||
|
||||
// Ycbcr related functions
|
||||
VkSamplerYcbcrModelConversion getYcbcrModelConversion(SamplerYcbcrModelConversion model);
|
||||
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range);
|
||||
VkChromaLocation getChromaLocation(ChromaLocation loc);
|
||||
|
||||
inline VkImageViewType getViewType(SamplerType target) {
|
||||
switch (target) {
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
|
||||
@@ -193,6 +193,49 @@ uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask) {
|
||||
return mostSignificantBit((sampleCount - 1) & mask);
|
||||
}
|
||||
|
||||
VkSamplerYcbcrModelConversion getYcbcrModelConversion(
|
||||
SamplerYcbcrModelConversion model) {
|
||||
switch (model) {
|
||||
case SamplerYcbcrModelConversion::RGB_IDENTITY:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
|
||||
case SamplerYcbcrModelConversion::YCBCR_IDENTITY:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY;
|
||||
case SamplerYcbcrModelConversion::YCBCR_709:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709;
|
||||
case SamplerYcbcrModelConversion::YCBCR_601:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601;
|
||||
case SamplerYcbcrModelConversion::YCBCR_2020:
|
||||
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020;
|
||||
default:
|
||||
assert_invariant(false &&
|
||||
"Unknown data type, conversion is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range) {
|
||||
switch (range) {
|
||||
case SamplerYcbcrRange::ITU_FULL:
|
||||
return VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
|
||||
case SamplerYcbcrRange::ITU_NARROW:
|
||||
return VK_SAMPLER_YCBCR_RANGE_ITU_NARROW;
|
||||
default:
|
||||
assert_invariant(false &&
|
||||
"Unknown data type, conversion is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
VkChromaLocation getChromaLocation(ChromaLocation loc) {
|
||||
switch (loc) {
|
||||
case ChromaLocation::COSITED_EVEN:
|
||||
return VK_CHROMA_LOCATION_COSITED_EVEN;
|
||||
case ChromaLocation::MIDPOINT:
|
||||
return VK_CHROMA_LOCATION_MIDPOINT;
|
||||
default:
|
||||
assert_invariant(false &&
|
||||
"Unknown data type, conversion is not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace filament::backend::fvkutils
|
||||
|
||||
bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b) {
|
||||
|
||||
@@ -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_WEBGPUCONSTANTS_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
|
||||
|
||||
#include <utils/Log.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// FWGPU is short for Filament WebGPU
|
||||
|
||||
// turn on runtime validation, namely for debugging, that would normally not run (for release)
|
||||
#define FWGPU_DEBUG_VALIDATION 0x00000001
|
||||
// print/log system component details to the console, e.g. about the
|
||||
// instance, surface, adapter, device, etc.
|
||||
#define FWGPU_PRINT_SYSTEM 0x00000002
|
||||
|
||||
// Set this to enable logging "only" to one output stream. This is useful in the case where we want
|
||||
// to debug with print statements and want ordered logging (e.g slog.i and slog.e will not appear in
|
||||
// order of calls).
|
||||
#define FWGPU_DEBUG_FORCE_LOG_TO_I 0x00000004
|
||||
|
||||
// Useful default combinations
|
||||
#define FWGPU_DEBUG_EVERYTHING 0xFFFFFFFF
|
||||
|
||||
#if defined(FILAMENT_BACKEND_DEBUG_FLAG)
|
||||
#define FWGPU_DEBUG_FORWARDED_FLAG (FILAMENT_BACKEND_DEBUG_FLAG & FWGPU_DEBUG_EVERYTHING)
|
||||
#else
|
||||
#define FWGPU_DEBUG_FORWARDED_FLAG 0
|
||||
#endif
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define FWGPU_DEBUG_FLAGS FWGPU_DEBUG_FORWARDED_FLAG
|
||||
#else
|
||||
#define FWGPU_DEBUG_FLAGS 0
|
||||
#endif
|
||||
|
||||
#define FWGPU_ENABLED(flags) (((FWGPU_DEBUG_FLAGS) & (flags)) == (flags))
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_DEBUG_FORCE_LOG_TO_I)
|
||||
#define FWGPU_LOGI (utils::slog.i)
|
||||
#define FWGPU_LOGD FWGPU_LOGI
|
||||
#define FWGPU_LOGE FWGPU_LOGI
|
||||
#define FWGPU_LOGW FWGPU_LOGI
|
||||
#else
|
||||
#define FWGPU_LOGE (utils::slog.e)
|
||||
#define FWGPU_LOGW (utils::slog.w)
|
||||
#define FWGPU_LOGD (utils::slog.d)
|
||||
#define FWGPU_LOGI (utils::slog.i)
|
||||
#endif
|
||||
|
||||
#endif// TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
|
||||
@@ -14,21 +14,254 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "webgpu/WebGPUDriver.h"
|
||||
|
||||
#include "webgpu/WebGPUConstants.h"
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include "CommandStreamDispatcher.h"
|
||||
#include "DriverBase.h"
|
||||
#include "private/backend/Dispatcher.h"
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include "webgpu/WebGPUDriver.h"
|
||||
#include "CommandStreamDispatcher.h"
|
||||
#include <math/mat3.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <dawn/webgpu_cpp_print.h>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
Driver* WebGPUDriver::create() {
|
||||
return new WebGPUDriver();
|
||||
namespace {
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printInstanceDetails(wgpu::Instance const& instance) {
|
||||
wgpu::SupportedWGSLLanguageFeatures supportedWGSLLanguageFeatures{};
|
||||
if (!instance.GetWGSLLanguageFeatures(&supportedWGSLLanguageFeatures)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU instance supported WGSL language features"
|
||||
<< utils::io::endl;
|
||||
} else {
|
||||
FWGPU_LOGI << "WebGPU instance supported WGSL language features ("
|
||||
<< supportedWGSLLanguageFeatures.featureCount << "):" << utils::io::endl;
|
||||
if (supportedWGSLLanguageFeatures.featureCount > 0 &&
|
||||
supportedWGSLLanguageFeatures.features != nullptr) {
|
||||
std::for_each(supportedWGSLLanguageFeatures.features,
|
||||
supportedWGSLLanguageFeatures.features +
|
||||
supportedWGSLLanguageFeatures.featureCount,
|
||||
[](wgpu::WGSLLanguageFeatureName const featureName) {
|
||||
std::stringstream nameStream{};
|
||||
nameStream << featureName;
|
||||
FWGPU_LOGI << " " << nameStream.str() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printLimit(std::string_view name, const std::variant<uint32_t, uint64_t> value) {
|
||||
FWGPU_LOGI << " " << name.data() << ": ";
|
||||
bool undefined = true;
|
||||
if (std::holds_alternative<uint32_t>(value)) {
|
||||
if (std::get<uint32_t>(value) != WGPU_LIMIT_U32_UNDEFINED) {
|
||||
undefined = false;
|
||||
FWGPU_LOGI << std::get<uint32_t>(value);
|
||||
}
|
||||
} else if (std::holds_alternative<uint64_t>(value)) {
|
||||
if (std::get<uint64_t>(value) != WGPU_LIMIT_U64_UNDEFINED) {
|
||||
undefined = false;
|
||||
FWGPU_LOGI << std::get<uint64_t>(value);
|
||||
}
|
||||
}
|
||||
if (undefined) {
|
||||
FWGPU_LOGI << "UNDEFINED";
|
||||
}
|
||||
FWGPU_LOGI << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printLimits(wgpu::Limits const& limits) {
|
||||
printLimit("maxTextureDimension1D", limits.maxTextureDimension1D);
|
||||
printLimit("maxTextureDimension2D", limits.maxTextureDimension2D);
|
||||
printLimit("maxTextureDimension3D", limits.maxTextureDimension3D);
|
||||
printLimit("maxTextureArrayLayers", limits.maxTextureArrayLayers);
|
||||
printLimit("maxBindGroups", limits.maxBindGroups);
|
||||
printLimit("maxBindGroupsPlusVertexBuffers", limits.maxBindGroupsPlusVertexBuffers);
|
||||
printLimit("maxBindingsPerBindGroup", limits.maxBindingsPerBindGroup);
|
||||
printLimit("maxDynamicUniformBuffersPerPipelineLayout",
|
||||
limits.maxDynamicUniformBuffersPerPipelineLayout);
|
||||
printLimit("maxDynamicStorageBuffersPerPipelineLayout",
|
||||
limits.maxDynamicStorageBuffersPerPipelineLayout);
|
||||
printLimit("maxSampledTexturesPerShaderStage", limits.maxSampledTexturesPerShaderStage);
|
||||
printLimit("maxSamplersPerShaderStage", limits.maxSamplersPerShaderStage);
|
||||
printLimit("maxStorageBuffersPerShaderStage", limits.maxStorageBuffersPerShaderStage);
|
||||
printLimit("maxStorageTexturesPerShaderStage", limits.maxStorageTexturesPerShaderStage);
|
||||
printLimit("maxUniformBuffersPerShaderStage", limits.maxUniformBuffersPerShaderStage);
|
||||
printLimit("maxUniformBufferBindingSize", limits.maxUniformBufferBindingSize);
|
||||
printLimit("maxStorageBufferBindingSize", limits.maxStorageBufferBindingSize);
|
||||
printLimit("minUniformBufferOffsetAlignment", limits.minUniformBufferOffsetAlignment);
|
||||
printLimit("minStorageBufferOffsetAlignment", limits.minStorageBufferOffsetAlignment);
|
||||
printLimit("maxVertexBuffers", limits.maxVertexBuffers);
|
||||
printLimit("maxBufferSize", limits.maxBufferSize);
|
||||
printLimit("maxVertexAttributes", limits.maxVertexAttributes);
|
||||
printLimit("maxVertexBufferArrayStride", limits.maxVertexBufferArrayStride);
|
||||
printLimit("maxInterStageShaderComponents", limits.maxInterStageShaderComponents);
|
||||
printLimit("maxInterStageShaderVariables", limits.maxInterStageShaderVariables);
|
||||
printLimit("maxColorAttachments", limits.maxColorAttachments);
|
||||
printLimit("maxColorAttachmentBytesPerSample", limits.maxColorAttachmentBytesPerSample);
|
||||
printLimit("maxComputeWorkgroupStorageSize", limits.maxComputeWorkgroupStorageSize);
|
||||
printLimit("maxComputeInvocationsPerWorkgroup", limits.maxComputeInvocationsPerWorkgroup);
|
||||
printLimit("maxComputeWorkgroupSizeX", limits.maxComputeWorkgroupSizeX);
|
||||
printLimit("maxComputeWorkgroupSizeY", limits.maxComputeWorkgroupSizeY);
|
||||
printLimit("maxComputeWorkgroupSizeZ", limits.maxComputeWorkgroupSizeZ);
|
||||
printLimit("maxComputeWorkgroupsPerDimension", limits.maxComputeWorkgroupsPerDimension);
|
||||
printLimit("maxStorageBuffersInVertexStage", limits.maxStorageBuffersInVertexStage);
|
||||
printLimit("maxStorageTexturesInVertexStage", limits.maxStorageTexturesInVertexStage);
|
||||
printLimit("maxStorageBuffersInFragmentStage", limits.maxStorageBuffersInFragmentStage);
|
||||
printLimit("maxStorageTexturesInFragmentStage", limits.maxStorageTexturesInFragmentStage);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printAdapterDetails(wgpu::Adapter const& adapter) {
|
||||
wgpu::DawnAdapterPropertiesPowerPreference powerPreferenceProperties{};
|
||||
wgpu::AdapterInfo adapterInfo{};
|
||||
adapterInfo.nextInChain = &powerPreferenceProperties;
|
||||
if (!adapter.GetInfo(&adapterInfo)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU adapter info" << utils::io::endl;
|
||||
} else {
|
||||
std::stringstream backendTypeStream{};
|
||||
backendTypeStream << adapterInfo.backendType;
|
||||
std::stringstream adapterTypeStream{};
|
||||
adapterTypeStream << adapterInfo.adapterType;
|
||||
std::stringstream powerPreferenceStream{};
|
||||
powerPreferenceStream << powerPreferenceProperties.powerPreference;
|
||||
FWGPU_LOGI << "WebGPU adapter info:" << utils::io::endl;
|
||||
FWGPU_LOGI << " vendor: " << adapterInfo.vendor.data << utils::io::endl;
|
||||
FWGPU_LOGI << " architecture: " << adapterInfo.architecture.data << utils::io::endl;
|
||||
FWGPU_LOGI << " device: " << adapterInfo.device.data << utils::io::endl;
|
||||
FWGPU_LOGI << " description: " << adapterInfo.description.data << utils::io::endl;
|
||||
FWGPU_LOGI << " backend type: " << backendTypeStream.str().data() << utils::io::endl;
|
||||
FWGPU_LOGI << " adapter type: " << adapterTypeStream.str().data() << utils::io::endl;
|
||||
FWGPU_LOGI << " device ID: " << adapterInfo.deviceID << utils::io::endl;
|
||||
FWGPU_LOGI << " vendor ID: " << adapterInfo.vendorID << utils::io::endl;
|
||||
FWGPU_LOGI << " subgroup min size: " << adapterInfo.subgroupMinSize << utils::io::endl;
|
||||
FWGPU_LOGI << " subgroup max size: " << adapterInfo.subgroupMaxSize << utils::io::endl;
|
||||
FWGPU_LOGI << " compatibility mode: " << bool(adapterInfo.compatibilityMode)
|
||||
<< utils::io::endl;
|
||||
FWGPU_LOGI << " power preference: " << powerPreferenceStream.str() << utils::io::endl;
|
||||
}
|
||||
wgpu::SupportedFeatures supportedFeatures{};
|
||||
adapter.GetFeatures(&supportedFeatures);
|
||||
FWGPU_LOGI << "WebGPU adapter supported features (" << supportedFeatures.featureCount
|
||||
<< "):" << utils::io::endl;
|
||||
if (supportedFeatures.featureCount > 0 && supportedFeatures.features != nullptr) {
|
||||
std::for_each(supportedFeatures.features,
|
||||
supportedFeatures.features + supportedFeatures.featureCount,
|
||||
[](wgpu::FeatureName const name) {
|
||||
std::stringstream nameStream{};
|
||||
nameStream << name;
|
||||
FWGPU_LOGI << " " << nameStream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
wgpu::SupportedLimits supportedLimits{};
|
||||
if (!adapter.GetLimits(&supportedLimits)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU adapter supported limits" << utils::io::endl;
|
||||
} else {
|
||||
FWGPU_LOGI << "WebGPU adapter supported limits:" << utils::io::endl;
|
||||
printLimits(supportedLimits.limits);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabilities) {
|
||||
std::stringstream usages_stream{};
|
||||
usages_stream << capabilities.usages;
|
||||
FWGPU_LOGI << "WebGPU surface capabilities:" << utils::io::endl;
|
||||
FWGPU_LOGI << " surface usages: " << usages_stream.str().data() << utils::io::endl;
|
||||
FWGPU_LOGI << " surface formats (" << capabilities.formatCount << "):" << utils::io::endl;
|
||||
if (capabilities.formatCount > 0 && capabilities.formats != nullptr) {
|
||||
std::for_each(capabilities.formats, capabilities.formats + capabilities.formatCount,
|
||||
[](wgpu::TextureFormat const format) {
|
||||
std::stringstream format_stream{};
|
||||
format_stream << format;
|
||||
FWGPU_LOGI << " " << format_stream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
FWGPU_LOGI << " surface present modes (" << capabilities.presentModeCount
|
||||
<< "):" << utils::io::endl;
|
||||
if (capabilities.presentModeCount > 0 && capabilities.presentModes != nullptr) {
|
||||
std::for_each(capabilities.presentModes,
|
||||
capabilities.presentModes + capabilities.presentModeCount,
|
||||
[](wgpu::PresentMode const presentMode) {
|
||||
std::stringstream present_mode_stream{};
|
||||
present_mode_stream << presentMode;
|
||||
FWGPU_LOGI << " " << present_mode_stream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
FWGPU_LOGI << " surface alpha modes (" << capabilities.alphaModeCount
|
||||
<< "):" << utils::io::endl;
|
||||
if (capabilities.alphaModeCount > 0 && capabilities.alphaModes != nullptr) {
|
||||
std::for_each(capabilities.alphaModes,
|
||||
capabilities.alphaModes + capabilities.alphaModeCount,
|
||||
[](wgpu::CompositeAlphaMode const alphaMode) {
|
||||
std::stringstream alpha_mode_stream{};
|
||||
alpha_mode_stream << alphaMode;
|
||||
FWGPU_LOGI << " " << alpha_mode_stream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printDeviceDetails(wgpu::Device const& device) {
|
||||
wgpu::SupportedFeatures supportedFeatures{};
|
||||
device.GetFeatures(&supportedFeatures);
|
||||
FWGPU_LOGI << "WebGPU device supported features (" << supportedFeatures.featureCount
|
||||
<< "):" << utils::io::endl;
|
||||
if (supportedFeatures.featureCount > 0 && supportedFeatures.features != nullptr) {
|
||||
std::for_each(supportedFeatures.features,
|
||||
supportedFeatures.features + supportedFeatures.featureCount,
|
||||
[](wgpu::FeatureName const name) {
|
||||
std::stringstream nameStream{};
|
||||
nameStream << name;
|
||||
FWGPU_LOGI << " " << nameStream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
wgpu::SupportedLimits supportedLimits{};
|
||||
if (!device.GetLimits(&supportedLimits)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU supported device limits" << utils::io::endl;
|
||||
} else {
|
||||
FWGPU_LOGI << "WebGPU device supported limits:" << utils::io::endl;
|
||||
printLimits(supportedLimits.limits);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}// namespace
|
||||
|
||||
Driver* WebGPUDriver::create(WebGPUPlatform& platform) noexcept {
|
||||
return new WebGPUDriver(platform);
|
||||
}
|
||||
|
||||
WebGPUDriver::WebGPUDriver() noexcept = default;
|
||||
WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform) noexcept
|
||||
: mPlatform(platform) {
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printInstanceDetails(mPlatform.getInstance());
|
||||
#endif
|
||||
}
|
||||
|
||||
WebGPUDriver::~WebGPUDriver() noexcept = default;
|
||||
|
||||
@@ -45,7 +278,7 @@ ShaderModel WebGPUDriver::getShaderModel() const noexcept {
|
||||
}
|
||||
|
||||
ShaderLanguage WebGPUDriver::getShaderLanguage() const noexcept {
|
||||
return ShaderLanguage::ESSL3;
|
||||
return ShaderLanguage::WGSL;
|
||||
}
|
||||
|
||||
// explicit instantiation of the Dispatcher
|
||||
@@ -123,6 +356,188 @@ void WebGPUDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh)
|
||||
void WebGPUDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) {
|
||||
}
|
||||
|
||||
Handle<HwSwapChain> WebGPUDriver::createSwapChainS() noexcept {
|
||||
return Handle<HwSwapChain>((Handle<HwSwapChain>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwSwapChain> WebGPUDriver::createSwapChainHeadlessS() noexcept {
|
||||
return Handle<HwSwapChain>((Handle<HwSwapChain>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwProgram> WebGPUDriver::createProgramS() noexcept {
|
||||
return Handle<HwProgram>((Handle<HwProgram>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwFence> WebGPUDriver::createFenceS() noexcept {
|
||||
return Handle<HwFence>((Handle<HwFence>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTimerQuery> WebGPUDriver::createTimerQueryS() noexcept {
|
||||
return Handle<HwTimerQuery>((Handle<HwTimerQuery>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
|
||||
return Handle<HwIndexBuffer>((Handle<HwIndexBuffer>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
|
||||
return Handle<HwBufferObject>((Handle<HwBufferObject>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwRenderTarget> WebGPUDriver::createRenderTargetS() noexcept {
|
||||
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwVertexBuffer> WebGPUDriver::createVertexBufferS() noexcept {
|
||||
return Handle<HwVertexBuffer>((Handle<HwVertexBuffer>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwDescriptorSet> WebGPUDriver::createDescriptorSetS() noexcept {
|
||||
return Handle<HwDescriptorSet>((Handle<HwDescriptorSet>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwRenderPrimitive> WebGPUDriver::createRenderPrimitiveS() noexcept {
|
||||
return Handle<HwRenderPrimitive>((Handle<HwRenderPrimitive>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
|
||||
return Handle<HwVertexBufferInfo>((Handle<HwVertexBufferInfo>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
|
||||
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcept {
|
||||
return Handle<HwDescriptorSetLayout>(
|
||||
(Handle<HwDescriptorSetLayout>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureExternalImageS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureExternalImage2S() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
|
||||
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
|
||||
}
|
||||
|
||||
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
|
||||
mSurface= mPlatform.createSurface(nativeWindow, flags);
|
||||
mAdapter = mPlatform.requestAdapter(mSurface);
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printAdapterDetails(mAdapter);
|
||||
wgpu::SurfaceCapabilities surfaceCapabilities{};
|
||||
if (!mSurface.GetCapabilities(mAdapter, &surfaceCapabilities)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU surface capabilities" << utils::io::endl;
|
||||
} else {
|
||||
printSurfaceCapabilitiesDetails(surfaceCapabilities);
|
||||
}
|
||||
#endif
|
||||
mDevice = mPlatform.requestDevice(mAdapter);
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printDeviceDetails(mDevice);
|
||||
#endif
|
||||
mQueue = mDevice.GetQueue();
|
||||
// TODO configure the surface (maybe before or after creating the swapchain?
|
||||
// how do we get the surface extent?)
|
||||
// TODO actually create the swapchain
|
||||
FWGPU_LOGW << "WebGPU support is still essentially a no-op at this point in development (only "
|
||||
"background components have been instantiated/selected, such as surface/screen, "
|
||||
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
|
||||
<< utils::io::endl;
|
||||
#if !FWGPU_ENABLED(FWGPU_PRINT_SYSTEM) && !defined(NDEBUG)
|
||||
FWGPU_LOGI << "If the FILAMENT_BACKEND_DEBUG_FLAG variable were set with the " << utils::io::hex
|
||||
<< FWGPU_PRINT_SYSTEM << utils::io::dec
|
||||
<< " bit flag on during build time the application would print system details "
|
||||
"about the selected graphics device, surface, etc. To see this try "
|
||||
"rebuilding Filament with that flag, e.g. ./build.sh -x "
|
||||
<< FWGPU_PRINT_SYSTEM << " ..." << utils::io::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
|
||||
uint32_t height, uint64_t flags) {}
|
||||
|
||||
void WebGPUDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
|
||||
uint8_t attributeCount, AttributeArray attributes) {}
|
||||
|
||||
void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint32_t vertexCount,
|
||||
Handle<HwVertexBufferInfo> vbih) {}
|
||||
|
||||
void WebGPUDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
|
||||
uint32_t indexCount, BufferUsage usage) {}
|
||||
|
||||
void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
|
||||
BufferObjectBinding bindingType, BufferUsage usage) {}
|
||||
|
||||
void WebGPUDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
|
||||
TextureUsage usage) {}
|
||||
|
||||
void WebGPUDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
uint8_t baseLevel, uint8_t levelCount) {}
|
||||
|
||||
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
|
||||
backend::TextureSwizzle a) {}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
Platform::ExternalImageHandleRef externalImage) {}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void* externalImage) {}
|
||||
|
||||
void WebGPUDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
|
||||
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
|
||||
void* image, uint32_t plane) {}
|
||||
|
||||
void WebGPUDriver::importTextureR(Handle<HwTexture> th, intptr_t id, SamplerType target,
|
||||
uint8_t levels, TextureFormat format, uint8_t samples, uint32_t w, uint32_t h,
|
||||
uint32_t depth, TextureUsage usage) {}
|
||||
|
||||
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
|
||||
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {}
|
||||
|
||||
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {}
|
||||
|
||||
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {}
|
||||
|
||||
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets,
|
||||
uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color,
|
||||
TargetBufferInfo depth, TargetBufferInfo stencil) {}
|
||||
|
||||
void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {}
|
||||
|
||||
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}
|
||||
|
||||
void WebGPUDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
|
||||
backend::DescriptorSetLayout&& info) {}
|
||||
|
||||
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
|
||||
Handle<HwDescriptorSetLayout> dslh) {}
|
||||
|
||||
Handle<HwStream> WebGPUDriver::createStreamNative(void* nativeStream) {
|
||||
return {};
|
||||
}
|
||||
@@ -131,9 +546,11 @@ Handle<HwStream> WebGPUDriver::createStreamAcquired() {
|
||||
return {};
|
||||
}
|
||||
|
||||
void WebGPUDriver::setAcquiredImage(Handle<HwStream> sh, void* image,
|
||||
CallbackHandler* handler, StreamCallback cb, void* userData) {
|
||||
}
|
||||
void WebGPUDriver::setAcquiredImage(Handle<HwStream> sh, void* image, const math::mat3f& transform,
|
||||
CallbackHandler* handler, StreamCallback cb, void* userData) {}
|
||||
|
||||
void WebGPUDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh,
|
||||
BufferObjectStreamDescriptor&& streams) {}
|
||||
|
||||
void WebGPUDriver::setStreamDimensions(Handle<HwStream> sh, uint32_t width, uint32_t height) {
|
||||
}
|
||||
@@ -234,10 +651,18 @@ uint8_t WebGPUDriver::getMaxDrawBuffers() {
|
||||
return MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT;
|
||||
}
|
||||
|
||||
size_t WebGPUDriver::getMaxUniformBufferSize() {
|
||||
size_t WebGPUDriver::getMaxUniformBufferSize(SamplerType) {
|
||||
return 16384u;
|
||||
}
|
||||
|
||||
size_t WebGPUDriver::getMaxTextureSize() {
|
||||
return 2048u;
|
||||
}
|
||||
|
||||
size_t WebGPUDriver::getMaxArrayTextureLayers() {
|
||||
return 256u;
|
||||
}
|
||||
|
||||
void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
|
||||
uint32_t byteOffset) {
|
||||
scheduleDestroy(std::move(p));
|
||||
|
||||
@@ -17,26 +17,44 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
|
||||
|
||||
#include "private/backend/Driver.h"
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include "DriverBase.h"
|
||||
#include "private/backend/Dispatcher.h"
|
||||
#include "private/backend/Driver.h"
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
/**
|
||||
* WebGPU backend (driver) implementation
|
||||
*/
|
||||
class WebGPUDriver final : public DriverBase {
|
||||
WebGPUDriver() noexcept;
|
||||
~WebGPUDriver() noexcept override;
|
||||
Dispatcher getDispatcher() const noexcept final;
|
||||
|
||||
public:
|
||||
static Driver* create();
|
||||
~WebGPUDriver() noexcept override;
|
||||
|
||||
[[nodiscard]] Dispatcher getDispatcher() const noexcept final;
|
||||
[[nodiscard]] static Driver* create(WebGPUPlatform& platform) noexcept;
|
||||
|
||||
private:
|
||||
ShaderModel getShaderModel() const noexcept final;
|
||||
ShaderLanguage getShaderLanguage() const noexcept final;
|
||||
explicit WebGPUDriver(WebGPUPlatform& platform) noexcept;
|
||||
[[nodiscard]] ShaderModel getShaderModel() const noexcept final;
|
||||
[[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final;
|
||||
|
||||
uint64_t nextFakeHandle = 1;
|
||||
// the platform (e.g. OS) specific aspects of the WebGPU backend are strictly only
|
||||
// handled in the WebGPUPlatform
|
||||
WebGPUPlatform& mPlatform;
|
||||
wgpu::Surface mSurface = nullptr;
|
||||
wgpu::Adapter mAdapter = nullptr;
|
||||
wgpu::Device mDevice = nullptr;
|
||||
wgpu::Queue mQueue = nullptr;
|
||||
uint64_t mNextFakeHandle = 1;
|
||||
|
||||
/*
|
||||
* Driver interface
|
||||
@@ -45,20 +63,19 @@ private:
|
||||
template<typename T>
|
||||
friend class ConcreteDispatcher;
|
||||
|
||||
#define DECL_DRIVER_API(methodName, paramsDecl, params) \
|
||||
#define DECL_DRIVER_API(methodName, paramsDecl, params) \
|
||||
UTILS_ALWAYS_INLINE inline void methodName(paramsDecl);
|
||||
|
||||
#define DECL_DRIVER_API_SYNCHRONOUS(RetType, methodName, paramsDecl, params) \
|
||||
#define DECL_DRIVER_API_SYNCHRONOUS(RetType, methodName, paramsDecl, params) \
|
||||
RetType methodName(paramsDecl) override;
|
||||
|
||||
#define DECL_DRIVER_API_RETURN(RetType, methodName, paramsDecl, params) \
|
||||
RetType methodName##S() noexcept override { \
|
||||
return RetType((RetType::HandleId)nextFakeHandle++); } \
|
||||
UTILS_ALWAYS_INLINE inline void methodName##R(RetType, paramsDecl) { }
|
||||
#define DECL_DRIVER_API_RETURN(RetType, methodName, paramsDecl, params) \
|
||||
RetType methodName##S() noexcept override; \
|
||||
UTILS_ALWAYS_INLINE inline void methodName##R(RetType, paramsDecl);
|
||||
|
||||
#include "private/backend/DriverAPI.inc"
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
}// namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
|
||||
#endif// TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "webgpu/WebGPUPlatform.h"
|
||||
#include "webgpu/WebGPUDriver.h"
|
||||
|
||||
#include "utils/Log.h"
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
Driver* WebGPUPlatform::createDriver(void* const sharedContext , const Platform::DriverConfig& driverConfig) noexcept {
|
||||
wgpu::InstanceDescriptor instance_descriptor {};
|
||||
wgpu::Instance instance = wgpu::CreateInstance(&instance_descriptor);
|
||||
if (instance) {
|
||||
utils::slog.i << " WebGPU instance created\n";
|
||||
} else {
|
||||
utils::slog.e << " WebGPU instance failed to create\n";
|
||||
return nullptr;
|
||||
}
|
||||
return WebGPUDriver::create();
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include "webgpu/WebGPUConstants.h"
|
||||
#include "webgpu/WebGPUDriver.h"
|
||||
|
||||
#include <backend/Platform.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <dawn/webgpu_cpp_print.h>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
|
||||
/**
|
||||
* WebGPU Backend implementation common across platforms or operating systems (at least for now).
|
||||
* Some of these functions may likely be refactored to platform/OS-specific implementations
|
||||
* over time as needed. The caller of the WebGPUPlatform doesn't need to care which is the case.
|
||||
*/
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
//either returns a valid instance or panics
|
||||
[[nodiscard]] wgpu::Instance createInstance() {
|
||||
wgpu::DawnTogglesDescriptor dawnTogglesDescriptor{};
|
||||
#if defined(FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING)
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
FWGPU_LOGI << "setting on toggle enable_immediate_error_handling" << utils::io::endl;
|
||||
#endif
|
||||
/**
|
||||
* Have the un-captured error callback invoked immediately when an error occurs, rather than
|
||||
* waiting for the next Tick. This enables using the stack trace in which the un-captured error
|
||||
* occurred when breaking into the un-captured error callback.
|
||||
* https://crbug.com/dawn/1789
|
||||
*/
|
||||
static const char* toggleName = "enable_immediate_error_handling";
|
||||
dawnTogglesDescriptor.enabledToggleCount = 1;
|
||||
dawnTogglesDescriptor.enabledToggles = &toggleName;
|
||||
#endif
|
||||
wgpu::InstanceDescriptor instanceDescriptor{
|
||||
.nextInChain = &dawnTogglesDescriptor,
|
||||
.capabilities = {
|
||||
.timedWaitAnyEnable = true// TODO consider using pure async instead
|
||||
}
|
||||
};
|
||||
wgpu::Instance instance = wgpu::CreateInstance(&instanceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(instance != nullptr) << "Unable to create webgpu instance.";
|
||||
return instance;
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
wgpu::Adapter WebGPUPlatform::requestAdapter(wgpu::Surface const& surface) {
|
||||
// TODO consider power preference etc. (can be custom preferences passed to the platform or
|
||||
// based on whether this is a Mobile or Desktop system,
|
||||
// etc...)
|
||||
wgpu::RequestAdapterOptions adaptorOptions{ .compatibleSurface = surface };
|
||||
// note this just gets the first adapter
|
||||
wgpu::Adapter adapter = nullptr;
|
||||
wgpu::WaitStatus status = mInstance.WaitAny(
|
||||
mInstance.RequestAdapter(&adaptorOptions, wgpu::CallbackMode::WaitAnyOnly,
|
||||
[&adapter](wgpu::RequestAdapterStatus const status,
|
||||
wgpu::Adapter const& readyAdapter, wgpu::StringView const message) {
|
||||
// TODO consider more robust error handling
|
||||
FILAMENT_CHECK_POSTCONDITION(status == wgpu::RequestAdapterStatus::Success)
|
||||
<< "Unable to request a WebGPU adapter. Status "
|
||||
<< static_cast<uint32_t>(status)
|
||||
<< " with message: " << message.data;
|
||||
adapter = readyAdapter;
|
||||
}),
|
||||
UINT16_MAX);// TODO define reasonable timeout (or do this asynchronously)
|
||||
FILAMENT_CHECK_POSTCONDITION(status == wgpu::WaitStatus::Success)
|
||||
<< "Non-successful wait status requesting a WebGPU adapter "
|
||||
<< static_cast<uint32_t>(status);
|
||||
FILAMENT_CHECK_POSTCONDITION(adapter != nullptr)
|
||||
<< "Failed to get a WebGPU adapter for the platform";
|
||||
// TODO consider validating adapter has required features and/or limits
|
||||
return adapter;
|
||||
}
|
||||
|
||||
wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
|
||||
// TODO consider passing required features and/or limits
|
||||
wgpu::DeviceDescriptor deviceDescriptor{};
|
||||
deviceDescriptor.label = "graphics_device";
|
||||
deviceDescriptor.defaultQueue.label = "default_queue";
|
||||
deviceDescriptor.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous,
|
||||
[](wgpu::Device const&, wgpu::DeviceLostReason const& reason,
|
||||
wgpu::StringView message) {
|
||||
if (reason == wgpu::DeviceLostReason::Destroyed) {
|
||||
FWGPU_LOGD << "WebGPU device lost due to being destroyed (expected)"
|
||||
<< utils::io::endl;
|
||||
return;
|
||||
}
|
||||
// TODO try recreating the device instead of just panicking
|
||||
std::stringstream reasonStream{};
|
||||
reasonStream << reason;
|
||||
FILAMENT_CHECK_POSTCONDITION(reason != wgpu::DeviceLostReason::Destroyed)
|
||||
<< "WebGPU device lost: " << reasonStream.str() << " " << message.data;
|
||||
});
|
||||
deviceDescriptor.SetUncapturedErrorCallback(
|
||||
[](wgpu::Device const&, wgpu::ErrorType errorType, wgpu::StringView message) {
|
||||
std::stringstream typeStream{};
|
||||
typeStream << errorType;
|
||||
FWGPU_LOGE << "WebGPU device error: " << typeStream.str() << " " << message.data
|
||||
<< utils::io::endl;
|
||||
});
|
||||
wgpu::Device device = nullptr;
|
||||
wgpu::WaitStatus status = mInstance.WaitAny(
|
||||
adapter.RequestDevice(&deviceDescriptor, wgpu::CallbackMode::WaitAnyOnly,
|
||||
[&device](wgpu::RequestDeviceStatus const status,
|
||||
wgpu::Device const& readyDevice, wgpu::StringView const message) {
|
||||
FILAMENT_CHECK_POSTCONDITION(status == wgpu::RequestDeviceStatus::Success)
|
||||
<< "Unable to request a WebGPU device. Status: "
|
||||
<< static_cast<uint32_t>(status)
|
||||
<< " with message: " << message.data;
|
||||
device = readyDevice;
|
||||
}),
|
||||
UINT64_MAX);// TODO define reasonable timeout (or do this asynchronously)
|
||||
FILAMENT_CHECK_POSTCONDITION(status == wgpu::WaitStatus::Success)
|
||||
<< "Non-successful wait status requesting a WebGPU device "
|
||||
<< static_cast<uint32_t>(status);
|
||||
FILAMENT_CHECK_POSTCONDITION(device != nullptr)
|
||||
<< "Failed to get a WebGPU device for the platform.";
|
||||
return device;
|
||||
}
|
||||
|
||||
Driver* WebGPUPlatform::createDriver(void* const sharedContext,
|
||||
const Platform::DriverConfig& /*driverConfig*/) noexcept {
|
||||
if (sharedContext) {
|
||||
FWGPU_LOGW << "sharedContext is ignored/unused in the WebGPU backend. A non-null "
|
||||
"sharedContext was provided, but it will be ignored."
|
||||
<< utils::io::endl;
|
||||
}
|
||||
return WebGPUDriver::create(*this);
|
||||
}
|
||||
|
||||
WebGPUPlatform::WebGPUPlatform()
|
||||
: mInstance(createInstance()) {}
|
||||
|
||||
}// namespace filament::backend
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* Android OS specific implementation aspects of the WebGPU backend
|
||||
*/
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
wgpu::SurfaceSourceAndroidNativeWindow surfaceSourceAndroidWindow{};
|
||||
surfaceSourceAndroidWindow.window = nativeWindow;
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
.nextInChain = &surfaceSourceAndroidWindow,
|
||||
.label = "android_surface"
|
||||
};
|
||||
wgpu::Surface surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Android-backed surface.";
|
||||
return surface;
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// Platform specific includes and defines
|
||||
#if defined(__APPLE__)
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
#elif defined(FILAMENT_IOS)
|
||||
// Metal is not available when building for the iOS simulator on Desktop.
|
||||
#define METAL_AVAILABLE __has_include(<QuartzCore/CAMetalLayer.h>)
|
||||
#if METAL_AVAILABLE
|
||||
#import <Metal/Metal.h>
|
||||
#import <QuartzCore/CAMetalLayer.h>
|
||||
#endif
|
||||
// is this needed?
|
||||
#define METALVIEW_TAG 255
|
||||
#else
|
||||
#error Not a supported Apple + WebGPU platform
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Apple (Mac OS and IOS) specific implementation aspects of the WebGPU backend
|
||||
*/
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
wgpu::Surface surface = nullptr;
|
||||
#if defined(__APPLE__)
|
||||
auto nsView = (__bridge NSView*) nativeWindow;
|
||||
FILAMENT_CHECK_POSTCONDITION(nsView) << "Unable to obtain Metal-backed NSView.";
|
||||
[nsView setWantsLayer:YES];
|
||||
id metalLayer = [CAMetalLayer layer];
|
||||
[nsView setLayer:metalLayer];
|
||||
wgpu::SurfaceSourceMetalLayer surfaceSourceMetalLayer{};
|
||||
surfaceSourceMetalLayer.layer = (__bridge void*) metalLayer;
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor = {
|
||||
.nextInChain = &surfaceSourceMetalLayer,
|
||||
.label = "metal_surface",
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Metal-backed surface.";
|
||||
#elif defined(FILAMENT_IOS)
|
||||
CAMetalLayer* metalLayer = (CAMetalLayer*) nativeWindow;
|
||||
wgpu::SurfaceSourceMetalLayer surfaceSourceMetalLayer{};
|
||||
surfaceSourceMetalLayer.layer = (__bridge void*) metalLayer;
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor = {
|
||||
.nextInChain = &surfaceSourceMetalLayer,
|
||||
.label = "metal_surface",
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Metal-backed surface.";
|
||||
#else
|
||||
#error Not a supported Apple + WebGPU platform
|
||||
#endif
|
||||
return surface;
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#define LINUX_OR_FREEBSD 1
|
||||
#endif
|
||||
|
||||
// Platform specific includes and defines
|
||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
#include <dlfcn.h>
|
||||
namespace {
|
||||
typedef struct _wl {
|
||||
struct wl_display* display;
|
||||
struct wl_surface* surface;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
} wl;
|
||||
}// namespace
|
||||
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11)
|
||||
// TODO: we should allow for headless on Linux explicitly. Right now this is the headless path
|
||||
// (with no FILAMENT_SUPPORTS_XCB or FILAMENT_SUPPORTS_XLIB).
|
||||
#include <dlfcn.h>
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
#include <xcb/xcb.h>
|
||||
namespace {
|
||||
typedef xcb_connection_t* (*XCB_CONNECT)(const char* displayname, int* screenp);
|
||||
}// namespace
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
#include <X11/Xlib.h>
|
||||
namespace {
|
||||
typedef Display* (*X11_OPEN_DISPLAY)(const char*);
|
||||
}// namespace
|
||||
#endif
|
||||
static constexpr const char* LIBRARY_X11 = "libX11.so.6";
|
||||
namespace {
|
||||
struct XEnv {
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
XCB_CONNECT xcbConnect;
|
||||
xcb_connection_t* connection;
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
X11_OPEN_DISPLAY openDisplay;
|
||||
Display* display;
|
||||
#endif
|
||||
void* library = nullptr;
|
||||
} g_x11;
|
||||
}// namespace
|
||||
#else
|
||||
#error Not a supported Linux or FeeBSD + WebGPU platform
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Linux OS specific implementation aspects of the WebGPU Backend
|
||||
*/
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t flags) {
|
||||
wgpu::Surface surface = nullptr;
|
||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
wl* ptrval = reinterpret_cast<wl*>(nativeWindow);
|
||||
wgpu::SurfaceSourceWaylandSurface surfaceSourceWayland{};
|
||||
surfaceSourceWayland.display = ptrval->display;
|
||||
surfaceSourceWayland.surface = ptrval->surface;
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
.nextInChain = &surfaceSourceWayland,
|
||||
.label = "linux_wayland_surface"
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Unable to create Linux Wayland-backed surface.";
|
||||
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11)
|
||||
if (g_x11.library == nullptr) {
|
||||
g_x11.library = dlopen(LIBRARY_X11, RTLD_LOCAL | RTLD_NOW);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11.library) << "Unable to open X11 library.";
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
g_x11.xcbConnect = (XCB_CONNECT) dlsym(g_x11.library, "xcb_connect");
|
||||
int screen = 0;
|
||||
g_x11.connection = g_x11.xcbConnect(nullptr, &screen);
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
g_x11.openDisplay = (X11_OPEN_DISPLAY) dlsym(g_x11.library, "XOpenDisplay");
|
||||
g_x11.display = g_x11.openDisplay(NULL);
|
||||
FILAMENT_CHECK_PRECONDITION(g_x11.display) << "Unable to open X11 display.";
|
||||
#endif
|
||||
}
|
||||
#if defined(FILAMENT_SUPPORTS_XCB) || defined(FILAMENT_SUPPORTS_XLIB)
|
||||
bool useXcb = false;
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XCB)
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
useXcb = (flags & SWAP_CHAIN_CONFIG_ENABLE_XCB) != 0;
|
||||
#else
|
||||
useXcb = true;
|
||||
#endif
|
||||
if (useXcb) {
|
||||
wgpu::SurfaceSourceXCBWindow surfaceSourceXcb{};
|
||||
surfaceSourceXcb.connection = g_x11.connection;
|
||||
surfaceSourceXcb.window = reinterpret_cast<uint32_t>(nativeWindow);
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
.nextInChain = &surfaceSourceXcb,
|
||||
.label = "linux_xcb_surface"
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Unable to create Linux (or FreeBSD) XCB-backed surface.";
|
||||
}
|
||||
#endif
|
||||
#if defined(FILAMENT_SUPPORTS_XLIB)
|
||||
if (!useXcb) {
|
||||
wgpu::SurfaceSourceXlibWindow surfaceSourceXlib{};
|
||||
surfaceSourceXlib.display = g_x11.display;
|
||||
surfaceSourceXlib.window = reinterpret_cast<uint64_t>(nativeWindow);
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
.nextInChain = &surfaceSourceXlib,
|
||||
.label = "linux_xlib_surface"
|
||||
};
|
||||
surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Unable to create Linux (or FreeBSD) XLib-backed surface.";
|
||||
}
|
||||
#endif
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Cannot create WebGPU X11 surface for Linux (or FreeBSD) OS "
|
||||
"(not built with support for XCB or XLIB?)";
|
||||
#elif defined(__linux__)
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Cannot create WebGPU surface for Linux (or FreeBSD) OS "
|
||||
"(not built with support for Wayland or X11?)";
|
||||
#else
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
|
||||
<< "Not a supported (Linux) OS + WebGPU platform";
|
||||
#endif
|
||||
return surface;
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
/**
|
||||
* Windows OS specific implementation aspects of the WebGPU backend
|
||||
*/
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
// TODO verify this is necessary for Dawn implementation as well:
|
||||
// On (at least) NVIDIA drivers, the Vulkan implementation (specifically the call to
|
||||
// vkGetPhysicalDeviceSurfaceCapabilitiesKHR()) does not correctly handle the fact that
|
||||
// each native window has its own DPI_AWARENESS_CONTEXT, and erroneously uses the context
|
||||
// of the calling thread. As a workaround, we set the current thread's DPI_AWARENESS_CONTEXT
|
||||
// to that of the native window we've been given. This isn't a perfect solution, because an
|
||||
// application could create swap chains on multiple native windows with varying DPI-awareness,
|
||||
// but even then, at least one of the windows would be guaranteed to work correctly.
|
||||
SetThreadDpiAwarenessContext(GetWindowDpiAwarenessContext((HWND) nativeWindow));
|
||||
wgpu::SurfaceSourceWindowsHWND surfaceSourceWin{};
|
||||
surfaceSourceWin.hinstance = GetModuleHandle(nullptr);
|
||||
surfaceSourceWin.hwnd = nativeWindow;
|
||||
wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
.nextInChain = &surfaceSourceWin,
|
||||
.label = "windows_surface"
|
||||
};
|
||||
wgpu::Surface surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Windows-backed surface.";
|
||||
return surface;
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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 "Lifetimes.h"
|
||||
|
||||
#include "backend/Platform.h"
|
||||
#include "private/backend/DriverApi.h"
|
||||
|
||||
RenderFrame::RenderFrame(filament::backend::DriverApi& api): mApi(api) {
|
||||
mApi.beginFrame(0, 0, 0);
|
||||
}
|
||||
|
||||
RenderFrame::~RenderFrame() {
|
||||
mApi.endFrame(0);
|
||||
}
|
||||
|
||||
Cleanup::Cleanup(filament::backend::DriverApi& driverApi) : mDriverApi(driverApi) {}
|
||||
|
||||
Cleanup::~Cleanup() {
|
||||
for (const auto swapChain: mSwapChains) {
|
||||
mDriverApi.destroySwapChain(swapChain);
|
||||
}
|
||||
for (const auto descriptorSet : mDescriptorSets) {
|
||||
mDriverApi.destroyDescriptorSet(descriptorSet);
|
||||
}
|
||||
for (const auto descriptorSetLayout : mDescriptorSetLayouts) {
|
||||
mDriverApi.destroyDescriptorSetLayout(descriptorSetLayout);
|
||||
}
|
||||
for (const auto bufferObject : mBufferObjects) {
|
||||
mDriverApi.destroyBufferObject(bufferObject);
|
||||
}
|
||||
for (const auto program : mPrograms) {
|
||||
mDriverApi.destroyProgram(program);
|
||||
}
|
||||
for (const auto texture : mTextures) {
|
||||
mDriverApi.destroyTexture(texture);
|
||||
}
|
||||
for (const auto renderTarget : mRenderTargets) {
|
||||
mDriverApi.destroyRenderTarget(renderTarget);
|
||||
}
|
||||
|
||||
for (const auto& callback : mCallbacks) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void Cleanup::addInternal(filament::backend::SwapChainHandle handle) {
|
||||
mSwapChains.push_back(handle);
|
||||
}
|
||||
template <>
|
||||
void Cleanup::addInternal(filament::backend::DescriptorSetHandle handle) {
|
||||
mDescriptorSets.push_back(handle);
|
||||
}
|
||||
template <>
|
||||
void Cleanup::addInternal(
|
||||
filament::backend::DescriptorSetLayoutHandle handle) {
|
||||
mDescriptorSetLayouts.push_back(handle);
|
||||
}
|
||||
template <>
|
||||
void Cleanup::addInternal(filament::backend::BufferObjectHandle handle) {
|
||||
mBufferObjects.push_back(handle);
|
||||
}
|
||||
template <>
|
||||
void Cleanup::addInternal(filament::backend::ProgramHandle handle) {
|
||||
mPrograms.push_back(handle);
|
||||
}
|
||||
template <>
|
||||
void Cleanup::addInternal(filament::backend::TextureHandle handle) {
|
||||
mTextures.push_back(handle);
|
||||
}
|
||||
template <>
|
||||
void Cleanup::addInternal(filament::backend::RenderTargetHandle handle) {
|
||||
mRenderTargets.push_back(handle);
|
||||
}
|
||||
|
||||
void Cleanup::addPostCall(std::function<void()> callback){
|
||||
mCallbacks.emplace_back(std::move(callback));
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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_LIFETIMES_H
|
||||
#define TNT_LIFETIMES_H
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
|
||||
#include "backend/Handle.h"
|
||||
#include "backend/DriverApiForward.h"
|
||||
#include "BackendTest.h"
|
||||
|
||||
class RenderFrame {
|
||||
public:
|
||||
explicit RenderFrame(filament::backend::DriverApi& api);
|
||||
~RenderFrame();
|
||||
|
||||
RenderFrame(RenderFrame const& rhs) noexcept = delete;
|
||||
RenderFrame& operator=(RenderFrame const& rhs) noexcept = delete;
|
||||
RenderFrame(RenderFrame const&& rhs) noexcept = delete;
|
||||
RenderFrame& operator=(RenderFrame const&& rhs) noexcept = delete;
|
||||
|
||||
private:
|
||||
filament::backend::DriverApi& mApi;
|
||||
};
|
||||
|
||||
class Cleanup {
|
||||
public:
|
||||
/**
|
||||
* @param driverApi Must be valid for the entire lifetime of the Cleanup object.
|
||||
*/
|
||||
explicit Cleanup(filament::backend::DriverApi& driverApi);
|
||||
~Cleanup();
|
||||
|
||||
template <typename HandleType>
|
||||
filament::backend::Handle<HandleType> add(filament::backend::Handle<HandleType> handle);
|
||||
|
||||
void addPostCall(std::function<void()> callback);
|
||||
|
||||
private:
|
||||
template <typename HandleType>
|
||||
void addInternal(filament::backend::Handle<HandleType> handle);
|
||||
|
||||
filament::backend::DriverApi & mDriverApi;
|
||||
|
||||
std::vector<filament::backend::SwapChainHandle> mSwapChains;
|
||||
std::vector<filament::backend::DescriptorSetHandle> mDescriptorSets;
|
||||
std::vector<filament::backend::DescriptorSetLayoutHandle> mDescriptorSetLayouts;
|
||||
std::vector<filament::backend::BufferObjectHandle> mBufferObjects;
|
||||
std::vector<filament::backend::ProgramHandle> mPrograms;
|
||||
std::vector<filament::backend::TextureHandle> mTextures;
|
||||
std::vector<filament::backend::RenderTargetHandle> mRenderTargets;
|
||||
|
||||
std::vector<std::function<void()>> mCallbacks;
|
||||
};
|
||||
|
||||
template <typename HandleType>
|
||||
filament::backend::Handle<HandleType> Cleanup::add(filament::backend::Handle<HandleType> handle) {
|
||||
addInternal(handle);
|
||||
return handle;
|
||||
}
|
||||
|
||||
#endif //TNT_LIFETIMES_H
|
||||
@@ -30,7 +30,8 @@ enum class Backend : uint8_t {
|
||||
OPENGL = 1,
|
||||
VULKAN = 2,
|
||||
METAL = 3,
|
||||
NOOP = 4,
|
||||
WEBGPU = 4,
|
||||
NOOP = 5,
|
||||
};
|
||||
|
||||
struct NativeView {
|
||||
|
||||
@@ -90,6 +90,9 @@ ShaderGenerator::ShaderGenerator(std::string vertex, std::string fragment, Backe
|
||||
case Backend::METAL:
|
||||
mShaderLanguage = filament::backend::ShaderLanguage::MSL;
|
||||
break;
|
||||
case Backend::WEBGPU:
|
||||
mShaderLanguage = filament::backend::ShaderLanguage::WGSL;
|
||||
break;
|
||||
case Backend::NOOP:
|
||||
mShaderLanguage = filament::backend::ShaderLanguage::ESSL3;
|
||||
break;
|
||||
@@ -115,6 +118,8 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(ShaderStage stage, std::s
|
||||
shader.insert(pos, "#define TARGET_METAL_ENVIRONMENT\n");
|
||||
} else if (backend == Backend::VULKAN) {
|
||||
shader.insert(pos, "#define TARGET_VULKAN_ENVIRONMENT\n");
|
||||
} else if (backend == Backend::WEBGPU) {
|
||||
shader.insert(pos, "#define TARGET_VULKAN_ENVIRONMENT\n");
|
||||
}
|
||||
|
||||
const char* shaderCString = shader.c_str();
|
||||
@@ -150,7 +155,8 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(ShaderStage stage, std::s
|
||||
|
||||
assert_invariant(backend == Backend::OPENGL ||
|
||||
backend == Backend::METAL ||
|
||||
backend == Backend::VULKAN);
|
||||
backend == Backend::VULKAN ||
|
||||
backend == Backend::WEBGPU);
|
||||
|
||||
if (backend == Backend::OPENGL) {
|
||||
if (isMobile) {
|
||||
@@ -166,6 +172,8 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(ShaderStage stage, std::s
|
||||
return { result.c_str(), result.c_str() + result.length() + 1 };
|
||||
} else if (backend == Backend::VULKAN) {
|
||||
return { (uint8_t*)spirv.data(), (uint8_t*)(spirv.data() + spirv.size()) };
|
||||
} else if (backend == Backend::WEBGPU){
|
||||
filamat::GLSLPostProcessor::spirvToWgsl(&spirv, &result);
|
||||
}
|
||||
|
||||
return {};
|
||||
|
||||
+110
-141
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "BackendTest.h"
|
||||
|
||||
#include "Lifetimes.h"
|
||||
#include "ShaderGenerator.h"
|
||||
#include "TrianglePrimitive.h"
|
||||
|
||||
@@ -195,6 +196,8 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
|
||||
|
||||
TEST_F(BackendTest, ColorMagnify) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(getDriverApi());
|
||||
cleanup.addPostCall([&]() { executeCommands(); });
|
||||
|
||||
constexpr int kSrcTexWidth = 256;
|
||||
constexpr int kSrcTexHeight = 256;
|
||||
@@ -205,30 +208,30 @@ TEST_F(BackendTest, ColorMagnify) {
|
||||
constexpr int kNumLevels = 3;
|
||||
|
||||
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
|
||||
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
|
||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a source texture.
|
||||
Handle<HwTexture> const srcTexture = api.createTexture(
|
||||
Handle<HwTexture> const srcTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT);
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
const bool flipY = sBackend == Backend::OPENGL;
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
|
||||
|
||||
// Create a destination texture.
|
||||
Handle<HwTexture> const dstTexture = api.createTexture(
|
||||
Handle<HwTexture> const dstTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// Create a RenderTarget for each texture's miplevel.
|
||||
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
|
||||
Handle<HwRenderTarget> dstRenderTargets[kNumLevels];
|
||||
for (uint8_t level = 0; level < kNumLevels; level++) {
|
||||
srcRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {});
|
||||
dstRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {});
|
||||
srcRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {}));
|
||||
dstRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {}));
|
||||
}
|
||||
|
||||
// Do a "magnify" blit from level 1 of the source RT to the level 0 of the destination RT.
|
||||
@@ -238,16 +241,18 @@ TEST_F(BackendTest, ColorMagnify) {
|
||||
{0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel}, SamplerMagFilter::LINEAR);
|
||||
|
||||
// Push through an empty frame to allow the texture to upload and the blit to execute.
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMagnify.png" };
|
||||
api.beginFrame(0, 0, 0);
|
||||
dumpScreenshot(api, dstRenderTargets[0], ¶ms);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
dumpScreenshot(api, dstRenderTargets[0], ¶ms);
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.finish();
|
||||
@@ -258,18 +263,12 @@ TEST_F(BackendTest, ColorMagnify) {
|
||||
const uint32_t expected = 0x410bdd31;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
|
||||
// Cleanup.
|
||||
api.destroyTexture(srcTexture);
|
||||
api.destroyTexture(dstTexture);
|
||||
api.destroySwapChain(swapChain);
|
||||
for (auto rt : srcRenderTargets) api.destroyRenderTarget(rt);
|
||||
for (auto rt : dstRenderTargets) api.destroyRenderTarget(rt);
|
||||
executeCommands();
|
||||
}
|
||||
|
||||
TEST_F(BackendTest, ColorMinify) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(getDriverApi());
|
||||
cleanup.addPostCall([&]() { executeCommands(); });
|
||||
|
||||
constexpr int kSrcTexWidth = 1024;
|
||||
constexpr int kSrcTexHeight = 1024;
|
||||
@@ -280,30 +279,30 @@ TEST_F(BackendTest, ColorMinify) {
|
||||
constexpr int kNumLevels = 3;
|
||||
|
||||
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
|
||||
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
|
||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a source texture.
|
||||
Handle<HwTexture> const srcTexture = api.createTexture(
|
||||
Handle<HwTexture> const srcTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT);
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
const bool flipY = sBackend == Backend::OPENGL;
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
|
||||
|
||||
// Create a destination texture.
|
||||
Handle<HwTexture> const dstTexture = api.createTexture(
|
||||
Handle<HwTexture> const dstTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// Create a RenderTarget for each texture's miplevel.
|
||||
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
|
||||
Handle<HwRenderTarget> dstRenderTargets[kNumLevels];
|
||||
for (uint8_t level = 0; level < kNumLevels; level++) {
|
||||
srcRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {});
|
||||
dstRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {});
|
||||
srcRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {}));
|
||||
dstRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {}));
|
||||
}
|
||||
|
||||
// Do a "minify" blit from level 1 of the source RT to the level 0 of the destination RT.
|
||||
@@ -314,6 +313,7 @@ TEST_F(BackendTest, ColorMinify) {
|
||||
srcRenderTargets[srcLevel], {0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel},
|
||||
SamplerMagFilter::LINEAR);
|
||||
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMinify.png" };
|
||||
dumpScreenshot(api, dstRenderTargets[0], ¶ms);
|
||||
@@ -325,18 +325,11 @@ TEST_F(BackendTest, ColorMinify) {
|
||||
const uint32_t expected = 0xf3d9c53f;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
|
||||
// Cleanup.
|
||||
api.destroyTexture(srcTexture);
|
||||
api.destroyTexture(dstTexture);
|
||||
api.destroySwapChain(swapChain);
|
||||
for (auto rt : srcRenderTargets) api.destroyRenderTarget(rt);
|
||||
for (auto rt : dstRenderTargets) api.destroyRenderTarget(rt);
|
||||
executeCommands();
|
||||
}
|
||||
|
||||
TEST_F(BackendTest, ColorResolve) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(getDriverApi());
|
||||
|
||||
constexpr int kSrcTexWidth = 256;
|
||||
constexpr int kSrcTexHeight = 256;
|
||||
@@ -356,40 +349,40 @@ TEST_F(BackendTest, ColorResolve) {
|
||||
triangleVs, triangleFs, sBackend, sIsMobilePlatform, std::move(descriptors));
|
||||
Program prog = shaderGen.getProgram(api);
|
||||
prog.descriptorBindings(1, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }});
|
||||
program = api.createProgram(std::move(prog));
|
||||
program = cleanup.add(api.createProgram(std::move(prog)));
|
||||
}
|
||||
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
|
||||
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
|
||||
{{
|
||||
DescriptorType::UNIFORM_BUFFER,
|
||||
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
|
||||
DescriptorFlags::NONE, 0
|
||||
}}});
|
||||
}}}));
|
||||
|
||||
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
|
||||
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
|
||||
|
||||
// Create a VertexBuffer, IndexBuffer, and RenderPrimitive.
|
||||
TrianglePrimitive const triangle(api);
|
||||
|
||||
// Create 4-sample texture.
|
||||
Handle<HwTexture> const srcColorTexture = api.createTexture(
|
||||
Handle<HwTexture> const srcColorTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, 1, kColorTexFormat, kSampleCount, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::COLOR_ATTACHMENT);
|
||||
TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// Create 1-sample texture.
|
||||
Handle<HwTexture> const dstColorTexture = api.createTexture(
|
||||
Handle<HwTexture> const dstColorTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, 1, kColorTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// Create a 4-sample render target with the 4-sample texture.
|
||||
Handle<HwRenderTarget> const srcRenderTarget = api.createRenderTarget(
|
||||
Handle<HwRenderTarget> const srcRenderTarget = cleanup.add(api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR, kSrcTexWidth, kSrcTexHeight, kSampleCount, 0,
|
||||
{{ srcColorTexture }}, {}, {});
|
||||
{{ srcColorTexture }}, {}, {}));
|
||||
|
||||
// Create a 1-sample render target with the 1-sample texture.
|
||||
Handle<HwRenderTarget> const dstRenderTarget = api.createRenderTarget(
|
||||
Handle<HwRenderTarget> const dstRenderTarget = cleanup.add(api.createRenderTarget(
|
||||
TargetBufferFlags::COLOR, kDstTexWidth, kDstTexHeight, 1, 0,
|
||||
{{ dstColorTexture }}, {}, {});
|
||||
{{ dstColorTexture }}, {}, {}));
|
||||
|
||||
// Prep for rendering.
|
||||
RenderPassParams params = {};
|
||||
@@ -408,8 +401,8 @@ TEST_F(BackendTest, ColorResolve) {
|
||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
|
||||
auto ubuffer = api.createBufferObject(sizeof(MaterialParams),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
|
||||
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams),
|
||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
|
||||
// Draw red triangle into srcRenderTarget.
|
||||
uploadUniforms(api, ubuffer, {
|
||||
.color = float4(1, 0, 0, 1),
|
||||
@@ -420,11 +413,12 @@ TEST_F(BackendTest, ColorResolve) {
|
||||
api.bindDescriptorSet(descriptorSet, 1, {});
|
||||
|
||||
// FIXME: on Metal this triangle is not drawn. Can't understand why.
|
||||
api.beginFrame(0, 0, 0);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
api.beginRenderPass(srcRenderTarget, params);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
api.endFrame(0);
|
||||
}
|
||||
|
||||
// Resolve the MSAA render target into the single-sample render target.
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR,
|
||||
@@ -443,23 +437,15 @@ TEST_F(BackendTest, ColorResolve) {
|
||||
const uint32_t expected = 0xebfac2ef;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", sparams.pixelHashResult, expected);
|
||||
EXPECT_TRUE(sparams.pixelHashResult == expected);
|
||||
|
||||
// Cleanup.
|
||||
api.destroyDescriptorSet(descriptorSet);
|
||||
api.destroyDescriptorSetLayout(descriptorSetLayout);
|
||||
api.destroyBufferObject(ubuffer);
|
||||
api.destroyProgram(program);
|
||||
api.destroyTexture(srcColorTexture);
|
||||
api.destroyTexture(dstColorTexture);
|
||||
api.destroyRenderTarget(srcRenderTarget);
|
||||
api.destroyRenderTarget(dstRenderTarget);
|
||||
flushAndWait();
|
||||
}
|
||||
|
||||
TEST_F(BackendTest, Blit2DTextureArray) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(getDriverApi());
|
||||
cleanup.addPostCall([&]() { executeCommands(); });
|
||||
|
||||
api.startCapture(0);
|
||||
cleanup.addPostCall([&]() { api.stopCapture(0); });
|
||||
|
||||
constexpr int kSrcTexWidth = 256;
|
||||
constexpr int kSrcTexHeight = 256;
|
||||
@@ -474,27 +460,27 @@ TEST_F(BackendTest, Blit2DTextureArray) {
|
||||
constexpr int kDstTexLayer = 0;
|
||||
|
||||
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
|
||||
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
|
||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a source texture.
|
||||
Handle<HwTexture> srcTexture = api.createTexture(
|
||||
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D_ARRAY, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, kSrcTexDepth,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT);
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
const bool flipY = sBackend == Backend::OPENGL;
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, kSrcTexLayer, float3(0.5, 0, 0), flipY);
|
||||
|
||||
// Create a destination texture.
|
||||
Handle<HwTexture> dstTexture = api.createTexture(
|
||||
Handle<HwTexture> dstTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// Create two RenderTargets.
|
||||
const int level = 0;
|
||||
Handle<HwRenderTarget> srcRenderTarget = api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, kSrcTexLayer }, {}, {});
|
||||
Handle<HwRenderTarget> dstRenderTarget = api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, kDstTexLayer }, {}, {});
|
||||
Handle<HwRenderTarget> srcRenderTarget = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, kSrcTexLayer }, {}, {}));
|
||||
Handle<HwRenderTarget> dstRenderTarget = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, kDstTexLayer }, {}, {}));
|
||||
|
||||
// Do a blit from kSrcTexLayer of the source RT to kDstTexLayer of the destination RT.
|
||||
const int srcLevel = 0;
|
||||
@@ -503,16 +489,18 @@ TEST_F(BackendTest, Blit2DTextureArray) {
|
||||
{0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel}, SamplerMagFilter::LINEAR);
|
||||
|
||||
// Push through an empty frame to allow the texture to upload and the blit to execute.
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "Blit2DTextureArray.png" };
|
||||
api.beginFrame(0, 0, 0);
|
||||
dumpScreenshot(api, dstRenderTarget, ¶ms);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
dumpScreenshot(api, dstRenderTarget, ¶ms);
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.finish();
|
||||
@@ -523,20 +511,12 @@ TEST_F(BackendTest, Blit2DTextureArray) {
|
||||
const uint32_t expected = 0x8de7d55b;
|
||||
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
|
||||
// Cleanup.
|
||||
api.destroyTexture(srcTexture);
|
||||
api.destroyTexture(dstTexture);
|
||||
api.destroySwapChain(swapChain);
|
||||
api.destroyRenderTarget(srcRenderTarget);
|
||||
api.destroyRenderTarget(dstRenderTarget);
|
||||
executeCommands();
|
||||
|
||||
api.stopCapture(0);
|
||||
}
|
||||
|
||||
TEST_F(BackendTest, BlitRegion) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(getDriverApi());
|
||||
cleanup.addPostCall([&]() { executeCommands(); });
|
||||
|
||||
constexpr int kSrcTexWidth = 1024;
|
||||
constexpr int kSrcTexHeight = 1024;
|
||||
@@ -549,21 +529,21 @@ TEST_F(BackendTest, BlitRegion) {
|
||||
constexpr int kDstLevel = 0;
|
||||
|
||||
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
|
||||
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
|
||||
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
// Create a source texture.
|
||||
Handle<HwTexture> srcTexture = api.createTexture(
|
||||
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT);
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
const bool flipY = sBackend == Backend::OPENGL;
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
|
||||
|
||||
// Create a destination texture.
|
||||
Handle<HwTexture> dstTexture = api.createTexture(
|
||||
Handle<HwTexture> dstTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
|
||||
// Blit one-quarter of src level 1 to dst level 0.
|
||||
Viewport srcRect = {
|
||||
@@ -583,34 +563,34 @@ TEST_F(BackendTest, BlitRegion) {
|
||||
// We purposely set the render target width and height to smaller than the texture, to check
|
||||
// that this case is handled correctly.
|
||||
Handle<HwRenderTarget> srcRenderTarget =
|
||||
api.createRenderTarget(TargetBufferFlags::COLOR, srcRect.width,
|
||||
srcRect.height, 1, 0, {srcTexture, kSrcLevel, 0}, {}, {});
|
||||
cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR, srcRect.width,
|
||||
srcRect.height, 1, 0, {srcTexture, kSrcLevel, 0}, {}, {}));
|
||||
Handle<HwRenderTarget> dstRenderTarget =
|
||||
api.createRenderTarget(TargetBufferFlags::COLOR, kDstTexWidth >> kDstLevel,
|
||||
kDstTexHeight >> kDstLevel, 1, 0, {dstTexture, kDstLevel, 0}, {}, {});
|
||||
cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR, kDstTexWidth >> kDstLevel,
|
||||
kDstTexHeight >> kDstLevel, 1, 0, {dstTexture, kDstLevel, 0}, {}, {}));
|
||||
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget, dstRect, srcRenderTarget, srcRect,
|
||||
SamplerMagFilter::LINEAR);
|
||||
|
||||
// Push through an empty frame to allow the texture to upload and the blit to execute.
|
||||
api.beginFrame(0, 0, 0);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Grab a screenshot.
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegion.png" };
|
||||
api.beginFrame(0, 0, 0);
|
||||
dumpScreenshot(api, dstRenderTarget, ¶ms);
|
||||
api.commit(swapChain);
|
||||
api.endFrame(0);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
dumpScreenshot(api, dstRenderTarget, ¶ms);
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
|
||||
// Check if the image matches perfectly to our golden run.
|
||||
//
|
||||
// TODO: for some reason, this test has very, very slight (as in one pixel) differences between
|
||||
// OpenGL and Metal. So disable golden checking for now.
|
||||
// Use the compare tool from ImageMagick to see visual differences:
|
||||
@@ -619,18 +599,12 @@ TEST_F(BackendTest, BlitRegion) {
|
||||
// const uint32_t expected = 0x74fa34ed;
|
||||
// printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
|
||||
// EXPECT_TRUE(params.pixelHashResult == expected);
|
||||
|
||||
// Cleanup.
|
||||
api.destroyTexture(srcTexture);
|
||||
api.destroyTexture(dstTexture);
|
||||
api.destroySwapChain(swapChain);
|
||||
api.destroyRenderTarget(srcRenderTarget);
|
||||
api.destroyRenderTarget(dstRenderTarget);
|
||||
executeCommands();
|
||||
}
|
||||
|
||||
TEST_F(BackendTest, BlitRegionToSwapChain) {
|
||||
auto& api = getDriverApi();
|
||||
Cleanup cleanup(getDriverApi());
|
||||
cleanup.addPostCall([&]() { executeCommands(); });
|
||||
|
||||
constexpr int kSrcTexWidth = 1024;
|
||||
constexpr int kSrcTexHeight = 1024;
|
||||
@@ -640,14 +614,15 @@ TEST_F(BackendTest, BlitRegionToSwapChain) {
|
||||
constexpr int kNumLevels = 3;
|
||||
|
||||
// Create a SwapChain and make it current.
|
||||
auto swapChain = createSwapChain();
|
||||
Handle<HwRenderTarget> dstRenderTarget = api.createDefaultRenderTarget();
|
||||
auto swapChain = cleanup.add(createSwapChain());
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
Handle<HwRenderTarget> dstRenderTarget = cleanup.add(api.createDefaultRenderTarget());
|
||||
|
||||
// Create a source texture.
|
||||
Handle<HwTexture> srcTexture = api.createTexture(
|
||||
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
|
||||
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT);
|
||||
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
|
||||
const bool flipY = sBackend == Backend::OPENGL;
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
|
||||
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
|
||||
@@ -655,8 +630,8 @@ TEST_F(BackendTest, BlitRegionToSwapChain) {
|
||||
// Create a RenderTarget for each texture's miplevel.
|
||||
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
|
||||
for (uint8_t level = 0; level < kNumLevels; level++) {
|
||||
srcRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {});
|
||||
srcRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
|
||||
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {}));
|
||||
}
|
||||
|
||||
// Blit one-quarter of src level 1 to dst level 0.
|
||||
@@ -674,29 +649,23 @@ TEST_F(BackendTest, BlitRegionToSwapChain) {
|
||||
.height = kDstTexHeight - 10,
|
||||
};
|
||||
|
||||
api.beginFrame(0, 0, 0);
|
||||
{
|
||||
RenderFrame frame(api);
|
||||
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
|
||||
dstRect, srcRenderTargets[srcLevel],
|
||||
srcRect, SamplerMagFilter::LINEAR);
|
||||
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
|
||||
dstRect, srcRenderTargets[srcLevel],
|
||||
srcRect, SamplerMagFilter::LINEAR);
|
||||
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain.png" };
|
||||
dumpScreenshot(api, dstRenderTarget, ¶ms);
|
||||
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain.png" };
|
||||
dumpScreenshot(api, dstRenderTarget, ¶ms);
|
||||
|
||||
api.commit(swapChain);
|
||||
|
||||
api.endFrame(0);
|
||||
api.commit(swapChain);
|
||||
}
|
||||
|
||||
// Wait for the ReadPixels result to come back.
|
||||
api.finish();
|
||||
executeCommands();
|
||||
getDriver().purge();
|
||||
|
||||
// Cleanup.
|
||||
api.destroyTexture(srcTexture);
|
||||
api.destroySwapChain(swapChain);
|
||||
for (auto rt : srcRenderTargets) api.destroyRenderTarget(rt);
|
||||
executeCommands();
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -106,6 +106,13 @@ public:
|
||||
/** @return Whether a combination of texture format, pixel format and type is valid. */
|
||||
static bool validatePixelFormatAndType(InternalFormat internalFormat, Format format, Type type) noexcept;
|
||||
|
||||
/** @return the maximum size in texels of a texture of type \p type. At least 2048 for
|
||||
* 2D textures, 256 for 3D textures. */
|
||||
static size_t getMaxTextureSize(Engine& engine, Sampler type) noexcept;
|
||||
|
||||
/** @return the maximum number of layers supported by texture arrays. At least 256. */
|
||||
static size_t getMaxArrayTextureLayers(Engine& engine) noexcept;
|
||||
|
||||
/**
|
||||
* Options for environment prefiltering into reflection map
|
||||
*
|
||||
|
||||
@@ -62,6 +62,8 @@ constexpr std::pair<ChunkType, ChunkType> shaderLanguageToTags(ShaderLanguage co
|
||||
return { MaterialEssl1, DictionaryText };
|
||||
case ShaderLanguage::MSL:
|
||||
return { MaterialMetal, DictionaryText };
|
||||
case ShaderLanguage::WGSL:
|
||||
return { MaterialWgsl, DictionaryText };
|
||||
case ShaderLanguage::SPIRV:
|
||||
return { MaterialSpirv, DictionarySpirv };
|
||||
case ShaderLanguage::METAL_LIBRARY:
|
||||
|
||||
@@ -108,4 +108,12 @@ bool Texture::validatePixelFormatAndType(InternalFormat internalFormat, Format f
|
||||
return FTexture::validatePixelFormatAndType(internalFormat, format, type);
|
||||
}
|
||||
|
||||
size_t Texture::getMaxTextureSize(Engine& engine, Sampler type) noexcept {
|
||||
return FTexture::getMaxTextureSize(downcast(engine), type);
|
||||
|
||||
}
|
||||
size_t Texture::getMaxArrayTextureLayers(Engine& engine) noexcept {
|
||||
return FTexture::getMaxArrayTextureLayers(downcast(engine));
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -751,7 +751,8 @@ int FEngine::loop() {
|
||||
#endif
|
||||
if (portString != nullptr) {
|
||||
const int port = atoi(portString);
|
||||
debug.server = new matdbg::DebugServer(mBackend, mDriver->getShaderLanguage(), port);
|
||||
debug.server = new matdbg::DebugServer(mBackend, mDriver->getShaderLanguage(),
|
||||
matdbg::DbgShaderModel((uint8_t) mDriver->getShaderModel()), port);
|
||||
|
||||
// Sometimes the server can fail to spin up (e.g. if the above port is already in use).
|
||||
// When this occurs, carry onward, developers can look at civetweb.txt for details.
|
||||
|
||||
@@ -388,12 +388,13 @@ void FMaterial::terminate(FEngine& engine) {
|
||||
<< "destroying material \"" << this->getName().c_str_safe() << "\" but "
|
||||
<< pos->second.size() << " instances still alive.";
|
||||
} else {
|
||||
utils::slog.e << "destroying material \"" << this->getName().c_str_safe() << "\" but "
|
||||
<< pos->second.size() << " instances still alive.";
|
||||
if (UTILS_UNLIKELY(!pos->second.empty())) {
|
||||
slog.e << "destroying material \"" << this->getName().c_str_safe() << "\" but "
|
||||
<< pos->second.size() << " instances still alive." << io::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
// Unregister the material with matdbg.
|
||||
matdbg::DebugServer* server = engine.debug.server;
|
||||
|
||||
@@ -154,7 +154,14 @@ public:
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
assert_invariant((size_t)variant.key < VARIANT_COUNT);
|
||||
std::unique_lock<utils::Mutex> lock(mActiveProgramsLock);
|
||||
mActivePrograms.set(variant.key);
|
||||
if (getMaterialDomain() == MaterialDomain::SURFACE) {
|
||||
auto vert = Variant::filterVariantVertex(variant);
|
||||
auto frag = Variant::filterVariantFragment(variant);
|
||||
mActivePrograms.set(vert.key);
|
||||
mActivePrograms.set(frag.key);
|
||||
} else {
|
||||
mActivePrograms.set(variant.key);
|
||||
}
|
||||
lock.unlock();
|
||||
|
||||
if (isSharedVariant(variant)) {
|
||||
|
||||
@@ -638,6 +638,9 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
|
||||
if (isRenderingMultiview) {
|
||||
hasPostProcess = false;
|
||||
msaaOptions.enabled = false;
|
||||
|
||||
// Picking is not supported for multiview rendering. Clear any pending picking queries.
|
||||
view.clearPickingQueries();
|
||||
}
|
||||
const uint8_t msaaSampleCount = msaaOptions.enabled ? msaaOptions.sampleCount : 1u;
|
||||
|
||||
|
||||
@@ -164,6 +164,40 @@ Texture* Texture::Builder::build(Engine& engine) {
|
||||
(isProtectedTexturesSupported && useProtectedMemory) || !useProtectedMemory)
|
||||
<< "Texture is PROTECTED but protected textures are not supported";
|
||||
|
||||
size_t const maxTextureDimension = getMaxTextureSize(engine, mImpl->mTarget);
|
||||
size_t const maxTextureDepth = (mImpl->mTarget == Sampler::SAMPLER_2D_ARRAY ||
|
||||
mImpl->mTarget == Sampler::SAMPLER_CUBEMAP_ARRAY)
|
||||
? getMaxArrayTextureLayers(engine)
|
||||
: maxTextureDimension;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
mImpl->mWidth <= maxTextureDimension &&
|
||||
mImpl->mHeight <= maxTextureDimension &&
|
||||
mImpl->mDepth <= maxTextureDepth) << "Texture dimensions out of range: "
|
||||
<< "width= " << mImpl->mWidth << " (>" << maxTextureDimension << ")"
|
||||
<<", height= " << mImpl->mHeight << " (>" << maxTextureDimension << ")"
|
||||
<< ", depth= " << mImpl->mDepth << " (>" << maxTextureDepth << ")";
|
||||
|
||||
const auto validateSamplerType = [&engine = downcast(engine)](SamplerType const sampler) -> bool {
|
||||
switch (sampler) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
return true;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
return engine.hasFeatureLevel(FeatureLevel::FEATURE_LEVEL_1);
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
return engine.hasFeatureLevel(FeatureLevel::FEATURE_LEVEL_2);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Validate sampler before any further interaction with it.
|
||||
FILAMENT_CHECK_PRECONDITION(validateSamplerType(mImpl->mTarget))
|
||||
<< "SamplerType " << uint8_t(mImpl->mTarget) << " not support at feature level "
|
||||
<< uint8_t(engine.getActiveFeatureLevel());
|
||||
|
||||
// SAMPLER_EXTERNAL implies imported.
|
||||
if (mImpl->mTarget == SamplerType::SAMPLER_EXTERNAL) {
|
||||
mImpl->mExternal = true;
|
||||
@@ -216,24 +250,6 @@ Texture* Texture::Builder::build(Engine& engine) {
|
||||
FILAMENT_CHECK_PRECONDITION(!swizzled) << "WebGL does not support texture swizzling.";
|
||||
#endif
|
||||
|
||||
auto validateSamplerType = [&engine = downcast(engine)](SamplerType const sampler) -> bool {
|
||||
switch (sampler) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
return true;
|
||||
case SamplerType::SAMPLER_3D:
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
return engine.hasFeatureLevel(FeatureLevel::FEATURE_LEVEL_1);
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
return engine.hasFeatureLevel(FeatureLevel::FEATURE_LEVEL_2);
|
||||
}
|
||||
};
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(validateSamplerType(mImpl->mTarget))
|
||||
<< "SamplerType " << uint8_t(mImpl->mTarget) << " not support at feature level "
|
||||
<< uint8_t(engine.getActiveFeatureLevel());
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION((swizzled && sampleable) || !swizzled)
|
||||
<< "Swizzled texture must be SAMPLEABLE";
|
||||
|
||||
@@ -670,6 +686,14 @@ bool FTexture::isTextureSwizzleSupported(FEngine& engine) noexcept {
|
||||
return engine.getDriverApi().isTextureSwizzleSupported();
|
||||
}
|
||||
|
||||
size_t FTexture::getMaxTextureSize(FEngine& engine, Sampler type) noexcept {
|
||||
return engine.getDriverApi().getMaxTextureSize(type);
|
||||
}
|
||||
|
||||
size_t FTexture::getMaxArrayTextureLayers(FEngine& engine) noexcept {
|
||||
return engine.getDriverApi().getMaxArrayTextureLayers();
|
||||
}
|
||||
|
||||
size_t FTexture::computeTextureDataSize(Format const format, Type const type,
|
||||
size_t const stride, size_t const height, size_t const alignment) noexcept {
|
||||
return PixelBufferDescriptor::computeDataSize(format, type, stride, height, alignment);
|
||||
|
||||
@@ -124,6 +124,10 @@ public:
|
||||
static bool validatePixelFormatAndType(backend::TextureFormat internalFormat,
|
||||
backend::PixelDataFormat format, backend::PixelDataType type) noexcept;
|
||||
|
||||
static size_t getMaxTextureSize(FEngine& engine, Sampler type) noexcept;
|
||||
|
||||
static size_t getMaxArrayTextureLayers(FEngine& engine) noexcept;
|
||||
|
||||
bool textureHandleCanMutate() const noexcept;
|
||||
void updateLodRange(uint8_t level) noexcept;
|
||||
|
||||
|
||||
@@ -137,12 +137,7 @@ FView::~FView() noexcept = default;
|
||||
void FView::terminate(FEngine& engine) {
|
||||
// Here we would cleanly free resources we've allocated, or we own (currently none).
|
||||
|
||||
while (mActivePickingQueriesList) {
|
||||
FPickingQuery* const pQuery = mActivePickingQueriesList;
|
||||
mActivePickingQueriesList = pQuery->next;
|
||||
pQuery->callback(pQuery->result, pQuery);
|
||||
FPickingQuery::put(pQuery);
|
||||
}
|
||||
clearPickingQueries();
|
||||
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
driver.destroyBufferObject(mLightUbh);
|
||||
@@ -1178,6 +1173,15 @@ void FView::executePickingQueries(DriverApi& driver,
|
||||
}
|
||||
}
|
||||
|
||||
void FView::clearPickingQueries() noexcept {
|
||||
while (mActivePickingQueriesList) {
|
||||
FPickingQuery* const pQuery = mActivePickingQueriesList;
|
||||
mActivePickingQueriesList = pQuery->next;
|
||||
pQuery->callback(pQuery->result, pQuery);
|
||||
FPickingQuery::put(pQuery);
|
||||
}
|
||||
}
|
||||
|
||||
void FView::setTemporalAntiAliasingOptions(TemporalAntiAliasingOptions options) noexcept {
|
||||
options.feedback = clamp(options.feedback, 0.0f, 1.0f);
|
||||
options.filterWidth = std::max(0.2f, options.filterWidth); // below 0.2 causes issues
|
||||
|
||||
@@ -463,6 +463,8 @@ public:
|
||||
void executePickingQueries(backend::DriverApi& driver,
|
||||
backend::RenderTargetHandle handle, math::float2 scale) noexcept;
|
||||
|
||||
void clearPickingQueries() noexcept;
|
||||
|
||||
void setMaterialGlobal(uint32_t index, math::float4 const& value);
|
||||
|
||||
math::float4 getMaterialGlobal(uint32_t index) const;
|
||||
|
||||
@@ -78,6 +78,10 @@ fragment {
|
||||
void dummy(){}
|
||||
|
||||
vec4 resolveFragment() {
|
||||
#ifdef TARGET_WEBGPU_ENVIRONMENT
|
||||
//WebGPU Can not handle subpasses.
|
||||
return vec4(0,0,0,1);
|
||||
#else
|
||||
#if POST_PROCESS_OPAQUE
|
||||
return vec4(subpassLoad(materialParams_colorBuffer).rgb, 1.0);
|
||||
#else
|
||||
@@ -85,6 +89,7 @@ vec4 resolveFragment() {
|
||||
color.rgb *= 1.0 / (color.a + FLT_EPS);
|
||||
return color;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void postProcess(inout PostProcessInputs postProcess) {
|
||||
|
||||
@@ -39,11 +39,16 @@ fragment {
|
||||
void dummy(){}
|
||||
|
||||
vec4 resolveFragment() {
|
||||
#ifdef TARGET_WEBGPU_ENVIRONMENT
|
||||
//WebGPU Can not handle subpasses.
|
||||
return vec4(0,0,0,1);
|
||||
#else
|
||||
#if POST_PROCESS_OPAQUE
|
||||
return vec4(subpassLoad(materialParams_colorBuffer).rgb, 1.0);
|
||||
#else
|
||||
return subpassLoad(materialParams_colorBuffer);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void postProcess(inout PostProcessInputs postProcess) {
|
||||
|
||||
@@ -31,13 +31,17 @@ if (WIN32)
|
||||
set(SRCS ${SRCS} src/BlueGLCoreWindowsImpl.S)
|
||||
endif()
|
||||
elseif (APPLE AND NOT IOS)
|
||||
set(SRCS ${SRCS} src/BlueGLDarwin.cpp)
|
||||
if (FILAMENT_SUPPORTS_OSMESA)
|
||||
set(SRCS ${SRCS} src/BlueGLOSMesa.cpp)
|
||||
else()
|
||||
set(SRCS ${SRCS} src/BlueGLDarwin.cpp)
|
||||
endif()
|
||||
set(SRCS ${SRCS} src/BlueGLCoreDarwinUniversalImpl.S)
|
||||
elseif(LINUX)
|
||||
if(FILAMENT_SUPPORTS_EGL_ON_LINUX)
|
||||
if (FILAMENT_SUPPORTS_EGL_ON_LINUX)
|
||||
set(SRCS ${SRCS} src/BlueGLLinuxEGL.cpp)
|
||||
elseif(FILAMENT_SUPPORTS_OSMESA)
|
||||
set(SRCS ${SRCS} src/BlueGLLinuxOSMesa.cpp)
|
||||
elseif (FILAMENT_SUPPORTS_OSMESA)
|
||||
set(SRCS ${SRCS} src/BlueGLOSMesa.cpp)
|
||||
else()
|
||||
set(SRCS ${SRCS} src/BlueGLLinux.cpp)
|
||||
endif()
|
||||
@@ -52,6 +56,14 @@ include_directories(${PUBLIC_HDR_DIR})
|
||||
# we're building a library
|
||||
add_library(${TARGET} STATIC ${PUBLIC_HDRS} ${SRCS})
|
||||
|
||||
if(FILAMENT_SUPPORTS_OSMESA)
|
||||
if (APPLE)
|
||||
target_compile_options(${TARGET} PRIVATE -I${FILAMENT_OSMESA_PATH}/include)
|
||||
else()
|
||||
target_compile_options(${TARGET} PRIVATE -I${FILAMENT_OSMESA_PATH}/include/GL)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# specify where the public headers of this library are
|
||||
target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
|
||||
set_target_properties(${TARGET} PROPERTIES FOLDER Libs)
|
||||
|
||||
@@ -14,10 +14,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <GL/gl.h>
|
||||
#include <dlfcn.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
#include <osmesa.h>
|
||||
|
||||
// This is to ensure that linking during compilation will not fail even if
|
||||
// OSMesaGetProcAddress is not linked.
|
||||
__attribute__((weak)) OSMESAproc OSMesaGetProcAddress(char const*);
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
|
||||
#include <GL/osmesa.h>
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
#if defined(__linux__)
|
||||
#endif
|
||||
|
||||
namespace bluegl {
|
||||
|
||||
namespace {
|
||||
@@ -30,20 +46,35 @@ struct Driver {
|
||||
} g_driver = {nullptr, nullptr};
|
||||
|
||||
bool initBinder() {
|
||||
constexpr char const* libraryNames[] = {"libOSMesa.so", "libosmesa.so"};
|
||||
for (char const* name : libraryNames) {
|
||||
static constexpr char const* libraryNames[] = {
|
||||
#if defined(__linux__)
|
||||
"libOSMesa.so",
|
||||
"libosmesa.so",
|
||||
#elif defined(__APPLE__)
|
||||
"libOSMesa.dylib",
|
||||
#endif
|
||||
};
|
||||
for (char const* name: libraryNames) {
|
||||
g_driver.library = dlopen(name, RTLD_GLOBAL | RTLD_NOW);
|
||||
if (g_driver.library) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!g_driver.library) {
|
||||
// The library has been linked explicitly during compile.
|
||||
g_driver.OSMesaGetProcAddress = (ProcAddressFunc) dlsym(RTLD_LOCAL, "OSMesaGetProcAddress");
|
||||
} else {
|
||||
if (g_driver.library) {
|
||||
// Linking against a libosmesa.so.
|
||||
g_driver.OSMesaGetProcAddress =
|
||||
(ProcAddressFunc) dlsym(g_driver.library, "OSMesaGetProcAddress");
|
||||
}
|
||||
#if defined(__linux__)
|
||||
else {
|
||||
// If Filament was built as a dynamic library.
|
||||
g_driver.OSMesaGetProcAddress = (ProcAddressFunc) dlsym(RTLD_LOCAL, "OSMesaGetProcAddress");
|
||||
}
|
||||
if (!g_driver.OSMesaGetProcAddress) {
|
||||
// If statically linking OSMesa.
|
||||
g_driver.OSMesaGetProcAddress = (ProcAddressFunc) OSMesaGetProcAddress;
|
||||
}
|
||||
#endif
|
||||
|
||||
return g_driver.OSMesaGetProcAddress;
|
||||
}
|
||||
@@ -26,16 +26,47 @@ set(SRCS
|
||||
src/JsonWriter.cpp
|
||||
)
|
||||
|
||||
# ==================================================================================================
|
||||
# Resources
|
||||
# ==================================================================================================
|
||||
|
||||
set(RESOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(RESOURCE_BINS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/web/api.js
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/web/app.js
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/web/index.html
|
||||
)
|
||||
|
||||
get_resgen_vars(${RESOURCE_DIR} fgviewer_resources)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${RESGEN_OUTPUTS}
|
||||
COMMAND resgen -t ${RESGEN_FLAGS} ${RESOURCE_BINS}
|
||||
DEPENDS resgen ${RESOURCE_BINS}
|
||||
)
|
||||
|
||||
if (DEFINED RESGEN_SOURCE_FLAGS)
|
||||
set_source_files_properties(${RESGEN_SOURCE} PROPERTIES COMPILE_FLAGS ${RESGEN_SOURCE_FLAGS})
|
||||
endif()
|
||||
|
||||
set(DUMMY_SRC "${RESOURCE_DIR}/dummy.c")
|
||||
add_custom_command(OUTPUT ${DUMMY_SRC} COMMAND echo "//" > ${DUMMY_SRC})
|
||||
|
||||
add_library(fgviewer_resources ${DUMMY_SRC} ${RESGEN_SOURCE})
|
||||
set_target_properties(fgviewer_resources PROPERTIES FOLDER Libs)
|
||||
|
||||
# ==================================================================================================
|
||||
# Include and target definitions
|
||||
# ==================================================================================================
|
||||
|
||||
include_directories(${PUBLIC_HDR_DIR})
|
||||
include_directories(${PUBLIC_HDR_DIR} ${RESOURCE_DIR})
|
||||
|
||||
add_library(${TARGET} STATIC ${PUBLIC_HDRS} ${SRCS})
|
||||
|
||||
target_link_libraries(${TARGET} PUBLIC
|
||||
civetweb
|
||||
fgviewer_resources
|
||||
utils
|
||||
)
|
||||
|
||||
@@ -57,11 +88,12 @@ endif()
|
||||
# Installation
|
||||
# ==================================================================================================
|
||||
|
||||
# matdbg has dependencies on non-installed libraries. Here we bundle them all together into a single
|
||||
# fgviewer has dependencies on non-installed libraries. Here we bundle them all together into a single
|
||||
# library that gets copied into the installation folder so users are only required to link against
|
||||
# matdbg.
|
||||
# fgviewer.
|
||||
set(FGVIEWER_DEPS
|
||||
fgviewer
|
||||
fgviewer_resources
|
||||
civetweb
|
||||
)
|
||||
|
||||
|
||||
@@ -30,9 +30,26 @@
|
||||
|
||||
namespace filament::fgviewer {
|
||||
|
||||
// If set to 0, this serves HTML from a resgen resource. Use 1 only during local development, which
|
||||
// serves files directly from the source code tree.
|
||||
#define SERVE_FROM_SOURCE_TREE 0
|
||||
|
||||
#if SERVE_FROM_SOURCE_TREE
|
||||
namespace {
|
||||
std::string const BASE_URL = "libs/fgviewer/web";
|
||||
} // anonymous
|
||||
#else
|
||||
#include "fgviewer_resources.h"
|
||||
#include <unordered_map>
|
||||
|
||||
namespace {
|
||||
struct Asset {
|
||||
std::string_view mime;
|
||||
std::string_view data;
|
||||
};
|
||||
std::unordered_map<std::string_view, Asset> ASSET_MAP;
|
||||
} // anonymous
|
||||
#endif // SERVE_FROM_SOURCE_TREE
|
||||
|
||||
using namespace utils;
|
||||
|
||||
@@ -56,10 +73,21 @@ public:
|
||||
uri = "/index.html";
|
||||
}
|
||||
|
||||
#if SERVE_FROM_SOURCE_TREE
|
||||
if (uri == "/index.html" || uri == "/app.js" || uri == "/api.js") {
|
||||
mg_send_file(conn, (BASE_URL + uri).c_str());
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
auto const& asset_itr = ASSET_MAP.find(uri);
|
||||
if (asset_itr != ASSET_MAP.end()) {
|
||||
auto const& mime = asset_itr->second.mime;
|
||||
auto const& data = asset_itr->second.data;
|
||||
mg_printf(conn, kSuccessHeader.data(), mime.data());
|
||||
mg_write(conn, data.data(), data.size());
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
slog.e << "[fgviewer] DebugServer: bad request at line " << __LINE__ << ": " << uri << io::endl;
|
||||
return false;
|
||||
}
|
||||
@@ -68,6 +96,21 @@ private:
|
||||
};
|
||||
|
||||
DebugServer::DebugServer(int port) {
|
||||
#if !SERVE_FROM_SOURCE_TREE
|
||||
ASSET_MAP["/index.html"] = {
|
||||
.mime = "text/html",
|
||||
.data = {(char const*) FGVIEWER_RESOURCES_INDEX_DATA},
|
||||
};
|
||||
ASSET_MAP["/app.js"] = {
|
||||
.mime = "text/javascript",
|
||||
.data = {(char const*) FGVIEWER_RESOURCES_APP_DATA},
|
||||
};
|
||||
ASSET_MAP["/api.js"] = {
|
||||
.mime = "text/javascript",
|
||||
.data = {(char const*) FGVIEWER_RESOURCES_API_DATA},
|
||||
};
|
||||
#endif
|
||||
|
||||
// By default the server spawns 50 threads so we override this to 10. According to the civetweb
|
||||
// documentation, "it is recommended to use num_threads of at least 5, since browsers often
|
||||
/// establish multiple connections to load a single web page, including all linked documents
|
||||
|
||||
+45
-29
@@ -16,7 +16,7 @@
|
||||
|
||||
import {LitElement, html, css, unsafeCSS, nothing} from "https://unpkg.com/lit@2.8.0?module";
|
||||
|
||||
const kUntitledPlaceholder = "untitled";
|
||||
const kUntitledPlaceholder = "Untitled View";
|
||||
|
||||
// CSS constants
|
||||
const FOREGROUND_COLOR = '#fafafa';
|
||||
@@ -293,11 +293,6 @@ class FrameGraphTable extends LitElement {
|
||||
margin-right: 5px;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scrollable-table td,
|
||||
.scrollable-table th {
|
||||
padding: 12px;
|
||||
@@ -351,6 +346,7 @@ class FrameGraphTable extends LitElement {
|
||||
super();
|
||||
this.frameGraphData = null;
|
||||
this.selectedResourceId = -1;
|
||||
this.expandedResourceSet = new Set();
|
||||
}
|
||||
|
||||
updated(props) {
|
||||
@@ -369,11 +365,13 @@ class FrameGraphTable extends LitElement {
|
||||
}
|
||||
|
||||
_toggleCollapse(resourceIndex) {
|
||||
const subresourceRows = this.shadowRoot.querySelectorAll(`[id^="subresource-${resourceIndex}-"]`);
|
||||
const icon = this.shadowRoot.querySelector(`#resource-${resourceIndex} .toggle-icon`);
|
||||
const isHidden = subresourceRows[0]?.classList.contains('hidden');
|
||||
subresourceRows.forEach(row => row.classList.toggle('hidden'));
|
||||
icon.textContent = isHidden ? '▼' : '▶';
|
||||
if (this.expandedResourceSet.has(resourceIndex)) {
|
||||
this.expandedResourceSet.delete(resourceIndex);
|
||||
}
|
||||
else {
|
||||
this.expandedResourceSet.add(resourceIndex);
|
||||
}
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
_handleResourceClick(ev) {
|
||||
@@ -384,13 +382,16 @@ class FrameGraphTable extends LitElement {
|
||||
}));
|
||||
}
|
||||
|
||||
_renderResourceUsage(allPasses, resourceId, defaultColor) {
|
||||
_renderResourceUsage(allPasses, resourceIds, defaultColor) {
|
||||
return allPasses.map((passData, index) => {
|
||||
const isRead = passData?.reads.includes(resourceId);
|
||||
const isWrite = passData?.writes.includes(resourceId);
|
||||
const isRead = resourceIds.some(resourceId => passData?.reads.includes(resourceId));
|
||||
const isWrite = resourceIds.some(resourceId => passData?.writes.includes(resourceId));
|
||||
let type = null;
|
||||
|
||||
const hasUsed = (passData) => {
|
||||
return passData?.reads.includes(resourceId) || passData?.writes.includes(resourceId);
|
||||
return resourceIds.some(resourceId =>
|
||||
passData?.reads.includes(resourceId) || passData?.writes.includes(resourceId)
|
||||
);
|
||||
};
|
||||
const hasBeenUsedBefore = allPasses.slice(0, index).some(hasUsed);
|
||||
const willBeUsedLater = allPasses.slice(index + 1).some(hasUsed);
|
||||
@@ -399,6 +400,7 @@ class FrameGraphTable extends LitElement {
|
||||
else if (isRead) type = RESOURCE_USAGE_TYPE_READ;
|
||||
else if (isWrite) type = RESOURCE_USAGE_TYPE_WRITE;
|
||||
else if (hasBeenUsedBefore && willBeUsedLater) type = RESOURCE_USAGE_TYPE_NO_ACCESS;
|
||||
|
||||
return html`
|
||||
<td style="background-color: ${unsafeCSS(this._getCellColor(type, defaultColor))};">
|
||||
${type ?? nothing}
|
||||
@@ -423,24 +425,38 @@ class FrameGraphTable extends LitElement {
|
||||
}
|
||||
|
||||
_renderResourceRow(resource, resourceIndex, resources, allPasses) {
|
||||
const hasSubresources = resources.some(subresource => this._isSubresourceOfParent(subresource, resource));
|
||||
const subresourceIds = resources
|
||||
.filter(subresource => this._isSubresourceOfParent(subresource, resource))
|
||||
.map(subresource => subresource.id);
|
||||
|
||||
const hasSubresources = subresourceIds.length > 0;
|
||||
const isExpanded = this.expandedResourceSet.has(resourceIndex);
|
||||
// Show the aggregated resource usage when the subresources are collapsed.
|
||||
const resourceIds = isExpanded ? [resource.id]:[resource.id, ...subresourceIds];
|
||||
|
||||
const onClickResource = () => this._handleResourceClick(resource.id);
|
||||
const selectedStyle = resource.id === this.selectedResourceId ? "selected" : "";
|
||||
|
||||
return html`
|
||||
<tr id="resource-${resourceIndex}">
|
||||
<th class="sticky-col resource ${selectedStyle}" @click="${onClickResource}">
|
||||
${hasSubresources ? html`
|
||||
<span class="toggle-icon"
|
||||
@click="${() => this._toggleCollapse(resourceIndex)}">▶</span>` : nothing}
|
||||
${resource.name}
|
||||
</th>
|
||||
${this._renderResourceUsage(allPasses, resource.id, DEFAULT_COLOR)}
|
||||
</tr>
|
||||
${this._renderSubresourceRows(resources, resource, resourceIndex, allPasses)}
|
||||
`;
|
||||
<tr id="resource-${resourceIndex}">
|
||||
<th class="sticky-col resource ${selectedStyle}" @click="${onClickResource}">
|
||||
${hasSubresources
|
||||
? html`
|
||||
<span class="toggle-icon"
|
||||
@click="${(e) => { e.stopPropagation(); this._toggleCollapse(resourceIndex); }}">
|
||||
${isExpanded ? '▼' : '▶'}
|
||||
</span>`
|
||||
: nothing}
|
||||
${resource.name}
|
||||
${hasSubresources && isExpanded ? nothing : html`(${subresourceIds.length})`}
|
||||
</th>
|
||||
${this._renderResourceUsage(allPasses, resourceIds, DEFAULT_COLOR)}
|
||||
</tr>
|
||||
${isExpanded ? this._renderSubresourceRows(resources, resource, resourceIndex, allPasses) : nothing}
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
_renderSubresourceRows(resources, parentResource, resourceIndex, allPasses) {
|
||||
return resources
|
||||
.filter(subresource => this._isSubresourceOfParent(subresource, parentResource))
|
||||
@@ -452,13 +468,13 @@ class FrameGraphTable extends LitElement {
|
||||
const selectedStyle = subresource.id === this.selectedResourceId ? "selected" : "";
|
||||
|
||||
return html`
|
||||
<tr id="subresource-${resourceIndex}-${subIndex}" class="collapsible hidden">
|
||||
<tr id="subresource-${resourceIndex}-${subIndex}" class="collapsible">
|
||||
<td class="sticky-col resource ${selectedStyle}"
|
||||
@click="${onClickResource}"
|
||||
style="background-color: ${SUBRESOURCE_COLOR}">
|
||||
${subresource.name}
|
||||
</td>
|
||||
${this._renderResourceUsage(allPasses, subresource.id, SUBRESOURCE_COLOR)}
|
||||
${this._renderResourceUsage(allPasses, [subresource.id], SUBRESOURCE_COLOR)}
|
||||
</tr>
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
|
||||
MaterialEssl1 = charTo64bitNum("MAT_ESS1"),
|
||||
MaterialSpirv = charTo64bitNum("MAT_SPIR"),
|
||||
MaterialMetal = charTo64bitNum("MAT_METL"),
|
||||
MaterialWgsl = charTo64bitNum("MAT_WGSL"),
|
||||
MaterialMetalLibrary = charTo64bitNum("MAT_MLIB"),
|
||||
MaterialShaderModels = charTo64bitNum("MAT_SMDL"),
|
||||
MaterialBindingUniformInfo = charTo64bitNum("MAT_UFRM"),
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
namespace filament {
|
||||
|
||||
// update this when a new version of filament wouldn't work with older materials
|
||||
static constexpr size_t MATERIAL_VERSION = 58;
|
||||
static constexpr size_t MATERIAL_VERSION = 57;
|
||||
|
||||
/**
|
||||
* Supported shading models
|
||||
|
||||
@@ -183,6 +183,7 @@ bool MaterialChunk::getShader(ShaderContent& shaderContent, BlobDictionary const
|
||||
switch (mMaterialTag) {
|
||||
case filamat::ChunkType::MaterialGlsl:
|
||||
case filamat::ChunkType::MaterialEssl1:
|
||||
case filamat::ChunkType::MaterialWgsl:
|
||||
case filamat::ChunkType::MaterialMetal:
|
||||
return getTextShader(mUnflattener, dictionary, shaderContent, shaderModel, variant, stage);
|
||||
case filamat::ChunkType::MaterialSpirv:
|
||||
|
||||
@@ -85,6 +85,10 @@ target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
|
||||
set_target_properties(${TARGET} PROPERTIES FOLDER Libs)
|
||||
target_link_libraries(${TARGET} backend_headers shaders filabridge utils smol-v)
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
target_link_libraries(${TARGET} libtint)
|
||||
endif ()
|
||||
|
||||
# We are being naughty and accessing private headers here
|
||||
# For spirv-tools, we're just following glslang's example
|
||||
target_include_directories(${TARGET} PRIVATE ${spirv-tools_SOURCE_DIR}/include)
|
||||
@@ -140,6 +144,12 @@ set(FILAMAT_LIB_NAME ${CMAKE_STATIC_LIBRARY_PREFIX}filamat${CMAKE_STATIC_LIBRARY
|
||||
install(FILES "${FILAMAT_COMBINED_OUTPUT}" DESTINATION lib/${DIST_DIR} RENAME ${FILAMAT_LIB_NAME})
|
||||
install(DIRECTORY ${PUBLIC_HDR_DIR}/filamat DESTINATION include)
|
||||
|
||||
# Need to install libtint for filamat on Android.
|
||||
# See libs/filamat/CMakeLists.txt and android/filamat-android/CMakeLists.txt.
|
||||
if (ANDROID)
|
||||
target_link_libraries(${TARGET} libtint_combined)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Tests
|
||||
# ==================================================================================================
|
||||
|
||||
@@ -81,7 +81,11 @@ public:
|
||||
VULKAN = 0x02u,
|
||||
METAL = 0x04u,
|
||||
WEBGPU = 0x08u,
|
||||
#ifdef FILAMENT_SUPPORTS_WEBGPU
|
||||
ALL = OPENGL | VULKAN | METAL | WEBGPU
|
||||
#else
|
||||
ALL = OPENGL | VULKAN | METAL
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -44,6 +44,10 @@
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#ifdef FILAMENT_SUPPORTS_WEBGPU
|
||||
#include <tint/tint.h>
|
||||
#endif
|
||||
|
||||
using namespace glslang;
|
||||
using namespace spirv_cross;
|
||||
using namespace spvtools;
|
||||
@@ -514,8 +518,49 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl,
|
||||
}
|
||||
}
|
||||
|
||||
bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
|
||||
#if FILAMENT_SUPPORTS_WEBGPU
|
||||
// We need to remove dead code for our variant-filter workaround for WebGPU to work
|
||||
// This is especially relevant for removing the push constants that morphing uses when not disabled
|
||||
spv::spirvbin_t remapper(0);
|
||||
remapper.remap(*spirv, spv::spirvbin_base_t::DCE_ALL);
|
||||
|
||||
//Currently no options we want to use
|
||||
const tint::spirv::reader::Options readerOpts{};
|
||||
tint::wgsl::writer::Options writerOpts{};
|
||||
|
||||
tint::Program tintRead = tint::spirv::reader::Read(*spirv, readerOpts);
|
||||
|
||||
if (tintRead.Diagnostics().ContainsErrors()) {
|
||||
//TODO remove this if block once combined image sampler conversion works.
|
||||
if (tintRead.Diagnostics().Str().rfind("error: WGSL does not support combined image-samplers") != std::string::npos) {
|
||||
slog.w << "This tint reader error is currently ignored during WebGPU bringup: " << tintRead.Diagnostics().Str() << io::endl;
|
||||
}
|
||||
else {
|
||||
slog.e << "Tint Reader Error: " << tintRead.Diagnostics().Str() << io::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
tint::Result<tint::wgsl::writer::Output> wgslOut = tint::wgsl::writer::Generate(tintRead,writerOpts);
|
||||
/// An instance of SuccessType that can be used to check a tint Result.
|
||||
tint::SuccessType tintSuccess;
|
||||
|
||||
if (wgslOut != tintSuccess) {
|
||||
slog.e << "Tint writer error: " << wgslOut.Failure().reason.Str() << io::endl;
|
||||
return false;
|
||||
}
|
||||
*outWsl = wgslOut->wgsl;
|
||||
return true;
|
||||
#else
|
||||
slog.i << "Trying to emit WGSL without including WebGPU dependencies, please set CMake arg FILAMENT_SUPPORTS_WEBGPU and FILAMENT_SUPPORTS_WEBGPU" << io::endl;
|
||||
return false;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
bool GLSLPostProcessor::process(const std::string& inputShader, Config const& config,
|
||||
std::string* outputGlsl, SpirvBlob* outputSpirv, std::string* outputMsl) {
|
||||
std::string* outputGlsl, SpirvBlob* outputSpirv, std::string* outputMsl, std::string* outputWgsl) {
|
||||
using TargetLanguage = MaterialBuilder::TargetLanguage;
|
||||
|
||||
if (config.targetLanguage == TargetLanguage::GLSL) {
|
||||
@@ -530,6 +575,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
|
||||
.glslOutput = outputGlsl,
|
||||
.spirvOutput = outputSpirv,
|
||||
.mslOutput = outputMsl,
|
||||
.wgslOutput = outputWgsl,
|
||||
};
|
||||
|
||||
switch (config.shaderType) {
|
||||
@@ -610,13 +656,20 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
|
||||
config.shaderType, config.shaderModel, config.hasFramebufferFetch, descriptors,
|
||||
mGenerateDebugInfo ? &internalConfig.minifier : nullptr);
|
||||
}
|
||||
if (internalConfig.wgslOutput) {
|
||||
if (!spirvToWgsl(internalConfig.spirvOutput, internalConfig.wgslOutput)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
slog.e << "GLSL post-processor invoked with optimization level NONE"
|
||||
<< io::endl;
|
||||
}
|
||||
break;
|
||||
case MaterialBuilder::Optimization::PREPROCESSOR:
|
||||
preprocessOptimization(tShader, config, internalConfig);
|
||||
if (!preprocessOptimization(tShader, config, internalConfig)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case MaterialBuilder::Optimization::SIZE:
|
||||
case MaterialBuilder::Optimization::PERFORMANCE:
|
||||
@@ -646,7 +699,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
|
||||
return true;
|
||||
}
|
||||
|
||||
void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
|
||||
bool GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
|
||||
GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const {
|
||||
using TargetApi = MaterialBuilder::TargetApi;
|
||||
assert_invariant(bool(internalConfig.spirvOutput) == (config.targetApi != TargetApi::OPENGL));
|
||||
@@ -662,6 +715,7 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
|
||||
|
||||
if (!ok) {
|
||||
slog.e << tShader.getInfoLog() << io::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (internalConfig.spirvOutput) {
|
||||
@@ -683,6 +737,7 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
|
||||
bool const linkOk = program.link(msg);
|
||||
if (!ok || !linkOk) {
|
||||
slog.e << spirvShader.getInfoLog() << io::endl;
|
||||
return false;
|
||||
} else {
|
||||
SpvOptions options;
|
||||
options.generateDebugInfo = mGenerateDebugInfo;
|
||||
@@ -702,10 +757,17 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
|
||||
config.shaderModel, config.hasFramebufferFetch, descriptors,
|
||||
mGenerateDebugInfo ? &internalConfig.minifier : nullptr);
|
||||
}
|
||||
if (internalConfig.wgslOutput) {
|
||||
if (!spirvToWgsl(internalConfig.spirvOutput, internalConfig.wgslOutput)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (internalConfig.glslOutput) {
|
||||
*internalConfig.glslOutput = glsl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GLSLPostProcessor::fullOptimization(const TShader& tShader,
|
||||
@@ -746,6 +808,11 @@ bool GLSLPostProcessor::fullOptimization(const TShader& tShader,
|
||||
config.hasFramebufferFetch, descriptors,
|
||||
mGenerateDebugInfo ? &internalConfig.minifier : nullptr);
|
||||
}
|
||||
if (internalConfig.wgslOutput) {
|
||||
if (!spirvToWgsl(&spirv, internalConfig.wgslOutput)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Transpile back to GLSL
|
||||
if (internalConfig.glslOutput) {
|
||||
|
||||
@@ -81,7 +81,8 @@ public:
|
||||
bool process(const std::string& inputShader, Config const& config,
|
||||
std::string* outputGlsl,
|
||||
SpirvBlob* outputSpirv,
|
||||
std::string* outputMsl);
|
||||
std::string* outputMsl,
|
||||
std::string* outputWgsl);
|
||||
|
||||
// public so backend_test can also use it
|
||||
static void spirvToMsl(const SpirvBlob* spirv, std::string* outMsl,
|
||||
@@ -89,11 +90,14 @@ public:
|
||||
bool useFramebufferFetch, const DescriptorSets& descriptorSets,
|
||||
const ShaderMinifier* minifier);
|
||||
|
||||
static bool spirvToWgsl(SpirvBlob* spirv, std::string* outWsl);
|
||||
|
||||
private:
|
||||
struct InternalConfig {
|
||||
std::string* glslOutput = nullptr;
|
||||
SpirvBlob* spirvOutput = nullptr;
|
||||
std::string* mslOutput = nullptr;
|
||||
std::string* wgslOutput = nullptr;
|
||||
EShLanguage shLang = EShLangFragment;
|
||||
// use 100 for ES environment, 110 for desktop
|
||||
int langVersion = 0;
|
||||
@@ -103,7 +107,7 @@ private:
|
||||
bool fullOptimization(const glslang::TShader& tShader,
|
||||
GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const;
|
||||
|
||||
void preprocessOptimization(glslang::TShader& tShader,
|
||||
bool preprocessOptimization(glslang::TShader& tShader,
|
||||
GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const;
|
||||
|
||||
/**
|
||||
|
||||
@@ -179,6 +179,14 @@ void MaterialBuilderBase::prepare(bool vulkanSemantics,
|
||||
effectiveFeatureLevel,
|
||||
});
|
||||
}
|
||||
if (any(mTargetApi & TargetApi::WEBGPU)) {
|
||||
mCodeGenPermutations.push_back({
|
||||
shaderModel,
|
||||
TargetApi::WEBGPU,
|
||||
TargetLanguage::SPIRV,
|
||||
effectiveFeatureLevel,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -836,6 +844,7 @@ static void showErrorMessage(const char* materialName, filament::Variant variant
|
||||
break;
|
||||
case TargetApi::WEBGPU:
|
||||
targetApiString = "WebGPU.\n";
|
||||
break;
|
||||
case TargetApi::ALL:
|
||||
assert(0); // Unreachable.
|
||||
break;
|
||||
@@ -879,6 +888,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
|
||||
std::vector<TextEntry> essl1Entries;
|
||||
std::vector<BinaryEntry> spirvEntries;
|
||||
std::vector<TextEntry> metalEntries;
|
||||
std::vector<TextEntry> wgslEntries;
|
||||
LineDictionary textDictionary;
|
||||
BlobDictionary spirvDictionary;
|
||||
// End: must be protected by lock
|
||||
@@ -907,8 +917,9 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
|
||||
|
||||
// Metal Shading Language is cross-compiled from Vulkan.
|
||||
const bool targetApiNeedsSpirv =
|
||||
(targetApi == TargetApi::VULKAN || targetApi == TargetApi::METAL);
|
||||
(targetApi == TargetApi::VULKAN || targetApi == TargetApi::METAL || targetApi == TargetApi::WEBGPU);
|
||||
const bool targetApiNeedsMsl = targetApi == TargetApi::METAL;
|
||||
const bool targetApiNeedsWgsl = targetApi == TargetApi::WEBGPU;
|
||||
const bool targetApiNeedsGlsl = targetApi == TargetApi::OPENGL;
|
||||
|
||||
// Set when a job fails
|
||||
@@ -923,21 +934,26 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
|
||||
// TODO: avoid allocations when not required
|
||||
std::vector<uint32_t> spirv;
|
||||
std::string msl;
|
||||
std::string wgsl;
|
||||
|
||||
std::vector<uint32_t>* pSpirv = targetApiNeedsSpirv ? &spirv : nullptr;
|
||||
std::string* pMsl = targetApiNeedsMsl ? &msl : nullptr;
|
||||
std::string* pWgsl = targetApiNeedsWgsl ? &wgsl : nullptr;
|
||||
|
||||
TextEntry glslEntry{};
|
||||
BinaryEntry spirvEntry{};
|
||||
TextEntry metalEntry{};
|
||||
TextEntry wgslEntry{};
|
||||
|
||||
glslEntry.shaderModel = params.shaderModel;
|
||||
spirvEntry.shaderModel = params.shaderModel;
|
||||
metalEntry.shaderModel = params.shaderModel;
|
||||
wgslEntry.shaderModel = params.shaderModel;
|
||||
|
||||
glslEntry.variant = v.variant;
|
||||
spirvEntry.variant = v.variant;
|
||||
metalEntry.variant = v.variant;
|
||||
wgslEntry.variant = v.variant;
|
||||
|
||||
// Generate raw shader code.
|
||||
// The quotes in Google-style line directives cause problems with certain drivers. These
|
||||
@@ -1006,7 +1022,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
|
||||
config.glsl.subpassInputToColorLocation.emplace_back(0, 0);
|
||||
}
|
||||
|
||||
bool const ok = postProcessor.process(shader, config, pGlsl, pSpirv, pMsl);
|
||||
bool const ok = postProcessor.process(shader, config, pGlsl, pSpirv, pMsl, pWgsl);
|
||||
if (!ok) {
|
||||
showErrorMessage(mMaterialName.c_str_safe(), v.variant, targetApi, v.stage,
|
||||
featureLevel, shader);
|
||||
@@ -1033,8 +1049,13 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
|
||||
|
||||
|
||||
switch (targetApi) {
|
||||
// TODO: Handle webgpu here
|
||||
case TargetApi::WEBGPU:
|
||||
assert(!spirv.empty());
|
||||
assert(wgsl.length() > 0);
|
||||
wgslEntry.stage = v.stage;
|
||||
wgslEntry.shader = wgsl;
|
||||
wgslEntries.push_back(wgslEntry);
|
||||
break;
|
||||
case TargetApi::ALL:
|
||||
// should never happen
|
||||
break;
|
||||
@@ -1096,6 +1117,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
|
||||
std::sort(essl1Entries.begin(), essl1Entries.end(), compare);
|
||||
std::sort(spirvEntries.begin(), spirvEntries.end(), compare);
|
||||
std::sort(metalEntries.begin(), metalEntries.end(), compare);
|
||||
std::sort(wgslEntries.begin(), wgslEntries.end(), compare);
|
||||
|
||||
// Generate the dictionaries.
|
||||
for (const auto& s : glslEntries) {
|
||||
@@ -1111,6 +1133,9 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
|
||||
for (const auto& s : metalEntries) {
|
||||
textDictionary.addText(s.shader);
|
||||
}
|
||||
for (const auto& s : wgslEntries) {
|
||||
textDictionary.addText(s.shader);
|
||||
}
|
||||
|
||||
// Emit dictionary chunk (TextDictionaryReader and DictionaryTextChunk)
|
||||
const auto& dictionaryChunk = container.push<filamat::DictionaryTextChunk>(
|
||||
@@ -1141,6 +1166,12 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
|
||||
dictionaryChunk.getDictionary(), ChunkType::MaterialMetal);
|
||||
}
|
||||
|
||||
// Emit WGSL chunk (MaterialTextChunk).
|
||||
if (!wgslEntries.empty()) {
|
||||
container.push<MaterialTextChunk>(std::move(wgslEntries),
|
||||
dictionaryChunk.getDictionary(), ChunkType::MaterialWgsl);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -525,6 +525,11 @@ EShMessages GLSLTools::glslangFlagsFromTargetApi(
|
||||
// choke on gl_VertexIndex.
|
||||
msg |= (Type)EShMessages::EShMsgVulkanRules;
|
||||
}
|
||||
if (targetApi == TargetApi::WEBGPU) {
|
||||
// FIXME: We have to use EShMsgVulkanRules for WEBGPU, otherwise compilation will
|
||||
// choke on gl_VertexIndex.
|
||||
msg |= (Type)EShMessages::EShMsgVulkanRules;
|
||||
}
|
||||
return (EShMessages)msg;
|
||||
}
|
||||
}
|
||||
@@ -540,13 +545,13 @@ void GLSLTools::prepareShaderParser(MaterialBuilder::TargetApi targetApi,
|
||||
shader.setEnvInput(EShSourceGlsl, stage, EShClientOpenGL, version);
|
||||
shader.setEnvClient(EShClientOpenGL, EShTargetOpenGL_450);
|
||||
break;
|
||||
case MaterialBuilderBase::TargetApi::WEBGPU:
|
||||
case MaterialBuilderBase::TargetApi::VULKAN:
|
||||
case MaterialBuilderBase::TargetApi::METAL:
|
||||
shader.setEnvInput(EShSourceGlsl, stage, EShClientVulkan, version);
|
||||
shader.setEnvClient(EShClientVulkan, EShTargetVulkan_1_1);
|
||||
break;
|
||||
// TODO: Handle webgpu here
|
||||
case MaterialBuilderBase::TargetApi::WEBGPU:
|
||||
case MaterialBuilderBase::TargetApi::ALL:
|
||||
// can't happen
|
||||
break;
|
||||
|
||||
@@ -175,6 +175,11 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
|
||||
break;
|
||||
// TODO: Handle webgpu here
|
||||
case TargetApi::WEBGPU:
|
||||
//For now, no differences so inherit the same changes.
|
||||
// TODO Define a separte environment, OR relevant checks
|
||||
out << "#define TARGET_VULKAN_ENVIRONMENT\n";
|
||||
out << "#define TARGET_WEBGPU_ENVIRONMENT\n";
|
||||
break;
|
||||
case TargetApi::ALL:
|
||||
// invalid should never happen
|
||||
break;
|
||||
@@ -190,6 +195,7 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
|
||||
}
|
||||
|
||||
if (mTargetApi == TargetApi::VULKAN ||
|
||||
mTargetApi == TargetApi::WEBGPU ||
|
||||
mTargetApi == TargetApi::METAL ||
|
||||
(mTargetApi == TargetApi::OPENGL && mShaderModel == ShaderModel::DESKTOP) ||
|
||||
mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_2) {
|
||||
@@ -297,12 +303,22 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
|
||||
generateSpecializationConstant(out, "BACKEND_FEATURE_LEVEL",
|
||||
+ReservedSpecializationConstants::BACKEND_FEATURE_LEVEL, 1);
|
||||
|
||||
generateSpecializationConstant(out, "CONFIG_MAX_INSTANCES",
|
||||
+ReservedSpecializationConstants::CONFIG_MAX_INSTANCES, (int)CONFIG_MAX_INSTANCES);
|
||||
if (mTargetApi == TargetApi::WEBGPU) {
|
||||
// Note: This is a revived hack for a hack.
|
||||
//
|
||||
// WGSL doesn't support specialization constants as an array length
|
||||
// CONFIG_MAX_INSTANCES is only needed for WebGL, so we can replace it with a constant.
|
||||
// More information at https://github.com/gpuweb/gpuweb/issues/572#issuecomment-649760005
|
||||
out << "const int CONFIG_MAX_INSTANCES = " << (int)CONFIG_MAX_INSTANCES << ";\n";
|
||||
out << "const int CONFIG_FROXEL_BUFFER_HEIGHT = 2048;\n";
|
||||
} else {
|
||||
generateSpecializationConstant(out, "CONFIG_MAX_INSTANCES",
|
||||
+ReservedSpecializationConstants::CONFIG_MAX_INSTANCES, (int)CONFIG_MAX_INSTANCES);
|
||||
|
||||
// the default of 1024 (16KiB) is needed for 32% of Android devices
|
||||
generateSpecializationConstant(out, "CONFIG_FROXEL_BUFFER_HEIGHT",
|
||||
+ReservedSpecializationConstants::CONFIG_FROXEL_BUFFER_HEIGHT, 1024);
|
||||
// the default of 1024 (16KiB) is needed for 32% of Android devices
|
||||
generateSpecializationConstant(out, "CONFIG_FROXEL_BUFFER_HEIGHT",
|
||||
+ReservedSpecializationConstants::CONFIG_FROXEL_BUFFER_HEIGHT, 1024);
|
||||
}
|
||||
|
||||
// directional shadowmap visualization
|
||||
generateSpecializationConstant(out, "CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP",
|
||||
@@ -715,6 +731,8 @@ io::sstream& CodeGenerator::generateBufferInterfaceBlock(io::sstream& out, Shade
|
||||
break;
|
||||
// TODO: Handle webgpu here
|
||||
case TargetApi::WEBGPU:
|
||||
out << "set = " << +set << ", binding = " << +binding << ", ";
|
||||
break;
|
||||
case TargetApi::ALL:
|
||||
// nonsensical, shouldn't happen.
|
||||
break;
|
||||
@@ -803,6 +821,8 @@ io::sstream& CodeGenerator::generateCommonSamplers(utils::io::sstream& out,
|
||||
break;
|
||||
// TODO: Handle webgpu here
|
||||
case TargetApi::WEBGPU:
|
||||
out << "layout(binding = " << +info.binding << ", set = " << +set << ") ";
|
||||
break;
|
||||
case TargetApi::ALL:
|
||||
// should not happen
|
||||
break;
|
||||
|
||||
@@ -796,8 +796,10 @@ std::string ShaderGenerator::createPostProcessFragmentProgram(ShaderModel sm,
|
||||
|
||||
cg.generateCommonSamplers(fs, DescriptorSetBindingPoints::PER_MATERIAL, material.sib);
|
||||
|
||||
// subpass
|
||||
CodeGenerator::generatePostProcessSubpass(fs, material.subpass);
|
||||
// Subpasses are not yet in WebGPU, https://github.com/gpuweb/gpuweb/issues/435
|
||||
CodeGenerator::generatePostProcessSubpass(fs, targetApi == MaterialBuilderBase::TargetApi::WEBGPU
|
||||
? SubpassInfo{}
|
||||
: material.subpass);
|
||||
|
||||
CodeGenerator::generatePostProcessCommon(fs, ShaderStage::FRAGMENT);
|
||||
CodeGenerator::generatePostProcessGetters(fs, ShaderStage::FRAGMENT);
|
||||
|
||||
@@ -891,7 +891,78 @@ TEST_F(MaterialCompiler, FeatureLevel0Ess3CallFails) {
|
||||
EXPECT_FALSE(result.isValid());
|
||||
}
|
||||
|
||||
#if FILAMENT_SUPPORTS_WEBGPU
|
||||
TEST_F(MaterialCompiler, WgslConversionBakedColor) {
|
||||
std::string bakedColorCodeFrag(R"(
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
material.baseColor = getColor();
|
||||
}
|
||||
)");
|
||||
filamat::MaterialBuilder builder;
|
||||
builder.targetApi(filamat::MaterialBuilderBase::TargetApi::WEBGPU);
|
||||
builder.material(bakedColorCodeFrag.c_str());
|
||||
builder.shading(filamat::MaterialBuilder::Shading::UNLIT);
|
||||
builder.name("BakedColor");
|
||||
builder.culling(CullingMode::NONE);
|
||||
builder.featureLevel(FeatureLevel::FEATURE_LEVEL_0);
|
||||
builder.require(filament::VertexAttribute::COLOR);
|
||||
builder.variantFilter(static_cast<filament::UserVariantFilterMask>(
|
||||
filament::UserVariantFilterBit::SKINNING | filament::UserVariantFilterBit::STE));
|
||||
filamat::Package result = builder.build(*jobSystem);
|
||||
EXPECT_TRUE(result.isValid());
|
||||
}
|
||||
|
||||
TEST_F(MaterialCompiler, WgslConversionSandboxLitTransparent) {
|
||||
std::string litTransparentCodeFrag(R"(
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
material.baseColor.rgb = materialParams.baseColor * materialParams.alpha;
|
||||
material.baseColor.a = materialParams.alpha;
|
||||
material.roughness = materialParams.roughness;
|
||||
material.metallic = materialParams.metallic;
|
||||
material.reflectance = materialParams.reflectance;
|
||||
material.sheenColor = materialParams.sheenColor;
|
||||
material.sheenRoughness = materialParams.sheenRoughness;
|
||||
material.clearCoat = materialParams.clearCoat;
|
||||
material.clearCoatRoughness = materialParams.clearCoatRoughness;
|
||||
material.anisotropy = materialParams.anisotropy;
|
||||
material.emissive = materialParams.emissive;
|
||||
}
|
||||
)");
|
||||
filamat::MaterialBuilder builder;
|
||||
builder.targetApi(filamat::MaterialBuilderBase::TargetApi::WEBGPU);
|
||||
builder.material(litTransparentCodeFrag.c_str());
|
||||
builder.shading(filamat::MaterialBuilder::Shading::LIT);
|
||||
builder.name("LitTransparent");
|
||||
builder.blending(filament::BlendingMode::TRANSPARENT);
|
||||
builder.specularAntiAliasing(true);
|
||||
|
||||
builder.parameter("alpha", 1, UniformType::FLOAT);
|
||||
builder.parameter("baseColor", 1, UniformType::FLOAT3);
|
||||
builder.parameter("roughness", 1, UniformType::FLOAT);
|
||||
builder.parameter("metallic", 1, UniformType::FLOAT);
|
||||
builder.parameter("reflectance", 1, UniformType::FLOAT);
|
||||
builder.parameter("sheenColor", 1, UniformType::FLOAT3);
|
||||
builder.parameter("sheenRoughness", 1, UniformType::FLOAT);
|
||||
builder.parameter("clearCoat", 1, UniformType::FLOAT);
|
||||
builder.parameter("clearCoatRoughness", 1, UniformType::FLOAT);
|
||||
builder.parameter("anisotropy", 1, UniformType::FLOAT);
|
||||
builder.parameter("emissive", 1, UniformType::FLOAT4);
|
||||
|
||||
builder.variantFilter(static_cast<filament::UserVariantFilterMask>(
|
||||
filament::UserVariantFilterBit::SKINNING | filament::UserVariantFilterBit::STE));
|
||||
filamat::Package result = builder.build(*jobSystem);
|
||||
EXPECT_TRUE(result.isValid());
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
const int rv = RUN_ALL_TESTS();
|
||||
if (testing::UnitTest::GetInstance()->test_to_run_count() == 0) {
|
||||
//If you run a test filter that contains 0 tests that was likely not intentional. Fail in that scenario.
|
||||
return 1;
|
||||
}
|
||||
return rv;
|
||||
}
|
||||
|
||||
@@ -41,6 +41,13 @@ struct MaterialRecord {
|
||||
VariantList activeVariants;
|
||||
};
|
||||
|
||||
// Matches DriverEnums' ShaderModel
|
||||
enum class DbgShaderModel {
|
||||
MOBILE = 1, //!< Mobile level functionality
|
||||
DESKTOP = 2, //!< Desktop level functionality
|
||||
MATINFO = 10, //!< To indicate debug server is running from matinfo
|
||||
};
|
||||
|
||||
/**
|
||||
* Server-side material debugger.
|
||||
*
|
||||
@@ -52,7 +59,8 @@ public:
|
||||
static std::string_view const kSuccessHeader;
|
||||
static std::string_view const kErrorHeader;
|
||||
|
||||
DebugServer(backend::Backend backend, backend::ShaderLanguage shaderLanguage, int port);
|
||||
DebugServer(backend::Backend backend, backend::ShaderLanguage shaderLanguage,
|
||||
DbgShaderModel perferredShaderModel, int port);
|
||||
~DebugServer();
|
||||
|
||||
/**
|
||||
@@ -85,10 +93,13 @@ public:
|
||||
bool isReady() const { return mServer; }
|
||||
|
||||
private:
|
||||
// called from ApiHandler
|
||||
MaterialRecord const* getRecord(const MaterialKey& key) const;
|
||||
|
||||
// called from ApiHandler
|
||||
void updateActiveVariants();
|
||||
|
||||
// called from ApiHandler
|
||||
/**
|
||||
* Replaces the entire content of a particular shader variant. The given shader index uses the
|
||||
* same ordering that the variants have within the package.
|
||||
@@ -96,8 +107,9 @@ private:
|
||||
bool handleEditCommand(const MaterialKey& mat, backend::Backend api, int shaderIndex,
|
||||
const char* newShaderContent, size_t newShaderLength);
|
||||
|
||||
const backend::Backend mBackend;
|
||||
const backend::ShaderLanguage mShaderLanguage;
|
||||
backend::Backend const mBackend;
|
||||
backend::ShaderLanguage const mShaderLanguage;
|
||||
DbgShaderModel const mPreferredShaderModel;
|
||||
|
||||
CivetServer* mServer;
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#ifndef MATDBG_JSONWRITER_H
|
||||
#define MATDBG_JSONWRITER_H
|
||||
|
||||
#include <matdbg/DebugServer.h>
|
||||
|
||||
#include <filaflat/ChunkContainer.h>
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
@@ -47,7 +49,8 @@ public:
|
||||
// shader index is an active variant. Each bit in the activeVariants bitmask
|
||||
// represents one of the possible variant combinations.
|
||||
bool writeActiveInfo(const filaflat::ChunkContainer& package,
|
||||
backend::ShaderLanguage shaderLanguage, VariantList activeVariants);
|
||||
backend::ShaderLanguage shaderLanguage, DbgShaderModel shaderModel,
|
||||
VariantList activeVariants);
|
||||
|
||||
private:
|
||||
utils::CString mJsonString;
|
||||
|
||||
@@ -332,11 +332,12 @@ bool ApiHandler::handleGet(CivetServer* server, struct mg_connection* conn) {
|
||||
return error(__LINE__, uri);
|
||||
}
|
||||
JsonWriter writer;
|
||||
if (!writer.writeActiveInfo(package, mServer->mShaderLanguage, record.activeVariants)) {
|
||||
if (!writer.writeActiveInfo(package, mServer->mShaderLanguage,
|
||||
mServer->mPreferredShaderModel, record.activeVariants)) {
|
||||
return error(__LINE__, uri);
|
||||
}
|
||||
bool const last = (++index) == mServer->mMaterialRecords.size();
|
||||
mg_printf(conn, "\"%8.8x\": %s %s", pair.first, writer.getJsonString(),
|
||||
mg_printf(conn, "\"%8.8x\": %s%s", pair.first, writer.getJsonString(),
|
||||
last ? "" : ",");
|
||||
}
|
||||
mg_printf(conn, "}");
|
||||
@@ -356,6 +357,18 @@ bool ApiHandler::handleGet(CivetServer* server, struct mg_connection* conn) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto writeMaterialRecord = [&](JsonWriter* writer, MaterialRecord const* record) {
|
||||
ChunkContainer package(record->package, record->packageSize);
|
||||
if (!package.parse()) {
|
||||
return error(__LINE__, uri);
|
||||
}
|
||||
|
||||
if (!writer->writeMaterialInfo(package)) {
|
||||
return error(__LINE__, uri);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
if (uri == "/api/materials") {
|
||||
std::unique_lock const lock(mServer->mMaterialRecordsMutex);
|
||||
mg_printf(conn, kSuccessHeader.data(), "application/json");
|
||||
@@ -363,17 +376,11 @@ bool ApiHandler::handleGet(CivetServer* server, struct mg_connection* conn) {
|
||||
int index = 0;
|
||||
for (auto const& record: mServer->mMaterialRecords) {
|
||||
bool const last = (++index) == mServer->mMaterialRecords.size();
|
||||
|
||||
ChunkContainer package(record.second.package, record.second.packageSize);
|
||||
if (!package.parse()) {
|
||||
return error(__LINE__, uri);
|
||||
}
|
||||
|
||||
auto const& mat = record.second;
|
||||
JsonWriter writer;
|
||||
if (!writer.writeMaterialInfo(package)) {
|
||||
return error(__LINE__, uri);
|
||||
if (!writeMaterialRecord(&writer, &mat)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mg_printf(conn, "{ \"matid\": \"%8.8x\", %s } %s", record.first, writer.getJsonString(),
|
||||
last ? "" : ",");
|
||||
}
|
||||
@@ -386,15 +393,9 @@ bool ApiHandler::handleGet(CivetServer* server, struct mg_connection* conn) {
|
||||
if (!result) {
|
||||
return error(__LINE__, uri);
|
||||
}
|
||||
|
||||
ChunkContainer package(result->package, result->packageSize);
|
||||
if (!package.parse()) {
|
||||
return error(__LINE__, uri);
|
||||
}
|
||||
|
||||
JsonWriter writer;
|
||||
if (!writer.writeMaterialInfo(package)) {
|
||||
return error(__LINE__, uri);
|
||||
if (!writeMaterialRecord(&writer, result)) {
|
||||
return false;
|
||||
}
|
||||
mg_printf(conn, kSuccessHeader.data(), "application/json");
|
||||
mg_printf(conn, "{ %s }", writer.getJsonString());
|
||||
|
||||
@@ -122,8 +122,11 @@ private:
|
||||
DebugServer* mServer;
|
||||
};
|
||||
|
||||
DebugServer::DebugServer(Backend backend, ShaderLanguage shaderLanguage, int port)
|
||||
: mBackend(backend), mShaderLanguage(shaderLanguage) {
|
||||
DebugServer::DebugServer(Backend backend, ShaderLanguage shaderLanguage,
|
||||
DbgShaderModel perferredShaderModel, int port)
|
||||
: mBackend(backend),
|
||||
mShaderLanguage(shaderLanguage),
|
||||
mPreferredShaderModel(perferredShaderModel) {
|
||||
|
||||
#if !SERVE_FROM_SOURCE_TREE
|
||||
ASSET_MAP["/index.html"] = {
|
||||
|
||||
@@ -232,7 +232,7 @@ size_t JsonWriter::getJsonSize() const {
|
||||
}
|
||||
|
||||
bool JsonWriter::writeActiveInfo(const filaflat::ChunkContainer& package,
|
||||
ShaderLanguage shaderLanguage, VariantList activeVariants) {
|
||||
ShaderLanguage shaderLanguage, DbgShaderModel shaderModel, VariantList activeVariants) {
|
||||
vector<ShaderInfo> shaders;
|
||||
ostringstream json;
|
||||
json << "[\"";
|
||||
@@ -254,13 +254,30 @@ bool JsonWriter::writeActiveInfo(const filaflat::ChunkContainer& package,
|
||||
json << "metal";
|
||||
chunkType = ChunkType::MaterialMetal;
|
||||
break;
|
||||
case ShaderLanguage::WGSL:
|
||||
json << "webgpu";
|
||||
chunkType = ChunkType::MaterialWgsl;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
shaders.resize(getShaderCount(package, chunkType));
|
||||
getShaderInfo(package, shaders.data(), chunkType);
|
||||
|
||||
json << "\", \"";
|
||||
switch (shaderModel) {
|
||||
case DbgShaderModel::DESKTOP:
|
||||
json << toString(ShaderModel::DESKTOP);
|
||||
break;
|
||||
case DbgShaderModel::MOBILE:
|
||||
json << toString(ShaderModel::MOBILE);
|
||||
break;
|
||||
case DbgShaderModel::MATINFO:
|
||||
json << "matinfo";
|
||||
break;
|
||||
}
|
||||
json << "\"";
|
||||
|
||||
for (size_t variant = 0; variant < activeVariants.size(); variant++) {
|
||||
if (activeVariants[variant]) {
|
||||
json << ", " << variant;
|
||||
|
||||
@@ -57,6 +57,10 @@ ShaderExtractor::ShaderExtractor(backend::ShaderLanguage target, const void* dat
|
||||
mMaterialTag = ChunkType::MaterialMetalLibrary;
|
||||
mDictionaryTag = ChunkType::DictionaryMetalLibrary;
|
||||
break;
|
||||
case backend::ShaderLanguage::WGSL:
|
||||
mMaterialTag = ChunkType::MaterialWgsl;
|
||||
mDictionaryTag = ChunkType::DictionaryText;
|
||||
break;
|
||||
case backend::ShaderLanguage::SPIRV:
|
||||
mMaterialTag = ChunkType::MaterialSpirv;
|
||||
mDictionaryTag = ChunkType::DictionarySpirv;
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#include <matdbg/ShaderInfo.h>
|
||||
|
||||
#include "CommonWriter.h"
|
||||
|
||||
#include <filaflat/ChunkContainer.h>
|
||||
#include <filaflat/DictionaryReader.h>
|
||||
#include <filaflat/MaterialChunk.h>
|
||||
@@ -48,12 +50,16 @@ size_t getShaderCount(const ChunkContainer& container, ChunkType type) {
|
||||
return shaderCount;
|
||||
}
|
||||
|
||||
|
||||
bool getShaderInfo(const ChunkContainer& container, ShaderInfo* info, ChunkType chunkType) {
|
||||
if (!container.hasChunk(chunkType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
MaterialDomain domain;
|
||||
if (!read(container, ChunkType::MaterialDomain, reinterpret_cast<uint8_t*>(&domain))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto [start, end] = container.getChunkRange(chunkType);
|
||||
Unflattener unflattener(start, end);
|
||||
|
||||
@@ -84,6 +90,12 @@ bool getShaderInfo(const ChunkContainer& container, ShaderInfo* info, ChunkType
|
||||
return false;
|
||||
}
|
||||
|
||||
auto stage = ShaderStage(pipelineStageValue);
|
||||
if (domain == MaterialDomain::SURFACE) {
|
||||
variant = stage == ShaderStage::VERTEX ?
|
||||
Variant::filterVariantVertex(variant) :
|
||||
Variant::filterVariantFragment(variant);
|
||||
}
|
||||
*info++ = {
|
||||
.shaderModel = ShaderModel(shaderModelValue),
|
||||
.variant = variant,
|
||||
|
||||
@@ -135,6 +135,10 @@ ShaderReplacer::ShaderReplacer(Backend backend, ShaderLanguage language,
|
||||
mMaterialTag = ChunkType::MaterialMetal;
|
||||
mDictionaryTag = ChunkType::DictionaryText;
|
||||
break;
|
||||
case backend::ShaderLanguage::WGSL:
|
||||
mMaterialTag = ChunkType::MaterialWgsl;
|
||||
mDictionaryTag = ChunkType::DictionaryText;
|
||||
break;
|
||||
case backend::ShaderLanguage::SPIRV:
|
||||
mMaterialTag = ChunkType::MaterialSpirv;
|
||||
mDictionaryTag = ChunkType::DictionarySpirv;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define MATDBG_SOURCE_FORMATTER_H
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
namespace filament::matdbg {
|
||||
|
||||
|
||||
@@ -429,7 +429,10 @@ static bool printShaderInfo(ostream& text, const ChunkContainer& container, Chun
|
||||
break;
|
||||
case ChunkType::MaterialMetalLibrary:
|
||||
text << "Metal precompiled shader libraries:" << endl;
|
||||
break;
|
||||
break;
|
||||
case ChunkType::MaterialWgsl:
|
||||
text << "WGSL precompiled shader libraries:" << endl;
|
||||
break;
|
||||
default:
|
||||
assert(false && "Invalid shader ChunkType");
|
||||
break;
|
||||
@@ -467,6 +470,9 @@ bool TextWriter::writeMaterialInfo(const filaflat::ChunkContainer& container) {
|
||||
if (!printShaderInfo(text, container, ChunkType::MaterialMetalLibrary)) {
|
||||
return false;
|
||||
}
|
||||
if (!printShaderInfo(text, container, ChunkType::MaterialWgsl)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
printChunks(text, container);
|
||||
|
||||
|
||||
@@ -68,14 +68,13 @@ async function fetchMatIds() {
|
||||
}
|
||||
|
||||
async function queryActiveShaders() {
|
||||
const activeMaterials = await _fetchJson("api/active");
|
||||
const activeVariants = await _fetchJson("api/active");
|
||||
const actives = {};
|
||||
for (matid in activeMaterials) {
|
||||
const backend = activeMaterials[matid][0];
|
||||
const variants = activeMaterials[matid].slice(1);
|
||||
actives[matid] = {
|
||||
backend, variants
|
||||
};
|
||||
for (matid in activeVariants) {
|
||||
const backend = activeVariants[matid][0];
|
||||
const shaderModel = activeVariants[matid][1];
|
||||
const variants = activeVariants[matid].slice(2);
|
||||
actives[matid] = { backend, shaderModel, variants };
|
||||
}
|
||||
return actives;
|
||||
}
|
||||
|
||||
+69
-50
@@ -59,8 +59,8 @@ const FOREGROUND_COLOR = '#fafafa';
|
||||
const INACTIVE_COLOR = '#9a9a9a';
|
||||
const DARKER_INACTIVE_COLOR = '#6f6f6f';
|
||||
const LIGHTER_INACTIVE_COLOR = '#d9d9d9';
|
||||
const UNSELECTED_COLOR = '#dfdfdf';
|
||||
const BACKGROUND_COLOR = '#5362e5';
|
||||
const UNSELECTED_COLOR = '#d0d0d0';
|
||||
const BACKGROUND_COLOR = '#6885c5';
|
||||
const HOVER_BACKGROUND_COLOR = '#b3c2ff';
|
||||
const CODE_VIEWER_BOTTOM_ROW_HEIGHT = 60;
|
||||
const REGULAR_FONT_SIZE = 12;
|
||||
@@ -343,7 +343,7 @@ class MenuSection extends LitElement {
|
||||
color: ${unsafeCSS(UNSELECTED_COLOR)};
|
||||
}
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-size: 20px;
|
||||
color: ${unsafeCSS(UNSELECTED_COLOR)};
|
||||
cursor: pointer;
|
||||
}
|
||||
@@ -365,6 +365,9 @@ class MenuSection extends LitElement {
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.expanded-title {
|
||||
color: ${unsafeCSS(FOREGROUND_COLOR)};
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -380,9 +383,10 @@ class MenuSection extends LitElement {
|
||||
render() {
|
||||
const expandedIcon = this.showing ? '-' : '+';
|
||||
const slot = (() => html`<slot></slot>`)();
|
||||
const epandedTitleClass = this.showing ? 'expanded-title' : '';
|
||||
return html`
|
||||
<div class="container">
|
||||
<div class="section-title expander" @click="${this._showClick}">
|
||||
<div class="section-title expander ${epandedTitleClass}" @click="${this._showClick}">
|
||||
<span>${this.title}</span> <span>${expandedIcon}</span>
|
||||
</div>
|
||||
<hr />
|
||||
@@ -460,6 +464,7 @@ class AdvancedOptions extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
currentBackend: {type: String, attribute: 'current-backend'},
|
||||
currentShaderModel: {type: String, attribute: 'current-shader-model'},
|
||||
hideInactiveVariants: {type: Boolean, attribute: 'hide-inactive-variants'},
|
||||
availableBackends: {type: Array, state: true},
|
||||
};
|
||||
@@ -478,6 +483,7 @@ class AdvancedOptions extends LitElement {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.borderless {
|
||||
border: 1px solid rgba(0,0,0,0);
|
||||
@@ -570,7 +576,7 @@ class AdvancedOptions extends LitElement {
|
||||
return html`
|
||||
<div class="option">
|
||||
<div class="option-heading">Current Backend</div>
|
||||
<form action="" id="backend-option-form">
|
||||
<form style="display:flex;flex-wrap:wrap" action="" id="backend-option-form">
|
||||
${div}
|
||||
</form>
|
||||
</div>
|
||||
@@ -578,6 +584,9 @@ class AdvancedOptions extends LitElement {
|
||||
}
|
||||
|
||||
_hideInactiveVariantsOption() {
|
||||
if (this.currentShaderModel == 'matinfo') {
|
||||
return null;
|
||||
}
|
||||
const onChange = (ev) => {
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(
|
||||
@@ -585,7 +594,7 @@ class AdvancedOptions extends LitElement {
|
||||
{detail: null, bubbles: true, composed: true}));
|
||||
}
|
||||
return html`
|
||||
<div class="option borderless">
|
||||
<div class="option">
|
||||
<!--<div class="option-heading">Current Backend</div>-->
|
||||
<form action="" id="hide-inactive-variants-form">
|
||||
<input type="checkbox" name="InactiveVariants"
|
||||
@@ -630,8 +639,8 @@ class MaterialSidePanel extends LitElement {
|
||||
color: white;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin: 0 0 10px 0;
|
||||
font-size: 20px;
|
||||
margin: 0 0 20px 0;
|
||||
font-size: 25px;
|
||||
}
|
||||
.materials {
|
||||
display: flex;
|
||||
@@ -675,6 +684,7 @@ class MaterialSidePanel extends LitElement {
|
||||
currentBackend: {type: String, attribute: 'current-backend'},
|
||||
currentLanguage: {type: String, attribute: 'current-language'},
|
||||
hideInactiveVariants: {type: Boolean, attribute: 'hide-inactive-variants'},
|
||||
currentShaderModel: {type: String, attribute: 'current-shader-model'},
|
||||
|
||||
database: {type: Object, state: true},
|
||||
materials: {type: Array, state: true},
|
||||
@@ -702,29 +712,19 @@ class MaterialSidePanel extends LitElement {
|
||||
|
||||
updated(props) {
|
||||
if (props.has('database')) {
|
||||
const items = [];
|
||||
|
||||
// Names need not be unique, so we display a numeric suffix for non-unique names.
|
||||
// To achieve stable ordering of anonymous materials, we first sort by matid.
|
||||
const labels = new Set();
|
||||
const matids = Object.keys(this.database).sort();
|
||||
const duplicatedLabels = {};
|
||||
for (const matid of matids) {
|
||||
const name = this.database[matid].name || kUntitledPlaceholder;
|
||||
if (labels.has(name)) {
|
||||
duplicatedLabels[name] = 0;
|
||||
} else {
|
||||
labels.add(name);
|
||||
}
|
||||
}
|
||||
const names = matids.map(matid => (this.database[matid].name || kUntitledPlaceholder));
|
||||
const labelCount = {};
|
||||
names.forEach(name => labelCount[name] = (labelCount[name] || 0) + 1);
|
||||
const duplicates = names.filter(name => labelCount[name] > 1);
|
||||
|
||||
this.materials = matids.map((matid) => {
|
||||
const material = this.database[matid];
|
||||
let name = material.name || kUntitledPlaceholder;
|
||||
if (name in duplicatedLabels) {
|
||||
const index = duplicatedLabels[name];
|
||||
duplicatedLabels[name] = index + 1;
|
||||
name = `${name} (${index})`;
|
||||
if (duplicates.includes(name)) {
|
||||
name = `${name} (${labelCount[name]--})`;
|
||||
}
|
||||
return {
|
||||
matid: matid,
|
||||
@@ -784,7 +784,7 @@ class MaterialSidePanel extends LitElement {
|
||||
let divClass =
|
||||
'material_variant_language language' +
|
||||
(isLanguageSelected ? ' selected' : '') +
|
||||
(!isActive ? ' inactive' : '');
|
||||
(!isActive && this.currentShaderModel != 'matinfo' ? ' inactive' : '');
|
||||
const onClickLanguage = this._handleLanguageClick.bind(this, lang);
|
||||
lang = (isLanguageSelected ? '● ' : '') + lang;
|
||||
return html`
|
||||
@@ -796,10 +796,11 @@ class MaterialSidePanel extends LitElement {
|
||||
return html`<div class="languages">${languagesDiv}</div>`;
|
||||
}
|
||||
|
||||
_buildShaderDiv(showAllShaders) {
|
||||
_buildShaderDiv() {
|
||||
if (!this.variants) {
|
||||
return null;
|
||||
}
|
||||
const isMatinfo = this.currentShaderModel == 'matinfo';
|
||||
let variants =
|
||||
this.variants
|
||||
.sort((a, b) => {
|
||||
@@ -808,7 +809,10 @@ class MaterialSidePanel extends LitElement {
|
||||
if (b.active && !a.active) return 1;
|
||||
return 0;
|
||||
})
|
||||
.filter((variant) => (!this.hideInactiveVariants || variant.shader.active))
|
||||
.filter(variant => (!this.hideInactiveVariants || variant.shader.active))
|
||||
.filter(variant => (
|
||||
isMatinfo || variant.shader.shaderModel == this.currentShaderModel
|
||||
))
|
||||
.map((variant) => {
|
||||
let divClass = 'material_variant_language';
|
||||
const shaderIndex = +variant.shader.index;
|
||||
@@ -817,7 +821,7 @@ class MaterialSidePanel extends LitElement {
|
||||
if (isVariantSelected) {
|
||||
divClass += ' selected';
|
||||
}
|
||||
if (!isActive) {
|
||||
if (!isActive && !isMatinfo) {
|
||||
divClass += ' inactive';
|
||||
}
|
||||
const onClickVariant = this._handleVariantClick.bind(this, shaderIndex);
|
||||
@@ -826,11 +830,18 @@ class MaterialSidePanel extends LitElement {
|
||||
if (vstring.length > 0) {
|
||||
vstring = `[${vstring}]`;
|
||||
}
|
||||
const shaderModelSymbol = {
|
||||
'desktop': '🄳 ',
|
||||
'mobile': '🄼 ',
|
||||
};
|
||||
|
||||
let languagesDiv = isVariantSelected ? this._buildLanguagesDiv(isActive) : null;
|
||||
const stage = (isVariantSelected ? '● ' : '') + variant.shader.pipelineStage;
|
||||
const stage =
|
||||
(isMatinfo ? shaderModelSymbol[variant.shader.shaderModel] : '') +
|
||||
(variant.shader.pipelineStage.indexOf('vertex') >=0 ? '𝕍ert' : '𝔽rag');
|
||||
return html`
|
||||
<div class="${divClass}" @click="${onClickVariant}">
|
||||
<div>${stage} ${vstring} </div>
|
||||
<div>${stage}  ${vstring} </div>
|
||||
</div>
|
||||
${languagesDiv ?? nothing}
|
||||
`
|
||||
@@ -839,6 +850,7 @@ class MaterialSidePanel extends LitElement {
|
||||
}
|
||||
|
||||
render() {
|
||||
const isMatinfo = this.currentShaderModel == 'matinfo';
|
||||
const sections = (title, domain) => {
|
||||
const mats = this.materials
|
||||
.filter((m) => m.domain == domain)
|
||||
@@ -854,14 +866,12 @@ class MaterialSidePanel extends LitElement {
|
||||
const isMaterialSelected = mat.matid === this.currentMaterial;
|
||||
if (isMaterialSelected) {
|
||||
divClass += ' selected';
|
||||
// If we are looking at an inactive material, show all shaders regardless.
|
||||
const showAllShaders = !material.active;
|
||||
shaderDiv = this._buildShaderDiv(showAllShaders);
|
||||
shaderDiv = this._buildShaderDiv();
|
||||
}
|
||||
if (!material.active) {
|
||||
if (!material.active && !isMatinfo) {
|
||||
divClass += " inactive";
|
||||
}
|
||||
const matName = (isMaterialSelected ? '● ' : '') + mat.name;
|
||||
const matName = mat.name;
|
||||
return html`
|
||||
<div class="${divClass}" @click="${onClick}" data-id="${mat.matid}">
|
||||
${matName}
|
||||
@@ -878,6 +888,7 @@ class MaterialSidePanel extends LitElement {
|
||||
(() => html`
|
||||
<advanced-options id="advanced-options"
|
||||
current-backend=${this.currentBackend}
|
||||
current-shader-model=${this.currentShaderModel}
|
||||
?hide-inactive-variants=${this.hideInactiveVariants}
|
||||
></advanced-options>
|
||||
`)();
|
||||
@@ -885,7 +896,7 @@ class MaterialSidePanel extends LitElement {
|
||||
return html`
|
||||
<style>${this.dynamicStyle()}</style>
|
||||
<div class="container">
|
||||
<div class="title">matdbg</div>
|
||||
<div class="title">${isMatinfo ? 'matinfo' : 'matdbg'}</div>
|
||||
${sections("Surface", "surface") ?? nothing}
|
||||
${sections("Post-processing", "postpro") ?? nothing}
|
||||
<material-info id="material-info"></material-info>
|
||||
@@ -938,8 +949,7 @@ class MatdbgViewer extends LitElement {
|
||||
}
|
||||
);
|
||||
|
||||
let materials = await fetchMaterials();
|
||||
this.database = materials;
|
||||
this.database = await fetchMaterials();
|
||||
|
||||
// This is the user preferences stored in localStorage
|
||||
let hideInactiveVariantsVal = localStorage.getItem('option-hide-inactive-variants');
|
||||
@@ -979,6 +989,7 @@ class MatdbgViewer extends LitElement {
|
||||
this.currentMaterial = null;
|
||||
this.currentLanguage = null;
|
||||
this.currentBackend = null;
|
||||
this.currentShaderModel = null;
|
||||
this.hideInactiveVariants = false;
|
||||
this.init();
|
||||
|
||||
@@ -1049,6 +1060,7 @@ class MatdbgViewer extends LitElement {
|
||||
// Each material has a list of variants compiled for it, this index tracks a position in the list.
|
||||
currentShaderIndex: {type: Number, state: true},
|
||||
currentBackend: {type: String, state: true},
|
||||
currentShaderModel: {type: String, state: true},
|
||||
codeViewerExpectedWidth: {type: Number, state: true},
|
||||
codeViewerExpectedHeight: {type: Number, state: true},
|
||||
|
||||
@@ -1070,8 +1082,8 @@ class MatdbgViewer extends LitElement {
|
||||
const activeVariants = this.activeShaders[this.currentMaterial].variants;
|
||||
const materialShaders = material[this.currentBackend];
|
||||
for (let shader in materialShaders) {
|
||||
let ind = activeVariants.indexOf(+shader);
|
||||
if (ind >= 0) {
|
||||
const shaderVariant = materialShaders[shader].variant;
|
||||
if (activeVariants.indexOf(shaderVariant) >= 0) {
|
||||
this.currentShaderIndex = +shader;
|
||||
break;
|
||||
}
|
||||
@@ -1095,7 +1107,6 @@ class MatdbgViewer extends LitElement {
|
||||
shader.modified = false;
|
||||
this.database = this.database;
|
||||
}
|
||||
|
||||
// Size of the editor will be adjusted due to the code being loaded, we try to
|
||||
// fit the editor again by calling the resize signal.
|
||||
setTimeout(this._onResize.bind(this), 700);
|
||||
@@ -1119,22 +1130,29 @@ class MatdbgViewer extends LitElement {
|
||||
}
|
||||
}
|
||||
if (_validDict(this.activeShaders)) {
|
||||
let backends = {};
|
||||
let setBackend = null;
|
||||
let setShaderModel = null;
|
||||
for (let matid in this.activeShaders) {
|
||||
const backend = this.activeShaders[matid].backend;
|
||||
if (backend in backends) {
|
||||
backends[backend] = backends[backend] + 1;
|
||||
} else {
|
||||
backends[backend] = 1;
|
||||
if (!setBackend && backend) {
|
||||
setBackend = backend;
|
||||
}
|
||||
const shaderModel = this.activeShaders[matid].shaderModel;
|
||||
if (!setShaderModel && shaderModel) {
|
||||
setShaderModel = shaderModel;
|
||||
}
|
||||
}
|
||||
let backendList = Object.keys(backends);
|
||||
if (backendList.length > 0) {
|
||||
this.currentBackend = backendList[0];
|
||||
if (this.currentBackend != setBackend) {
|
||||
this.currentBackend = setBackend;
|
||||
}
|
||||
} else if (!this.currentBackend) {
|
||||
if (this.currentShaderModel != setShaderModel) {
|
||||
this.currentShaderModel = setShaderModel;
|
||||
}
|
||||
|
||||
} else if (!this.currentBackend || !this.currentShaderModel) {
|
||||
// Make a guess on the backend if one wasn't from activeShaders.
|
||||
this.currentBackend = guessBackend();
|
||||
this.currentShaderModel = 'matinfo';
|
||||
}
|
||||
|
||||
this._sidepanel.database = this.database;
|
||||
@@ -1166,6 +1184,7 @@ class MatdbgViewer extends LitElement {
|
||||
current-shader-index="${this.currentShaderIndex}"
|
||||
current-material="${this.currentMaterial}"
|
||||
current-backend="${this.currentBackend}"
|
||||
current-shader-model="${this.currentShaderModel}"
|
||||
?hide-inactive-variants="${this.hideInactiveVariants}" >
|
||||
</material-sidepanel>
|
||||
<code-viewer id="code-viewer"
|
||||
|
||||
+11
-2
@@ -20,7 +20,6 @@ set(MATERIAL_SRCS
|
||||
materials/aiDefaultMat.mat
|
||||
materials/bakedColor.mat
|
||||
materials/bakedTexture.mat
|
||||
materials/pointSprites.mat
|
||||
materials/aoPreview.mat
|
||||
materials/arrayTexture.mat
|
||||
materials/groundShadow.mat
|
||||
@@ -42,6 +41,12 @@ set(MATERIAL_SRCS
|
||||
materials/texturedLit.mat
|
||||
)
|
||||
|
||||
# Tint does not support setting gl_PointSize, disable the relevant sample if using WebGPU
|
||||
# https://github.com/gpuweb/gpuweb/issues/1190
|
||||
if (NOT FILAMENT_SUPPORTS_WEBGPU)
|
||||
set(MATERIAL_SRCS ${MATERIAL_SRCS} materials/pointSprites.mat)
|
||||
endif ()
|
||||
|
||||
if (CMAKE_CROSSCOMPILING)
|
||||
include(${IMPORT_EXECUTABLES})
|
||||
endif()
|
||||
@@ -258,7 +263,11 @@ if (NOT ANDROID)
|
||||
add_demo(lightbulb)
|
||||
add_demo(material_sandbox)
|
||||
add_demo(multiple_windows)
|
||||
add_demo(point_sprites)
|
||||
# Tint does not support setting gl_PointSize, disable the relevant sample if using WebGPU
|
||||
# https://github.com/gpuweb/gpuweb/issues/1190
|
||||
if (NOT FILAMENT_SUPPORTS_WEBGPU)
|
||||
add_demo(point_sprites)
|
||||
endif ()
|
||||
add_demo(rendertarget)
|
||||
add_demo(sample_cloth)
|
||||
add_demo(sample_full_pbr)
|
||||
|
||||
@@ -77,7 +77,11 @@ static void printUsage(char* name) {
|
||||
" --help, -h\n"
|
||||
" Prints this message\n\n"
|
||||
" --api, -a\n"
|
||||
" Specify the backend API: opengl, vulkan, or metal\n"
|
||||
" Specify the backend API: opengl, vulkan, metal, or webgpu\n"
|
||||
" (note: webgpu is a no-op for now, printing backend\n"
|
||||
" component info if FILAMENT_BACKEND_DEBUG_FLAG, \n"
|
||||
" set at build time, includes the \n"
|
||||
" FWGPU_PRINT_SYSTEM bit flag 0x2)\n"
|
||||
);
|
||||
const std::string from("HELLOTRIANGLE");
|
||||
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
|
||||
@@ -109,8 +113,11 @@ static int handleCommandLineArguments(int argc, char* argv[], App* app) {
|
||||
app->config.backend = Engine::Backend::VULKAN;
|
||||
} else if (arg == "metal") {
|
||||
app->config.backend = Engine::Backend::METAL;
|
||||
} else if (arg == "webgpu") {
|
||||
app->config.backend = Engine::Backend::WEBGPU;
|
||||
} else {
|
||||
std::cerr << "Unrecognized backend. Must be 'opengl'|'vulkan'|'metal'.\n";
|
||||
std::cerr << "Unrecognized backend. Must be "
|
||||
"'opengl'|'vulkan'|'metal'|'webgpu'.\n";
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -46,7 +46,12 @@ def render_test(gltf_viewer, test_config, output_dir,
|
||||
for backend in test_config.backends:
|
||||
env = None
|
||||
if backend == 'opengl' and opengl_lib and os.path.isdir(opengl_lib):
|
||||
env = {'LD_LIBRARY_PATH': opengl_lib}
|
||||
env = {
|
||||
'LD_LIBRARY_PATH': opengl_lib,
|
||||
|
||||
# for macOS
|
||||
'DYLD_LIBRARY_PATH': opengl_lib,
|
||||
}
|
||||
|
||||
for model in test.models:
|
||||
model_path = test_config.models[model]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user