Compare commits

..
Author SHA1 Message Date
Eliza Velasquez 3308ec7e34 wip: editor stuff 2025-03-13 17:59:50 -07:00
547 changed files with 47920 additions and 53964 deletions
+10 -2
View File
@@ -1,7 +1,15 @@
;;; Directory Local Variables -*- no-byte-compile: t -*-
;;; For more information see (info "(emacs) Directory Variables")
((c++-mode . ((c-file-style . "filament")
((c++-mode . ((eval . (progn
(make-local-variable 'eglot-ignored-server-capabilities)
(add-to-list 'eglot-ignored-server-capabilities
:documentOnTypeFormattingProvider)))
(c-file-style . "filament")
(apheleia-inhibit . t)))
(c-mode . ((c-file-style . "filament")
(c-mode . ((eval . (progn
(make-local-variable 'eglot-ignored-server-capabilities)
(add-to-list 'eglot-ignored-server-capabilities
:documentOnTypeFormattingProvider)))
(c-file-style . "filament")
(apheleia-inhibit . t))))
+1 -1
View File
@@ -5,7 +5,7 @@ end_of_line = lf
insert_final_newline = true
charset = utf-8
[*.{c,cpp,h,inc,kt,java,js,md}]
[*.{c,cpp,h,inc,kt,java,js,md,fs,vs}]
indent_style = space
indent_size = 4
max_line_length = 100
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
name: build-android
# We intentially use a larger runner here to enable larger disk space
# (standard linux runner will fail on disk space and faster build time).
runs-on: 'ubuntu-24.04-32core'
runs-on: ubuntu-22.04-32core
steps:
- uses: actions/checkout@v4.1.6
+1 -1
View File
@@ -10,7 +10,7 @@ on:
jobs:
build-linux:
name: build-linux
runs-on: 'ubuntu-24.04-16core'
runs-on: ubuntu-22.04-16core
steps:
- uses: actions/checkout@v4.1.6
+12 -19
View File
@@ -40,7 +40,7 @@ jobs:
build-android:
name: build-android
runs-on: 'ubuntu-24.04-16core'
runs-on: ubuntu-22.04-16core
steps:
- uses: actions/checkout@v4.1.6
@@ -69,7 +69,7 @@ jobs:
build-web:
name: build-web
runs-on: 'ubuntu-24.04-16core'
runs-on: ubuntu-22.04-16core
steps:
- uses: actions/checkout@v4.1.6
@@ -79,7 +79,7 @@ jobs:
validate-docs:
name: validate-docs
runs-on: 'ubuntu-24.04-4core'
runs-on: ubuntu-22.04-4core
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -92,33 +92,26 @@ jobs:
test-renderdiff:
name: test-renderdiff
runs-on: macos-14-xlarge
runs-on: ubuntu-22.04-16core
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
- uses: ./.github/actions/ubuntu-apt-add-src
- name: Run script
run: |
bash test/renderdiff/test.sh
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
validate-wgsl-webgpu:
name: validate-wgsl-webgpu
runs-on: 'ubuntu-24.04-8core'
validate-wgsl-pipeline:
name: validate-wgsl-pipeline
runs-on: macos-14-xlarge
steps:
- uses: actions/checkout@v4.1.6
- name: Run build script
run: source ./build/linux/ci-common.sh && ./build.sh -W debug test_filamat filament
run: ./build.sh -W debug test_filamat
- name: Run test
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
+2 -2
View File
@@ -70,7 +70,7 @@ jobs:
build-web:
name: build-web
runs-on: 'ubuntu-24.04-16core'
runs-on: ubuntu-22.04-16core
if: github.event_name == 'release' || github.event.inputs.platform == 'web'
steps:
@@ -103,7 +103,7 @@ jobs:
build-android:
name: build-android
runs-on: 'ubuntu-24.04-16core'
runs-on: ubuntu-22.04-16core
if: github.event_name == 'release' || github.event.inputs.platform == 'android'
steps:
+1 -1
View File
@@ -10,7 +10,7 @@ on:
jobs:
build-web:
name: build-web
runs-on: 'ubuntu-24.04-16core'
runs-on: ubuntu-22.04-16core
steps:
- uses: actions/checkout@v4.1.6
+7 -21
View File
@@ -139,14 +139,14 @@ else()
set(LINUX FALSE)
endif()
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 (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 (FILAMENT_SUPPORTS_WAYLAND)
add_definitions(-DFILAMENT_SUPPORTS_WAYLAND)
set(FILAMENT_SUPPORTS_X11 FALSE)
@@ -184,12 +184,6 @@ if (NOT ANDROID AND NOT WEBGL AND NOT IOS AND NOT FILAMENT_LINUX_IS_MOBILE)
set(IS_HOST_PLATFORM TRUE)
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
@@ -607,14 +601,6 @@ 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)
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.58.2'
implementation 'com.google.android.filament:filament-android:1.58.0'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.58.2'
pod 'Filament', '~> 1.58.0'
```
## Documentation
-8
View File
@@ -7,14 +7,6 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.59.0
- materials: five custom variables (varyings) are now available on the condition that the `color` attribute is not requested (b/404930099). [⚠️ **New Material Version**]
## v1.58.2
- engine: Generate 1D instead of 3D LUTs for color grading whenever possible.
## v1.58.1
+5 -5
View File
@@ -12,12 +12,12 @@ if (FILAMENT_SUPPORTS_VULKAN)
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)
add_library(webgpu_dawn STATIC IMPORTED)
set_target_properties(webgpu_dawn PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libwebgpu_dawn.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)
@@ -57,5 +57,5 @@ target_link_libraries(filamat-jni
utils
log
smol-v
$<$<STREQUAL:${FILAMENT_SUPPORTS_WEBGPU},ON>:tint>
$<$<STREQUAL:${FILAMENT_SUPPORTS_WEBGPU},ON>:webgpu_dawn> # for tint
)
+9 -5
View File
@@ -29,6 +29,10 @@ 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)
@@ -55,11 +59,11 @@ add_library(smol-v STATIC IMPORTED)
set_target_properties(smol-v PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libsmol-v.a)
if (FILAMENT_ENABLE_FGVIEWER)
add_library(fgviewer STATIC IMPORTED)
set_target_properties(fgviewer PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfgviewer.a)
endif()
if (FILAMENT_ENABLE_FGVIEWER)
add_library(fgviewer STATIC IMPORTED)
set_target_properties(fgviewer PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfgviewer.a)
endif()
if (FILAMENT_ENABLE_MATDBG)
add_library(matdbg STATIC IMPORTED)
@@ -68,21 +68,6 @@ 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) {
@@ -46,8 +46,9 @@ import static com.google.android.filament.Asserts.assertFloat4In;
*
* <h1>Performance</h1>
*
* Creating a new ColorGrading object may be more expensive than other Filament objects as a LUT may
* need to be generated. The generation of this LUT, if necessary, may happen on the CPU.
* Creating a new ColorGrading object may be more expensive than other Filament objects as a
* 3D LUT may need to be generated. The generation of a 3D LUT, if necessary, may happen on
* the CPU.
*
* <h1>Ordering</h1>
*
@@ -159,10 +160,6 @@ public class ColorGrading {
* quality level will use a 32x32x32 10 bit LUT, a high quality will use a 32x32x32 16 bit
* LUT, and a ultra quality will use a 64x64x64 16 bit LUT.
*
* This setting has no effect if generating a 1D LUT.
*
* This overrides the values set by format() and dimensions().
*
* The default quality is {@link QualityLevel#MEDIUM}.
*
* @param qualityLevel The desired quality of the color grading process
@@ -178,8 +175,6 @@ public class ColorGrading {
* When color grading is implemented using a 3D LUT, this sets the texture format of
* of the LUT. This overrides the value set by quality().
*
* This setting has no effect if generating a 1D LUT.
*
* The default is INTEGER
*
* @param format The desired format of the 3D LUT.
@@ -195,8 +190,6 @@ public class ColorGrading {
* When color grading is implemented using a 3D LUT, this sets the dimension of the LUT.
* This overrides the value set by quality().
*
* This setting has no effect if generating a 1D LUT.
*
* The default is 32
*
* @param dim The desired dimension of the LUT. Between 16 and 64.
@@ -623,3 +616,4 @@ public class ColorGrading {
private static native long nBuilderBuild(long nativeBuilder, long nativeEngine);
}
@@ -691,24 +691,6 @@ 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.
*/
@@ -1307,8 +1289,6 @@ public class Texture {
private static native boolean nIsTextureFormatSupported(long nativeEngine, int internalFormat);
private static native boolean nIsTextureFormatMipmappable(long nativeEngine, int internalFormat);
private static native boolean nIsTextureSwizzleSupported(long nativeEngine);
private static native int nGetMaxTextureSize(long nativeObject, int ordinal);
private static native int nGetMaxArrayTextureLayers(long nativeObject);
private static native boolean nValidatePixelFormatAndType(int internalFormat, int pixelDataFormat,
int pixelDataType);
@@ -31,6 +31,8 @@ package com.google.android.filament;
* <li>DisplayRangeToneMapper</li>
* </ul>
* </ul>
*
* You can create custom tone mapping operators by subclassing ToneMapper.
*/
public class ToneMapper {
private final long mNativeObject;
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.58.2
VERSION_NAME=1.58.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+1 -4
View File
@@ -200,9 +200,8 @@ WEBGPU_ANDROID_GRADLE_OPTION=""
EGL_ON_LINUX_OPTION="-DFILAMENT_SUPPORTS_EGL_ON_LINUX=OFF"
MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=OFF"
MATDBG_GRADLE_OPTION=""
FGVIEWER_OPTION="-DFILAMENT_ENABLE_FGVIEWER=OFF"
FGVIEWER_GRADLE_OPTION=""
MATDBG_GRADLE_OPTION=""
MATOPT_OPTION=""
MATOPT_GRADLE_OPTION=""
@@ -534,7 +533,6 @@ function build_android {
${VULKAN_ANDROID_GRADLE_OPTION} \
${WEBGPU_ANDROID_GRADLE_OPTION} \
${MATDBG_GRADLE_OPTION} \
${FGVIEWER_GRADLE_OPTION} \
${MATOPT_GRADLE_OPTION} \
:filament-android:assembleDebug \
:gltfio-android:assembleDebug \
@@ -586,7 +584,6 @@ function build_android {
${VULKAN_ANDROID_GRADLE_OPTION} \
${WEBGPU_ANDROID_GRADLE_OPTION} \
${MATDBG_GRADLE_OPTION} \
${FGVIEWER_GRADLE_OPTION} \
${MATOPT_GRADLE_OPTION} \
:filament-android:assembleRelease \
:gltfio-android:assembleRelease \
-3
View File
@@ -30,9 +30,6 @@ if [[ "$GITHUB_WORKFLOW" ]]; then
sudo apt-get install clang-$GITHUB_CLANG_VERSION libc++-$GITHUB_CLANG_VERSION-dev libc++abi-$GITHUB_CLANG_VERSION-dev
sudo apt-get install mesa-common-dev libxi-dev libxxf86vm-dev
# For dawn
sudo apt-get install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev libx11-xcb-dev
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${GITHUB_CLANG_VERSION} 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${GITHUB_CLANG_VERSION} 100
fi
+16 -24
View File
@@ -13,7 +13,7 @@ This document is part of the [Filament project](https://github.com/google/filame
## Authors
- [Romain Guy](https://github.com/romainguy), [@romainguy](https://twitter.com/romainguy)
- [Mathias Agopian](https://github.com/pixelflinger), [@pixelflinger](https://bsky.app/profile/pixelflinger.bsky.social)
- [Mathias Agopian](https://github.com/pixelflinger), [@darthmoosious](https://twitter.com/darthmoosious)
# Overview
@@ -78,27 +78,26 @@ in table [standardProperties].
Property | Definition
-----------------------:|:---------------------
**baseColor** | Diffuse albedo for non-metallic surfaces, and specular color for metallic surfaces
**roughness** | Perceived smoothness (1.0) or roughness (0.0) of a surface. Smooth surfaces exhibit sharp reflections
**metallic** | Whether a surface appears to be dielectric (0.0) or conductor (1.0). Often used as a binary value (0 or 1)
**roughness** | Perceived smoothness (1.0) or roughness (0.0) of a surface. Smooth surfaces exhibit sharp reflections
**reflectance** | Fresnel reflectance at normal incidence for dielectric surfaces. This directly controls the strength of the reflections
**ambientOcclusion** | Defines how much of the ambient light is accessible to a surface point. It is a per-pixel shadowing factor between 0.0 and 1.0
**clearCoat** | Strength of the clear coat layer
**clearCoatRoughness** | Perceived smoothness or roughness of the clear coat layer
**clearCoatNormal** | A detail normal used to perturb the clear coat layer using _bump mapping_ (_normal mapping_)
**anisotropy** | Amount of anisotropy in either the tangent or bitangent direction
**anisotropyDirection** | Local surface direction in tangent space
**thickness** | Thickness of the solid volume of refractive objects
**sheenColor** | Strength of the sheen layer
**sheenRoughness** | Perceived smoothness or roughness of the sheen layer
**emissive** | Additional diffuse albedo to simulate emissive surfaces (such as neons, etc.) This property is mostly useful in an HDR pipeline with a bloom pass
**clearCoat** | Strength of the clear coat layer
**clearCoatRoughness** | Perceived smoothness or roughness of the clear coat layer
**anisotropy** | Amount of anisotropy in either the tangent or bitangent direction
**anisotropyDirection** | Local surface direction in tangent space
**ambientOcclusion** | Defines how much of the ambient light is accessible to a surface point. It is a per-pixel shadowing factor between 0.0 and 1.0
**normal** | A detail normal used to perturb the surface using _bump mapping_ (_normal mapping_)
**postLightingColor** | Additional color that can be blended with the result of the lighting computations. See `postLightingBlending`
**absorption** | Absorption factor for refractive objects
**transmission** | Defines how much of the diffuse light of a dielectric is transmitted through the object, in other words this defines how transparent an object is
**ior** | Index of refraction, either for refractive objects or as an alternative to reflectance
**microThickness** | Thickness of the thin layer of refractive objects
**bentNormal** | A normal pointing in the average unoccluded direction. Can be used to improve indirect lighting quality
**shadowStrength** | Strength factor between 0 and 1 for all shadows received by this material
**clearCoatNormal** | A detail normal used to perturb the clear coat layer using _bump mapping_ (_normal mapping_)
**emissive** | Additional diffuse albedo to simulate emissive surfaces (such as neons, etc.) This property is mostly useful in an HDR pipeline with a bloom pass
**postLightingColor** | Additional color that can be blended with the result of the lighting computations. See `postLightingBlending`
**ior** | Index of refraction, either for refractive objects or as an alternative to reflectance
**transmission** | Defines how much of the diffuse light of a dielectric is transmitted through the object, in other words this defines how transparent an object is
**absorption** | Absorption factor for refractive objects
**microThickness** | Thickness of the thin layer of refractive objects
**thickness** | Thickness of the solid volume of refractive objects
[Table [standardProperties]: Properties of the standard model]
The type and range of each property is described in table [standardPropertiesTypes].
@@ -1273,9 +1272,6 @@ Description
when selecting any shading model that is not `unlit`. See the shader sections of this document
for more information on how to access these attributes from the shaders.
!!! Note: Interaction with custom variables
When the `color` attribute is specified, only four custom variables are available instead of five.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
parameters : [
@@ -1306,7 +1302,7 @@ Type
: array of `string`
Value
: Up to 5 strings, each must be a valid GLSL identifier.
: Up to 4 strings, each must be a valid GLSL identifier.
Description
: Defines custom interpolants (or variables) that are output by the material's vertex shader.
@@ -1322,10 +1318,6 @@ Description
particular if `default` is specified the default precision is used is the fragment shader
(`mediump`) and in the vertex shader (`highp`).
!!! Warning: Interaction with required attributes
If the `color` attribute is specified in the `required` list, then only four variables can be used
instead of five.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
name : Skybox,
File diff suppressed because one or more lines are too long
Binary file not shown.
+12 -48
View File
@@ -108,12 +108,8 @@ if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3)
list(APPEND SRCS src/opengl/platforms/PlatformCocoaTouchGL.mm)
list(APPEND SRCS src/opengl/platforms/CocoaTouchExternalImage.mm)
elseif (APPLE)
if (FILAMENT_SUPPORTS_OSMESA)
list(APPEND SRCS src/opengl/platforms/PlatformOSMesa.cpp)
else()
list(APPEND SRCS src/opengl/platforms/PlatformCocoaGL.mm)
list(APPEND SRCS src/opengl/platforms/CocoaExternalImage.mm)
endif()
list(APPEND SRCS src/opengl/platforms/PlatformCocoaGL.mm)
list(APPEND SRCS src/opengl/platforms/CocoaExternalImage.mm)
elseif (WEBGL)
list(APPEND SRCS src/opengl/platforms/PlatformWebGL.cpp)
elseif (LINUX)
@@ -177,12 +173,10 @@ endif()
if (FILAMENT_SUPPORTS_VULKAN)
list(APPEND SRCS
include/backend/platforms/VulkanPlatform.h
src/vulkan/VulkanDescriptorSetCache.cpp
src/vulkan/VulkanDescriptorSetCache.h
src/vulkan/VulkanDescriptorSetLayoutCache.cpp
src/vulkan/VulkanDescriptorSetLayoutCache.h
src/vulkan/VulkanPipelineLayoutCache.cpp
src/vulkan/VulkanPipelineLayoutCache.h
src/vulkan/caching/VulkanDescriptorSetManager.cpp
src/vulkan/caching/VulkanDescriptorSetManager.h
src/vulkan/caching/VulkanPipelineLayoutCache.cpp
src/vulkan/caching/VulkanPipelineLayoutCache.h
src/vulkan/memory/ResourceManager.cpp
src/vulkan/memory/ResourceManager.h
src/vulkan/memory/ResourcePointer.h
@@ -248,31 +242,11 @@ 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)
@@ -422,12 +396,9 @@ set(LINUX_LINKER_OPTIMIZATION_FLAGS
-Wl,--exclude-libs,bluegl
)
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()
if (LINUX AND FILAMENT_SUPPORTS_OSMESA)
set(OSMESA_COMPILE_FLAGS
-I${FILAMENT_OSMESA_PATH}/include/GL)
endif()
if (MSVC)
@@ -462,10 +433,6 @@ 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}>
@@ -494,9 +461,7 @@ if (APPLE OR LINUX)
test/ShaderGenerator.cpp
test/TrianglePrimitive.cpp
test/Arguments.cpp
test/ImageExpectations.cpp
test/Lifetimes.cpp
test/Shader.cpp
test/test_FeedbackLoops.cpp
test/test_Blit.cpp
test/test_MissingRequiredAttributes.cpp
@@ -512,7 +477,6 @@ if (APPLE OR LINUX)
test/test_Handles.cpp
)
set(BACKEND_TEST_LIBS
absl::str_format
backend
getopt
gtest
@@ -528,7 +492,7 @@ if (APPLE AND NOT IOS)
list(APPEND BACKEND_TEST_SRC
test/test_RenderExternalImage.cpp)
add_library(backend_test STATIC ${BACKEND_TEST_SRC})
target_link_libraries(backend_test PUBLIC ${BACKEND_TEST_LIBS})
target_link_libraries(backend_test PRIVATE ${BACKEND_TEST_LIBS})
set(BACKEND_TEST_DEPS
OSDependent
+8 -122
View File
@@ -126,10 +126,6 @@ static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT,
static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 9; // This is guaranteed by OpenGL ES.
static constexpr size_t CONFIG_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.
*/
@@ -256,21 +252,21 @@ struct DescriptorSetLayoutBinding {
DescriptorFlags flags = DescriptorFlags::NONE;
uint16_t count = 0;
// TODO: uncomment when needed. Note that this class is used as hash key. We need to ensure
// no uninitialized padding bytes.
// uint8_t externalSamplerDataIndex = EXTERNAL_SAMPLER_DATA_INDEX_UNUSED;
friend inline bool operator==(DescriptorSetLayoutBinding const& lhs,
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
*/
@@ -972,28 +968,8 @@ 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)
@@ -1048,7 +1024,6 @@ 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
@@ -1056,95 +1031,6 @@ static_assert(sizeof(SamplerParams) == 4);
static_assert(sizeof(SamplerParams) <= sizeof(uint64_t),
"SamplerParams must be no more than 64 bits");
//! Sampler parameters
struct SamplerYcbcrConversion {// NOLINT
SamplerYcbcrModelConversion ycbcrModel : 4;
TextureSwizzle r : 4;
TextureSwizzle g : 4;
TextureSwizzle b : 4;
TextureSwizzle a : 4;
SamplerYcbcrRange ycbcrRange : 1;
ChromaLocation xChromaOffset : 1;
ChromaLocation yChromaOffset : 1;
SamplerMagFilter chromaFilter : 1;
uint8_t padding;
struct Hasher {
size_t operator()(const SamplerYcbcrConversion p) const noexcept {
// we don't use std::hash<> here, so we don't have to include <functional>
return *reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&p));
}
};
struct EqualTo {
bool operator()(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) const noexcept {
assert_invariant(lhs.padding == 0);
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
return *pLhs == *pRhs;
}
};
struct LessThan {
bool operator()(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) const noexcept {
assert_invariant(lhs.padding == 0);
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
return *pLhs < *pRhs;
}
};
private:
friend inline bool operator == (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
noexcept {
return SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
}
friend inline bool operator != (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
noexcept {
return !SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
}
friend inline bool operator < (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
noexcept {
return SamplerYcbcrConversion::LessThan{}(lhs, rhs);
}
};
static_assert(sizeof(SamplerYcbcrConversion) == 4);
static_assert(sizeof(SamplerYcbcrConversion) <= sizeof(uint64_t),
"SamplerYcbcrConversion must be no more than 64 bits");
struct ExternalSamplerDatum {
ExternalSamplerDatum(SamplerYcbcrConversion ycbcr, SamplerParams spm, uint32_t extFmt)
: YcbcrConversion(ycbcr),
samplerParams(spm),
externalFormat(extFmt) {}
bool operator==(ExternalSamplerDatum const& rhs) const {
return (YcbcrConversion == rhs.YcbcrConversion && samplerParams == rhs.samplerParams &&
externalFormat == rhs.externalFormat);
}
struct EqualTo {
bool operator()(const ExternalSamplerDatum& lhs,
const ExternalSamplerDatum& rhs) const noexcept {
return (lhs.YcbcrConversion == rhs.YcbcrConversion &&
lhs.samplerParams == rhs.samplerParams &&
lhs.externalFormat == rhs.externalFormat);
}
};
SamplerYcbcrConversion YcbcrConversion;
SamplerParams samplerParams;
uint32_t externalFormat;
};
// No implicit padding allowed due to it being a hash key.
static_assert(sizeof(ExternalSamplerDatum) == 12);
struct DescriptorSetLayout {
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
// TODO: uncomment when needed
// utils::FixedCapacityVector<ExternalSamplerDatum> externalSamplerData;
};
//! blending equation function
enum class BlendEquation : uint8_t {
ADD, //!< the fragment is added to the color buffer
@@ -33,7 +33,7 @@ public:
PlatformCocoaGL();
~PlatformCocoaGL() noexcept override;
ExternalImageHandle UTILS_PUBLIC createExternalImage(void* cvPixelBuffer) noexcept;
ExternalImageHandle createExternalImage(void* cvPixelBuffer) noexcept;
protected:
// --------------------------------------------------------------------------------------------
@@ -32,7 +32,7 @@ public:
PlatformCocoaTouchGL();
~PlatformCocoaTouchGL() noexcept override;
ExternalImageHandle UTILS_PUBLIC createExternalImage(void* cvPixelBuffer) noexcept;
ExternalImageHandle createExternalImage(void* cvPixelBuffer) noexcept;
// --------------------------------------------------------------------------------------------
// Platform Interface
@@ -50,7 +50,7 @@ public:
/**
* Creates an ExternalImage from a EGLImageKHR
*/
ExternalImageHandle UTILS_PUBLIC createExternalImage(EGLImageKHR eglImage) noexcept;
ExternalImageHandle createExternalImage(EGLImageKHR eglImage) noexcept;
protected:
// --------------------------------------------------------------------------------------------
@@ -23,7 +23,6 @@
#include <backend/platforms/PlatformEGL.h>
#include <utils/android/PerformanceHintManager.h>
#include <utils/compiler.h>
#include <chrono>
@@ -44,21 +43,8 @@ public:
PlatformEGLAndroid() noexcept;
~PlatformEGLAndroid() noexcept override;
/**
* Creates an ExternalImage from a EGLImageKHR
*/
ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer, bool sRGB) noexcept;
struct UTILS_PUBLIC ExternalImageDescAndroid {
uint32_t width; // Texture width
uint32_t height; // Texture height
TextureFormat format;// Texture format
TextureUsage usage; // Texture usage flags
};
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(ExternalImageHandle externalImage) noexcept;
protected:
// --------------------------------------------------------------------------------------------
// Platform Interface
@@ -71,6 +57,11 @@ protected:
Driver* createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) noexcept override;
/**
* Creates an ExternalImage from a EGLImageKHR
*/
ExternalImageHandle createExternalImage(AHardwareBuffer const *buffer, bool sRGB) noexcept;
// --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface
@@ -104,26 +95,15 @@ protected:
*/
AcquiredImage transformAcquiredImage(AcquiredImage source) noexcept override;
OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override;
void destroyExternalImageTexture(ExternalTexture* texture) noexcept override;
bool setExternalImage(ExternalImageHandleRef externalImage, ExternalTexture* texture) noexcept override;
struct ExternalImageEGLAndroid : public ExternalImageEGL {
AHardwareBuffer* aHardwareBuffer = nullptr;
uint32_t width; // Texture width
uint32_t height; // Texture height
TextureFormat format;// Texture format
TextureUsage usage; // Texture usage flags
bool sRGB = false;
protected:
~ExternalImageEGLAndroid() override;
};
bool setExternalImage(ExternalImageHandleRef externalImage,
ExternalTexture* texture) noexcept override;
bool setImage(ExternalImageEGLAndroid const* eglExternalImage,
ExternalTexture* texture) noexcept;
protected:
bool makeCurrent(ContextType type,
SwapChain* drawSwapChain,
@@ -21,12 +21,7 @@
#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,7 +18,6 @@
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H
#include <backend/Platform.h>
#include <backend/DriverEnums.h>
#include <bluevk/BlueVK.h>
@@ -318,11 +317,6 @@ public:
*/
uint32_t layers;
/**
* The numbers of samples per texel
*/
VkSampleCountFlagBits samples;
/**
* The format of the external image
*/
@@ -357,44 +351,20 @@ public:
using ImageData = std::pair<VkImage, VkDeviceMemory>;
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);
protected:
virtual ExtensionSet getSwapchainInstanceExtensions() const;
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept;
const ExternalImageMetadata& metadata);
private:
// Platform dependent helper methods
static ExtensionSet getSwapchainInstanceExtensionsImpl();
static ExtensionSet getSwapchainInstanceExtensions();
static ExternalImageMetadata getExternalImageMetadataImpl(ExternalImageHandleRef externalImage,
VkDevice device);
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);
VkDevice device, const ExternalImageMetadata& metadata);
// Platform dependent helper methods
static SurfaceBundle createVkSurfaceKHRImpl(void* nativeWindow, VkInstance instance,
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
static SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) noexcept;
friend struct VulkanPlatformPrivate;
@@ -17,55 +17,27 @@
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
#include <backend/DriverEnums.h>
#include <backend/platforms/VulkanPlatform.h>
#include <backend/Platform.h>
#include <android/hardware_buffer.h>
namespace filament::backend {
namespace filament::backend::fvkandroid {
class VulkanPlatformAndroid : public VulkanPlatform {
public:
Platform::ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept;
struct UTILS_PUBLIC ExternalImageDescAndroid {
uint32_t width; // Texture width
uint32_t height; // Texture height
TextureFormat format;// Texture format
TextureUsage usage; // Texture usage flags
};
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(
ExternalImageHandleRef externalImage) const noexcept;
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:
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;
};
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage);
using ImageData = VulkanPlatform::ImageData;
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage);
virtual ExtensionSet getSwapchainInstanceExtensions() const;
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept;
~ExternalImageVulkanAndroid() override;
};
}// namespace filament::backend
Platform::ExternalImageHandle createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept;
#endif// TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
} // namespace filament::backend::fvkandroid
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
@@ -1,59 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H
#include <backend/Platform.h>
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
namespace filament::backend {
/**
* A Platform interface, handling the environment-specific concerns, e.g. OS, for creating a WebGPU
* driver (backend).
*/
class WebGPUPlatform final : public Platform {
public:
WebGPUPlatform();
~WebGPUPlatform() override = default;
[[nodiscard]] int getOSVersion() const noexcept final { return 0; }
[[nodiscard]] wgpu::Instance& getInstance() noexcept { return mInstance; }
// either returns a valid surface or panics
[[nodiscard]] wgpu::Surface createSurface(void* nativeWindow, uint64_t flags);
// either returns a valid adapter or panics
[[nodiscard]] wgpu::Adapter requestAdapter(wgpu::Surface const& surface);
// either returns a valid device or panics
[[nodiscard]] wgpu::Device requestDevice(wgpu::Adapter const& adapter);
protected:
[[nodiscard]] Driver* createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) noexcept override;
private:
// we may consider having the driver own this in the future
wgpu::Instance mInstance;
};
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H
@@ -362,8 +362,6 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedTexturesSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthClampSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(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)
+7 -25
View File
@@ -19,11 +19,6 @@
#include <utils/Systrace.h>
#include <utils/debug.h>
// We need to keep this up top for the linux (X11) name collisions.
#if defined(FILAMENT_SUPPORTS_WEBGPU)
#include "backend/platforms/WebGPUPlatform.h"
#endif
#if defined(__ANDROID__)
#include <sys/system_properties.h>
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3)
@@ -35,11 +30,7 @@
#endif
#elif defined(__APPLE__)
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3)
#if defined(FILAMENT_SUPPORTS_OSMESA)
#include <backend/platforms/PlatformOSMesa.h>
#else
#include <backend/platforms/PlatformCocoaGL.h>
#endif
#include <backend/platforms/PlatformCocoaGL.h>
#endif
#elif defined(__linux__)
#if defined(FILAMENT_SUPPORTS_X11)
@@ -64,11 +55,7 @@
#endif
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
#if defined(__ANDROID__)
#include "backend/platforms/VulkanPlatformAndroid.h"
#else
#include "backend/platforms/VulkanPlatform.h"
#endif
#include "backend/platforms/VulkanPlatform.h"
#endif
#if defined (FILAMENT_SUPPORTS_METAL)
@@ -78,6 +65,9 @@ filament::backend::Platform* createDefaultMetalPlatform();
#endif
#include "noop/PlatformNoop.h"
#if defined(FILAMENT_SUPPORTS_WEBGPU)
#include "webgpu/WebGPUPlatform.h"
#endif
namespace filament::backend {
@@ -114,11 +104,7 @@ Platform* PlatformFactory::create(Backend* backend) noexcept {
}
if (*backend == Backend::VULKAN) {
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
#if defined(__ANDROID__)
return new VulkanPlatformAndroid();
#else
return new VulkanPlatform();
#endif
return new VulkanPlatform();
#else
return nullptr;
#endif
@@ -146,11 +132,7 @@ Platform* PlatformFactory::create(Backend* backend) noexcept {
#elif defined(FILAMENT_IOS)
return new PlatformCocoaTouchGL();
#elif defined(__APPLE__)
#if defined(FILAMENT_SUPPORTS_OSMESA)
return new PlatformOSMesa();
#else
return new PlatformCocoaGL();
#endif
return new PlatformCocoaGL();
#elif defined(__linux__)
#if defined(FILAMENT_SUPPORTS_X11)
return new PlatformGLX();
-18
View File
@@ -1177,24 +1177,6 @@ size_t MetalDriver::getMaxUniformBufferSize() {
return 256 * 1024 * 1024; // TODO: return the actual size instead of hardcoding the minspec
}
size_t MetalDriver::getMaxTextureSize(SamplerType type) {
if (type == SamplerType::SAMPLER_3D) {
return 2048;
}
if (mContext->highestSupportedGpuFamily.apple >= 3 ||
mContext->highestSupportedGpuFamily.mac >= 2) {
return 16384;
} else if (mContext->highestSupportedGpuFamily.apple >= 2) {
return 8192;
}
return 4096;
}
size_t MetalDriver::getMaxArrayTextureLayers() {
// TODO: return the actual size instead of hardcoding the minspec
return 256;
}
void MetalDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& data,
uint32_t byteOffset) {
FILAMENT_CHECK_PRECONDITION(data.buffer)
-10
View File
@@ -240,16 +240,6 @@ size_t NoopDriver::getMaxUniformBufferSize() {
return 16384u;
}
size_t NoopDriver::getMaxTextureSize(SamplerType target) {
// NoopDriver is being actively used for other purposes. This needs to be resolved before we
// can change it to 2048. b/406832484
return 16384u;
}
size_t NoopDriver::getMaxArrayTextureLayers() {
return 256u;
}
void NoopDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
uint32_t byteOffset) {
scheduleDestroy(std::move(p));
+1 -1
View File
@@ -20,7 +20,7 @@
namespace filament::backend {
Driver* PlatformNoop::createDriver(void* sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
Driver* PlatformNoop::createDriver(void* const sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
return NoopDriver::create();
}
+7 -34
View File
@@ -94,22 +94,18 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
}
#endif
initExtensions(&ext, state.major, state.minor);
OpenGLContext::initExtensions(&ext, state.major, state.minor);
initProcs(&procs, ext, state.major, state.minor);
OpenGLContext::initProcs(&procs, ext, state.major, state.minor);
initBugs(&bugs, ext, state.major, state.minor,
OpenGLContext::initBugs(&bugs, ext, state.major, state.minor,
state.vendor, state.renderer, state.version, state.shader);
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 = resolveFeatureLevel(state.major, state.minor, ext, gets, bugs);
mFeatureLevel = OpenGLContext::resolveFeatureLevel(state.major, state.minor, ext, gets, bugs);
#ifdef BACKEND_OPENGL_VERSION_GLES
mShaderModel = ShaderModel::MOBILE;
@@ -181,14 +177,6 @@ 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 = "
@@ -566,11 +554,7 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
} else if (strstr(renderer, "AMD") ||
strstr(renderer, "ATI")) {
// AMD/ATI GPU
} else if (strstr(renderer, "Mozilla")) {
bugs->disable_invalidate_framebuffer = true;
}
if (strstr(vendor, "Mesa")) {
} else if (strstr(vendor, "Mesa")) {
// Seen on
// [Mesa],
// [llvmpipe (LLVM 17.0.6, 256 bits)],
@@ -578,6 +562,8 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
// [4.50]
// not known which version are affected
bugs->rebind_buffer_after_deletion = true;
} else if (strstr(renderer, "Mozilla")) {
bugs->disable_invalidate_framebuffer = true;
}
} else {
// When running under ANGLE, it's a different set of workaround that we need.
@@ -589,19 +575,6 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
}
}
if (strstr(vendor, "Mozilla")) {
// Seen on
// [Mozilla],
// [GeForce GTX 980, or similar]
// or [ANGLE (NVIDIA, NVIDIA GeForce GTX 980 Direct3D11 vs_5_0 ps_5_0), or similar]
// or anything else,
// [OpenGL ES 3.0 (WebGL 2.0)],
// [OpenGL ES GLSL ES 3.00 (WebGL GLSL ES 3.00)]
// For Mozilla, the issue appears to be observed regardless of whether the renderer is
// ANGLE or not. (b/376125497)
bugs->rebind_buffer_after_deletion = true;
}
#ifdef BACKEND_OPENGL_VERSION_GLES
# ifndef FILAMENT_IOS // FILAMENT_IOS is guaranteed to have ES3.x
if (UTILS_UNLIKELY(major == 2)) {
+2 -6
View File
@@ -203,10 +203,6 @@ 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;
@@ -320,8 +316,8 @@ public:
// a glFinish. So we must delay the destruction until we know the GPU is finished.
bool delay_fbo_destruction;
// Mesa and Mozilla(web) sometimes clear the generic buffer binding when *another* buffer
// is destroyed, if that other buffer is bound on an *indexed* buffer binding.
// Mesa sometimes clears the generic buffer binding when *another* buffer is destroyed,
// if that other buffer is bound on an *indexed* buffer binding.
bool rebind_buffer_after_deletion;
// Force feature level 0. Typically used for low end ES3 devices with significant driver
+6 -26
View File
@@ -148,8 +148,8 @@ using namespace utils;
namespace filament::backend {
Driver* OpenGLDriverFactory::create(
OpenGLPlatform* platform,
void* sharedGLContext,
OpenGLPlatform* const platform,
void* const sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept {
return OpenGLDriver::create(platform, sharedGLContext, driverConfig);
}
@@ -159,10 +159,10 @@ using namespace GLUtils;
// ------------------------------------------------------------------------------------------------
UTILS_NOINLINE
OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* platform,
void* /*sharedGLContext*/, const Platform::DriverConfig& driverConfig) noexcept {
OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* const platform,
void* const /*sharedGLContext*/, const Platform::DriverConfig& driverConfig) noexcept {
assert_invariant(platform);
OpenGLPlatform* ec = platform;
OpenGLPlatform* const ec = platform;
#if 0
// this is useful for development, but too verbose even for debug builds
@@ -230,7 +230,7 @@ OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* platform,
constexpr size_t defaultSize = FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
Platform::DriverConfig validConfig{ driverConfig };
validConfig.handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize);
OpenGLDriver* driver = new(std::nothrow) OpenGLDriver(ec, validConfig);
OpenGLDriver* const driver = new(std::nothrow) OpenGLDriver(ec, validConfig);
return driver;
}
@@ -2460,26 +2460,6 @@ 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
// ------------------------------------------------------------------------------------------------
@@ -64,7 +64,7 @@ PlatformCocoaTouchGL::~PlatformCocoaTouchGL() noexcept {
delete pImpl;
}
Driver* PlatformCocoaTouchGL::createDriver(void* sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
Driver* PlatformCocoaTouchGL::createDriver(void* const sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
EAGLSharegroup* sharegroup = (__bridge EAGLSharegroup*) sharedGLContext;
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 sharegroup:sharegroup];
@@ -19,7 +19,6 @@
#include <backend/platforms/PlatformEGL.h>
#include <backend/platforms/PlatformEGLAndroid.h>
#include <private/backend/BackendUtilsAndroid.h>
#include <private/backend/VirtualMachineEnv.h>
#include "opengl/GLUtils.h"
@@ -35,8 +34,6 @@
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <utils/Log.h>
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
@@ -234,133 +231,26 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
return driver;
}
PlatformEGLAndroid::ExternalImageEGLAndroid::~ExternalImageEGLAndroid() {
if (__builtin_available(android 26, *)) {
if (aHardwareBuffer) {
AHardwareBuffer_release(aHardwareBuffer);
}
}
}
PlatformEGLAndroid::ExternalImageEGLAndroid::~ExternalImageEGLAndroid() = default;
Platform::ExternalImageHandle PlatformEGLAndroid::createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept {
if (__builtin_available(android 26, *)) {
auto* const p = new (std::nothrow) ExternalImageEGLAndroid;
auto hardwareBuffer = const_cast<AHardwareBuffer*>(buffer);
AHardwareBuffer_acquire(hardwareBuffer);
p->aHardwareBuffer = hardwareBuffer;
p->sRGB = sRGB;
AHardwareBuffer_Desc hardwareBufferDescription = {};
AHardwareBuffer_describe(hardwareBuffer, &hardwareBufferDescription);
p->height = hardwareBufferDescription.height;
p->width = hardwareBufferDescription.width;
auto textureFormat = mapToFilamentFormat(hardwareBufferDescription.format, sRGB);
p->usage = mapToFilamentUsage(hardwareBufferDescription.usage, textureFormat);
return ExternalImageHandle{ p };
}
return Platform::ExternalImageHandle{};
}
PlatformEGLAndroid::ExternalImageDescAndroid PlatformEGLAndroid::getExternalImageDesc(
ExternalImageHandle externalImage) noexcept {
auto const* const eglExternalImage =
static_cast<ExternalImageEGLAndroid const*>(externalImage.get());
ExternalImageDescAndroid metadata = {};
if (!eglExternalImage) {
return metadata;
}
metadata.height = eglExternalImage->height;
metadata.width = eglExternalImage->width;
metadata.format = eglExternalImage->format;
metadata.usage = eglExternalImage->usage;
return metadata;
Platform::ExternalImageHandle PlatformEGLAndroid::createExternalImage(AHardwareBuffer const* buffer, bool sRGB) noexcept {
auto* const p = new(std::nothrow) ExternalImageEGLAndroid;
p->aHardwareBuffer = const_cast<AHardwareBuffer*>(buffer);
p->sRGB = sRGB;
return ExternalImageHandle{ p };
}
bool PlatformEGLAndroid::setExternalImage(ExternalImageHandleRef externalImage,
UTILS_UNUSED_IN_RELEASE ExternalTexture* texture) noexcept {
auto const* const eglExternalImage =
static_cast<ExternalImageEGLAndroid const*>(externalImage.get());
auto const* const eglExternalImage = static_cast<ExternalImageEGLAndroid const*>(externalImage.get());
if (eglExternalImage->aHardwareBuffer) {
return PlatformEGLAndroid::setImage(eglExternalImage, texture);
// TODO: implement PlatformEGLAndroid::setExternalImage w/ AHardwareBuffer
return true;
}
// not a AHardwareBuffer, fallback to the inherited version
return PlatformEGL::setExternalImage(externalImage, texture);
}
OpenGLPlatform::ExternalTexture* PlatformEGLAndroid::createExternalImageTexture() noexcept {
ExternalTexture* outTexture = new (std::nothrow) ExternalTexture{};
glGenTextures(1, &outTexture->id);
return outTexture;
}
void PlatformEGLAndroid::destroyExternalImageTexture(ExternalTexture* texture) noexcept {
glDeleteTextures(1, &texture->id);
delete texture;
}
bool PlatformEGLAndroid::setImage(ExternalImageEGLAndroid const* eglExternalImage,
UTILS_UNUSED_IN_RELEASE ExternalTexture* texture) noexcept {
AHardwareBuffer* hardwareBuffer = eglExternalImage->aHardwareBuffer;
// Get the EGL client buffer from AHardwareBuffer
EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(hardwareBuffer);
EGLint imageAttrs[] = {
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
EGL_NONE, EGL_NONE, // Reserve space
EGL_NONE, EGL_NONE, // Reserve space
EGL_NONE // Ensure the list always ends with EGL_NONE
};
int attrIndex = 2;
if (eglExternalImage->sRGB) {
imageAttrs[attrIndex++] = EGL_GL_COLORSPACE;
imageAttrs[attrIndex++] = EGL_GL_COLORSPACE_SRGB;
}
if (static_cast<bool>(eglExternalImage->usage & TextureUsage::PROTECTED)) {
imageAttrs[attrIndex++] = EGL_PROTECTED_CONTENT_EXT;
imageAttrs[attrIndex++] = EGL_TRUE;
}
// Create an EGLImage from the client buffer
EGLImageKHR eglImage = eglCreateImageKHR(eglGetCurrentDisplay(), EGL_NO_CONTEXT,
EGL_NATIVE_BUFFER_ANDROID, clientBuffer, imageAttrs);
if (eglImage == EGL_NO_IMAGE_KHR) {
// Handle error
slog.e << "Failed to create EGL image" << io::endl;
glDeleteTextures(1, &texture->id);
return false;
}
// Create and bind the OpenGL texture
GLint prevActiveTexture, prevTexture;
glGetIntegerv(GL_ACTIVE_TEXTURE, &prevActiveTexture);
glGetIntegerv(GL_TEXTURE_BINDING_2D, &prevTexture);
glActiveTexture(GL_TEXTURE0);
glBindTexture(texture->target, texture->id);
GLenum error = glGetError();
if (UTILS_UNLIKELY(error != GL_NO_ERROR)) {
slog.e << "Error after glBindTexture: " << error << io::endl;
glDeleteTextures(1, &texture->id);
eglDestroyImageKHR(eglGetCurrentDisplay(), eglImage);
glActiveTexture(prevActiveTexture);
glBindTexture(GL_TEXTURE_2D, prevTexture);
return false;
}
glEGLImageTargetTexture2DOES(texture->target, static_cast<GLeglImageOES>(eglImage));
error = glGetError();
if (UTILS_UNLIKELY(error != GL_NO_ERROR)) {
slog.e << "Error after glEGLImageTargetTexture2DOES: " << error << io::endl;
glDeleteTextures(1, &texture->id);
eglDestroyImageKHR(eglGetCurrentDisplay(), eglImage);
glActiveTexture(prevActiveTexture);
glBindTexture(GL_TEXTURE_2D, prevTexture);
return false;
}
glActiveTexture(prevActiveTexture);
glBindTexture(GL_TEXTURE_2D, prevTexture);
return true;
}
void PlatformEGLAndroid::setPresentationTime(int64_t presentationTimeInNanosecond) noexcept {
EGLSurface currentDrawSurface = eglGetCurrentSurface(EGL_DRAW);
if (currentDrawSurface != EGL_NO_SURFACE) {
@@ -128,7 +128,7 @@ namespace filament::backend {
using namespace backend;
Driver* PlatformGLX::createDriver(void* sharedGLContext,
Driver* PlatformGLX::createDriver(void* const sharedGLContext,
const DriverConfig& driverConfig) noexcept {
loadLibraries();
// Get the display device
@@ -22,11 +22,9 @@
#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 {
@@ -50,27 +48,20 @@ struct OSMesaSwapchain {
struct OSMesaAPI {
private:
using CreateContextAttribsFunc = OSMesaContext (*)(const int *, OSMesaContext);
using CreateContextFunc = OSMesaContext (*)(GLenum format, 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:
CreateContextAttribsFunc fOSMesaCreateContextAttribs;
CreateContextFunc fOSMesaCreateContext;
DestroyContextFunc fOSMesaDestroyContext;
MakeCurrentFunc fOSMesaMakeCurrent;
GetProcAddressFunc fOSMesaGetProcAddress;
OSMesaAPI() {
static constexpr char const* libraryNames[] = {
#if defined(__linux__)
"libOSMesa.so",
"libosmesa.so",
#elif defined(__APPLE__)
"libOSMesa.dylib",
#endif
};
constexpr char const* libraryNames[] = {"libOSMesa.so", "libosmesa.so"};
for (char const* libName: libraryNames) {
mLib = dlopen(libName, RTLD_GLOBAL | RTLD_NOW);
if (mLib) {
@@ -80,24 +71,22 @@ public:
if (mLib) {
// Loading from a libosmesa.os
fOSMesaGetProcAddress = (GetProcAddressFunc) dlsym(mLib, "OSMesaGetProcAddress");
}
#if defined(__linux__)
else {
} 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(fOSMesaGetProcAddress)
<< "Unable to link against libOSMesa to create a software GL context";
fOSMesaCreateContextAttribs =
(CreateContextAttribsFunc) fOSMesaGetProcAddress("OSMesaCreateContextAttribs");
fOSMesaDestroyContext = (DestroyContextFunc) fOSMesaGetProcAddress("OSMesaDestroyContext");
fOSMesaCreateContext = (CreateContextFunc) fOSMesaGetProcAddress("OSMesaCreateContext");
fOSMesaDestroyContext =
(DestroyContextFunc) fOSMesaGetProcAddress("OSMesaDestroyContext");
fOSMesaMakeCurrent = (MakeCurrentFunc) fOSMesaGetProcAddress("OSMesaMakeCurrent");
}
@@ -112,24 +101,14 @@ private:
}// anonymous namespace
Driver* PlatformOSMesa::createDriver(void* sharedGLContext,
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->fOSMesaCreateContextAttribs(attribs, NULL);
mContext = api->fOSMesaCreateContext(GL_RGBA, NULL);
// We need to do a no-op makecurrent here so that the context will be in a correct state before
// any GL calls.
@@ -75,7 +75,7 @@ struct WGLSwapChain {
static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs = nullptr;
Driver* PlatformWGL::createDriver(void* sharedGLContext,
Driver* PlatformWGL::createDriver(void* const sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept {
int result = 0;
int pixelFormat = 0;
@@ -20,7 +20,7 @@ namespace filament::backend {
using namespace backend;
Driver* PlatformWebGL::createDriver(void* sharedGLContext,
Driver* PlatformWebGL::createDriver(void* const sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept {
return OpenGLPlatform::createDefaultDriver(this, sharedGLContext, driverConfig);
}
@@ -1,110 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VulkanDescriptorSetLayoutCache.h"
#include "VulkanHandles.h"
namespace filament::backend {
namespace {
using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask;
template<typename Bitmask>
uint32_t appendBindings(VkDescriptorSetLayoutBinding* toBind, VkDescriptorType type,
Bitmask const& mask) {
uint32_t count = 0;
Bitmask alreadySeen;
mask.forEachSetBit([&](size_t index) {
VkShaderStageFlags stages = 0;
uint32_t binding = 0;
if (index < fvkutils::getFragmentStageShift<Bitmask>()) {
binding = (uint32_t) index;
stages |= VK_SHADER_STAGE_VERTEX_BIT;
auto fragIndex = index + fvkutils::getFragmentStageShift<Bitmask>();
if (mask.test(fragIndex)) {
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
alreadySeen.set(fragIndex);
}
} else if (!alreadySeen.test(index)) {
// We are in fragment stage bits
binding = (uint32_t) (index - fvkutils::getFragmentStageShift<Bitmask>());
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
}
if (stages) {
toBind[count++] = {
.binding = binding,
.descriptorType = type,
.descriptorCount = 1,
.stageFlags = stages,
};
}
});
return count;
}
} // anonymous namespace
VulkanDescriptorSetLayoutCache::VulkanDescriptorSetLayoutCache(VkDevice device,
fvkmemory::ResourceManager* resourceManager)
: mDevice(device),
mResourceManager(resourceManager) {}
VulkanDescriptorSetLayoutCache::~VulkanDescriptorSetLayoutCache() = default;
void VulkanDescriptorSetLayoutCache::terminate() noexcept {
for (auto& itr: mVkLayouts) {
vkDestroyDescriptorSetLayout(mDevice, itr.second, VKALLOC);
}
}
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> VulkanDescriptorSetLayoutCache::createLayout(
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info) {
auto layout = fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::make(mResourceManager, handle,
info);
VkDescriptorSetLayout vklayout = VK_NULL_HANDLE;
auto const& bitmasks = layout->bitmask;
if (auto itr = mVkLayouts.find(bitmasks); itr != mVkLayouts.end()) {
vklayout = itr->second;
} else {
VkDescriptorSetLayoutBinding toBind[VulkanDescriptorSetLayout::MAX_BINDINGS];
uint32_t count = 0;
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
bitmasks.dynamicUbo);
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bitmasks.ubo);
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
bitmasks.sampler);
count += appendBindings(&toBind[count], VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
bitmasks.inputAttachment);
assert_invariant(count != 0 && "Need at least one binding for descriptor set layout.");
VkDescriptorSetLayoutCreateInfo dlinfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.bindingCount = count,
.pBindings = toBind,
};
vkCreateDescriptorSetLayout(mDevice, &dlinfo, VKALLOC, &vklayout);
mVkLayouts[bitmasks] = vklayout;
}
layout->setVkLayout(vklayout);
return layout;
}
} // namespace filament::backend
@@ -1,63 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETLAYOUTCACHE_H
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETLAYOUTCACHE_H
#include "VulkanHandles.h"
#include "vulkan/memory/ResourcePointer.h"
#include <backend/DriverEnums.h>
#include <backend/Program.h>
#include <backend/TargetBufferInfo.h>
#include <utils/bitset.h>
#include <bluevk/BlueVK.h>
#include <tsl/robin_map.h>
#include <memory>
namespace filament::backend {
class VulkanDescriptorSetLayoutCache {
public:
VulkanDescriptorSetLayoutCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
~VulkanDescriptorSetLayoutCache();
void terminate() noexcept;
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> createLayout(
Handle<HwDescriptorSetLayout> handle, backend::DescriptorSetLayout&& info);
private:
VkDevice mDevice;
fvkmemory::ResourceManager* mResourceManager;
using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask;
using BitmaskGroupHashFn = utils::hash::MurmurHashFn<BitmaskGroup>;
struct BitmaskGroupEqual {
bool operator()(BitmaskGroup const& k1, BitmaskGroup const& k2) const { return k1 == k2; }
};
tsl::robin_map<BitmaskGroup, VkDescriptorSetLayout, BitmaskGroupHashFn, BitmaskGroupEqual>
mVkLayouts;
};
} // namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETLAYOUTCACHE_H
+30 -58
View File
@@ -17,6 +17,7 @@
#include "VulkanDriver.h"
#include "CommandStreamDispatcher.h"
#include "DataReshaper.h"
#include "SystraceProfile.h"
#include "VulkanAsyncHandles.h"
#include "VulkanBuffer.h"
@@ -34,6 +35,7 @@
#include <backend/platforms/VulkanPlatform.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Panic.h>
#ifndef NDEBUG
@@ -42,6 +44,8 @@
using namespace bluevk;
using utils::FixedCapacityVector;
#if defined(__clang__)
// Vulkan functions often immediately dereference pointers, so it's fine to pass in a pointer
// to a stack-allocated variable.
@@ -215,8 +219,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mSamplerCache(mPlatform->getDevice()),
mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
mReadPixels(mPlatform->getDevice()),
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager),
mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager),
mDescriptorSetManager(mPlatform->getDevice(), &mResourceManager),
mQueryManager(mPlatform->getDevice()),
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
mStereoscopicType(driverConfig.stereoscopicType) {
@@ -326,8 +329,7 @@ void VulkanDriver::terminate() {
mPipelineCache.terminate();
mFramebufferCache.reset();
mSamplerCache.terminate();
mDescriptorSetLayoutCache.terminate();
mDescriptorSetCache.terminate();
mDescriptorSetManager.terminate();
mPipelineLayoutCache.terminate();
// Before terminating ResourceManager, we must make sure all of the resource_ptrs have been unset.
@@ -363,7 +365,7 @@ void VulkanDriver::collectGarbage() {
FVK_SYSTRACE_SCOPE();
// Command buffers need to be submitted and completed before other resources can be gc'd.
mCommands.gc();
mDescriptorSetCache.clearHistory();
mDescriptorSetManager.clearHistory();
mStagePool.gc();
mFramebufferCache.gc();
mPipelineCache.gc();
@@ -406,7 +408,7 @@ void VulkanDriver::updateDescriptorSetBuffer(
FVK_SYSTRACE_SCOPE();
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
auto buffer = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh);
mDescriptorSetCache.updateBuffer(set, binding, buffer, offset, size);
mDescriptorSetManager.updateBuffer(set, binding, buffer, offset, size);
}
void VulkanDriver::updateDescriptorSetTexture(
@@ -419,7 +421,7 @@ void VulkanDriver::updateDescriptorSetTexture(
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
VkSampler const vksampler = mSamplerCache.getSampler(params);
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
mDescriptorSetManager.updateSampler(set, binding, texture, vksampler);
}
void VulkanDriver::flush(int) {
@@ -552,43 +554,26 @@ 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;
}
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);
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);
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();
}
@@ -776,7 +761,8 @@ void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
void VulkanDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
backend::DescriptorSetLayout&& info) {
auto layout = mDescriptorSetLayoutCache.createLayout(dslh, std::move(info));
auto layout = resource_ptr<VulkanDescriptorSetLayout>::make(&mResourceManager, dslh, info);
mDescriptorSetManager.initVkLayout(layout);
layout.inc();
}
@@ -785,7 +771,7 @@ void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
FVK_SYSTRACE_SCOPE();
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout =
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
auto set = mDescriptorSetCache.createSet(dsh, layout);
auto set = mDescriptorSetManager.createSet(dsh, layout);
set.inc();
}
@@ -1057,7 +1043,7 @@ bool VulkanDriver::isDepthStencilBlitSupported(TextureFormat format) {
formats.end();
}
bool VulkanDriver::isProtectedTexturesSupported() { return isProtectedContentSupported(); }
bool VulkanDriver::isProtectedTexturesSupported() { return false; }
bool VulkanDriver::isDepthClampSupported() {
return mContext.isDepthClampSupported();
@@ -1134,28 +1120,14 @@ math::float2 VulkanDriver::getClipSpaceParams() {
}
uint8_t VulkanDriver::getMaxDrawBuffers() {
return mContext.getPhysicalDeviceLimits().maxColorAttachments;
return MRT::MIN_SUPPORTED_RENDER_TARGET_COUNT; // TODO: query real value
}
size_t VulkanDriver::getMaxUniformBufferSize() {
return mContext.getPhysicalDeviceLimits().maxUniformBufferRange;
}
size_t VulkanDriver::getMaxTextureSize(SamplerType type) {
switch (type) {
case SamplerType::SAMPLER_2D:
return mContext.getPhysicalDeviceLimits().maxImageDimension2D;
case SamplerType::SAMPLER_3D:
return mContext.getPhysicalDeviceLimits().maxImageDimension3D;
case SamplerType::SAMPLER_CUBEMAP:
return mContext.getPhysicalDeviceLimits().maxImageDimensionCube;
default:
return mContext.getPhysicalDeviceLimits().maxImageDimension1D;
}
}
size_t VulkanDriver::getMaxArrayTextureLayers() {
return mContext.getPhysicalDeviceLimits().maxImageArrayLayers;
// TODO: return the actual size instead of hardcoded value
// TODO: devices that return less than 32768 should be rejected. This represents only 3%
// of android devices.
return 32768;
}
void VulkanDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index,
@@ -1494,7 +1466,7 @@ void VulkanDriver::nextSubpass(int) {
if (mCurrentRenderPass.params.subpassMask & 0x1) {
VulkanAttachment& subpassInput = renderTarget->getColor0();
mDescriptorSetCache.updateInputAttachment({}, subpassInput);
mDescriptorSetManager.updateInputAttachment({}, subpassInput);
}
}
@@ -1821,9 +1793,9 @@ void VulkanDriver::bindDescriptorSet(
backend::DescriptorSetOffsetArray&& offsets) {
if (dsh) {
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
mDescriptorSetCache.bind(setIndex, set, std::move(offsets));
mDescriptorSetManager.bind(setIndex, set, std::move(offsets));
} else {
mDescriptorSetCache.unbind(setIndex);
mDescriptorSetManager.unbind(setIndex);
}
}
@@ -1831,7 +1803,7 @@ void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
FVK_SYSTRACE_SCOPE();
VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer();
mDescriptorSetCache.commit(mCurrentRenderPass.commandBuffer,
mDescriptorSetManager.commit(mCurrentRenderPass.commandBuffer,
mBoundPipeline.pipelineLayout,
mBoundPipeline.descriptorSetMask);
+3 -5
View File
@@ -27,9 +27,8 @@
#include "VulkanSamplerCache.h"
#include "VulkanStagePool.h"
#include "VulkanQueryManager.h"
#include "vulkan/VulkanDescriptorSetCache.h"
#include "vulkan/VulkanDescriptorSetLayoutCache.h"
#include "vulkan/VulkanPipelineLayoutCache.h"
#include "vulkan/caching/VulkanDescriptorSetManager.h"
#include "vulkan/caching/VulkanPipelineLayoutCache.h"
#include "vulkan/memory/ResourceManager.h"
#include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Definitions.h"
@@ -138,8 +137,7 @@ private:
VulkanSamplerCache mSamplerCache;
VulkanBlitter mBlitter;
VulkanReadPixels mReadPixels;
VulkanDescriptorSetLayoutCache mDescriptorSetLayoutCache;
VulkanDescriptorSetCache mDescriptorSetCache;
VulkanDescriptorSetManager mDescriptorSetManager;
VulkanQueryManager mQueryManager;
// This is necessary for us to write to push constants after binding a pipeline.
@@ -15,12 +15,15 @@
*/
#include "VulkanPipelineCache.h"
#include "VulkanMemory.h"
#include "caching/VulkanDescriptorSetManager.h"
#include <utils/Log.h>
#include <utils/Panic.h>
#include "VulkanConstants.h"
#include "VulkanHandles.h"
#include "VulkanTexture.h"
#include "vulkan/utils/Conversion.h"
#if defined(__clang__)
@@ -23,6 +23,79 @@ 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) {}
@@ -33,18 +106,18 @@ VkSampler VulkanSamplerCache::getSampler(SamplerParams params) noexcept {
}
VkSamplerCreateInfo samplerInfo {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.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,
.magFilter = getFilter(params.filterMag),
.minFilter = getFilter(params.filterMin),
.mipmapMode = getMipmapMode(params.filterMin),
.addressModeU = getWrapMode(params.wrapS),
.addressModeV = getWrapMode(params.wrapT),
.addressModeW = getWrapMode(params.wrapR),
.anisotropyEnable = params.anisotropyLog2 == 0 ? 0u : 1u,
.maxAnisotropy = (float)(1u << params.anisotropyLog2),
.compareEnable = fvkutils::getCompareEnable(params.compareMode),
.compareEnable = getCompareEnable(params.compareMode),
.compareOp = fvkutils::getCompareOp(params.compareFunc),
.minLod = 0.0f,
.maxLod = fvkutils::getMaxLod(params.filterMin),
.maxLod = getMaxLod(params.filterMin),
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE
};
+56 -60
View File
@@ -55,61 +55,63 @@ VkComponentMapping composeSwizzle(VkComponentMapping const& prev, VkComponentMap
VK_COMPONENT_SWIZZLE_A,
};
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;
}
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];
}
return { vals[0], vals[1], vals[2], vals[3] };
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;
};
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];
auto const identityToChannel = [](VkComponentSwizzle val, uint8_t channelIndex) {
if (val != VK_COMPONENT_SWIZZLE_IDENTITY) {
return val;
}
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] };
return IDENTITY[channelIndex];
};
// 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);
VkComponentMapping const nextExplicit = identityToChannel(next);
return channelToIdentity(compose(prevExplicit, nextExplicit));
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),
};
}
inline VulkanLayout getDefaultLayoutImpl(TextureUsage usage) {
@@ -151,6 +153,7 @@ uint8_t getLayerCountFromDepth(uint32_t const depth) {
return getLayerCount(getSamplerTypeFromDepth(depth), depth);
}
} // anonymous namespace
VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator,
@@ -246,19 +249,12 @@ 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) &&
// 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);
// Lazily allocated memory is available.
context.isLazilyAllocatedMemorySupported() &&
// Usage consists of attachment flags only.
none(tusage & ~TextureUsage::ALL_ATTACHMENTS) &&
// Usage contains at least one attachment flag.
any(tusage & TextureUsage::ALL_ATTACHMENTS);
mState->mIsTransientAttachment = useTransientAttachment;
const VkImageUsageFlags transientFlag =
@@ -14,25 +14,35 @@
* limitations under the License.
*/
#include "VulkanDescriptorSetCache.h"
#include "VulkanDescriptorSetManager.h"
#include "VulkanCommands.h"
#include "VulkanHandles.h"
#include "VulkanConstants.h"
#include "vulkan/VulkanCommands.h"
#include "vulkan/VulkanHandles.h"
#include "vulkan/VulkanConstants.h"
#include <utils/FixedCapacityVector.h>
#include <utils/Panic.h>
#include <math.h>
#include <algorithm>
#include <memory>
#include <type_traits>
#include <vector>
namespace filament::backend {
namespace {
using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask;
using DescriptorCount = VulkanDescriptorSetLayout::Count;
using DescriptorSetLayoutArray = VulkanDescriptorSetCache::DescriptorSetLayoutArray;
using DescriptorSetLayoutArray = VulkanDescriptorSetManager::DescriptorSetLayoutArray;
using BitmaskGroupHashFn = utils::hash::MurmurHashFn<BitmaskGroup>;
struct BitmaskGroupEqual {
bool operator()(BitmaskGroup const& k1, BitmaskGroup const& k2) const {
return k1 == k2;
}
};
// We create a pool for each layout as defined by the number of descriptors of each type. For
// example, a layout of
@@ -189,12 +199,72 @@ struct Equal {
}
};
template<typename Bitmask>
uint32_t createBindings(VkDescriptorSetLayoutBinding* toBind, uint32_t count, VkDescriptorType type,
Bitmask const& mask) {
Bitmask alreadySeen;
mask.forEachSetBit([&](size_t index) {
VkShaderStageFlags stages = 0;
uint32_t binding = 0;
if (index < fvkutils::getFragmentStageShift<Bitmask>()) {
binding = (uint32_t) index;
stages |= VK_SHADER_STAGE_VERTEX_BIT;
auto fragIndex = index + fvkutils::getFragmentStageShift<Bitmask>();
if (mask.test(fragIndex)) {
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
alreadySeen.set(fragIndex);
}
} else if (!alreadySeen.test(index)) {
// We are in fragment stage bits
binding = (uint32_t) (index - fvkutils::getFragmentStageShift<Bitmask>());
stages |= VK_SHADER_STAGE_FRAGMENT_BIT;
}
if (stages) {
toBind[count++] = {
.binding = binding,
.descriptorType = type,
.descriptorCount = 1,
.stageFlags = stages,
};
}
});
return count;
}
inline VkDescriptorSetLayout createLayout(VkDevice device, BitmaskGroup const& bitmaskGroup) {
// Note that the following *needs* to be static so that VkDescriptorSetLayoutCreateInfo will not
// refer to stack memory.
VkDescriptorSetLayoutBinding toBind[VulkanDescriptorSetLayout::MAX_BINDINGS];
uint32_t count = 0;
count = createBindings(toBind, count, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC,
bitmaskGroup.dynamicUbo);
count = createBindings(toBind, count, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, bitmaskGroup.ubo);
count = createBindings(toBind, count, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
bitmaskGroup.sampler);
count = createBindings(toBind, count, VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT,
bitmaskGroup.inputAttachment);
assert_invariant(count != 0 && "Need at least one binding for descriptor set layout.");
VkDescriptorSetLayoutCreateInfo dlinfo = {
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
.pNext = nullptr,
.bindingCount = count,
.pBindings = toBind,
};
VkDescriptorSetLayout layout;
vkCreateDescriptorSetLayout(device, &dlinfo, VKALLOC, &layout);
return layout;
}
} // anonymous namespace
// This is an ever-expanding pool of sets where it
// 1. Keeps a list of smaller pools of different layout-dimensions.
// 2. Will add a pool if existing pool are not compatible with the requested layout o runs out.
class VulkanDescriptorSetCache::DescriptorInfinitePool {
class VulkanDescriptorSetManager::DescriptorInfinitePool {
private:
static constexpr uint16_t EXPECTED_SET_COUNT = 10;
static constexpr float SET_COUNT_GROWTH_FACTOR = 1.5;
@@ -249,34 +319,61 @@ private:
std::vector<std::unique_ptr<DescriptorPool>> mPools;
};
class VulkanDescriptorSetManager::DescriptorSetLayoutManager {
public:
DescriptorSetLayoutManager(VkDevice device)
: mDevice(device) {}
VulkanDescriptorSetCache::VulkanDescriptorSetCache(VkDevice device,
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks) {
if (auto itr = mVkLayouts.find(bitmasks); itr != mVkLayouts.end()) {
return itr->second;
}
auto vklayout = createLayout(mDevice, bitmasks);
mVkLayouts[bitmasks] = vklayout;
return vklayout;
}
~DescriptorSetLayoutManager() {
for (auto& itr: mVkLayouts) {
vkDestroyDescriptorSetLayout(mDevice, itr.second, VKALLOC);
}
}
private:
VkDevice mDevice;
tsl::robin_map<BitmaskGroup, VkDescriptorSetLayout, BitmaskGroupHashFn, BitmaskGroupEqual>
mVkLayouts;
};
VulkanDescriptorSetManager::VulkanDescriptorSetManager(VkDevice device,
fvkmemory::ResourceManager* resourceManager)
: mDevice(device),
mResourceManager(resourceManager),
mLayoutManager(std::make_unique<DescriptorSetLayoutManager>(device)),
mDescriptorPool(std::make_unique<DescriptorInfinitePool>(device)) {}
VulkanDescriptorSetCache::~VulkanDescriptorSetCache() = default;
VulkanDescriptorSetManager::~VulkanDescriptorSetManager() = default;
void VulkanDescriptorSetCache::terminate() noexcept{
void VulkanDescriptorSetManager::terminate() noexcept{
mLayoutManager.reset();
mDescriptorPool.reset();
clearHistory();
}
// bind() is not really binding the set but just stashing until we have all the info
// (pipelinelayout).
void VulkanDescriptorSetCache::bind(uint8_t setIndex,
void VulkanDescriptorSetManager::bind(uint8_t setIndex,
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
backend::DescriptorSetOffsetArray&& offsets) {
set->setOffsets(std::move(offsets));
mStashedSets[setIndex] = set;
}
void VulkanDescriptorSetCache::unbind(uint8_t setIndex) {
void VulkanDescriptorSetManager::unbind(uint8_t setIndex) {
mStashedSets[setIndex] = {};
}
void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands,
VkPipelineLayout pipelineLayout, fvkutils::DescriptorSetMask const& setMask) {
// setMask indicates the set of descriptor sets the driver wants to bind, curMask is the
// actual set of sets that *needs* to be bound.
@@ -315,7 +412,7 @@ void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
};
}
void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
void VulkanDescriptorSetManager::updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanBufferObject> bufferObject,
VkDeviceSize offset, VkDeviceSize size) noexcept {
VkDescriptorBufferInfo const info = {
@@ -341,7 +438,7 @@ void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescri
set->acquire(bufferObject);
}
void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
void VulkanDescriptorSetManager::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
VkSampler sampler) noexcept {
VkDescriptorImageInfo info{
@@ -373,13 +470,13 @@ void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescr
set->acquire(texture);
}
void VulkanDescriptorSetCache::updateInputAttachment(
void VulkanDescriptorSetManager::updateInputAttachment(
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
VulkanAttachment const& attachment) noexcept {
// TOOD: fill this in.
// TOOD: fill-in this region
}
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet(
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetManager::createSet(
Handle<HwDescriptorSet> handle, fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
auto const vkSet = mDescriptorPool->obtainSet(layout);
auto const& count = layout->count;
@@ -395,7 +492,12 @@ fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet
});
}
void VulkanDescriptorSetCache::clearHistory() {
void VulkanDescriptorSetManager::initVkLayout(
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
layout->setVkLayout(mLayoutManager->getVkLayout(layout->bitmask));
}
void VulkanDescriptorSetManager::clearHistory() {
mStashedSets = {};
}
@@ -14,10 +14,10 @@
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETCACHE_H
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETCACHE_H
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
#include "VulkanHandles.h"
#include "vulkan/VulkanHandles.h"
#include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Definitions.h" // For DescriptorSetMask
@@ -34,16 +34,20 @@
namespace filament::backend {
// Abstraction over the descriptor set pool.
class VulkanDescriptorSetCache {
// [GDSR]: Great-Descriptor-Set-Refactor: As of 03/20/24, the Filament frontend is planning to
// introduce descriptor set. This PR will arrive before that change is complete. As such, some of
// the methods introduced here will be obsolete, and certain logic will be generalized.
// Abstraction over the pool and the layout cache.
class VulkanDescriptorSetManager {
public:
static constexpr uint8_t UNIQUE_DESCRIPTOR_SET_COUNT =
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT;
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
VulkanDescriptorSetCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
~VulkanDescriptorSetCache();
VulkanDescriptorSetManager(VkDevice device, fvkmemory::ResourceManager* resourceManager);
~VulkanDescriptorSetManager();
void terminate() noexcept;
@@ -68,9 +72,12 @@ public:
fvkmemory::resource_ptr<VulkanDescriptorSet> createSet(Handle<HwDescriptorSet> handle,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
void initVkLayout(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
void clearHistory();
private:
class DescriptorSetLayoutManager;
class DescriptorInfinitePool;
using DescriptorSetArray =
@@ -78,6 +85,7 @@ private:
VkDevice mDevice;
fvkmemory::ResourceManager* mResourceManager;
std::unique_ptr<DescriptorSetLayoutManager> mLayoutManager;
std::unique_ptr<DescriptorInfinitePool> mDescriptorPool;
std::pair<VulkanAttachment, VkDescriptorImageInfo> mInputAttachment;
DescriptorSetArray mStashedSets = {};
@@ -91,4 +99,4 @@ private:
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETCACHE_H
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
@@ -17,8 +17,7 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H
#define TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H
#include "VulkanHandles.h"
#include <vulkan/VulkanHandles.h>
#include <bluevk/BlueVK.h>
#include <utils/Hash.h>
@@ -736,6 +736,10 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
chainStruct(&context.mPhysicalDeviceFeatures, &queryProtectedMemoryFeatures);
chainStruct(&context.mPhysicalDeviceProperties, &protectedMemoryProperties);
// We know we need to allocate the protected version of the VK objects
context.mProtectedMemorySupported =
static_cast<bool>(queryProtectedMemoryFeatures.protectedMemory);
// Initialize the following fields: physicalDeviceProperties, memoryProperties,
// physicalDeviceFeatures, graphicsQueueFamilyIndex.
vkGetPhysicalDeviceProperties2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceProperties);
@@ -755,10 +759,6 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
mImpl->mGraphicsQueueIndex = 0;
}
// We know we need to allocate the protected version of the VK objects
context.mProtectedMemorySupported =
static_cast<bool>(queryProtectedMemoryFeatures.protectedMemory);
if (context.mProtectedMemorySupported) {
mImpl->mProtectedGraphicsQueueFamilyIndex = identifyGraphicsQueueFamilyIndex(
mImpl->mPhysicalDevice, (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_PROTECTED_BIT));
@@ -983,32 +983,10 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadata(
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageData(
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata,
uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return createExternalImageDataImpl(externalImage, mImpl->mDevice, metadata, memoryTypeIndex,
usage);
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata) {
return createExternalImageDataImpl(externalImage, mImpl->mDevice, metadata);
}
VkSampler VulkanPlatform::createExternalSampler(SamplerYcbcrConversion chroma,
SamplerParams sampler, uint32_t internalFormat) {
return createExternalSamplerImpl(mImpl->mDevice, chroma, sampler, internalFormat);
}
VkImageView VulkanPlatform::createExternalImageView(SamplerYcbcrConversion chroma,
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
VkImageViewType viewType, VkComponentMapping swizzle) {
return createExternalImageViewImpl(mImpl->mDevice, chroma, internalFormat, image, range,
viewType, swizzle);
}
ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() const {
return getSwapchainInstanceExtensionsImpl();
}
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWindow,
VkInstance instance, uint64_t flags) const noexcept {
return createVkSurfaceKHRImpl(nativeWindow, instance, flags);
}
#undef SWAPCHAIN_RET_FUNC
}// namespace filament::backend
@@ -13,16 +13,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <backend/platforms/VulkanPlatformAndroid.h>
#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>
@@ -137,8 +136,7 @@ std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer
}
VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevice device,
VulkanPlatform::ExternalImageMetadata const& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage) {
VulkanPlatform::ExternalImageMetadata const& metadata) {
VulkanPlatform::ImageData data;
// if external format we need to specifiy it in the allocation
@@ -147,8 +145,8 @@ VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevic
const VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.pNext = nullptr,
// pass down the format (external means we don't have it VK defined)
.externalFormat = metadata.externalFormat,
.externalFormat = metadata
.externalFormat,// pass down the format (external means we don't have it VK defined)
};
const VkExternalMemoryImageCreateInfo externalCreateInfo = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
@@ -156,7 +154,7 @@ VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevic
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_ANDROID_HARDWARE_BUFFER_BIT_ANDROID,
};
VkImageCreateInfo imageInfo{ .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO };
VkImageCreateInfo imageInfo{.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
imageInfo.pNext = &externalCreateInfo;
imageInfo.format = metadata.format;
imageInfo.imageType = VK_IMAGE_TYPE_2D;
@@ -167,8 +165,10 @@ VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevic
};
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = metadata.layers;
imageInfo.samples = metadata.samples;
imageInfo.usage = usage;
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;
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &data.first);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
@@ -186,12 +186,10 @@ VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevic
.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 = memoryTypeIndex,
};
.memoryTypeIndex = metadata.memoryTypeBits};
result = vkAllocateMemory(device, &allocInfo, VKALLOC, &data.second);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkAllocateMemory failed with error=" << static_cast<int32_t>(result);
@@ -199,12 +197,14 @@ VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevic
return data;
}
}// namespace
} // namespace
VulkanPlatformAndroid::ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() = default;
namespace fvkandroid {
Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
AHardwareBuffer const* buffer, bool sRGB) noexcept {
ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() = default;
Platform::ExternalImageHandle createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept {
if (__builtin_available(android 26, *)) {
AHardwareBuffer_Desc hardwareBufferDescription = {};
AHardwareBuffer_describe(buffer, &hardwareBufferDescription);
@@ -223,23 +223,12 @@ Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
return Platform::ExternalImageHandle{};
}
VulkanPlatformAndroid::ExternalImageDescAndroid VulkanPlatformAndroid::getExternalImageDesc(
ExternalImageHandleRef externalImage) const noexcept {
auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
} // namespace fvkandroid
return {
.width = fvkExternalImage->width,
.height = fvkExternalImage->height,
.format = fvkExternalImage->format,
.usage = fvkExternalImage->usage,
};
}
VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMetadata(
ExternalImageHandleRef externalImage) {
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
ExternalImageHandleRef externalImage, VkDevice device) {
auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
static_cast<fvkandroid::ExternalImageVulkanAndroid const*>(externalImage.get());
ExternalImageMetadata metadata;
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
@@ -253,8 +242,10 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMet
std::tie(metadata.format, metadata.usage) =
getVKFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
}
metadata.samples = VK_SAMPLE_COUNT_1_BIT;
// 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;
}
VkAndroidHardwareBufferFormatPropertiesANDROID formatInfo = {
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID,
@@ -264,11 +255,11 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMet
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_PROPERTIES_ANDROID,
.pNext = &formatInfo,
};
VkResult result = vkGetAndroidHardwareBufferPropertiesANDROID(getDevice(), buffer, &properties);
VkResult result = vkGetAndroidHardwareBufferPropertiesANDROID(device, buffer, &properties);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkGetAndroidHardwareBufferProperties failed with error="
<< static_cast<int32_t>(result);
<< "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("
@@ -279,140 +270,26 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMet
return metadata;
}
VulkanPlatformAndroid::ImageData VulkanPlatformAndroid::createExternalImageData(
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata,
uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata) {
auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
ImageData data = allocateExternalImage(fvkExternalImage->aHardwareBuffer, getDevice(), metadata,
memoryTypeIndex, usage);
VkResult result = vkBindImageMemory(getDevice(), data.first, data.second, 0);
static_cast<fvkandroid::ExternalImageVulkanAndroid const*>(externalImage.get());
ImageData data = allocateExternalImage(fvkExternalImage->aHardwareBuffer, device, metadata);
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 VulkanPlatformAndroid::getSwapchainInstanceExtensions() const {
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
return {
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
};
}
VulkanPlatform::SurfaceBundle VulkanPlatformAndroid::createVkSurfaceKHR(void* nativeWindow,
VkInstance instance, uint64_t flags) const noexcept {
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept {
VkSurfaceKHR surface;
VkExtent2D extent;
@@ -424,26 +301,6 @@ VulkanPlatform::SurfaceBundle VulkanPlatformAndroid::createVkSurfaceKHR(void* na
vkCreateAndroidSurfaceKHR(instance, &createInfo, VKALLOC, (VkSurfaceKHR*) &surface);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkCreateAndroidSurfaceKHR with error=" << static_cast<int32_t>(result);
return { surface, extent };
return {surface, extent};
}
// Deprecated platform dependent helper methods
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() { return {}; }
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
ExternalImageHandleRef externalImage, VkDevice device) {
return ExternalImageMetadata{};
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return ImageData{};
}
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept {
return SurfaceBundle{};
}
}// namespace filament::backend
@@ -52,7 +52,7 @@ using namespace bluevk;
namespace filament::backend {
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
ExtensionSet const ret = {
#if defined(__APPLE__)
VK_MVK_MACOS_SURFACE_EXTENSION_NAME, // TODO: replace with VK_EXT_metal_surface
@@ -70,24 +70,11 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataIm
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
const ExternalImageMetadata& metadata) {
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::createVkSurfaceKHRImpl(void* nativeWindow,
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept {
VkSurfaceKHR surface;
#if defined(__APPLE__)
@@ -91,24 +91,11 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataIm
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
const ExternalImageMetadata& metadata) {
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::getSwapchainInstanceExtensionsImpl() {
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
VulkanPlatform::ExtensionSet const ret = {
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
VK_KHR_WAYLAND_SURFACE_EXTENSION_NAME,
@@ -126,7 +113,7 @@ VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl(
return ret;
}
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept {
VkSurfaceKHR surface;
@@ -588,79 +588,6 @@ 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,19 +56,6 @@ 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,49 +193,6 @@ 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) {
@@ -1,66 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
#define TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
#include <utils/Log.h>
#include <cstdint>
// FWGPU is short for Filament WebGPU
// turn on runtime validation, namely for debugging, that would normally not run (for release)
#define FWGPU_DEBUG_VALIDATION 0x00000001
// print/log system component details to the console, e.g. about the
// instance, surface, adapter, device, etc.
#define FWGPU_PRINT_SYSTEM 0x00000002
// Set this to enable logging "only" to one output stream. This is useful in the case where we want
// to debug with print statements and want ordered logging (e.g slog.i and slog.e will not appear in
// order of calls).
#define FWGPU_DEBUG_FORCE_LOG_TO_I 0x00000004
// Useful default combinations
#define FWGPU_DEBUG_EVERYTHING 0xFFFFFFFF
#if defined(FILAMENT_BACKEND_DEBUG_FLAG)
#define FWGPU_DEBUG_FORWARDED_FLAG (FILAMENT_BACKEND_DEBUG_FLAG & FWGPU_DEBUG_EVERYTHING)
#else
#define FWGPU_DEBUG_FORWARDED_FLAG 0
#endif
#ifndef NDEBUG
#define FWGPU_DEBUG_FLAGS FWGPU_DEBUG_FORWARDED_FLAG
#else
#define FWGPU_DEBUG_FLAGS 0
#endif
#define FWGPU_ENABLED(flags) (((FWGPU_DEBUG_FLAGS) & (flags)) == (flags))
#if FWGPU_ENABLED(FWGPU_DEBUG_FORCE_LOG_TO_I)
#define FWGPU_LOGI (utils::slog.i)
#define FWGPU_LOGD FWGPU_LOGI
#define FWGPU_LOGE FWGPU_LOGI
#define FWGPU_LOGW FWGPU_LOGI
#else
#define FWGPU_LOGE (utils::slog.e)
#define FWGPU_LOGW (utils::slog.w)
#define FWGPU_LOGD (utils::slog.d)
#define FWGPU_LOGI (utils::slog.i)
#endif
#endif// TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
+6 -429
View File
@@ -14,254 +14,21 @@
* 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 <math/mat3.h>
#include <utils/CString.h>
#include <utils/ostream.h>
#include "webgpu/WebGPUDriver.h"
#include "CommandStreamDispatcher.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>
#include <stdint.h>
namespace filament::backend {
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);
Driver* WebGPUDriver::create() {
return new WebGPUDriver();
}
WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform) noexcept
: mPlatform(platform) {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printInstanceDetails(mPlatform.getInstance());
#endif
}
WebGPUDriver::WebGPUDriver() noexcept = default;
WebGPUDriver::~WebGPUDriver() noexcept = default;
@@ -356,188 +123,6 @@ 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 {};
}
@@ -655,14 +240,6 @@ size_t WebGPUDriver::getMaxUniformBufferSize() {
return 16384u;
}
size_t WebGPUDriver::getMaxTextureSize(SamplerType target) {
return 2048u;
}
size_t WebGPUDriver::getMaxArrayTextureLayers() {
return 256u;
}
void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
uint32_t byteOffset) {
scheduleDestroy(std::move(p));
+16 -33
View File
@@ -17,44 +17,26 @@
#ifndef TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
#define TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
#include <backend/platforms/WebGPUPlatform.h>
#include "DriverBase.h"
#include "private/backend/Dispatcher.h"
#include "private/backend/Driver.h"
#include <backend/DriverEnums.h>
#include "DriverBase.h"
#include <utils/compiler.h>
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
namespace filament::backend {
/**
* WebGPU backend (driver) implementation
*/
class WebGPUDriver final : public DriverBase {
public:
WebGPUDriver() noexcept;
~WebGPUDriver() noexcept override;
Dispatcher getDispatcher() const noexcept final;
[[nodiscard]] Dispatcher getDispatcher() const noexcept final;
[[nodiscard]] static Driver* create(WebGPUPlatform& platform) noexcept;
public:
static Driver* create();
private:
explicit WebGPUDriver(WebGPUPlatform& platform) noexcept;
[[nodiscard]] ShaderModel getShaderModel() const noexcept final;
[[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final;
ShaderModel getShaderModel() const noexcept final;
ShaderLanguage getShaderLanguage() const noexcept final;
// 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;
uint64_t nextFakeHandle = 1;
/*
* Driver interface
@@ -63,19 +45,20 @@ 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; \
UTILS_ALWAYS_INLINE inline void methodName##R(RetType, paramsDecl);
#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) { }
#include "private/backend/DriverAPI.inc"
};
}// namespace filament::backend
} // namespace filament
#endif// TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
#endif // TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
@@ -0,0 +1,36 @@
/*
* 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,42 @@
/*
* 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_WEBGPUPLATFORM_H
#define TNT_FILAMENT_BACKEND_WEBGPUPLATFORM_H
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include "webgpu/webgpu_cpp.h"
namespace filament::backend {
class WebGPUPlatform
final : public Platform {
public:
int getOSVersion() const noexcept final { return 0; }
~WebGPUPlatform() noexcept override = default;
protected:
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
};
} // namespace filament
#endif // TNT_FILAMENT_BACKEND_WEBGPUPLATFORM_H
@@ -1,159 +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 <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* 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
@@ -1,43 +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 <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
@@ -1,81 +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 <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
@@ -1,163 +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 <backend/platforms/WebGPUPlatform.h>
#include <backend/DriverEnums.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;
// TODO: this looks really wrong, please fix!!
surfaceSourceXcb.window = *((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
@@ -1,53 +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 <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
-172
View File
@@ -1,172 +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 "ImageExpectations.h"
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "absl/strings/str_format.h"
#include "utils/Hash.h"
#include <fstream>
#include "backend/PixelBufferDescriptor.h"
#include "private/backend/DriverApi.h"
#ifndef FILAMENT_IOS
#include <imageio/ImageEncoder.h>
#include <image/ColorTransform.h>
#endif
ScreenshotParams::ScreenshotParams(int width, int height, std::string fileName,
uint32_t expectedPixelHash)
: mWidth(width), mHeight(height), mFileName(std::move(fileName)),
mExpectedPixelHash(expectedPixelHash) {}
int ScreenshotParams::width() const {
return mWidth;
}
int ScreenshotParams::height() const {
return mHeight;
}
uint32_t ScreenshotParams::expectedHash() const {
return mExpectedPixelHash;
}
std::string ScreenshotParams::outputDirectoryPath() const {
return ".";
}
std::string ScreenshotParams::generatedActualFileName() const {
return absl::StrFormat("%s_actual.png", mFileName);
}
std::string ScreenshotParams::generatedActualFilePath() const {
return absl::StrFormat("%s/%s", outputDirectoryPath(), generatedActualFileName());
}
std::string ScreenshotParams::goldenFileName() const {
return absl::StrFormat("%s_golden.png", mFileName);
}
std::string ScreenshotParams::goldenFilePath() const {
return absl::StrFormat("%s/%s", outputDirectoryPath(), goldenFileName());
}
ImageExpectation::ImageExpectation(const char* fileName, int lineNumber,
filament::backend::DriverApi& api, ScreenshotParams params,
filament::backend::RenderTargetHandle renderTarget)
: mFileName(fileName), mLineNumber(lineNumber), mParams(std::move(params)),
mResult(api, renderTarget, mParams) {}
void ImageExpectation::evaluate() {
// Ensure this is only evaluated once.
if (mEvaluated) {
return;
}
mEvaluated = true;
// Do the actual image comparison inside a scoped trace with the stored file and line.
{
testing::ScopedTrace trace(mFileName, mLineNumber, "");
compareImage();
}
}
void ImageExpectation::compareImage() const {
bool bytesFilled = mResult.bytesFilled();
EXPECT_THAT(bytesFilled, testing::IsTrue())
<< "Render target wasn't copied to the buffer for " << mFileName;
if (bytesFilled) {
uint32_t actualHash = mResult.hash();
EXPECT_THAT(actualHash, testing::Eq(mParams.expectedHash()));
}
}
ImageExpectations::ImageExpectations(filament::backend::DriverApi& api) : mApi(api) {}
ImageExpectations::~ImageExpectations() {
// Guarantee that all expectations are evaluated when this leaves scope even if the caller
// forgot to manually evaluate them.
evaluate();
}
void ImageExpectations::addExpectation(const char* fileName, int lineNumber,
filament::backend::RenderTargetHandle renderTarget, ScreenshotParams params) {
mExpectations.emplace_back(fileName, lineNumber, mApi, std::move(params), renderTarget);
}
void ImageExpectations::evaluate() {
for (auto& expectation: mExpectations) {
expectation.evaluate();
}
mExpectations.clear();
}
RenderTargetDump::RenderTargetDump(filament::backend::DriverApi& api,
filament::backend::RenderTargetHandle renderTarget, const ScreenshotParams& params)
: mInternal(std::make_unique<RenderTargetDump::Internal>(params)) {
#ifdef FILAMENT_IOS
bytesFilled_ = true;
bytes_.resize(size);
std::fill(bytes_.begin(), bytes_.end(), 0);
#else
const size_t size = mInternal->params.width() * mInternal->params.height() * 4;
mInternal->bytes.resize(size);
auto cb = [](void* buffer, size_t size, void* user) {
auto* internal = static_cast<RenderTargetDump::Internal*>(user);
image::LinearImage image(internal->params.width(), internal->params.width(), 4);
image = image::toLinearWithAlpha<uint8_t>(internal->params.width(),
internal->params.height(),
internal->params.width() * 4, (uint8_t*)buffer);
std::string filePath = internal->params.generatedActualFilePath();
std::ofstream pngStream(filePath, std::ios::binary | std::ios::trunc);
image::ImageEncoder::encode(pngStream, image::ImageEncoder::Format::PNG, image, "",
filePath);
internal->bytesFilled = true;
};
filament::backend::PixelBufferDescriptor pb(mInternal->bytes.data(), size,
filament::backend::PixelDataFormat::RGBA, filament::backend::PixelDataType::UBYTE, cb,
(void*)mInternal.get());
api.readPixels(renderTarget, 0, 0, mInternal->params.width(), mInternal->params.height(),
std::move(pb));
#endif
}
RenderTargetDump::~RenderTargetDump() {
// If the GPU callback hasn't been made yet then there's a callback elsewhere that has a copy of
// the internal pointer. But there's no guarantee that the callback will be ever made if the GPU
// pipeline wasn't run for some reason. So it is necessary to leak the memory.
// It would be possible to try to coordinate with the callback to have it clean up the memory,
// but if this condition happens there's already an issue with the test case so there's no need.
if (!bytesFilled()) {
mInternal.release();
}
}
uint32_t RenderTargetDump::hash() const {
return utils::hash::murmur3((uint32_t*)mInternal->bytes.data(), mInternal->bytes.size() / 4, 0);
}
bool RenderTargetDump::bytesFilled() const {
return mInternal->bytesFilled;
}
RenderTargetDump::Internal::Internal(const ScreenshotParams& params) : params(params) {}
-136
View File
@@ -1,136 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_IMAGE_EXPECTATIONS_H
#define TNT_IMAGE_EXPECTATIONS_H
#include <vector>
#include "gtest/gtest.h"
#include "backend/Handle.h"
#include "backend/DriverApiForward.h"
// Arguments are (RenderTargetHandle renderTarget, ImageExpectations& expectations,
// ScreenshotParams screenshotParams)
#define EXPECT_IMAGE(renderTarget, expectations, screenshotParams) \
do { \
expectations.addExpectation( \
__FILE__, \
__LINE__, \
renderTarget, \
screenshotParams); \
} while (0)
/**
* Stores user-provided configuration values for an image expectation
*/
class ScreenshotParams {
public:
ScreenshotParams(int width, int height, std::string fileName, uint32_t expectedPixelHash);
int width() const;
int height() const;
uint32_t expectedHash() const;
std::string outputDirectoryPath() const;
std::string generatedActualFileName() const;
std::string generatedActualFilePath() const;
std::string goldenFileName() const;
std::string goldenFilePath() const;
private:
int mWidth;
int mHeight;
uint32_t mExpectedPixelHash;
std::string mFileName;
};
/**
* When created adds a command to the GPU pipeline to copy the render target into a buffer and
* stores the result.
* If this object is destroyed before the GPU pipeline is flushed it will leak memory in order to
* avoid the GPU pipeline callback being a use-after-free.
*/
class RenderTargetDump {
public:
RenderTargetDump(filament::backend::DriverApi& api,
filament::backend::RenderTargetHandle renderTarget, const ScreenshotParams& params);
RenderTargetDump(RenderTargetDump&& other) = default;
RenderTargetDump& operator=(RenderTargetDump&& other) = default;
~RenderTargetDump();
/**
* Should only bue used if BytesFilled returns true.
* @return The hash of the stored bytes.
*/
uint32_t hash() const;
/**
* Thread safe as this is backed by an atomic.
* Once this returns true it will never return false.
* @return Whether the bytes have actually been copied from the GPU to the buffer.
*/
bool bytesFilled() const;
private:
struct Internal {
explicit Internal(const ScreenshotParams& params);
ScreenshotParams params;
std::atomic<bool> bytesFilled = false;
std::vector<unsigned char> bytes;
};
// We need a memory location that won't be invalidated to pass to GPU callbacks as they can't
// be canceled during the destructor.
std::unique_ptr<Internal> mInternal;
};
class ImageExpectation {
public:
ImageExpectation(const char* fileName, int lineNumber, filament::backend::DriverApi& api,
ScreenshotParams params, filament::backend::RenderTargetHandle renderTarget);
void evaluate();
private:
void compareImage() const;
bool mEvaluated = false;
const char* mFileName;
int mLineNumber;
ScreenshotParams mParams;
RenderTargetDump mResult;
};
class ImageExpectations {
public:
explicit ImageExpectations(filament::backend::DriverApi& api);
~ImageExpectations();
/**
* Not meant to be called directly, use EXPECT_IMAGE to get the file name and line number
*/
void addExpectation(const char* fileName, int lineNumber,
filament::backend::RenderTargetHandle renderTarget, ScreenshotParams params);
void evaluate();
private:
filament::backend::DriverApi& mApi;
std::vector<ImageExpectation> mExpectations;
};
#endif //TNT_IMAGE_EXPECTATIONS_H
-64
View File
@@ -1,64 +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 "Shader.h"
#include "BackendTest.h"
#include "ShaderGenerator.h"
namespace test {
using namespace filament::backend;
Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) {
utils::FixedCapacityVector<DescriptorSetLayoutBinding> kLayouts(config.uniformNames.size());
for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
kLayouts[i] =
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, i };
};
filamat::DescriptorSets descriptors;
for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
descriptors[i + 1] = {{ config.uniformNames[i], kLayouts[i], {}}};
}
ShaderGenerator shaderGen(
std::move(config.vertexShader), std::move(config.fragmentShader), BackendTest::sBackend,
BackendTest::sIsMobilePlatform, std::move(descriptors));
Program prog = shaderGen.getProgram(api);
for (unsigned char i = 0; i < config.uniformNames.size(); ++i) {
prog.descriptorBindings(1, {{ config.uniformNames[i], DescriptorType::UNIFORM_BUFFER, i }});
}
mProgram = cleanup.add(api.createProgram(std::move(prog)));
mDescriptorSetLayout = cleanup.add(
api.createDescriptorSetLayout(DescriptorSetLayout{ kLayouts }));
mDescriptorSet = cleanup.add(api.createDescriptorSet(mDescriptorSetLayout));
}
filament::backend::ProgramHandle Shader::getProgram() const {
return mProgram;
}
filament::backend::DescriptorSetLayoutHandle Shader::getDescriptorSetLayout() const {
return mDescriptorSetLayout;
}
filament::backend::DescriptorSetHandle Shader::getDescriptorSet() const {
return mDescriptorSet;
}
} // namespace test
-143
View File
@@ -1,143 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_SHADER_H
#define TNT_SHADER_H
#include "Lifetimes.h"
namespace test {
// All describing a shader that should be created.
struct ShaderConfig {
std::string vertexShader;
std::string fragmentShader;
std::vector<utils::CString> uniformNames;
};
// All values describing a uniform.
struct ResolvedUniformBindingConfig {
size_t dataSize;
size_t bufferSize;
uint32_t byteOffset;
filament::backend::descriptor_set_t set;
filament::backend::descriptor_binding_t binding;
};
// An equivalent to ResolvedUniformBindingConfig with all fields optional.
// resolve can be called to create a ResolvedBindingConfig with default values
// use in place of nullopt values.
struct UniformBindingConfig {
std::optional<size_t> dataSize;
std::optional<size_t> bufferSize;
std::optional<uint32_t> byteOffset;
std::optional<filament::backend::descriptor_set_t> set;
std::optional<filament::backend::descriptor_binding_t> binding;
template<typename UniformType>
ResolvedUniformBindingConfig resolve();
};
class Shader {
public:
// All graphics resources have their lifetime controlled by the Cleanup and not this object.
// Shader must not outlive either the DriverApi or the Cleanup.
Shader(filament::backend::DriverApi& api, Cleanup& cleanup, ShaderConfig config);
// Uploads a uniform with the default config.
template<typename UniformType>
void uploadUniform(filament::backend::DriverApi& api,
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer,
UniformType uniforms) const;
// Binds a uniform with the default config.
template<typename UniformType>
void bindUniform(filament::backend::DriverApi& api,
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer) const;
// Uploads a uniform, with UniformBindingConfig providing the arguments.
template<typename UniformType>
void uploadUniform(filament::backend::DriverApi& api,
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer,
UniformBindingConfig config, UniformType uniforms) const;
// Binds a uniform, with UniformBindingConfig providing the arguments.
template<typename UniformType>
void bindUniform(filament::backend::DriverApi& api,
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer,
UniformBindingConfig config) const;
filament::backend::ProgramHandle getProgram() const;
filament::backend::DescriptorSetLayoutHandle getDescriptorSetLayout() const;
filament::backend::DescriptorSetHandle getDescriptorSet() const;
protected:
filament::backend::ProgramHandle mProgram;
filament::backend::DescriptorSetLayoutHandle mDescriptorSetLayout;
filament::backend::DescriptorSetHandle mDescriptorSet;
};
template<typename UniformType>
ResolvedUniformBindingConfig UniformBindingConfig::resolve() {
auto resolvedDataSize = dataSize.value_or(sizeof(UniformType));
return ResolvedUniformBindingConfig{
.dataSize = resolvedDataSize,
.bufferSize = bufferSize.value_or(resolvedDataSize),
.byteOffset = byteOffset.value_or(0),
.set = set.value_or(1),
.binding = binding.value_or(0)
};
}
template<typename UniformType>
void Shader::uploadUniform(filament::backend::DriverApi& api,
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer,
UniformBindingConfig config, UniformType uniforms) const {
auto resolvedConfig = config.resolve<UniformType>();
UniformType* tmp = new UniformType(uniforms);
auto cb = [](void* buffer, size_t size, void* user) {
UniformType* sp = (UniformType*)buffer;
delete sp;
};
filament::backend::BufferDescriptor bd(tmp, resolvedConfig.dataSize, cb);
api.updateBufferObject(hwBuffer, std::move(bd), resolvedConfig.byteOffset);
}
template<typename UniformType>
void Shader::bindUniform(filament::backend::DriverApi& api,
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer,
UniformBindingConfig config) const {
auto resolvedConfig = config.resolve<UniformType>();
api.updateDescriptorSetBuffer(getDescriptorSet(), resolvedConfig.binding, hwBuffer, 0,
resolvedConfig.bufferSize);
api.bindDescriptorSet(getDescriptorSet(), resolvedConfig.set, {});
}
template<typename UniformType>
void Shader::uploadUniform(filament::backend::DriverApi& api,
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer,
UniformType uniforms) const {
uploadUniform(api, hwBuffer, UniformBindingConfig{}, uniforms);
}
template<typename UniformType>
void Shader::bindUniform(filament::backend::DriverApi& api,
filament::backend::Handle<filament::backend::HwBufferObject> hwBuffer) const {
bindUniform<UniformType>(api, hwBuffer, UniformBindingConfig{});
}
} // namespace test
#endif //TNT_SHADER_H
+260 -208
View File
@@ -16,9 +16,8 @@
#include "BackendTest.h"
#include "ImageExpectations.h"
#include "Lifetimes.h"
#include "Shader.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include <utils/Hash.h>
@@ -27,10 +26,8 @@
#include <fstream>
#ifndef FILAMENT_IOS
#include <imageio/ImageEncoder.h>
#include <image/ColorTransform.h>
#endif
namespace test {
@@ -40,21 +37,6 @@ using namespace filament::backend;
using namespace filament::math;
using namespace utils;
struct MaterialParams {
float4 color;
float4 scale;
};
class BlitTest : public BackendTest {
public:
BlitTest() : mCleanup(getDriverApi()) {}
protected:
Shader createShader();
Cleanup mCleanup;
};
static const char* const triangleVs = R"(#version 450 core
layout(location = 0) in vec4 mesh_position;
layout(binding = 0, set = 1) uniform Params { highp vec4 color; highp vec4 scale; } params;
@@ -74,12 +56,49 @@ void main() {
fragColor = params.color;
})";
Shader BlitTest::createShader() {
return Shader(getDriverApi(), mCleanup, ShaderConfig{
.vertexShader = triangleVs,
.fragmentShader = triangleFs,
.uniformNames = { "Params" },
});
struct MaterialParams {
float4 color;
float4 scale;
};
struct ScreenshotParams {
int width;
int height;
const char* filename;
uint32_t pixelHashResult;
};
#ifdef FILAMENT_IOS
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt, ScreenshotParams* params) {}
#else
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt, ScreenshotParams* params) {
using namespace image;
const size_t size = params->width * params->height * 4;
void* buffer = calloc(1, size);
auto cb = [](void* buffer, size_t size, void* user) {
ScreenshotParams* params = (ScreenshotParams*) user;
int w = params->width, h = params->height;
const uint32_t* texels = (uint32_t*) buffer;
params->pixelHashResult = utils::hash::murmur3(texels, size / 4, 0);
LinearImage image(w, h, 4);
image = toLinearWithAlpha<uint8_t>(w, h, w * 4, (uint8_t*) buffer);
std::ofstream pngstrm(params->filename, std::ios::binary | std::ios::trunc);
ImageEncoder::encode(pngstrm, ImageEncoder::Format::PNG, image, "", params->filename);
};
PixelBufferDescriptor pb(buffer, size, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb,
(void*) params);
dapi.readPixels(rt, 0, 0, params->width, params->height, std::move(pb));
}
#endif
static void uploadUniforms(DriverApi& dapi, Handle<HwBufferObject> ubh, MaterialParams params) {
MaterialParams* tmp = new MaterialParams(params);
auto cb = [](void* buffer, size_t size, void* user) {
MaterialParams* sp = (MaterialParams*) buffer;
delete sp;
};
BufferDescriptor bd(tmp, sizeof(MaterialParams), cb);
dapi.updateBufferObject(ubh, std::move(bd), 0);
}
static uint32_t toUintColor(float4 color) {
@@ -97,7 +116,7 @@ static void createBitmap(DriverApi& dapi, Handle<HwTexture> texture, int baseWid
const int width = baseWidth >> level;
const int height = baseHeight >> level;
const size_t size0 = height * width * 4;
uint8_t* buffer0 = (uint8_t*)calloc(size0, 1);
uint8_t* buffer0 = (uint8_t*) calloc(size0, 1);
PixelBufferDescriptor pb(buffer0, size0, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb);
const float3 foreground = color;
@@ -121,7 +140,7 @@ static void createBitmap(DriverApi& dapi, Handle<HwTexture> texture, int baseWid
// | ........
// low addresses
// This is because OpenGL automatically flips image data when uploading into the texture.
uint32_t* texels = (uint32_t*)buffer0;
uint32_t* texels = (uint32_t*) buffer0;
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
float2 uv = { (col - width / 2.0f) / width, (row - height / 2.0f) / height };
@@ -149,7 +168,7 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
const int width = baseWidth >> level;
const int height = baseHeight >> level;
const size_t size0 = height * width * 4 * 6;
uint8_t* buffer0 = (uint8_t*)calloc(size0, 1);
uint8_t* buffer0 = (uint8_t*) calloc(size0, 1);
PixelBufferDescriptor pb(buffer0, size0, PixelDataFormat::RGBA, PixelDataType::UBYTE, cb);
const float3 foreground = color;
@@ -157,7 +176,7 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
const float radius = 0.25f;
// Draw a circle on a yellow background.
uint32_t* texels = (uint32_t*)buffer0;
uint32_t* texels = (uint32_t*) buffer0;
for (int face = 0; face < 6; face++) {
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
@@ -175,9 +194,10 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
dapi.update3DImage(texture, level, 0, 0, 0, width, height, 6, std::move(pb));
}
TEST_F(BlitTest, ColorMagnify) {
TEST_F(BackendTest, ColorMagnify) {
auto& api = getDriverApi();
mCleanup.addPostCall([&]() { executeCommands(); });
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 256;
constexpr int kSrcTexHeight = 256;
@@ -188,40 +208,37 @@ TEST_F(BlitTest, 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 = mCleanup.add(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 = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> const srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
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 = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> const dstTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
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] = mCleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {},
{}));
dstRenderTargets[level] = mCleanup.add(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.
const int srcLevel = 1;
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTargets[0],
{ 0, 0, kDstTexWidth, kDstTexHeight }, srcRenderTargets[srcLevel],
{ 0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel },
SamplerMagFilter::LINEAR);
{0, 0, kDstTexWidth, kDstTexHeight}, srcRenderTargets[srcLevel],
{0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel}, SamplerMagFilter::LINEAR);
// Push through an empty frame to allow the texture to upload and the blit to execute.
{
@@ -229,23 +246,29 @@ TEST_F(BlitTest, ColorMagnify) {
api.commit(swapChain);
}
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMagnify.png" };
{
ImageExpectations expectations(api);
{
RenderFrame frame(api);
EXPECT_IMAGE(dstRenderTargets[0], expectations,
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorMagnify", 0x410bdd31));
api.commit(swapChain);
}
flushAndWait();
RenderFrame frame(api);
dumpScreenshot(api, dstRenderTargets[0], &params);
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.
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);
}
TEST_F(BlitTest, ColorMinify) {
TEST_F(BackendTest, ColorMinify) {
auto& api = getDriverApi();
mCleanup.addPostCall([&]() { executeCommands(); });
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 1024;
constexpr int kSrcTexHeight = 1024;
@@ -256,55 +279,57 @@ TEST_F(BlitTest, 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 = mCleanup.add(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 = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> const srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
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 = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> const dstTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
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] = mCleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {},
{}));
dstRenderTargets[level] = mCleanup.add(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.
const int srcLevel = 1;
api.blitDEPRECATED(TargetBufferFlags::COLOR0,
dstRenderTargets[0], { 0, 0, kDstTexWidth, kDstTexHeight },
srcRenderTargets[srcLevel],
{ 0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel },
dstRenderTargets[0], {0, 0, kDstTexWidth, kDstTexHeight},
srcRenderTargets[srcLevel], {0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel},
SamplerMagFilter::LINEAR);
{
ImageExpectations expectations(api);
EXPECT_IMAGE(dstRenderTargets[0], expectations,
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorMinify", 0xf3d9c53f));
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMinify.png" };
dumpScreenshot(api, dstRenderTargets[0], &params);
flushAndWait();
}
// Wait for the ReadPixels result to come back.
flushAndWait();
// Check if the image matches perfectly to our golden run.
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);
}
TEST_F(BlitTest, ColorResolve) {
TEST_F(BackendTest, ColorResolve) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
constexpr int kSrcTexWidth = 256;
constexpr int kSrcTexHeight = 256;
@@ -313,29 +338,49 @@ TEST_F(BlitTest, ColorResolve) {
constexpr auto kColorTexFormat = TextureFormat::RGBA8;
constexpr int kSampleCount = 4;
Shader shader = createShader();
// Create a program.
ProgramHandle program;
{
filamat::DescriptorSets descriptors;
descriptors[1] = { { "Params",
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0 },
{} } };
ShaderGenerator shaderGen(
triangleVs, triangleFs, sBackend, sIsMobilePlatform, std::move(descriptors));
Program prog = shaderGen.getProgram(api);
prog.descriptorBindings(1, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }});
program = cleanup.add(api.createProgram(std::move(prog)));
}
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
{{
DescriptorType::UNIFORM_BUFFER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}}));
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 = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, 1, kColorTexFormat, kSampleCount, kSrcTexWidth, kSrcTexHeight,
1,
TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> const srcColorTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, 1, kColorTexFormat, kSampleCount, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::COLOR_ATTACHMENT));
// Create 1-sample texture.
Handle<HwTexture> const dstColorTexture = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, 1, kColorTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> const dstColorTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, 1, kColorTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
// Create a 4-sample render target with the 4-sample texture.
Handle<HwRenderTarget> const srcRenderTarget = mCleanup.add(api.createRenderTarget(
Handle<HwRenderTarget> const srcRenderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR, kSrcTexWidth, kSrcTexHeight, kSampleCount, 0,
{{ srcColorTexture }}, {}, {}));
// Create a 1-sample render target with the 1-sample texture.
Handle<HwRenderTarget> const dstRenderTarget = mCleanup.add(api.createRenderTarget(
Handle<HwRenderTarget> const dstRenderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR, kDstTexWidth, kDstTexHeight, 1, 0,
{{ dstColorTexture }}, {}, {}));
@@ -349,52 +394,58 @@ TEST_F(BlitTest, ColorResolve) {
params.viewport.height = kSrcTexHeight;
PipelineState state = {};
state.program = shader.getProgram();
state.pipelineLayout.setLayout[1] = { shader.getDescriptorSetLayout() };
state.program = program;
state.pipelineLayout.setLayout[1] = { descriptorSetLayout };
state.rasterState.colorWrite = true;
state.rasterState.depthWrite = false;
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
auto ubuffer = mCleanup.add(api.createBufferObject(sizeof(MaterialParams),
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams),
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
// Draw red triangle into srcRenderTarget.
shader.uploadUniform(api, ubuffer, MaterialParams{
.color = float4(1, 0, 0, 1),
.scale = float4(1, 1, 0.5, 0),
uploadUniforms(api, ubuffer, {
.color = float4(1, 0, 0, 1),
.scale = float4(1, 1, 0.5, 0),
});
shader.bindUniform<MaterialParams>(api, ubuffer);
api.updateDescriptorSetBuffer(descriptorSet, 0, ubuffer, 0, sizeof(MaterialParams));
api.bindDescriptorSet(descriptorSet, 1, {});
// FIXME: on Metal this triangle is not drawn. Can't understand why.
{
RenderFrame frame(api);
api.beginRenderPass(srcRenderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
}
// Resolve the MSAA render target into the single-sample render target.
api.blitDEPRECATED(TargetBufferFlags::COLOR,
dstRenderTarget, { 0, 0, kDstTexWidth, kDstTexHeight },
srcRenderTarget, { 0, 0, kSrcTexWidth, kSrcTexHeight },
dstRenderTarget, {0, 0, kDstTexWidth, kDstTexHeight},
srcRenderTarget, {0, 0, kSrcTexWidth, kSrcTexHeight},
SamplerMagFilter::NEAREST);
{
ImageExpectations expectations(api);
// Grab a screenshot.
ScreenshotParams sparams{ kDstTexWidth, kDstTexHeight, "ColorResolve.png" };
dumpScreenshot(api, dstRenderTarget, &sparams);
EXPECT_IMAGE(dstRenderTarget, expectations,
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorResolve", 0xebfac2ef));
// Wait for the ReadPixels result to come back.
flushAndWait();
flushAndWait();
}
// Check if the image matches perfectly to our golden run.
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);
}
TEST_F(BlitTest, Blit2DTextureArray) {
TEST_F(BackendTest, Blit2DTextureArray) {
auto& api = getDriverApi();
mCleanup.addPostCall([&]() { executeCommands(); });
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
api.startCapture(0);
mCleanup.addPostCall([&]() { api.stopCapture(0); });
cleanup.addPostCall([&]() { api.stopCapture(0); });
constexpr int kSrcTexWidth = 256;
constexpr int kSrcTexHeight = 256;
@@ -409,40 +460,33 @@ TEST_F(BlitTest, 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 = mCleanup.add(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 = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D_ARRAY, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth,
kSrcTexHeight, kSrcTexDepth,
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D_ARRAY, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, kSrcTexDepth,
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);
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, kSrcTexLayer, float3(0.5, 0, 0), flipY);
// Create a destination texture.
Handle<HwTexture> dstTexture = mCleanup.add(api.createTexture(
Handle<HwTexture> dstTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
// Create two RenderTargets.
const int level = 0;
Handle<HwRenderTarget> srcRenderTarget = mCleanup.add(
api.createRenderTarget(TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0,
{ srcTexture, level, kSrcTexLayer }, {}, {}));
Handle<HwRenderTarget> dstRenderTarget = mCleanup.add(
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;
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
{ 0, 0, kDstTexWidth, kDstTexHeight }, srcRenderTarget,
{ 0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel },
SamplerMagFilter::LINEAR);
{0, 0, kDstTexWidth, kDstTexHeight}, srcRenderTarget,
{0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel}, SamplerMagFilter::LINEAR);
// Push through an empty frame to allow the texture to upload and the blit to execute.
{
@@ -450,24 +494,29 @@ TEST_F(BlitTest, Blit2DTextureArray) {
api.commit(swapChain);
}
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "Blit2DTextureArray.png" };
{
ImageExpectations expectations(api);
{
RenderFrame frame(api);
EXPECT_IMAGE(dstRenderTarget, expectations,
ScreenshotParams(kDstTexWidth, kDstTexHeight, "Blit2DTextureArray",
0x8de7d55b));
api.commit(swapChain);
}
flushAndWait();
RenderFrame frame(api);
dumpScreenshot(api, dstRenderTarget, &params);
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.
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);
}
TEST_F(BlitTest, BlitRegion) {
TEST_F(BackendTest, BlitRegion) {
auto& api = getDriverApi();
mCleanup.addPostCall([&]() { executeCommands(); });
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 1024;
constexpr int kSrcTexHeight = 1024;
@@ -480,48 +529,47 @@ TEST_F(BlitTest, 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 = mCleanup.add(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 = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
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 = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> dstTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
// Blit one-quarter of src level 1 to dst level 0.
Viewport srcRect = {
.left = 0,
.bottom = 0,
.width = (kSrcTexWidth >> kSrcLevel) / 2,
.height = (kSrcTexHeight >> kSrcLevel) / 2,
.left = 0,
.bottom = 0,
.width = (kSrcTexWidth >> kSrcLevel) / 2,
.height = (kSrcTexHeight >> kSrcLevel) / 2,
};
Viewport dstRect = {
.left = 10,
.bottom = 10,
.width = kDstTexWidth - 10,
.height = kDstTexHeight - 10,
.left = 10,
.bottom = 10,
.width = kDstTexWidth - 10,
.height = kDstTexHeight - 10,
};
// Create a RenderTarget for each texture's miplevel.
// 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 =
mCleanup.add(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 =
mCleanup.add(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,
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.
@@ -530,25 +578,33 @@ TEST_F(BlitTest, BlitRegion) {
api.commit(swapChain);
}
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegion.png" };
{
ImageExpectations expectations(api);
{
RenderFrame frame(api);
// 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.
// EXPECT_IMAGE(dstRenderTarget, expectations, ScreenshotParams(kDstTexWidth,
// kDstTexHeight, "BlitRegion", 0x74fa34ed));
api.commit(swapChain);
}
flushAndWait();
RenderFrame frame(api);
dumpScreenshot(api, dstRenderTarget, &params);
api.commit(swapChain);
}
// Wait for the ReadPixels result to come back.
api.finish();
executeCommands();
getDriver().purge();
// 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:
// compare -verbose -metric mae BlitRegion_Metal.png BlitRegion_OpenGL.png difference.png
//
// 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);
}
TEST_F(BlitTest, BlitRegionToSwapChain) {
TEST_F(BackendTest, BlitRegionToSwapChain) {
auto& api = getDriverApi();
mCleanup.addPostCall([&]() { executeCommands(); });
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 1024;
constexpr int kSrcTexHeight = 1024;
@@ -558,15 +614,15 @@ TEST_F(BlitTest, BlitRegionToSwapChain) {
constexpr int kNumLevels = 3;
// Create a SwapChain and make it current.
auto swapChain = mCleanup.add(createSwapChain());
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
Handle<HwRenderTarget> dstRenderTarget = mCleanup.add(api.createDefaultRenderTarget());
Handle<HwRenderTarget> dstRenderTarget = cleanup.add(api.createDefaultRenderTarget());
// Create a source texture.
Handle<HwTexture> srcTexture = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
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);
@@ -574,46 +630,42 @@ TEST_F(BlitTest, BlitRegionToSwapChain) {
// Create a RenderTarget for each texture's miplevel.
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
for (uint8_t level = 0; level < kNumLevels; level++) {
srcRenderTargets[level] = mCleanup.add(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.
const int srcLevel = 1;
Viewport srcRect = {
.left = (kSrcTexWidth >> srcLevel) / 2,
.bottom = (kSrcTexHeight >> srcLevel) / 2,
.width = (kSrcTexWidth >> srcLevel) / 2,
.height = (kSrcTexHeight >> srcLevel) / 2,
.left = (kSrcTexWidth >> srcLevel) / 2,
.bottom = (kSrcTexHeight >> srcLevel) / 2,
.width = (kSrcTexWidth >> srcLevel) / 2,
.height = (kSrcTexHeight >> srcLevel) / 2,
};
Viewport dstRect = {
.left = 10,
.bottom = 10,
.width = kDstTexWidth - 10,
.height = kDstTexHeight - 10,
.left = 10,
.bottom = 10,
.width = kDstTexWidth - 10,
.height = kDstTexHeight - 10,
};
{
ImageExpectations expectations(api);
{
{
RenderFrame frame(api);
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);
api.commit(swapChain);
}
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain.png" };
dumpScreenshot(api, dstRenderTarget, &params);
// TODO: for some reason, this test has been disabled. It needs to be tested on all
// machines.
// EXPECT_IMAGE(dstRenderTarget, expectations,
// ScreenshotParams(kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain", 0x0));
}
flushAndWait();
api.commit(swapChain);
}
// Wait for the ReadPixels result to come back.
api.finish();
executeCommands();
getDriver().purge();
}
} // namespace test
+114 -70
View File
@@ -16,8 +16,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "Shader.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -80,28 +78,8 @@ struct MaterialParams {
math::float4 offset;
};
static_assert(sizeof(MaterialParams) == 8 * sizeof(float));
// Uniform config for writing MaterialParams to the shader uniform with 64 bytes of padding.
const UniformBindingConfig kBindingConfig = {
.dataSize = sizeof(MaterialParams),
.bufferSize = sizeof(MaterialParams) + 64,
.byteOffset = 64
};
class BufferUpdatesTest : public BackendTest {
public:
BufferUpdatesTest() : mCleanup(getDriverApi()) {}
protected:
Shader createShader() {
return Shader(getDriverApi(), mCleanup, ShaderConfig{
vertex, fragment, {"Params"}
});
}
Cleanup mCleanup;
};
TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
TEST_F(BackendTest, VertexBufferUpdate) {
const bool largeBuffers = false;
// If updateIndices is true, then even-numbered triangles will have their indices set to
@@ -112,15 +90,31 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
// executeCommands().
{
auto& api = getDriverApi();
Cleanup cleanup(api);
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
Shader shader = createShader();
// Create a program.
filamat::DescriptorSets descriptors;
descriptors[1] = { { "Params",
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 0 }, {} } };
ShaderGenerator shaderGen(
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
Program p = shaderGen.getProgram(api);
p.descriptorBindings(1, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }});
auto program = api.createProgram(std::move(p));
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
{{
DescriptorType::UNIFORM_BUFFER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}});
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// To test large buffers (which exercise a different code path) create an extra large
// buffer. Only the first 3 vertices will be used.
@@ -134,29 +128,38 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState state;
state.program = shader.getProgram();
state.pipelineLayout.setLayout[1] = { shader.getDescriptorSetLayout() };
state.program = program;
state.pipelineLayout.setLayout[1] = { descriptorSetLayout };
state.rasterState.colorWrite = true;
state.rasterState.depthWrite = false;
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
// Create a uniform buffer.
// We use STATIC here, even though the buffer is updated, to force the Metal backend to use
// a GPU buffer, which is more interesting to test.
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams) + 64,
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
// We use STATIC here, even though the buffer is updated, to force the Metal backend to use a
// GPU buffer, which is more interesting to test.
auto ubuffer = api.createBufferObject(sizeof(MaterialParams) + 64,
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
shader.bindUniform<MaterialParams>(api, ubuffer, kBindingConfig);
api.updateDescriptorSetBuffer(descriptorSet, 0, ubuffer, 0, sizeof(MaterialParams) + 64);
api.bindDescriptorSet(descriptorSet, 1, {});
api.startCapture(0);
// Upload the uniform, but with an offset to accommodate the padding in the shader's
// uniform definition.
shader.uploadUniform(api, ubuffer, kBindingConfig, MaterialParams{
.color = { 1.0f, 1.0f, 1.0f, 1.0f },
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
});
// Upload uniforms.
{
MaterialParams params {
.color = { 1.0f, 1.0f, 1.0f, 1.0f },
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
};
auto* tmp = new MaterialParams(params);
auto cb = [](void* buffer, size_t size, void* user) {
auto* sp = (MaterialParams*) buffer;
delete sp;
};
BufferDescriptor bd(tmp, sizeof(MaterialParams), cb);
api.updateBufferObject(ubuffer, std::move(bd), 64);
}
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
@@ -199,6 +202,13 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
api.endFrame(0);
api.stopCapture(0);
api.destroyProgram(program);
api.destroySwapChain(swapChain);
api.destroyDescriptorSet(descriptorSet);
api.destroyDescriptorSetLayout(descriptorSetLayout);
api.destroyBufferObject(ubuffer);
api.destroyRenderTarget(defaultRenderTarget);
}
executeCommands();
@@ -206,38 +216,62 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
// This test renders two triangles in two separate draw calls. Between the draw calls, a uniform
// buffer object is partially updated.
TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
TEST_F(BackendTest, BufferObjectUpdateWithOffset) {
auto& api = getDriverApi();
Cleanup cleanup(api);
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
// Create a program.
Shader shader = createShader();
filamat::DescriptorSets descriptors;
descriptors[1] = { { "Params",
{ DescriptorType::UNIFORM_BUFFER, ShaderStageFlags::FRAGMENT, 0 }, {} } };
ShaderGenerator shaderGen(
vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
Program p = shaderGen.getProgram(api);
p.descriptorBindings(1, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }});
auto program = api.createProgram(std::move(p));
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
{{
DescriptorType::UNIFORM_BUFFER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}});
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
// Create a uniform buffer.
// We use STATIC here, even though the buffer is updated, to force the Metal backend to use a
// GPU buffer, which is more interesting to test.
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams) + 64,
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
auto ubuffer = api.createBufferObject(sizeof(MaterialParams) + 64,
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
shader.bindUniform<MaterialParams>(api, ubuffer, kBindingConfig);
api.updateDescriptorSetBuffer(descriptorSet, 0, ubuffer, 0, sizeof(MaterialParams) + 64);
api.bindDescriptorSet(descriptorSet, 1, {});
// Create a render target.
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
auto renderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR0, 512, 512, 1, 0, {{colorTexture}}, {}, {}));
auto colorTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT);
auto renderTarget = api.createRenderTarget(
TargetBufferFlags::COLOR0, 512, 512, 1, 0, {{colorTexture}}, {}, {});
// Upload uniforms for the first triangle.
// Upload the uniform, but with an offset to accommodate the padding in the shader's
// uniform definition.
shader.uploadUniform(api, ubuffer, kBindingConfig, MaterialParams{
{
MaterialParams params {
.color = { 1.0f, 0.0f, 0.5f, 1.0f },
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
});
};
auto* tmp = new MaterialParams(params);
auto cb = [](void* buffer, size_t size, void* user) {
auto* sp = (MaterialParams*) buffer;
delete sp;
};
BufferDescriptor bd(tmp, sizeof(MaterialParams), cb);
api.updateBufferObject(ubuffer, std::move(bd), 64);
}
RenderPassParams params = {};
params.flags.clear = TargetBufferFlags::COLOR;
@@ -246,27 +280,29 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
params.flags.discardEnd = TargetBufferFlags::NONE;
params.viewport.height = 512;
params.viewport.width = 512;
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
renderTarget, swapChain, shader.getProgram(), params);
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
renderTarget, swapChain, program, params);
// Upload uniforms for the second triangle. To test partial buffer updates, we'll only update
// color.b, color.a, offset.x, and offset.y.
shader.uploadUniform(api, ubuffer, UniformBindingConfig{
.dataSize = sizeof(std::array<float, 4>),
.bufferSize = kBindingConfig.bufferSize,
.byteOffset = *kBindingConfig.byteOffset + offsetof(MaterialParams, color.b),
},
std::array<float, 4>{
// color.b, color.a
1.0f, 1.0f,
// offset.x, offset.y
0.5f, 0.5f }
);
{
MaterialParams params {
.color = { 1.0f, 0.0f, 1.0f, 1.0f },
.offset = { 0.5f, 0.5f, 0.0f, 0.0f }
};
auto* tmp = new MaterialParams(params);
auto cb = [](void* buffer, size_t size, void* user) {
auto* sp = (MaterialParams*) ((char*)buffer - offsetof(MaterialParams, color.b));
delete sp;
};
BufferDescriptor bd((char*)tmp + offsetof(MaterialParams, color.b), sizeof(float) * 4, cb);
api.updateBufferObject(ubuffer, std::move(bd), 64 + offsetof(MaterialParams, color.b));
}
params.flags.clear = TargetBufferFlags::NONE;
params.flags.discardStart = TargetBufferFlags::NONE;
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
renderTarget, swapChain, shader.getProgram(), params);
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
renderTarget, swapChain, program, params);
static const uint32_t expectedHash = 91322442;
readPixelsAndAssertHash(
@@ -276,6 +312,14 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
api.commit(swapChain);
api.endFrame(0);
api.destroyProgram(program);
api.destroySwapChain(swapChain);
api.destroyDescriptorSet(descriptorSet);
api.destroyDescriptorSetLayout(descriptorSetLayout);
api.destroyBufferObject(ubuffer);
api.destroyRenderTarget(renderTarget);
api.destroyTexture(colorTexture);
// This ensures all driver commands have finished before exiting the test.
api.finish();
+3 -7
View File
@@ -16,8 +16,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
using namespace filament;
using namespace filament::backend;
@@ -25,14 +23,13 @@ namespace test {
TEST_F(BackendTest, FrameScheduledCallback) {
auto& api = getDriverApi();
Cleanup cleanup(api);
// Create a SwapChain.
// In order for the frameScheduledCallback to be called, this must be a real SwapChain (not
// headless) so we obtain a drawable.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createDefaultRenderTarget());
Handle<HwRenderTarget> renderTarget = api.createDefaultRenderTarget();
int callbackCountA = 0;
api.setFrameScheduledCallback(swapChain, nullptr, [&callbackCountA](PresentCallable callable) {
@@ -82,10 +79,9 @@ TEST_F(BackendTest, FrameScheduledCallback) {
TEST_F(BackendTest, FrameCompletedCallback) {
auto& api = getDriverApi();
Cleanup cleanup(api);
// Create a SwapChain.
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
int callbackCountA = 0;
api.setFrameCompletedCallback(swapChain, nullptr,
+27 -18
View File
@@ -16,7 +16,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -130,13 +129,12 @@ static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt) {
// backend's readPixels does not work correctly with textures that have image data uploaded.
TEST_F(BackendTest, FeedbackLoops) {
auto& api = getDriverApi();
Cleanup cleanup(api);
// The test is executed within this block scope to force destructors to run before
// executeCommands().
{
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
// Create a program.
@@ -157,10 +155,10 @@ TEST_F(BackendTest, FeedbackLoops) {
{ "test_tex", DescriptorType::SAMPLER, 0 },
{ "Params", DescriptorType::UNIFORM_BUFFER, 1 }
});
program = cleanup.add(api.createProgram(std::move(prog)));
program = api.createProgram(std::move(prog));
}
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
{{
DescriptorType::SAMPLER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
@@ -170,27 +168,27 @@ TEST_F(BackendTest, FeedbackLoops) {
DescriptorType::UNIFORM_BUFFER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 1,
DescriptorFlags::NONE, 0
}}}));
}}});
TrianglePrimitive const triangle(getDriverApi());
// Create a texture.
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
Handle<HwTexture> const texture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kTexFormat, 1, kTexWidth, kTexHeight, 1, usage));
Handle<HwTexture> const texture = api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kTexFormat, 1, kTexWidth, kTexHeight, 1, usage);
// Create ubo
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams),
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
auto ubuffer = api.createBufferObject(sizeof(MaterialParams),
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
// Create a RenderTarget for each miplevel.
Handle<HwRenderTarget> renderTargets[kNumLevels];
for (uint8_t level = 0; level < kNumLevels; level++) {
slog.i << "Level " << int(level) << ": " <<
(kTexWidth >> level) << "x" << (kTexHeight >> level) << io::endl;
renderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kTexWidth >> level, kTexHeight >> level, 1, 0, { texture, level, 0 }, {}, {}));
renderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
kTexWidth >> level, kTexHeight >> level, 1, 0, { texture, level, 0 }, {}, {});
}
// Fill the base level of the texture with interesting colors.
@@ -228,13 +226,12 @@ TEST_F(BackendTest, FeedbackLoops) {
params.flags.discardStart = TargetBufferFlags::ALL;
state.rasterState.disableBlending();
for (int targetLevel = 1; targetLevel < kNumLevels; targetLevel++) {
Cleanup passCleanup(api);
const uint32_t sourceLevel = targetLevel - 1;
params.viewport.width = kTexWidth >> targetLevel;
params.viewport.height = kTexHeight >> targetLevel;
auto textureView = passCleanup.add(api.createTextureView(texture, sourceLevel, 1));
DescriptorSetHandle descriptorSet = passCleanup.add(api.createDescriptorSet(descriptorSetLayout));
auto textureView = api.createTextureView(texture, sourceLevel, 1);
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
api.updateDescriptorSetTexture(descriptorSet, 0, textureView, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
@@ -250,6 +247,8 @@ TEST_F(BackendTest, FeedbackLoops) {
api.beginRenderPass(renderTargets[targetLevel], params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
api.destroyTexture(textureView);
api.destroyDescriptorSet(descriptorSet);
}
// Upsample passes
@@ -257,13 +256,12 @@ TEST_F(BackendTest, FeedbackLoops) {
state.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
state.rasterState.blendFunctionDstRGB = BlendFunction::ONE;
for (int targetLevel = kNumLevels - 2; targetLevel >= 0; targetLevel--) {
Cleanup passCleanup(api);
const uint32_t sourceLevel = targetLevel + 1;
params.viewport.width = kTexWidth >> targetLevel;
params.viewport.height = kTexHeight >> targetLevel;
auto textureView = passCleanup.add(api.createTextureView(texture, sourceLevel, 1));
DescriptorSetHandle descriptorSet = passCleanup.add(api.createDescriptorSet(descriptorSetLayout));
auto textureView = api.createTextureView(texture, sourceLevel, 1);
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
api.updateDescriptorSetTexture(descriptorSet, 0, textureView, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
@@ -279,6 +277,8 @@ TEST_F(BackendTest, FeedbackLoops) {
api.beginRenderPass(renderTargets[targetLevel], params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
api.destroyTexture(textureView);
api.destroyDescriptorSet(descriptorSet);
}
// Read back the render target corresponding to the base level.
@@ -296,6 +296,15 @@ TEST_F(BackendTest, FeedbackLoops) {
executeCommands();
getDriver().purge();
}
api.destroyDescriptorSetLayout(descriptorSetLayout);
api.destroyProgram(program);
api.destroySwapChain(swapChain);
api.destroyTexture(texture);
api.destroyBufferObject(ubuffer);
for (auto rt : renderTargets) {
api.destroyRenderTarget(rt);
}
}
const uint32_t expected = 0x70695aa1;
+16 -12
View File
@@ -16,10 +16,9 @@
#include "BackendTest.h"
#include "BackendTestUtils.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include "BackendTestUtils.h"
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
@@ -296,12 +295,10 @@ TEST_F(BackendTest, UpdateImage2D) {
// The test is executed within this block scope to force destructors to run before
// executeCommands().
for (const auto& t : testCases) {
Cleanup cleanup(api);
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a program.
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
@@ -318,21 +315,21 @@ TEST_F(BackendTest, UpdateImage2D) {
Program prog = shaderGen.getProgram(api);
prog.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}});
ProgramHandle const program = cleanup.add(api.createProgram(std::move(prog)));
ProgramHandle const program = api.createProgram(std::move(prog));
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
{{
DescriptorType::SAMPLER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}}));
}}});
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
// Create a Texture.
auto usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
Handle<HwTexture> const texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
t.textureFormat, 1, 512, 512, 1u, usage));
Handle<HwTexture> const texture = api.createTexture(SamplerType::SAMPLER_2D, 1,
t.textureFormat, 1, 512, 512, 1u, usage);
// Upload some pixel data.
if (t.uploadSubregions) {
@@ -362,6 +359,13 @@ TEST_F(BackendTest, UpdateImage2D) {
api.commit(swapChain);
api.endFrame(0);
api.destroyDescriptorSet(descriptorSet);
api.destroyDescriptorSetLayout(descriptorSetLayout);
api.destroyProgram(program);
api.destroySwapChain(swapChain);
api.destroyRenderTarget(defaultRenderTarget);
api.destroyTexture(texture);
}
api.finish();
+26 -26
View File
@@ -16,7 +16,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -61,28 +60,25 @@ using namespace filament;
using namespace filament::backend;
TEST_F(BackendTest, MRT) {
DriverApi& api = getDriverApi();
Cleanup cleanup(api);
// The test is executed within this block scope to force destructors to run before
// executeCommands().
{
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
auto swapChain = createSwapChain();
getDriverApi().makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
auto program = cleanup.add(api.createProgram(std::move(p)));
Program p = shaderGen.getProgram(getDriverApi());
auto program = getDriverApi().createProgram(std::move(p));
TrianglePrimitive triangle(api);
TrianglePrimitive triangle(getDriverApi());
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
auto defaultRenderTarget = getDriverApi().createDefaultRenderTarget(0);
// Create two Textures.
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
Handle<HwTexture> textureA = cleanup.add(api.createTexture(
Handle<HwTexture> textureA = getDriverApi().createTexture(
SamplerType::SAMPLER_2D, // target
1, // levels
TextureFormat::RGBA8, // format
@@ -90,8 +86,8 @@ TEST_F(BackendTest, MRT) {
512, // width
512, // height
1, // depth
usage)); // usage
Handle<HwTexture> textureB = cleanup.add(api.createTexture(
usage); // usage
Handle<HwTexture> textureB = getDriverApi().createTexture(
SamplerType::SAMPLER_2D, // target
1, // levels
TextureFormat::RGBA8, // format
@@ -99,10 +95,10 @@ TEST_F(BackendTest, MRT) {
512, // width
512, // height
1, // depth
usage)); // usage
usage); // usage
// Create a RenderTarget with two attachments.
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
Handle<HwRenderTarget> renderTarget = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::COLOR1,
// The width and height must match the width and height of the respective mip
// level (at least for OpenGL).
@@ -112,7 +108,7 @@ TEST_F(BackendTest, MRT) {
0, // layerCount
{{textureA },{textureB }}, // color
{}, // depth
{})); // stencil
{}); // stencil
RenderPassParams params = {};
fullViewport(params);
@@ -128,21 +124,25 @@ TEST_F(BackendTest, MRT) {
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
api.startCapture(0);
getDriverApi().startCapture(0);
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
getDriverApi().makeCurrent(swapChain, swapChain);
getDriverApi().beginFrame(0, 0, 0);
// Draw a triangle.
api.beginRenderPass(renderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
getDriverApi().beginRenderPass(renderTarget, params);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().endRenderPass();
api.flush();
api.commit(swapChain);
api.endFrame(0);
getDriverApi().flush();
getDriverApi().commit(swapChain);
getDriverApi().endFrame(0);
api.stopCapture(0);
getDriverApi().stopCapture(0);
getDriverApi().destroyProgram(program);
getDriverApi().destroySwapChain(swapChain);
getDriverApi().destroyRenderTarget(defaultRenderTarget);
}
executeCommands();
+27 -17
View File
@@ -17,10 +17,9 @@
#include "BackendTest.h"
#include "BackendTestUtils.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include "BackendTestUtils.h"
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
@@ -79,13 +78,12 @@ using namespace filament::backend;
TEST_F(BackendTest, TextureViewLod) {
auto& api = getDriverApi();
api.startCapture(0);
Cleanup cleanup(api);
// The test is executed within this block scope to force destructors to run before
// executeCommands().
{
// Create a SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
// Create a program that draws only white.
@@ -94,7 +92,7 @@ TEST_F(BackendTest, TextureViewLod) {
ShaderGenerator shaderGen(vertex, whiteFragment, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
p.descriptorBindings(0, {{"backend_test_sib_tex", DescriptorType::SAMPLER, 0}});
whiteProgram = cleanup.add(api.createProgram(std::move(p)));
whiteProgram = api.createProgram(std::move(p));
}
// Create a program that samples a texture.
@@ -108,19 +106,19 @@ TEST_F(BackendTest, TextureViewLod) {
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform, std::move(descriptors));
Program p = shaderGen.getProgram(api);
p.descriptorBindings(0, {{"backend_test_sib_tex", DescriptorType::SAMPLER, 0}});
textureProgram = cleanup.add(api.createProgram(std::move(p)));
textureProgram = api.createProgram(std::move(p));
}
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
{{
DescriptorType::SAMPLER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}}));
}}});
DescriptorSetHandle descriptorSet[2];
descriptorSet[0] = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
descriptorSet[1] = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
descriptorSet[0] = api.createDescriptorSet(descriptorSetLayout);
descriptorSet[1] = api.createDescriptorSet(descriptorSetLayout);
// Create a texture that has 4 mip levels. Each level is a different color.
// Level 0: 128x128 (red)
@@ -129,9 +127,9 @@ TEST_F(BackendTest, TextureViewLod) {
// Level 3: 16x16 (yellow)
const size_t kTextureSize = 128;
const size_t kMipLevels = 4;
Handle<HwTexture> texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, kMipLevels,
Handle<HwTexture> texture = api.createTexture(SamplerType::SAMPLER_2D, kMipLevels,
TextureFormat::RGBA8, 1, kTextureSize, kTextureSize, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT | TextureUsage::UPLOADABLE));
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT | TextureUsage::UPLOADABLE);
// Create image data.
auto pixelFormat = PixelDataFormat::RGBA;
@@ -165,13 +163,13 @@ TEST_F(BackendTest, TextureViewLod) {
// Level 1: 64x64 (green) <-- base
// Level 2: 32x32 (blue) <--- white triangle rendered
// Level 3: 16x16 (yellow) <-- max
auto texture13 = cleanup.add(api.createTextureView(texture, 1, 3));
auto texture13 = api.createTextureView(texture, 1, 3);
// Render a white triangle into level 2.
// We specify mip level 2, because minMaxLevels has no effect when rendering into a texture.
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
Handle<HwRenderTarget> renderTarget = api.createRenderTarget(
TargetBufferFlags::COLOR, 32, 32, 1, 0,
{texture, 2 /* level */, 0 /* layer */}, {}, {}));
{texture, 2 /* level */, 0 /* layer */}, {}, {});
{
RenderPassParams params = {};
fullViewport(params);
@@ -187,7 +185,7 @@ TEST_F(BackendTest, TextureViewLod) {
api.endRenderPass();
}
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
backend::Handle<HwRenderTarget> defaultRenderTarget = api.createDefaultRenderTarget(0);
RenderPassParams params = {};
fullViewport(params);
@@ -219,7 +217,7 @@ TEST_F(BackendTest, TextureViewLod) {
api.endRenderPass();
// Adjust the base mip to 2.
auto texture22 = cleanup.add(api.createTextureView(texture, 2, 2));
auto texture22 = api.createTextureView(texture, 2, 2);
api.updateDescriptorSetTexture(descriptorSet[1], 0, texture22, {
.filterMag = SamplerMagFilter::NEAREST,
@@ -246,6 +244,18 @@ TEST_F(BackendTest, TextureViewLod) {
api.endFrame(0);
api.stopCapture(0);
// Cleanup.
api.destroySwapChain(swapChain);
api.destroyRenderTarget(renderTarget);
api.destroyDescriptorSet(descriptorSet[0]);
api.destroyDescriptorSet(descriptorSet[1]);
api.destroyDescriptorSetLayout(descriptorSetLayout);
api.destroyTexture(texture);
api.destroyTexture(texture13);
api.destroyTexture(texture22);
api.destroyProgram(whiteProgram);
api.destroyProgram(textureProgram);
}
api.finish();
@@ -16,7 +16,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -71,20 +70,18 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
// The test is executed within this block scope to force destructors to run before
// executeCommands().
{
DriverApi& api = getDriverApi();
Cleanup cleanup(api);
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
auto swapChain = createSwapChain();
getDriverApi().makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
auto program = cleanup.add(api.createProgram(std::move(p)));
Program p = shaderGen.getProgram(getDriverApi());
auto program = getDriverApi().createProgram(std::move(p));
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
auto defaultRenderTarget = getDriverApi().createDefaultRenderTarget(0);
TrianglePrimitive triangle(api);
TrianglePrimitive triangle(getDriverApi());
RenderPassParams params = {};
fullViewport(params);
@@ -100,21 +97,25 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
api.startCapture(0);
getDriverApi().startCapture(0);
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
getDriverApi().makeCurrent(swapChain, swapChain);
getDriverApi().beginFrame(0, 0, 0);
// Render a triangle.
api.beginRenderPass(defaultRenderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
getDriverApi().beginRenderPass(defaultRenderTarget, params);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().endRenderPass();
api.flush();
api.commit(swapChain);
api.endFrame(0);
getDriverApi().flush();
getDriverApi().commit(swapChain);
getDriverApi().endFrame(0);
api.stopCapture(0);
getDriverApi().stopCapture(0);
getDriverApi().destroyProgram(program);
getDriverApi().destroySwapChain(swapChain);
getDriverApi().destroyRenderTarget(defaultRenderTarget);
}
executeCommands();
+7 -5
View File
@@ -16,7 +16,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -81,21 +80,20 @@ TEST_F(BackendTest, PushConstants) {
auto& api = getDriverApi();
api.startCapture(0);
Cleanup cleanup(api);
// The test is executed within this block scope to force destructors to run before
// executeCommands().
{
// Create a SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
ProgramHandle program = cleanup.add(api.createProgram(std::move(p)));
ProgramHandle program = api.createProgram(std::move(p));
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createDefaultRenderTarget());
Handle<HwRenderTarget> renderTarget = api.createDefaultRenderTarget();
TrianglePrimitive triangle(api);
@@ -152,6 +150,10 @@ TEST_F(BackendTest, PushConstants) {
api.commit(swapChain);
api.endFrame(0);
// Cleanup.
api.destroySwapChain(swapChain);
api.destroyRenderTarget(renderTarget);
}
api.stopCapture(0);
+59 -55
View File
@@ -16,10 +16,9 @@
#include "BackendTest.h"
#include "BackendTestUtils.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include "BackendTestUtils.h"
#include <utils/Hash.h>
@@ -237,20 +236,17 @@ TEST_F(ReadPixelsTest, ReadPixels) {
TestCase const testCases[] = { t0, t2, t3, t4, t5, t6, t7, t8 };
DriverApi& api = getDriverApi();
Cleanup cleanup(api);
// Create programs.
Handle<HwProgram> programFloat, programUint;
{
ShaderGenerator shaderGen(vertex, fragmentFloat, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
programFloat = cleanup.add(api.createProgram(std::move(p)));
Program p = shaderGen.getProgram(getDriverApi());
programFloat = getDriverApi().createProgram(std::move(p));
}
{
ShaderGenerator shaderGen(vertex, fragmentUint, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
programUint = cleanup.add(api.createProgram(std::move(p)));
Program p = shaderGen.getProgram(getDriverApi());
programUint = getDriverApi().createProgram(std::move(p));
}
@@ -259,35 +255,35 @@ TEST_F(ReadPixelsTest, ReadPixels) {
// Create a platform-specific SwapChain and make it current.
Handle<HwSwapChain> swapChain;
if (t.useDefaultRT) {
swapChain = cleanup.add(createSwapChain());
swapChain = createSwapChain();
} else {
swapChain = cleanup.add(api.createSwapChainHeadless(t.getRenderTargetSize(),
t.getRenderTargetSize(), 0));
swapChain = getDriverApi().createSwapChainHeadless(t.getRenderTargetSize(),
t.getRenderTargetSize(), 0);
}
api.makeCurrent(swapChain, swapChain);
getDriverApi().makeCurrent(swapChain, swapChain);
// Create a Texture and RenderTarget to render into.
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
Handle<HwTexture> const texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D,
Handle<HwTexture> const texture = getDriverApi().createTexture(SamplerType::SAMPLER_2D,
t.mipLevels, t.textureFormat, 1, renderTargetBaseSize, renderTargetBaseSize, 1,
usage));
usage);
Handle<HwRenderTarget> renderTarget;
if (t.useDefaultRT) {
// The width and height must match the width and height of the respective mip
// level (at least for OpenGL).
renderTarget = cleanup.add(api.createDefaultRenderTarget());
renderTarget = getDriverApi().createDefaultRenderTarget();
} else {
// The width and height must match the width and height of the respective mip
// level (at least for OpenGL).
renderTarget = cleanup.add(api.createRenderTarget(
renderTarget = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR, t.getRenderTargetSize(),
t.getRenderTargetSize(), t.samples, 0, {{ texture, uint8_t(t.mipLevel) }}, {},
{}));
{});
}
TrianglePrimitive const triangle(api);
TrianglePrimitive const triangle(getDriverApi());
RenderPassParams params = {};
fullViewport(params);
@@ -298,11 +294,11 @@ TEST_F(ReadPixelsTest, ReadPixels) {
params.viewport.height = t.getRenderTargetSize();
params.viewport.width = t.getRenderTargetSize();
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
getDriverApi().makeCurrent(swapChain, swapChain);
getDriverApi().beginFrame(0, 0, 0);
// Render a white triangle over blue.
api.beginRenderPass(renderTarget, params);
getDriverApi().beginRenderPass(renderTarget, params);
PipelineState state;
state.program = programFloat;
@@ -313,21 +309,21 @@ TEST_F(ReadPixelsTest, ReadPixels) {
state.rasterState.depthWrite = false;
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
getDriverApi().endRenderPass();
if (t.mipLevel > 0) {
Cleanup localCleanup(api);
// Render red to the first mip level to check that the backend is actually reading the
// correct mip.
RenderPassParams p = params;
Handle<HwRenderTarget> mipLevelOneRT = localCleanup.add(api.createRenderTarget(
Handle<HwRenderTarget> mipLevelOneRT = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR, renderTargetBaseSize, renderTargetBaseSize, 1, 0,
{{ texture }}, {}, {}));
{{ texture }}, {}, {});
p.clearColor = {1.f, 0.f, 0.f, 1.f};
api.beginRenderPass(mipLevelOneRT, p);
api.endRenderPass();
getDriverApi().beginRenderPass(mipLevelOneRT, p);
getDriverApi().endRenderPass();
getDriverApi().destroyRenderTarget(mipLevelOneRT);
}
// Read pixels.
@@ -351,19 +347,26 @@ TEST_F(ReadPixelsTest, ReadPixels) {
free(buffer);
}, (void*) &t);
api.readPixels(renderTarget, t.readRect.x, t.readRect.y, t.readRect.width,
getDriverApi().readPixels(renderTarget, t.readRect.x, t.readRect.y, t.readRect.width,
t.readRect.height, std::move(descriptor));
// Now render red over what was just rendered. This ensures that readPixels captures the
// state of rendering between render passes.
params.clearColor = {1.f, 0.f, 0.f, 1.f};
api.beginRenderPass(renderTarget, params);
api.endRenderPass();
getDriverApi().beginRenderPass(renderTarget, params);
getDriverApi().endRenderPass();
api.commit(swapChain);
api.endFrame(0);
getDriverApi().commit(swapChain);
getDriverApi().endFrame(0);
getDriverApi().destroySwapChain(swapChain);
getDriverApi().destroyRenderTarget(renderTarget);
getDriverApi().destroyTexture(texture);
}
getDriverApi().destroyProgram(programFloat);
getDriverApi().destroyProgram(programUint);
// This ensures all driver commands have finished before exiting the test.
flushAndWait();
}
@@ -372,21 +375,18 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
const size_t renderTargetSize = 2000;
const int iterationCount = 100;
DriverApi& api = getDriverApi();
Cleanup cleanup(api);
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(api.createSwapChainHeadless(renderTargetSize, renderTargetSize, 0));
api.makeCurrent(swapChain, swapChain);
auto swapChain = getDriverApi().createSwapChainHeadless(renderTargetSize, renderTargetSize, 0);
getDriverApi().makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(vertex, fragmentFloat, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
auto program = cleanup.add(api.createProgram(std::move(p)));
Program p = shaderGen.getProgram(getDriverApi());
auto program = getDriverApi().createProgram(std::move(p));
// Create a Texture and RenderTarget to render into.
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
Handle<HwTexture> texture = cleanup.add(api.createTexture(
Handle<HwTexture> texture = getDriverApi().createTexture(
SamplerType::SAMPLER_2D, // target
1, // levels
TextureFormat::RGBA8, // format
@@ -394,9 +394,9 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
renderTargetSize, // width
renderTargetSize, // height
1, // depth
usage)); // usage
usage); // usage
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
Handle<HwRenderTarget> renderTarget = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR,
renderTargetSize, // width
renderTargetSize, // height
@@ -404,9 +404,9 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
0, // layerCount
{{ texture }}, // color
{}, // depth
{})); // stencil
{}); // stencil
TrianglePrimitive triangle(api);
TrianglePrimitive triangle(getDriverApi());
RenderPassParams params = {};
fullViewport(params);
@@ -433,13 +433,13 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
printf("Executing test %d / %d\n", iteration, iterationCount);
}
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
getDriverApi().makeCurrent(swapChain, swapChain);
getDriverApi().beginFrame(0, 0, 0);
// Render some content, just so we don't read back uninitialized data.
api.beginRenderPass(renderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
getDriverApi().beginRenderPass(renderTarget, params);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().endRenderPass();
PixelBufferDescriptor descriptor(buffer, renderTargetSize * renderTargetSize * 4,
PixelDataFormat::RGBA, PixelDataType::UBYTE, 1, 0, 0, renderTargetSize,
@@ -448,9 +448,9 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
test->readPixelsFinished = true;
}, this);
api.readPixels(renderTarget, 0, 0, renderTargetSize, renderTargetSize, std::move(descriptor));
api.commit(swapChain);
api.endFrame(0);
getDriverApi().readPixels(renderTarget, 0, 0, renderTargetSize, renderTargetSize, std::move(descriptor));
getDriverApi().commit(swapChain);
getDriverApi().endFrame(0);
flushAndWait();
getDriver().purge();
@@ -460,7 +460,11 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
free(buffer);
api.finish();
getDriverApi().destroyProgram(program);
getDriverApi().destroySwapChain(swapChain);
getDriverApi().destroyRenderTarget(renderTarget);
getDriverApi().destroyTexture(texture);
getDriverApi().finish();
executeCommands();
}
@@ -16,7 +16,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -67,11 +66,10 @@ using namespace filament::backend;
// Rendering an external image without setting any data should not crash.
TEST_F(BackendTest, RenderExternalImageWithoutSet) {
auto& api = getDriverApi();
Cleanup cleanup(api);
TrianglePrimitive triangle(api);
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH, false };
@@ -84,22 +82,22 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
// Create a program that samples a texture.
Program p = shaderGen.getProgram(api);
p.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}});
backend::Handle<HwProgram> program = cleanup.add(api.createProgram(std::move(p)));
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
backend::Handle<HwProgram> program = api.createProgram(std::move(p));
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
{{
DescriptorType::SAMPLER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}}));
}}});
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
backend::Handle<HwRenderTarget> defaultRenderTarget = api.createDefaultRenderTarget(0);
// Create a texture that will be backed by an external image.
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
const NativeView& view = getNativeView();
backend::Handle<HwTexture> texture = cleanup.add(api.createTexture(
backend::Handle<HwTexture> texture = api.createTexture(
SamplerType::SAMPLER_EXTERNAL, // target
1, // levels
TextureFormat::RGBA8, // format
@@ -107,7 +105,7 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
view.width, // width
view.height, // height
1, // depth
usage)); // usage
usage); // usage
RenderPassParams params = {};
fullViewport(params);
@@ -142,6 +140,15 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
api.stopCapture(0);
// Delete our resources.
api.destroyDescriptorSet(descriptorSet);
api.destroyDescriptorSetLayout(descriptorSetLayout);
api.destroyTexture(texture);
// Destroy frame resources.
api.destroyProgram(program);
api.destroyRenderTarget(defaultRenderTarget);
api.finish();
executeCommands();
@@ -149,11 +156,10 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
TEST_F(BackendTest, RenderExternalImage) {
auto& api = getDriverApi();
Cleanup cleanup(api);
TrianglePrimitive triangle(api);
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = createSwapChain();
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH, false };
@@ -166,18 +172,18 @@ TEST_F(BackendTest, RenderExternalImage) {
// Create a program that samples a texture.
Program p = shaderGen.getProgram(api);
p.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}});
auto program = cleanup.add(api.createProgram(std::move(p)));
auto program = api.createProgram(std::move(p));
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
{{
DescriptorType::SAMPLER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}}));
}}});
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
backend::Handle<HwRenderTarget> defaultRenderTarget = api.createDefaultRenderTarget(0);
// require users to create two Filament textures and have two material parameters
// add a "plane" parameter to setExternalImage
@@ -221,8 +227,8 @@ TEST_F(BackendTest, RenderExternalImage) {
api.setupExternalImage(pixBuffer);
backend::Handle<HwTexture> texture =
cleanup.add(api.createTextureExternalImage(SamplerType::SAMPLER_EXTERNAL,
TextureFormat::RGBA8, 1024, 1024, usage, pixBuffer));
api.createTextureExternalImage(SamplerType::SAMPLER_EXTERNAL,
TextureFormat::RGBA8, 1024, 1024, usage, pixBuffer);
// We're now free to release the buffer.
CVBufferRelease(pixBuffer);
@@ -263,6 +269,15 @@ TEST_F(BackendTest, RenderExternalImage) {
api.stopCapture(0);
// Delete our resources.
api.destroyDescriptorSet(descriptorSet);
api.destroyDescriptorSetLayout(descriptorSetLayout);
api.destroyTexture(texture);
// Destroy frame resources.
api.destroyProgram(program);
api.destroyRenderTarget(defaultRenderTarget);
api.finish();
executeCommands();
+26 -19
View File
@@ -16,7 +16,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -56,7 +55,6 @@ TEST_F(BackendTest, ScissorViewportRegion) {
constexpr int kSrcRtHeight = 384;
api.startCapture(0);
Cleanup cleanup(api);
// color texture (mip level 1) 512x512 depth texture (mip level 0) 512x512
// +----------------------------------------+ +------------------------------------------+
@@ -82,21 +80,21 @@ TEST_F(BackendTest, ScissorViewportRegion) {
// executeCommands().
{
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
api.makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
ProgramHandle program = cleanup.add(api.createProgram(std::move(p)));
ProgramHandle program = api.createProgram(std::move(p));
// Create source color and depth textures.
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, kNumLevels,
Handle<HwTexture> srcTexture = api.createTexture(SamplerType::SAMPLER_2D, kNumLevels,
kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> depthTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
Handle<HwTexture> depthTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::DEPTH16, 1, 512, 512, 1,
TextureUsage::DEPTH_ATTACHMENT));
TextureUsage::DEPTH_ATTACHMENT);
// Render into the bottom-left quarter of the texture.
Viewport srcRect = {
@@ -114,13 +112,13 @@ TEST_F(BackendTest, ScissorViewportRegion) {
// 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 = cleanup.add(api.createRenderTarget(
Handle<HwRenderTarget> srcRenderTarget = api.createRenderTarget(
TargetBufferFlags::COLOR | TargetBufferFlags::DEPTH, kSrcRtHeight, kSrcRtHeight, 1, 0,
{srcTexture, kSrcLevel, 0}, {depthTexture, 0, 0}, {}));
{srcTexture, kSrcLevel, 0}, {depthTexture, 0, 0}, {});
Handle<HwRenderTarget> fullRenderTarget = cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR,
Handle<HwRenderTarget> fullRenderTarget = api.createRenderTarget(TargetBufferFlags::COLOR,
kSrcTexHeight >> kSrcLevel, kSrcTexWidth >> kSrcLevel, 1, 0,
{srcTexture, kSrcLevel, 0}, {}, {}));
{srcTexture, kSrcLevel, 0}, {}, {});
TrianglePrimitive triangle(api);
@@ -152,6 +150,11 @@ TEST_F(BackendTest, ScissorViewportRegion) {
api.endFrame(0);
api.stopCapture(0);
// Cleanup.
api.destroyTexture(srcTexture);
api.destroySwapChain(swapChain);
api.destroyRenderTarget(srcRenderTarget);
}
// Wait for the ReadPixels result to come back.
@@ -166,24 +169,23 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
auto& api = getDriverApi();
api.startCapture(0);
Cleanup cleanup(api);
// The test is executed within this block scope to force destructors to run before
// executeCommands().
{
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
api.makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
ProgramHandle program = cleanup.add(api.createProgram(std::move(p)));
ProgramHandle program = api.createProgram(std::move(p));
// Create a source color textures.
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
Handle<HwTexture> srcTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
// Render into the bottom-left quarter of the texture, checking 3 special cases.
// 1. negative viewport left/bottom
@@ -205,9 +207,9 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
Viewport scissor = {0, 0, (uint32_t)std::numeric_limits<int32_t>::max(),
(uint32_t)std::numeric_limits<int32_t>::max()};
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
Handle<HwRenderTarget> renderTarget = api.createRenderTarget(
TargetBufferFlags::COLOR, 512, 512, 1, 0,
{srcTexture, 0, 0}, {}, {}));
{srcTexture, 0, 0}, {}, {});
TrianglePrimitive triangle(api);
@@ -247,6 +249,11 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
api.endFrame(0);
api.stopCapture(0);
// Cleanup.
api.destroyTexture(srcTexture);
api.destroySwapChain(swapChain);
api.destroyRenderTarget(renderTarget);
}
// Wait for the ReadPixels result to come back.
+47 -38
View File
@@ -16,7 +16,6 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -64,23 +63,20 @@ namespace test {
class BasicStencilBufferTest : public BackendTest {
public:
Handle<HwSwapChain> mSwapChain;
ProgramHandle mProgram;
Cleanup mCleanup;
BasicStencilBufferTest() : mCleanup(getDriverApi()) {}
Handle<HwSwapChain> swapChain;
ProgramHandle program;
void SetUp() override {
auto& api = getDriverApi();
// Create a platform-specific SwapChain and make it current.
mSwapChain = mCleanup.add(createSwapChain());
api.makeCurrent(mSwapChain, mSwapChain);
swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
mProgram = mCleanup.add(api.createProgram(std::move(p)));
program = api.createProgram(std::move(p));
}
void RunTest(Handle<HwRenderTarget> renderTarget) {
@@ -107,13 +103,13 @@ public:
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState ps = {};
ps.program = mProgram;
ps.program = program;
ps.rasterState.colorWrite = false;
ps.rasterState.depthWrite = false;
ps.stencilState.stencilWrite = true;
ps.stencilState.front.stencilOpDepthStencilPass = StencilOperation::INCR;
api.makeCurrent(mSwapChain, mSwapChain);
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
api.beginRenderPass(renderTarget, params);
@@ -133,27 +129,29 @@ public:
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
api.commit(mSwapChain);
api.commit(swapChain);
api.endFrame(0);
}
void TearDown() override {
auto& api = getDriverApi();
api.destroyProgram(program);
api.destroySwapChain(swapChain);
flushAndWait();
}
};
TEST_F(BasicStencilBufferTest, StencilBuffer) {
auto& api = getDriverApi();
Cleanup cleanup(api);
// Create two textures: a color and a stencil, and an associated RenderTarget.
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
auto stencilTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT));
auto renderTarget = cleanup.add(api.createRenderTarget(
auto colorTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT);
auto stencilTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT);
auto renderTarget = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
{{colorTexture}}, {}, {{stencilTexture}}));
{{colorTexture}}, {}, {{stencilTexture}});
RunTest(renderTarget);
@@ -161,20 +159,23 @@ TEST_F(BasicStencilBufferTest, StencilBuffer) {
flushAndWait();
getDriver().purge();
api.destroyTexture(colorTexture);
api.destroyTexture(stencilTexture);
api.destroyRenderTarget(renderTarget);
}
TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
auto& api = getDriverApi();
Cleanup cleanup(api);
// Create two textures: a color and a stencil, and an associated RenderTarget.
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
auto depthStencilTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::DEPTH24_STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
auto renderTarget = cleanup.add(api.createRenderTarget(
auto colorTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT);
auto depthStencilTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::DEPTH24_STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT);
auto renderTarget = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
{{colorTexture}}, {depthStencilTexture}, {{depthStencilTexture}}));
{{colorTexture}}, {depthStencilTexture}, {{depthStencilTexture}});
RunTest(renderTarget);
@@ -182,27 +183,30 @@ TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
flushAndWait();
getDriver().purge();
api.destroyTexture(colorTexture);
api.destroyTexture(depthStencilTexture);
api.destroyRenderTarget(renderTarget);
}
TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
auto& api = getDriverApi();
Cleanup cleanup(api);
// Create two textures: a single-sampled color and a MSAA stencil texture.
// We also create two RenderTargets, one for each pass:
// Pass 0: Render a triangle only into the MSAA stencil buffer.
// Pass 1: Render a triangle into (an auto-created) MSAA color buffer using the stencil test.
// Performs an auto-resolve on the color.
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE));
auto depthStencilTextureMSAA = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::DEPTH24_STENCIL8, 4, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
auto renderTarget0 = cleanup.add(api.createRenderTarget(
auto colorTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE);
auto depthStencilTextureMSAA = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::DEPTH24_STENCIL8, 4, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT);
auto renderTarget0 = getDriverApi().createRenderTarget(
TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
{{}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA}));
auto renderTarget1 = cleanup.add(api.createRenderTarget(
{{}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA});
auto renderTarget1 = getDriverApi().createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
{{colorTexture}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA}));
{{colorTexture}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA});
api.startCapture(0);
@@ -226,13 +230,13 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState ps = {};
ps.program = mProgram;
ps.program = program;
ps.rasterState.colorWrite = false;
ps.rasterState.depthWrite = false;
ps.stencilState.stencilWrite = true;
ps.stencilState.front.stencilOpDepthStencilPass = StencilOperation::INCR;
api.makeCurrent(mSwapChain, mSwapChain);
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
api.beginRenderPass(renderTarget0, params);
@@ -254,7 +258,7 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
api.commit(mSwapChain);
api.commit(swapChain);
api.stopCapture(0);
api.endFrame(0);
@@ -262,6 +266,11 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
flushAndWait();
getDriver().purge();
api.destroyTexture(colorTexture);
api.destroyTexture(depthStencilTextureMSAA);
api.destroyRenderTarget(renderTarget0);
api.destroyRenderTarget(renderTarget1);
}
} // namespace test
-13
View File
@@ -25,7 +25,6 @@
#include <backend/BufferDescriptor.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <stdint.h>
#include <stddef.h>
@@ -91,21 +90,9 @@ public:
* @param name A string to identify this BufferObject
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
* @deprecated Use name(utils::StaticString const&) instead.
*/
UTILS_DEPRECATED
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
/**
* Associate an optional name with this BufferObject for debugging purposes.
*
* name will show in error messages and should be kept as short as possible.
*
* @param name A string literal to identify this BufferObject
* @return This Builder, for chaining calls.
*/
Builder& name(utils::StaticString const& name) noexcept;
/**
* Creates the BufferObject and returns a pointer to it. After creation, the buffer
* object is uninitialized. Use BufferObject::setBuffer() to initialize it.
+3 -9
View File
@@ -64,8 +64,9 @@ class ColorSpace;
* Performance
* ===========
*
* Creating a new ColorGrading object may be more expensive than other Filament objects as a LUT may
* need to be generated. The generation of this LUT, if necessary, may happen on the CPU.
* Creating a new ColorGrading object may be more expensive than other Filament objects as a
* 3D LUT may need to be generated. The generation of a 3D LUT, if necessary, may happen on
* the CPU.
*
* Ordering
* ========
@@ -154,9 +155,6 @@ public:
* 3D texture. For instance, a low quality level will use a 16x16x16 10 bit LUT, a medium
* quality level will use a 32x32x32 10 bit LUT, a high quality will use a 32x32x32 16 bit
* LUT, and a ultra quality will use a 64x64x64 16 bit LUT.
*
* This setting has no effect if generating a 1D LUT.
*
* This overrides the values set by format() and dimensions().
*
* The default quality is medium.
@@ -171,8 +169,6 @@ public:
* When color grading is implemented using a 3D LUT, this sets the texture format of
* of the LUT. This overrides the value set by quality().
*
* This setting has no effect if generating a 1D LUT.
*
* The default is INTEGER
*
* @param format The desired format of the 3D LUT.
@@ -185,8 +181,6 @@ public:
* When color grading is implemented using a 3D LUT, this sets the dimension of the LUT.
* This overrides the value set by quality().
*
* This setting has no effect if generating a 1D LUT.
*
* The default is 32
*
* @param dim The desired dimension of the LUT. Between 16 and 64.
-7
View File
@@ -20,7 +20,6 @@
#include <utils/compiler.h>
#include <utils/PrivateImplementation.h>
#include <utils/CString.h>
#include <utils/StaticString.h>
#include <stddef.h>
@@ -62,17 +61,11 @@ UTILS_PUBLIC void builderMakeName(utils::CString& outName, const char* name, siz
template <typename Builder>
class UTILS_PUBLIC BuilderNameMixin {
public:
UTILS_DEPRECATED
Builder& name(const char* name, size_t len) noexcept {
builderMakeName(mName, name, len);
return static_cast<Builder&>(*this);
}
Builder& name(utils::StaticString const& name) noexcept {
builderMakeName(mName, name.data(), name.size());
return static_cast<Builder&>(*this);
}
utils::CString const& getName() const noexcept { return mName; }
private:
-13
View File
@@ -26,7 +26,6 @@
#include <backend/BufferDescriptor.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <stdint.h>
#include <stddef.h>
@@ -96,21 +95,9 @@ public:
* @param name A string to identify this IndexBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
* @deprecated Use name(utils::StaticString const&) instead.
*/
UTILS_DEPRECATED
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
/**
* Associate an optional name with this IndexBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible.
*
* @param name A string literal to identify this IndexBuffer
* @return This Builder, for chaining calls.
*/
Builder& name(utils::StaticString const& name) noexcept;
/**
* Creates the IndexBuffer object and returns a pointer to it. After creation, the index
* buffer is uninitialized. Use IndexBuffer::setBuffer() to initialize the IndexBuffer.
@@ -21,7 +21,6 @@
#include <filament/Engine.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <math/mathfwd.h>
@@ -83,21 +82,9 @@ public:
* @param name A string to identify this InstanceBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
* @deprecated Use name(utils::StaticString const&) instead.
*/
UTILS_DEPRECATED
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
/**
* Associate an optional name with this InstanceBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible.
*
* @param name A string literal to identify this InstanceBuffer
* @return This Builder, for chaining calls.
*/
Builder& name(utils::StaticString const& name) noexcept;
/**
* Creates the InstanceBuffer object and returns a pointer to it.
*/
@@ -22,7 +22,6 @@
#include <filament/Engine.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <math/mathfwd.h>
@@ -76,21 +75,9 @@ public:
* @param name A string to identify this MorphTargetBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
* @deprecated Use name(utils::StaticString const&) instead.
*/
UTILS_DEPRECATED
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
/**
* Associate an optional name with this MorphTargetBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible.
*
* @param name A string literal to identify this MorphTargetBuffer
* @return This Builder, for chaining calls.
*/
Builder& name(utils::StaticString const& name) noexcept;
/**
* Creates the MorphTargetBuffer object and returns a pointer to it.
*
@@ -22,7 +22,6 @@
#include <filament/RenderableManager.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <math/mathfwd.h>
@@ -82,21 +81,9 @@ public:
* @param name A string to identify this SkinningBuffer
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
* @deprecated Use name(utils::StaticString const&) instead.
*/
UTILS_DEPRECATED
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
/**
* Associate an optional name with this SkinningBuffer for debugging purposes.
*
* name will show in error messages and should be kept as short as possible.
*
* @param name A string literal to identify this SkinningBuffer
* @return This Builder, for chaining calls.
*/
Builder& name(utils::StaticString const& name) noexcept;
/**
* Creates the SkinningBuffer object and returns a pointer to it.
*
-13
View File
@@ -23,7 +23,6 @@
#include <backend/CallbackHandler.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <math/mat3.h>
@@ -151,21 +150,9 @@ public:
* @param name A string to identify this Stream
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
* @deprecated Use name(utils::StaticString const&) instead.
*/
UTILS_DEPRECATED
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
/**
* Associate an optional name with this Stream for debugging purposes.
*
* name will show in error messages and should be kept as short as possible.
*
* @param name A string literal to identify this Stream
* @return This Builder, for chaining calls.
*/
Builder& name(utils::StaticString const& name) noexcept;
/**
* Creates the Stream object and returns a pointer to it.
*
+1 -21
View File
@@ -26,7 +26,6 @@
#include <backend/Platform.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <utility>
@@ -107,13 +106,6 @@ 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
*
@@ -230,20 +222,8 @@ public:
* @param name A string to identify this Texture
* @param len Length of name, should be less than or equal to 128
* @return This Builder, for chaining calls.
* @deprecated Use name(utils::StaticString const&) instead.
*/
UTILS_DEPRECATED
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
/**
* Associate an optional name with this Texture for debugging purposes.
*
* name will show in error messages and should be kept as short as possible.
*
* @param name A string literal to identify this Texture
* @return This Builder, for chaining calls.
*/
Builder& name(utils::StaticString const& name) noexcept;
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
/**
* Creates an external texture. The content must be set using setExternalImage().

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