Compare commits

..
Author SHA1 Message Date
bridgewaterrobbie 25c76d1396 Stash old version of rebind logic to still be able to play with it. 2025-03-31 17:12:39 -04:00
bridgewaterrobbie f0ea0fa345 Avoid running the inline pass without merge return on WebGPU 2025-03-31 16:57:43 -04:00
bridgewaterrobbie 60280d91da Split and rebind combined image samplers for WebGPU API target 2025-03-31 16:50:22 -04:00
bridgewaterrobbie b0256e5375 Allow non uniform derivitives 2025-03-31 16:41:49 -04:00
bridgewaterrobbie 4d17c917a0 Hack a workaround for OpConstantLoad issue in Tint to work with most spec constants 2025-03-31 16:41:49 -04:00
bridgewaterrobbie ba7a9924b9 Workaround Tint issues with texelFetchOffset in dofMedian.mat 2025-03-31 16:41:49 -04:00
bridgewaterrobbie ad9a23ca60 Update SPIR-V tools to commit ada1771a9f7a125573aa94fe551fdc44b45769bd to pull in splitter pass 2025-03-31 16:41:07 -04:00
Powei Feng 0bb96f0b62 Update MATERIAL_VERSION to 59 2025-03-28 15:38:42 -07:00
Powei Feng 06c0667636 Release Filament 1.58.2 2025-03-28 15:34:06 -07:00
c873552eaf Update runners to ubuntu-24.04 from deprecated ubuntu-20.04 label (#8581)
Co-authored-by: Bill Napier <napier@google.com>
Co-authored-by: Powei Feng <powei@google.com>
2025-03-28 15:30:15 -07:00
Powei Feng 38cc77807b noop: increase texture size (#8574) 2025-03-28 15:29:39 -07:00
Mathias Agopian ded1cb1b09 builders now accept StaticString names
The const char* version of name is node [[deprecated]].

This change is only cosmetic, no implementation is changed. This is
a first step toward eliminating non literal tags.
2025-03-28 15:18:08 -07:00
Mathias Agopian 653b2e859c fix CString::compare() 2025-03-28 15:18:08 -07:00
Mathias Agopian 7fd24c38d7 a new StaticString class 2025-03-28 15:18:08 -07:00
Mathias Agopian d4166e3a84 fix SGSR upscaling when a subpass is used
SGSR upscaling didn't take the viewport into account.
2025-03-28 15:03:36 -07:00
Matthew Hoffman c9f3d40993 Add helper for creating shaders in tests. (#8532)
Add helper for creating shaders in tests.

BUGS=[403541835]
2025-03-28 16:49:21 -05:00
Bill Napier 3cf76a837c Update runners to ubuntu-24.04 from deprecated ubuntu-20.04 label 2025-03-28 13:15:19 -07:00
Matthew Hoffman d1781fd66c Use the backend test cleanup helper. (#8560)
BUGS=[402472830]
2025-03-28 13:44:59 -05:00
Matthew Hoffman 577f1f83ca Add helper for tests to expect image results. (#8518)
Currently still based around using image hashes but will be improved later.

BUGS=[402474472]
2025-03-28 13:20:18 -05:00
Sungun Park 7956a28c71 Fix INVALID_OPERATION error for WebGL (#8576)
Mozilla sometimes clears the generic buffer binding when another buffer
is destroyed, if that other buffer is bound on an indexed buffer
binding.

The fix makes sure the generic buffer binding is unbound from the
context when being destroyed.

BUGS=[376125497]
2025-03-28 17:59:36 +00:00
e264dbad98 Upgrade all self-hosted ubuntu-22.04 runners to ubuntu-24.04. (#8579)
* Update runners to ubuntu-24.04 from deprecated ubuntu-20.04 label

---------

Co-authored-by: Bill Napier <napier@google.com>
Co-authored-by: Powei Feng <powei@google.com>
2025-03-28 17:31:33 +00:00
Mathias Agopian ef14e9c90e fix MSAA custom resolve
we were not binding the proper descriptor set. on gl this is only
an assert, on vulkan it would be a problem if we supported subpasses,
on metal there is no assert.
2025-03-28 10:07:04 -07:00
Bill Napier 6c27340d22 Update runners to ubuntu-24.04 from deprecated ubuntu-20.04 label 2025-03-28 08:43:46 -07:00
Powei Feng 3302c1ef4b Update presubmit.yml 2025-03-28 08:24:39 -07:00
Bill Napier c3ae2cdc8f Update runners to ubuntu-24.04 from deprecated ubuntu-20.04 label 2025-03-28 08:24:39 -07:00
Powei FengandBenjamin Doherty e52c6d8e53 Metal: add max texture size (#8572)
Use highestSupportedGpuFamily to query

---------

Co-authored-by: Benjamin Doherty <bendoherty@google.com>
2025-03-27 21:06:49 +00:00
Powei Feng fdcf82f17a vk: use real device limits (#8564) 2025-03-27 18:03:26 +00:00
Mathias Agopian 60f5c4d32c update remove ui (#8573) 2025-03-27 10:35:37 -07:00
Powei Feng 42ce76e285 Update MaterialVersion to 58 2025-03-26 10:56:33 -07:00
Doris Wu f903683c92 Pass the fgviewer gradle option (#8565) 2025-03-26 14:12:59 +08:00
Powei Feng 546ba48043 Fix test breaking changes with workarounds (#8569)
- Some tests require textures larger than 2048. Change the minimum
  to 4096 for now, and file proper bugs for tests.
- The introduction of uint8_t to DescriptorSetLayoutBinding
  caused unintended padding with using this struct as a hash key.
  We comment out the relevent code for now.
2025-03-25 16:11:35 -07:00
Powei Feng c81af315b2 engine: Add 1D LUT feature flag (#8568)
Note that the flag is default to false while we roll out this
feature.
2025-03-25 15:52:52 -07:00
rafadevai ea64db4218 vk: Fix Vulkan protected memory checks (#8561)
Set the support for isProtectedTexturesSupported
depending if the context supports protected content.

Fix checking if the protected feature is supported
on the host after retrieving the results from querying
the Physical Device.
2025-03-25 20:04:42 +00:00
alizahlalani 0c8df766d0 make createExternalImage UTILS_PUBLIC across platforms for future proofing (#8547) 2025-03-24 11:18:38 -07:00
Mathias Agopian 8f58743405 support up to five custom varyings
Five custom variables (varyings) are now available on the condition that
the `color` attribute is not requested.

FIXES=[404930099]
2025-03-24 09:38:31 -07:00
Syed Idris Shah af079b42a6 Make decl/def of createDriver api consistent
Replace "void* const" with "void*" as const here serves no purpose because of following reasons:
  1. void* const in function parameter has a limited and local effect on the code.
  2. clang tidy would complain and report error if const is added to the function declaration.
  3. const is dropped later in the call stack making the code incosistent.
2025-03-21 13:44:10 -04:00
rafadevai 7f7bceb970 vk: New method to describe external image in Vulkan Android (#8551)
To match the GL platform add a new function to describe
an external image.

Also make `ExternalImageVulkanAndroid` and
`getExternalImageMetadata` protected, so only the
backend has access to it.
2025-03-21 17:09:29 +00:00
Powei Feng 625603d8d4 vk: split layout cache from descriptorSet cache (#8554)
This is just splitting the layout cache into its own class and
files.
2025-03-21 16:33:35 +00:00
Doris Wu d2d5d62a20 Some cleanups (#8558)
* Clean up includes

* Fix the logic
2025-03-22 00:17:22 +08:00
Doris Wu 5e9be5dd2d Remove trailing whitespace (#8556) 2025-03-21 02:10:05 +00:00
Powei Feng df897b3fb2 osmesa: Enable Mesa/OSMesa rendering for mac (#8530)
This commit enables local running of the renderdiff tests and also
for github workflows.
2025-03-20 22:37:23 +00:00
Powei Feng 8c396caba0 vk: removing caching/ directory (#8553)
Move the files out of that directory for consistency. (Should have
never created the directory in the first place).

Also did some small clean-ups of removing old comments and no
longer needed includes.
2025-03-20 22:03:08 +00:00
Mathias AgopianandPowei Feng 6b91f30389 Materials can now specify a shadow attenuation factor (#8540)
* Materials can now specify a shadow strength factor

Materials have a new property: shadowStrength that can be used to
attenuate all shadows received by this material. e.g.:

```
void material(inout MaterialInputs material) {
  prepareMaterial(material);
  material. shadowStrength = 0.1;
}
```

FIXES=[391663042]

Co-authored-by: Powei Feng <powei@google.com>

---------

Co-authored-by: Powei Feng <powei@google.com>
2025-03-20 14:03:52 -07:00
Powei Feng de5d0e55af webgpu: enable webgpu build for test on linux (#8549) 2025-03-20 20:57:49 +00:00
205 changed files with 10657 additions and 1422 deletions
+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-22.04-32core
runs-on: 'ubuntu-24.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-22.04-16core
runs-on: 'ubuntu-24.04-16core'
steps:
- uses: actions/checkout@v4.1.6
+19 -13
View File
@@ -40,7 +40,7 @@ jobs:
build-android:
name: build-android
runs-on: ubuntu-22.04-16core
runs-on: 'ubuntu-24.04-16core'
steps:
- uses: actions/checkout@v4.1.6
@@ -69,7 +69,7 @@ jobs:
build-web:
name: build-web
runs-on: ubuntu-22.04-16core
runs-on: 'ubuntu-24.04-16core'
steps:
- uses: actions/checkout@v4.1.6
@@ -79,7 +79,7 @@ jobs:
validate-docs:
name: validate-docs
runs-on: ubuntu-22.04-4core
runs-on: 'ubuntu-24.04-4core'
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -92,27 +92,33 @@ jobs:
test-renderdiff:
name: test-renderdiff
runs-on: ubuntu-22.04-4core
runs-on: macos-14-xlarge
steps:
- uses: actions/checkout@v4.1.6
- uses: ./.github/actions/ubuntu-apt-add-src
- name: Set up Homebrew
id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install python prereqs
run: pip install mako setuptools pyyaml
- name: Run script
run: |
echo "Disabled renderdiff due to Mesa -> Currently planned outage: 2025-03-16 -> 2025-03-22"
# source ./build/linux/ci-common.sh && bash test/renderdiff/test.sh
bash test/renderdiff/test.sh
- uses: actions/upload-artifact@v4
with:
name: presubmit-renderdiff-result
path: ./out/renderdiff_tests
validate-wgsl-pipeline:
name: validate-wgsl-pipeline
runs-on: ubuntu-22.04-4core
validate-wgsl-webgpu:
name: validate-wgsl-webgpu
runs-on: 'ubuntu-24.04-8core'
steps:
- uses: actions/checkout@v4.1.6
- name: Run build script
run: source ./build/linux/ci-common.sh && ./build.sh -W debug test_filamat
run: source ./build/linux/ci-common.sh && ./build.sh -W debug test_filamat filament
- name: Run test
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
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-22.04-16core
runs-on: 'ubuntu-24.04-16core'
if: github.event_name == 'release' || github.event.inputs.platform == 'web'
steps:
@@ -103,7 +103,7 @@ jobs:
build-android:
name: build-android
runs-on: ubuntu-22.04-16core
runs-on: 'ubuntu-24.04-16core'
if: github.event_name == 'release' || github.event.inputs.platform == 'android'
steps:
+1 -1
View File
@@ -10,7 +10,7 @@ on:
jobs:
build-web:
name: build-web
runs-on: ubuntu-22.04-16core
runs-on: 'ubuntu-24.04-16core'
steps:
- uses: actions/checkout@v4.1.6
+12 -6
View File
@@ -139,14 +139,14 @@ else()
set(LINUX FALSE)
endif()
if (LINUX)
if (NOT FILAMENT_OSMESA_PATH STREQUAL "")
if (NOT EXISTS ${FILAMENT_OSMESA_PATH}/)
message(FATAL_ERROR "Cannot find specified OSMesa build directory: ${FILAMENT_OSMESA_PATH}")
endif()
set(FILAMENT_SUPPORTS_OSMESA TRUE)
if (NOT FILAMENT_OSMESA_PATH STREQUAL "")
if (NOT EXISTS ${FILAMENT_OSMESA_PATH}/)
message(FATAL_ERROR "Cannot find specified OSMesa build directory: ${FILAMENT_OSMESA_PATH}")
endif()
set(FILAMENT_SUPPORTS_OSMESA TRUE)
endif()
if (LINUX)
if (FILAMENT_SUPPORTS_WAYLAND)
add_definitions(-DFILAMENT_SUPPORTS_WAYLAND)
set(FILAMENT_SUPPORTS_X11 FALSE)
@@ -184,6 +184,12 @@ if (NOT ANDROID AND NOT WEBGL AND NOT IOS AND NOT FILAMENT_LINUX_IS_MOBILE)
set(IS_HOST_PLATFORM TRUE)
endif()
if (APPLE)
if (FILAMENT_SUPPORTS_OSMESA)
add_definitions(-DFILAMENT_SUPPORTS_OSMESA)
endif()
endif()
if (WIN32)
# Link statically against c/c++ lib to avoid missing redistriburable such as
# "VCRUNTIME140.dll not found. Try reinstalling the app.", but give users
+4
View File
@@ -7,6 +7,10 @@ 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.
+4 -1
View File
@@ -200,8 +200,9 @@ WEBGPU_ANDROID_GRADLE_OPTION=""
EGL_ON_LINUX_OPTION="-DFILAMENT_SUPPORTS_EGL_ON_LINUX=OFF"
MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=OFF"
FGVIEWER_OPTION="-DFILAMENT_ENABLE_FGVIEWER=OFF"
MATDBG_GRADLE_OPTION=""
FGVIEWER_OPTION="-DFILAMENT_ENABLE_FGVIEWER=OFF"
FGVIEWER_GRADLE_OPTION=""
MATOPT_OPTION=""
MATOPT_GRADLE_OPTION=""
@@ -533,6 +534,7 @@ function build_android {
${VULKAN_ANDROID_GRADLE_OPTION} \
${WEBGPU_ANDROID_GRADLE_OPTION} \
${MATDBG_GRADLE_OPTION} \
${FGVIEWER_GRADLE_OPTION} \
${MATOPT_GRADLE_OPTION} \
:filament-android:assembleDebug \
:gltfio-android:assembleDebug \
@@ -584,6 +586,7 @@ function build_android {
${VULKAN_ANDROID_GRADLE_OPTION} \
${WEBGPU_ANDROID_GRADLE_OPTION} \
${MATDBG_GRADLE_OPTION} \
${FGVIEWER_GRADLE_OPTION} \
${MATOPT_GRADLE_OPTION} \
:filament-android:assembleRelease \
:gltfio-android:assembleRelease \
+1 -1
View File
@@ -31,7 +31,7 @@ if [[ "$GITHUB_WORKFLOW" ]]; then
sudo apt-get install mesa-common-dev libxi-dev libxxf86vm-dev
# For dawn
sudo apt-get install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev
sudo 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
+23 -15
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), [@darthmoosious](https://twitter.com/darthmoosious)
- [Mathias Agopian](https://github.com/pixelflinger), [@pixelflinger](https://bsky.app/profile/pixelflinger.bsky.social)
# Overview
@@ -78,26 +78,27 @@ in table [standardProperties].
Property | Definition
-----------------------:|:---------------------
**baseColor** | Diffuse albedo for non-metallic surfaces, and specular color for metallic surfaces
**metallic** | Whether a surface appears to be dielectric (0.0) or conductor (1.0). Often used as a binary value (0 or 1)
**roughness** | Perceived smoothness (1.0) or roughness (0.0) of a surface. Smooth surfaces exhibit sharp reflections
**metallic** | Whether a surface appears to be dielectric (0.0) or conductor (1.0). Often used as a binary value (0 or 1)
**reflectance** | Fresnel reflectance at normal incidence for dielectric surfaces. This directly controls the strength of the reflections
**sheenColor** | Strength of the sheen layer
**sheenRoughness** | Perceived smoothness or roughness of the sheen layer
**ambientOcclusion** | Defines how much of the ambient light is accessible to a surface point. It is a per-pixel shadowing factor between 0.0 and 1.0
**clearCoat** | Strength of the clear coat layer
**clearCoatRoughness** | Perceived smoothness or roughness of the clear coat layer
**clearCoatNormal** | A detail normal used to perturb the clear coat layer using _bump mapping_ (_normal mapping_)
**anisotropy** | Amount of anisotropy in either the tangent or bitangent direction
**anisotropyDirection** | Local surface direction in tangent space
**ambientOcclusion** | Defines how much of the ambient light is accessible to a surface point. It is a per-pixel shadowing factor between 0.0 and 1.0
**normal** | A detail normal used to perturb the surface using _bump mapping_ (_normal mapping_)
**bentNormal** | A normal pointing in the average unoccluded direction. Can be used to improve indirect lighting quality
**clearCoatNormal** | A detail normal used to perturb the clear coat layer using _bump mapping_ (_normal mapping_)
**emissive** | Additional diffuse albedo to simulate emissive surfaces (such as neons, etc.) This property is mostly useful in an HDR pipeline with a bloom pass
**postLightingColor** | Additional color that can be blended with the result of the lighting computations. See `postLightingBlending`
**ior** | Index of refraction, either for refractive objects or as an alternative to reflectance
**transmission** | Defines how much of the diffuse light of a dielectric is transmitted through the object, in other words this defines how transparent an object is
**absorption** | Absorption factor for refractive objects
**microThickness** | Thickness of the thin layer of refractive objects
**thickness** | Thickness of the solid volume of refractive objects
**sheenColor** | Strength of the sheen layer
**sheenRoughness** | Perceived smoothness or roughness of the sheen layer
**emissive** | Additional diffuse albedo to simulate emissive surfaces (such as neons, etc.) This property is mostly useful in an HDR pipeline with a bloom pass
**normal** | A detail normal used to perturb the surface using _bump mapping_ (_normal mapping_)
**postLightingColor** | Additional color that can be blended with the result of the lighting computations. See `postLightingBlending`
**absorption** | Absorption factor for refractive objects
**transmission** | Defines how much of the diffuse light of a dielectric is transmitted through the object, in other words this defines how transparent an object is
**ior** | Index of refraction, either for refractive objects or as an alternative to reflectance
**microThickness** | Thickness of the thin layer of refractive objects
**bentNormal** | A normal pointing in the average unoccluded direction. Can be used to improve indirect lighting quality
**shadowStrength** | Strength factor between 0 and 1 for all shadows received by this material
[Table [standardProperties]: Properties of the standard model]
The type and range of each property is described in table [standardPropertiesTypes].
@@ -1272,6 +1273,9 @@ Description
when selecting any shading model that is not `unlit`. See the shader sections of this document
for more information on how to access these attributes from the shaders.
!!! Note: Interaction with custom variables
When the `color` attribute is specified, only four custom variables are available instead of five.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
parameters : [
@@ -1302,7 +1306,7 @@ Type
: array of `string`
Value
: Up to 4 strings, each must be a valid GLSL identifier.
: Up to 5 strings, each must be a valid GLSL identifier.
Description
: Defines custom interpolants (or variables) that are output by the material's vertex shader.
@@ -1318,6 +1322,10 @@ Description
particular if `default` is specified the default precision is used is the fragment shader
(`mediump`) and in the vertex shader (`highp`).
!!! Warning: Interaction with required attributes
If the `color` attribute is specified in the `required` list, then only four variables can be used
instead of five.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
name : Skybox,
File diff suppressed because one or more lines are too long
Binary file not shown.
+22 -10
View File
@@ -108,8 +108,12 @@ if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3)
list(APPEND SRCS src/opengl/platforms/PlatformCocoaTouchGL.mm)
list(APPEND SRCS src/opengl/platforms/CocoaTouchExternalImage.mm)
elseif (APPLE)
list(APPEND SRCS src/opengl/platforms/PlatformCocoaGL.mm)
list(APPEND SRCS src/opengl/platforms/CocoaExternalImage.mm)
if (FILAMENT_SUPPORTS_OSMESA)
list(APPEND SRCS src/opengl/platforms/PlatformOSMesa.cpp)
else()
list(APPEND SRCS src/opengl/platforms/PlatformCocoaGL.mm)
list(APPEND SRCS src/opengl/platforms/CocoaExternalImage.mm)
endif()
elseif (WEBGL)
list(APPEND SRCS src/opengl/platforms/PlatformWebGL.cpp)
elseif (LINUX)
@@ -173,10 +177,12 @@ endif()
if (FILAMENT_SUPPORTS_VULKAN)
list(APPEND SRCS
include/backend/platforms/VulkanPlatform.h
src/vulkan/caching/VulkanDescriptorSetManager.cpp
src/vulkan/caching/VulkanDescriptorSetManager.h
src/vulkan/caching/VulkanPipelineLayoutCache.cpp
src/vulkan/caching/VulkanPipelineLayoutCache.h
src/vulkan/VulkanDescriptorSetCache.cpp
src/vulkan/VulkanDescriptorSetCache.h
src/vulkan/VulkanDescriptorSetLayoutCache.cpp
src/vulkan/VulkanDescriptorSetLayoutCache.h
src/vulkan/VulkanPipelineLayoutCache.cpp
src/vulkan/VulkanPipelineLayoutCache.h
src/vulkan/memory/ResourceManager.cpp
src/vulkan/memory/ResourceManager.h
src/vulkan/memory/ResourcePointer.h
@@ -416,9 +422,12 @@ set(LINUX_LINKER_OPTIMIZATION_FLAGS
-Wl,--exclude-libs,bluegl
)
if (LINUX AND FILAMENT_SUPPORTS_OSMESA)
set(OSMESA_COMPILE_FLAGS
-I${FILAMENT_OSMESA_PATH}/include/GL)
if (FILAMENT_SUPPORTS_OSMESA)
if (LINUX)
set(OSMESA_COMPILE_FLAGS -I${FILAMENT_OSMESA_PATH}/include/GL)
elseif (APPLE)
set(OSMESA_COMPILE_FLAGS -I${FILAMENT_OSMESA_PATH}/include)
endif()
endif()
if (MSVC)
@@ -485,7 +494,9 @@ 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
@@ -501,6 +512,7 @@ if (APPLE OR LINUX)
test/test_Handles.cpp
)
set(BACKEND_TEST_LIBS
absl::str_format
backend
getopt
gtest
@@ -516,7 +528,7 @@ if (APPLE AND NOT IOS)
list(APPEND BACKEND_TEST_SRC
test/test_RenderExternalImage.cpp)
add_library(backend_test STATIC ${BACKEND_TEST_SRC})
target_link_libraries(backend_test PRIVATE ${BACKEND_TEST_LIBS})
target_link_libraries(backend_test PUBLIC ${BACKEND_TEST_LIBS})
set(BACKEND_TEST_DEPS
OSDependent
@@ -149,13 +149,6 @@ public:
* - PlatformEGLAndroid
*/
bool assertNativeWindowIsValid = false;
/**
* The action to take if a Drawable cannot be acquired. If true, the
* frame is aborted instead of panic. This is only supported for:
* - PlatformMetal
*/
bool metalDisablePanicOnDrawableFailure = false;
};
Platform() noexcept;
@@ -33,7 +33,7 @@ public:
PlatformCocoaGL();
~PlatformCocoaGL() noexcept override;
ExternalImageHandle createExternalImage(void* cvPixelBuffer) noexcept;
ExternalImageHandle UTILS_PUBLIC createExternalImage(void* cvPixelBuffer) noexcept;
protected:
// --------------------------------------------------------------------------------------------
@@ -32,7 +32,7 @@ public:
PlatformCocoaTouchGL();
~PlatformCocoaTouchGL() noexcept override;
ExternalImageHandle createExternalImage(void* cvPixelBuffer) noexcept;
ExternalImageHandle UTILS_PUBLIC createExternalImage(void* cvPixelBuffer) noexcept;
// --------------------------------------------------------------------------------------------
// Platform Interface
@@ -50,7 +50,7 @@ public:
/**
* Creates an ExternalImage from a EGLImageKHR
*/
ExternalImageHandle createExternalImage(EGLImageKHR eglImage) noexcept;
ExternalImageHandle UTILS_PUBLIC createExternalImage(EGLImageKHR eglImage) noexcept;
protected:
// --------------------------------------------------------------------------------------------
@@ -47,16 +47,16 @@ public:
/**
* Creates an ExternalImage from a EGLImageKHR
*/
ExternalImageHandle createExternalImage(AHardwareBuffer const* buffer, bool sRGB) noexcept;
ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer, bool sRGB) noexcept;
struct ExternalImageDescAndroid {
struct UTILS_PUBLIC ExternalImageDescAndroid {
uint32_t width; // Texture width
uint32_t height; // Texture height
TextureFormat format;// Texture format
TextureUsage usage; // Texture usage flags
};
ExternalImageDescAndroid getExternalImageDesc(ExternalImageHandle externalImage) noexcept;
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(ExternalImageHandle externalImage) noexcept;
protected:
// --------------------------------------------------------------------------------------------
@@ -21,7 +21,12 @@
#include "bluegl/BlueGL.h"
#if defined(__linux__)
#include <osmesa.h>
#elif defined(__APPLE__)
#undef GLAPI
#include <GL/osmesa.h>
#endif
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h>
@@ -26,6 +26,20 @@ namespace filament::backend {
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;
protected:
struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
AHardwareBuffer* aHardwareBuffer = nullptr;
bool sRGB = false;
@@ -38,9 +52,6 @@ public:
~ExternalImageVulkanAndroid() override;
};
Platform::ExternalImageHandle createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept;
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage);
using ImageData = VulkanPlatform::ImageData;
@@ -48,7 +59,6 @@ public:
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage);
protected:
virtual ExtensionSet getSwapchainInstanceExtensions() const;
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
+15 -5
View File
@@ -19,6 +19,11 @@
#include <utils/Systrace.h>
#include <utils/debug.h>
// We need to keep this up top for the linux (X11) name collisions.
#if defined(FILAMENT_SUPPORTS_WEBGPU)
#include "backend/platforms/WebGPUPlatform.h"
#endif
#if defined(__ANDROID__)
#include <sys/system_properties.h>
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3)
@@ -30,7 +35,11 @@
#endif
#elif defined(__APPLE__)
#if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3)
#include <backend/platforms/PlatformCocoaGL.h>
#if defined(FILAMENT_SUPPORTS_OSMESA)
#include <backend/platforms/PlatformOSMesa.h>
#else
#include <backend/platforms/PlatformCocoaGL.h>
#endif
#endif
#elif defined(__linux__)
#if defined(FILAMENT_SUPPORTS_X11)
@@ -69,9 +78,6 @@ filament::backend::Platform* createDefaultMetalPlatform();
#endif
#include "noop/PlatformNoop.h"
#if defined(FILAMENT_SUPPORTS_WEBGPU)
#include "backend/platforms/WebGPUPlatform.h"
#endif
namespace filament::backend {
@@ -140,7 +146,11 @@ Platform* PlatformFactory::create(Backend* backend) noexcept {
#elif defined(FILAMENT_IOS)
return new PlatformCocoaTouchGL();
#elif defined(__APPLE__)
return new PlatformCocoaGL();
#if defined(FILAMENT_SUPPORTS_OSMESA)
return new PlatformOSMesa();
#else
return new PlatformCocoaGL();
#endif
#elif defined(__linux__)
#if defined(FILAMENT_SUPPORTS_X11)
return new PlatformGLX();
+11 -3
View File
@@ -1177,9 +1177,17 @@ size_t MetalDriver::getMaxUniformBufferSize() {
return 256 * 1024 * 1024; // TODO: return the actual size instead of hardcoding the minspec
}
size_t MetalDriver::getMaxTextureSize(SamplerType) {
// TODO: return the actual size instead of hardcoding the minspec
return 2048;
size_t MetalDriver::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() {
@@ -45,9 +45,6 @@ PlatformMetal::~PlatformMetal() noexcept {
}
Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::DriverConfig& driverConfig) noexcept {
pImpl->mDrawableFailureBehavior = driverConfig.metalDisablePanicOnDrawableFailure
? DrawableFailureBehavior::ABORT_FRAME
: DrawableFailureBehavior::PANIC;
return MetalDriverFactory::create(this, driverConfig);
}
+3 -1
View File
@@ -241,7 +241,9 @@ size_t NoopDriver::getMaxUniformBufferSize() {
}
size_t NoopDriver::getMaxTextureSize(SamplerType target) {
return 4096u;
// 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() {
+1 -1
View File
@@ -20,7 +20,7 @@
namespace filament::backend {
Driver* PlatformNoop::createDriver(void* const sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
Driver* PlatformNoop::createDriver(void* sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
return NoopDriver::create();
}
+18 -3
View File
@@ -566,7 +566,11 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
} else if (strstr(renderer, "AMD") ||
strstr(renderer, "ATI")) {
// AMD/ATI GPU
} else if (strstr(vendor, "Mesa")) {
} else if (strstr(renderer, "Mozilla")) {
bugs->disable_invalidate_framebuffer = true;
}
if (strstr(vendor, "Mesa")) {
// Seen on
// [Mesa],
// [llvmpipe (LLVM 17.0.6, 256 bits)],
@@ -574,8 +578,6 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
// [4.50]
// not known which version are affected
bugs->rebind_buffer_after_deletion = true;
} else if (strstr(renderer, "Mozilla")) {
bugs->disable_invalidate_framebuffer = true;
}
} else {
// When running under ANGLE, it's a different set of workaround that we need.
@@ -587,6 +589,19 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
}
}
if (strstr(vendor, "Mozilla")) {
// Seen on
// [Mozilla],
// [GeForce GTX 980, or similar]
// or [ANGLE (NVIDIA, NVIDIA GeForce GTX 980 Direct3D11 vs_5_0 ps_5_0), or similar]
// or anything else,
// [OpenGL ES 3.0 (WebGL 2.0)],
// [OpenGL ES GLSL ES 3.00 (WebGL GLSL ES 3.00)]
// For Mozilla, the issue appears to be observed regardless of whether the renderer is
// ANGLE or not. (b/376125497)
bugs->rebind_buffer_after_deletion = true;
}
#ifdef BACKEND_OPENGL_VERSION_GLES
# ifndef FILAMENT_IOS // FILAMENT_IOS is guaranteed to have ES3.x
if (UTILS_UNLIKELY(major == 2)) {
+2 -2
View File
@@ -320,8 +320,8 @@ public:
// a glFinish. So we must delay the destruction until we know the GPU is finished.
bool delay_fbo_destruction;
// Mesa sometimes clears the generic buffer binding when *another* buffer is destroyed,
// if that other buffer is bound on an *indexed* buffer binding.
// Mesa and Mozilla(web) sometimes clear the generic buffer binding when *another* buffer
// is destroyed, if that other buffer is bound on an *indexed* buffer binding.
bool rebind_buffer_after_deletion;
// Force feature level 0. Typically used for low end ES3 devices with significant driver
+6 -6
View File
@@ -148,8 +148,8 @@ using namespace utils;
namespace filament::backend {
Driver* OpenGLDriverFactory::create(
OpenGLPlatform* const platform,
void* const sharedGLContext,
OpenGLPlatform* platform,
void* sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept {
return OpenGLDriver::create(platform, sharedGLContext, driverConfig);
}
@@ -159,10 +159,10 @@ using namespace GLUtils;
// ------------------------------------------------------------------------------------------------
UTILS_NOINLINE
OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* const platform,
void* const /*sharedGLContext*/, const Platform::DriverConfig& driverConfig) noexcept {
OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* platform,
void* /*sharedGLContext*/, const Platform::DriverConfig& driverConfig) noexcept {
assert_invariant(platform);
OpenGLPlatform* const ec = platform;
OpenGLPlatform* ec = platform;
#if 0
// this is useful for development, but too verbose even for debug builds
@@ -230,7 +230,7 @@ OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* const platform,
constexpr size_t defaultSize = FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
Platform::DriverConfig validConfig{ driverConfig };
validConfig.handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize);
OpenGLDriver* const driver = new(std::nothrow) OpenGLDriver(ec, validConfig);
OpenGLDriver* driver = new(std::nothrow) OpenGLDriver(ec, validConfig);
return driver;
}
@@ -64,7 +64,7 @@ PlatformCocoaTouchGL::~PlatformCocoaTouchGL() noexcept {
delete pImpl;
}
Driver* PlatformCocoaTouchGL::createDriver(void* const sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
Driver* PlatformCocoaTouchGL::createDriver(void* sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
EAGLSharegroup* sharegroup = (__bridge EAGLSharegroup*) sharedGLContext;
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 sharegroup:sharegroup];
@@ -128,7 +128,7 @@ namespace filament::backend {
using namespace backend;
Driver* PlatformGLX::createDriver(void* const sharedGLContext,
Driver* PlatformGLX::createDriver(void* sharedGLContext,
const DriverConfig& driverConfig) noexcept {
loadLibraries();
// Get the display device
@@ -22,9 +22,11 @@
#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 {
@@ -48,20 +50,27 @@ struct OSMesaSwapchain {
struct OSMesaAPI {
private:
using CreateContextFunc = OSMesaContext (*)(GLenum format, OSMesaContext);
using CreateContextAttribsFunc = OSMesaContext (*)(const int *, OSMesaContext);
using DestroyContextFunc = GLboolean (*)(OSMesaContext);
using MakeCurrentFunc = GLboolean (*)(OSMesaContext ctx, void* buffer, GLenum type,
GLsizei width, GLsizei height);
using GetProcAddressFunc = OSMESAproc (*)(const char* funcName);
public:
CreateContextFunc fOSMesaCreateContext;
CreateContextAttribsFunc fOSMesaCreateContextAttribs;
DestroyContextFunc fOSMesaDestroyContext;
MakeCurrentFunc fOSMesaMakeCurrent;
GetProcAddressFunc fOSMesaGetProcAddress;
OSMesaAPI() {
constexpr char const* libraryNames[] = {"libOSMesa.so", "libosmesa.so"};
static constexpr char const* libraryNames[] = {
#if defined(__linux__)
"libOSMesa.so",
"libosmesa.so",
#elif defined(__APPLE__)
"libOSMesa.dylib",
#endif
};
for (char const* libName: libraryNames) {
mLib = dlopen(libName, RTLD_GLOBAL | RTLD_NOW);
if (mLib) {
@@ -71,22 +80,24 @@ public:
if (mLib) {
// Loading from a libosmesa.os
fOSMesaGetProcAddress = (GetProcAddressFunc) dlsym(mLib, "OSMesaGetProcAddress");
} else {
}
#if defined(__linux__)
else {
// Filament is built into a .so
fOSMesaGetProcAddress = (GetProcAddressFunc) dlsym(RTLD_LOCAL, "OSMesaGetProcAddress");
}
if (!fOSMesaGetProcAddress) {
// Statically linking osmesa
fOSMesaGetProcAddress = OSMesaGetProcAddress;
}
#endif // __linux__
FILAMENT_CHECK_PRECONDITION(fOSMesaGetProcAddress)
<< "Unable to link against libOSMesa to create a software GL context";
fOSMesaCreateContext = (CreateContextFunc) fOSMesaGetProcAddress("OSMesaCreateContext");
fOSMesaDestroyContext =
(DestroyContextFunc) fOSMesaGetProcAddress("OSMesaDestroyContext");
fOSMesaCreateContextAttribs =
(CreateContextAttribsFunc) fOSMesaGetProcAddress("OSMesaCreateContextAttribs");
fOSMesaDestroyContext = (DestroyContextFunc) fOSMesaGetProcAddress("OSMesaDestroyContext");
fOSMesaMakeCurrent = (MakeCurrentFunc) fOSMesaGetProcAddress("OSMesaMakeCurrent");
}
@@ -101,14 +112,24 @@ private:
}// anonymous namespace
Driver* PlatformOSMesa::createDriver(void* const sharedGLContext,
Driver* PlatformOSMesa::createDriver(void* sharedGLContext,
const DriverConfig& driverConfig) noexcept {
OSMesaAPI* api = new OSMesaAPI();
mOsMesaApi = api;
static constexpr int attribs[] = {
OSMESA_FORMAT, GL_RGBA,
OSMESA_DEPTH_BITS, 24,
OSMESA_STENCIL_BITS, 8,
OSMESA_ACCUM_BITS, 0,
OSMESA_PROFILE, OSMESA_CORE_PROFILE,
0,
};
FILAMENT_CHECK_PRECONDITION(sharedGLContext == nullptr)
<< "shared GL context is not supported with PlatformOSMesa";
mContext = api->fOSMesaCreateContext(GL_RGBA, NULL);
mContext = api->fOSMesaCreateContextAttribs(attribs, NULL);
// We need to do a no-op makecurrent here so that the context will be in a correct state before
// any GL calls.
@@ -75,7 +75,7 @@ struct WGLSwapChain {
static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs = nullptr;
Driver* PlatformWGL::createDriver(void* const sharedGLContext,
Driver* PlatformWGL::createDriver(void* sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept {
int result = 0;
int pixelFormat = 0;
@@ -20,7 +20,7 @@ namespace filament::backend {
using namespace backend;
Driver* PlatformWebGL::createDriver(void* const sharedGLContext,
Driver* PlatformWebGL::createDriver(void* sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept {
return OpenGLPlatform::createDefaultDriver(this, sharedGLContext, driverConfig);
}
@@ -14,35 +14,25 @@
* limitations under the License.
*/
#include "VulkanDescriptorSetManager.h"
#include "VulkanDescriptorSetCache.h"
#include "vulkan/VulkanCommands.h"
#include "vulkan/VulkanHandles.h"
#include "vulkan/VulkanConstants.h"
#include "VulkanCommands.h"
#include "VulkanHandles.h"
#include "VulkanConstants.h"
#include <utils/FixedCapacityVector.h>
#include <utils/Panic.h>
#include <math.h>
#include <algorithm>
#include <memory>
#include <type_traits>
#include <vector>
namespace filament::backend {
namespace {
using BitmaskGroup = VulkanDescriptorSetLayout::Bitmask;
using DescriptorCount = VulkanDescriptorSetLayout::Count;
using DescriptorSetLayoutArray = VulkanDescriptorSetManager::DescriptorSetLayoutArray;
using BitmaskGroupHashFn = utils::hash::MurmurHashFn<BitmaskGroup>;
struct BitmaskGroupEqual {
bool operator()(BitmaskGroup const& k1, BitmaskGroup const& k2) const {
return k1 == k2;
}
};
using DescriptorSetLayoutArray = VulkanDescriptorSetCache::DescriptorSetLayoutArray;
// We create a pool for each layout as defined by the number of descriptors of each type. For
// example, a layout of
@@ -199,72 +189,12 @@ 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 VulkanDescriptorSetManager::DescriptorInfinitePool {
class VulkanDescriptorSetCache::DescriptorInfinitePool {
private:
static constexpr uint16_t EXPECTED_SET_COUNT = 10;
static constexpr float SET_COUNT_GROWTH_FACTOR = 1.5;
@@ -319,61 +249,34 @@ private:
std::vector<std::unique_ptr<DescriptorPool>> mPools;
};
class VulkanDescriptorSetManager::DescriptorSetLayoutManager {
public:
DescriptorSetLayoutManager(VkDevice device)
: mDevice(device) {}
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks) {
if (auto itr = mVkLayouts.find(bitmasks); itr != mVkLayouts.end()) {
return itr->second;
}
auto vklayout = createLayout(mDevice, bitmasks);
mVkLayouts[bitmasks] = vklayout;
return vklayout;
}
~DescriptorSetLayoutManager() {
for (auto& itr: mVkLayouts) {
vkDestroyDescriptorSetLayout(mDevice, itr.second, VKALLOC);
}
}
private:
VkDevice mDevice;
tsl::robin_map<BitmaskGroup, VkDescriptorSetLayout, BitmaskGroupHashFn, BitmaskGroupEqual>
mVkLayouts;
};
VulkanDescriptorSetManager::VulkanDescriptorSetManager(VkDevice device,
VulkanDescriptorSetCache::VulkanDescriptorSetCache(VkDevice device,
fvkmemory::ResourceManager* resourceManager)
: mDevice(device),
mResourceManager(resourceManager),
mLayoutManager(std::make_unique<DescriptorSetLayoutManager>(device)),
mDescriptorPool(std::make_unique<DescriptorInfinitePool>(device)) {}
VulkanDescriptorSetManager::~VulkanDescriptorSetManager() = default;
VulkanDescriptorSetCache::~VulkanDescriptorSetCache() = default;
void VulkanDescriptorSetManager::terminate() noexcept{
mLayoutManager.reset();
void VulkanDescriptorSetCache::terminate() noexcept{
mDescriptorPool.reset();
clearHistory();
}
// bind() is not really binding the set but just stashing until we have all the info
// (pipelinelayout).
void VulkanDescriptorSetManager::bind(uint8_t setIndex,
void VulkanDescriptorSetCache::bind(uint8_t setIndex,
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
backend::DescriptorSetOffsetArray&& offsets) {
set->setOffsets(std::move(offsets));
mStashedSets[setIndex] = set;
}
void VulkanDescriptorSetManager::unbind(uint8_t setIndex) {
void VulkanDescriptorSetCache::unbind(uint8_t setIndex) {
mStashedSets[setIndex] = {};
}
void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands,
void VulkanDescriptorSetCache::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.
@@ -412,7 +315,7 @@ void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands,
};
}
void VulkanDescriptorSetManager::updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanBufferObject> bufferObject,
VkDeviceSize offset, VkDeviceSize size) noexcept {
VkDescriptorBufferInfo const info = {
@@ -438,7 +341,7 @@ void VulkanDescriptorSetManager::updateBuffer(fvkmemory::resource_ptr<VulkanDesc
set->acquire(bufferObject);
}
void VulkanDescriptorSetManager::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
VkSampler sampler) noexcept {
VkDescriptorImageInfo info{
@@ -470,13 +373,13 @@ void VulkanDescriptorSetManager::updateSampler(fvkmemory::resource_ptr<VulkanDes
set->acquire(texture);
}
void VulkanDescriptorSetManager::updateInputAttachment(
void VulkanDescriptorSetCache::updateInputAttachment(
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
VulkanAttachment const& attachment) noexcept {
// TOOD: fill-in this region
// TOOD: fill this in.
}
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetManager::createSet(
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet(
Handle<HwDescriptorSet> handle, fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
auto const vkSet = mDescriptorPool->obtainSet(layout);
auto const& count = layout->count;
@@ -492,12 +395,7 @@ fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetManager::createS
});
}
void VulkanDescriptorSetManager::initVkLayout(
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
layout->setVkLayout(mLayoutManager->getVkLayout(layout->bitmask));
}
void VulkanDescriptorSetManager::clearHistory() {
void VulkanDescriptorSetCache::clearHistory() {
mStashedSets = {};
}
@@ -14,10 +14,10 @@
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETCACHE_H
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETCACHE_H
#include "vulkan/VulkanHandles.h"
#include "VulkanHandles.h"
#include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Definitions.h" // For DescriptorSetMask
@@ -34,20 +34,16 @@
namespace filament::backend {
// [GDSR]: Great-Descriptor-Set-Refactor: As of 03/20/24, the Filament frontend is planning to
// introduce descriptor set. This PR will arrive before that change is complete. As such, some of
// the methods introduced here will be obsolete, and certain logic will be generalized.
// Abstraction over the pool and the layout cache.
class VulkanDescriptorSetManager {
// Abstraction over the descriptor set pool.
class VulkanDescriptorSetCache {
public:
static constexpr uint8_t UNIQUE_DESCRIPTOR_SET_COUNT =
VulkanDescriptorSetLayout::UNIQUE_DESCRIPTOR_SET_COUNT;
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
VulkanDescriptorSetManager(VkDevice device, fvkmemory::ResourceManager* resourceManager);
~VulkanDescriptorSetManager();
VulkanDescriptorSetCache(VkDevice device, fvkmemory::ResourceManager* resourceManager);
~VulkanDescriptorSetCache();
void terminate() noexcept;
@@ -72,12 +68,9 @@ 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 =
@@ -85,7 +78,6 @@ private:
VkDevice mDevice;
fvkmemory::ResourceManager* mResourceManager;
std::unique_ptr<DescriptorSetLayoutManager> mLayoutManager;
std::unique_ptr<DescriptorInfinitePool> mDescriptorPool;
std::pair<VulkanAttachment, VkDescriptorImageInfo> mInputAttachment;
DescriptorSetArray mStashedSets = {};
@@ -99,4 +91,4 @@ private:
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
#endif// TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETCACHE_H
@@ -0,0 +1,110 @@
/*
* 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
@@ -0,0 +1,63 @@
/*
* 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
+28 -27
View File
@@ -17,7 +17,6 @@
#include "VulkanDriver.h"
#include "CommandStreamDispatcher.h"
#include "DataReshaper.h"
#include "SystraceProfile.h"
#include "VulkanAsyncHandles.h"
#include "VulkanBuffer.h"
@@ -35,7 +34,6 @@
#include <backend/platforms/VulkanPlatform.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Panic.h>
#ifndef NDEBUG
@@ -44,8 +42,6 @@
using namespace bluevk;
using utils::FixedCapacityVector;
#if defined(__clang__)
// Vulkan functions often immediately dereference pointers, so it's fine to pass in a pointer
// to a stack-allocated variable.
@@ -219,7 +215,8 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mSamplerCache(mPlatform->getDevice()),
mBlitter(mPlatform->getPhysicalDevice(), &mCommands),
mReadPixels(mPlatform->getDevice()),
mDescriptorSetManager(mPlatform->getDevice(), &mResourceManager),
mDescriptorSetLayoutCache(mPlatform->getDevice(), &mResourceManager),
mDescriptorSetCache(mPlatform->getDevice(), &mResourceManager),
mQueryManager(mPlatform->getDevice()),
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
mStereoscopicType(driverConfig.stereoscopicType) {
@@ -329,7 +326,8 @@ void VulkanDriver::terminate() {
mPipelineCache.terminate();
mFramebufferCache.reset();
mSamplerCache.terminate();
mDescriptorSetManager.terminate();
mDescriptorSetLayoutCache.terminate();
mDescriptorSetCache.terminate();
mPipelineLayoutCache.terminate();
// Before terminating ResourceManager, we must make sure all of the resource_ptrs have been unset.
@@ -365,7 +363,7 @@ void VulkanDriver::collectGarbage() {
FVK_SYSTRACE_SCOPE();
// Command buffers need to be submitted and completed before other resources can be gc'd.
mCommands.gc();
mDescriptorSetManager.clearHistory();
mDescriptorSetCache.clearHistory();
mStagePool.gc();
mFramebufferCache.gc();
mPipelineCache.gc();
@@ -408,7 +406,7 @@ void VulkanDriver::updateDescriptorSetBuffer(
FVK_SYSTRACE_SCOPE();
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
auto buffer = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh);
mDescriptorSetManager.updateBuffer(set, binding, buffer, offset, size);
mDescriptorSetCache.updateBuffer(set, binding, buffer, offset, size);
}
void VulkanDriver::updateDescriptorSetTexture(
@@ -421,7 +419,7 @@ void VulkanDriver::updateDescriptorSetTexture(
auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
VkSampler const vksampler = mSamplerCache.getSampler(params);
mDescriptorSetManager.updateSampler(set, binding, texture, vksampler);
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
}
void VulkanDriver::flush(int) {
@@ -778,8 +776,7 @@ void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
void VulkanDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
backend::DescriptorSetLayout&& info) {
auto layout = resource_ptr<VulkanDescriptorSetLayout>::make(&mResourceManager, dslh, info);
mDescriptorSetManager.initVkLayout(layout);
auto layout = mDescriptorSetLayoutCache.createLayout(dslh, std::move(info));
layout.inc();
}
@@ -788,7 +785,7 @@ void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
FVK_SYSTRACE_SCOPE();
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout =
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
auto set = mDescriptorSetManager.createSet(dsh, layout);
auto set = mDescriptorSetCache.createSet(dsh, layout);
set.inc();
}
@@ -1060,7 +1057,7 @@ bool VulkanDriver::isDepthStencilBlitSupported(TextureFormat format) {
formats.end();
}
bool VulkanDriver::isProtectedTexturesSupported() { return false; }
bool VulkanDriver::isProtectedTexturesSupported() { return isProtectedContentSupported(); }
bool VulkanDriver::isDepthClampSupported() {
return mContext.isDepthClampSupported();
@@ -1137,24 +1134,28 @@ math::float2 VulkanDriver::getClipSpaceParams() {
}
uint8_t VulkanDriver::getMaxDrawBuffers() {
return MRT::MIN_SUPPORTED_RENDER_TARGET_COUNT; // TODO: query real value
return mContext.getPhysicalDeviceLimits().maxColorAttachments;
}
size_t VulkanDriver::getMaxUniformBufferSize() {
// TODO: return the actual size instead of hardcoded value
// TODO: devices that return less than 32768 should be rejected. This represents only 3%
// of android devices.
return 32768;
return mContext.getPhysicalDeviceLimits().maxUniformBufferRange;
}
size_t VulkanDriver::getMaxTextureSize(SamplerType) {
// TODO: return the actual size instead of hardcoded value
return 4096;
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() {
// TODO: return the actual size instead of hardcoded value
return 256;
return mContext.getPhysicalDeviceLimits().maxImageArrayLayers;
}
void VulkanDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index,
@@ -1493,7 +1494,7 @@ void VulkanDriver::nextSubpass(int) {
if (mCurrentRenderPass.params.subpassMask & 0x1) {
VulkanAttachment& subpassInput = renderTarget->getColor0();
mDescriptorSetManager.updateInputAttachment({}, subpassInput);
mDescriptorSetCache.updateInputAttachment({}, subpassInput);
}
}
@@ -1820,9 +1821,9 @@ void VulkanDriver::bindDescriptorSet(
backend::DescriptorSetOffsetArray&& offsets) {
if (dsh) {
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
mDescriptorSetManager.bind(setIndex, set, std::move(offsets));
mDescriptorSetCache.bind(setIndex, set, std::move(offsets));
} else {
mDescriptorSetManager.unbind(setIndex);
mDescriptorSetCache.unbind(setIndex);
}
}
@@ -1830,7 +1831,7 @@ void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
FVK_SYSTRACE_SCOPE();
VkCommandBuffer cmdbuffer = mCurrentRenderPass.commandBuffer->buffer();
mDescriptorSetManager.commit(mCurrentRenderPass.commandBuffer,
mDescriptorSetCache.commit(mCurrentRenderPass.commandBuffer,
mBoundPipeline.pipelineLayout,
mBoundPipeline.descriptorSetMask);
+5 -3
View File
@@ -27,8 +27,9 @@
#include "VulkanSamplerCache.h"
#include "VulkanStagePool.h"
#include "VulkanQueryManager.h"
#include "vulkan/caching/VulkanDescriptorSetManager.h"
#include "vulkan/caching/VulkanPipelineLayoutCache.h"
#include "vulkan/VulkanDescriptorSetCache.h"
#include "vulkan/VulkanDescriptorSetLayoutCache.h"
#include "vulkan/VulkanPipelineLayoutCache.h"
#include "vulkan/memory/ResourceManager.h"
#include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Definitions.h"
@@ -137,7 +138,8 @@ private:
VulkanSamplerCache mSamplerCache;
VulkanBlitter mBlitter;
VulkanReadPixels mReadPixels;
VulkanDescriptorSetManager mDescriptorSetManager;
VulkanDescriptorSetLayoutCache mDescriptorSetLayoutCache;
VulkanDescriptorSetCache mDescriptorSetCache;
VulkanQueryManager mQueryManager;
// This is necessary for us to write to push constants after binding a pipeline.
@@ -15,15 +15,12 @@
*/
#include "VulkanPipelineCache.h"
#include "VulkanMemory.h"
#include "caching/VulkanDescriptorSetManager.h"
#include <utils/Log.h>
#include <utils/Panic.h>
#include "VulkanConstants.h"
#include "VulkanHandles.h"
#include "VulkanTexture.h"
#include "vulkan/utils/Conversion.h"
#if defined(__clang__)
@@ -17,7 +17,8 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H
#define TNT_FILAMENT_BACKEND_VULKANPIPELINELAYOUTCACHE_H
#include <vulkan/VulkanHandles.h>
#include "VulkanHandles.h"
#include <bluevk/BlueVK.h>
#include <utils/Hash.h>
@@ -736,10 +736,6 @@ 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);
@@ -759,6 +755,10 @@ 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));
@@ -223,6 +223,19 @@ Platform::ExternalImageHandle VulkanPlatformAndroid::createExternalImage(
return Platform::ExternalImageHandle{};
}
VulkanPlatformAndroid::ExternalImageDescAndroid VulkanPlatformAndroid::getExternalImageDesc(
ExternalImageHandleRef externalImage) const noexcept {
auto const* fvkExternalImage =
static_cast<ExternalImageVulkanAndroid const*>(externalImage.get());
return {
.width = fvkExternalImage->width,
.height = fvkExternalImage->height,
.format = fvkExternalImage->format,
.usage = fvkExternalImage->usage,
};
}
VulkanPlatform::ExternalImageMetadata VulkanPlatformAndroid::getExternalImageMetadata(
ExternalImageHandleRef externalImage) {
auto const* fvkExternalImage =
@@ -143,7 +143,7 @@ wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
return device;
}
Driver* WebGPUPlatform::createDriver(void* const sharedContext,
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 "
@@ -15,6 +15,8 @@
*/
#include <backend/platforms/WebGPUPlatform.h>
#include <backend/DriverEnums.h>
#include <utils/Panic.h>
#include <webgpu/webgpu_cpp.h>
@@ -118,7 +120,9 @@ wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t flags)
if (useXcb) {
wgpu::SurfaceSourceXCBWindow surfaceSourceXcb{};
surfaceSourceXcb.connection = g_x11.connection;
surfaceSourceXcb.window = reinterpret_cast<uint32_t>(nativeWindow);
// TODO: this looks really wrong, please fix!!
surfaceSourceXcb.window = *((uint32_t*) nativeWindow);
wgpu::SurfaceDescriptor surfaceDescriptor{
.nextInChain = &surfaceSourceXcb,
.label = "linux_xcb_surface"
+172
View File
@@ -0,0 +1,172 @@
/*
* 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
@@ -0,0 +1,136 @@
/*
* 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
@@ -0,0 +1,64 @@
/*
* 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
@@ -0,0 +1,143 @@
/*
* 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
+208 -260
View File
@@ -16,8 +16,9 @@
#include "BackendTest.h"
#include "ImageExpectations.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "Shader.h"
#include "TrianglePrimitive.h"
#include <utils/Hash.h>
@@ -26,8 +27,10 @@
#include <fstream>
#ifndef FILAMENT_IOS
#include <imageio/ImageEncoder.h>
#include <image/ColorTransform.h>
#endif
namespace test {
@@ -37,6 +40,21 @@ 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;
@@ -56,49 +74,12 @@ void main() {
fragColor = params.color;
})";
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);
Shader BlitTest::createShader() {
return Shader(getDriverApi(), mCleanup, ShaderConfig{
.vertexShader = triangleVs,
.fragmentShader = triangleFs,
.uniformNames = { "Params" },
});
}
static uint32_t toUintColor(float4 color) {
@@ -116,7 +97,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;
@@ -140,7 +121,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 };
@@ -168,7 +149,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;
@@ -176,7 +157,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++) {
@@ -194,10 +175,9 @@ 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(BackendTest, ColorMagnify) {
TEST_F(BlitTest, ColorMagnify) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
mCleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 256;
constexpr int kSrcTexHeight = 256;
@@ -208,37 +188,40 @@ TEST_F(BackendTest, ColorMagnify) {
constexpr int kNumLevels = 3;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
auto swapChain = mCleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
Handle<HwTexture> const srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> const srcTexture = mCleanup.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 = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> const dstTexture = mCleanup.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] = 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 }, {}, {}));
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 }, {},
{}));
}
// 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.
{
@@ -246,29 +229,23 @@ TEST_F(BackendTest, ColorMagnify) {
api.commit(swapChain);
}
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMagnify.png" };
{
RenderFrame frame(api);
dumpScreenshot(api, dstRenderTargets[0], &params);
api.commit(swapChain);
ImageExpectations expectations(api);
{
RenderFrame frame(api);
EXPECT_IMAGE(dstRenderTargets[0], expectations,
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorMagnify", 0x410bdd31));
api.commit(swapChain);
}
flushAndWait();
}
// 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(BackendTest, ColorMinify) {
TEST_F(BlitTest, ColorMinify) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
mCleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 1024;
constexpr int kSrcTexHeight = 1024;
@@ -279,57 +256,55 @@ TEST_F(BackendTest, ColorMinify) {
constexpr int kNumLevels = 3;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
auto swapChain = mCleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
Handle<HwTexture> const srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> const srcTexture = mCleanup.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 = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> const dstTexture = mCleanup.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] = 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 }, {}, {}));
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 }, {},
{}));
}
// 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);
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMinify.png" };
dumpScreenshot(api, dstRenderTargets[0], &params);
EXPECT_IMAGE(dstRenderTargets[0], expectations,
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorMinify", 0xf3d9c53f));
// 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);
flushAndWait();
}
}
TEST_F(BackendTest, ColorResolve) {
TEST_F(BlitTest, ColorResolve) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
constexpr int kSrcTexWidth = 256;
constexpr int kSrcTexHeight = 256;
@@ -338,49 +313,29 @@ TEST_F(BackendTest, ColorResolve) {
constexpr auto kColorTexFormat = TextureFormat::RGBA8;
constexpr int kSampleCount = 4;
// 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));
Shader shader = createShader();
// Create a VertexBuffer, IndexBuffer, and RenderPrimitive.
TrianglePrimitive const triangle(api);
// Create 4-sample texture.
Handle<HwTexture> const srcColorTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, 1, kColorTexFormat, kSampleCount, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> const srcColorTexture = mCleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, 1, kColorTexFormat, kSampleCount, kSrcTexWidth, kSrcTexHeight,
1,
TextureUsage::COLOR_ATTACHMENT));
// Create 1-sample texture.
Handle<HwTexture> const dstColorTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, 1, kColorTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> const dstColorTexture = mCleanup.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 = cleanup.add(api.createRenderTarget(
Handle<HwRenderTarget> const srcRenderTarget = mCleanup.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 = cleanup.add(api.createRenderTarget(
Handle<HwRenderTarget> const dstRenderTarget = mCleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR, kDstTexWidth, kDstTexHeight, 1, 0,
{{ dstColorTexture }}, {}, {}));
@@ -394,58 +349,52 @@ TEST_F(BackendTest, ColorResolve) {
params.viewport.height = kSrcTexHeight;
PipelineState state = {};
state.program = program;
state.pipelineLayout.setLayout[1] = { descriptorSetLayout };
state.program = shader.getProgram();
state.pipelineLayout.setLayout[1] = { shader.getDescriptorSetLayout() };
state.rasterState.colorWrite = true;
state.rasterState.depthWrite = false;
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams),
auto ubuffer = mCleanup.add(api.createBufferObject(sizeof(MaterialParams),
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
// Draw red triangle into srcRenderTarget.
uploadUniforms(api, ubuffer, {
.color = float4(1, 0, 0, 1),
.scale = float4(1, 1, 0.5, 0),
shader.uploadUniform(api, ubuffer, MaterialParams{
.color = float4(1, 0, 0, 1),
.scale = float4(1, 1, 0.5, 0),
});
api.updateDescriptorSetBuffer(descriptorSet, 0, ubuffer, 0, sizeof(MaterialParams));
api.bindDescriptorSet(descriptorSet, 1, {});
shader.bindUniform<MaterialParams>(api, ubuffer);
// 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);
// Grab a screenshot.
ScreenshotParams sparams{ kDstTexWidth, kDstTexHeight, "ColorResolve.png" };
dumpScreenshot(api, dstRenderTarget, &sparams);
{
ImageExpectations expectations(api);
// Wait for the ReadPixels result to come back.
flushAndWait();
EXPECT_IMAGE(dstRenderTarget, expectations,
ScreenshotParams(kDstTexWidth, kDstTexHeight, "ColorResolve", 0xebfac2ef));
// 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);
flushAndWait();
}
}
TEST_F(BackendTest, Blit2DTextureArray) {
TEST_F(BlitTest, Blit2DTextureArray) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
mCleanup.addPostCall([&]() { executeCommands(); });
api.startCapture(0);
cleanup.addPostCall([&]() { api.stopCapture(0); });
mCleanup.addPostCall([&]() { api.stopCapture(0); });
constexpr int kSrcTexWidth = 256;
constexpr int kSrcTexHeight = 256;
@@ -460,33 +409,40 @@ TEST_F(BackendTest, Blit2DTextureArray) {
constexpr int kDstTexLayer = 0;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
auto swapChain = mCleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D_ARRAY, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, kSrcTexDepth,
Handle<HwTexture> srcTexture = mCleanup.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 = cleanup.add(api.createTexture(
Handle<HwTexture> dstTexture = mCleanup.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 = 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 }, {}, {}));
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 }, {}, {}));
// 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.
{
@@ -494,29 +450,24 @@ TEST_F(BackendTest, Blit2DTextureArray) {
api.commit(swapChain);
}
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "Blit2DTextureArray.png" };
{
RenderFrame frame(api);
dumpScreenshot(api, dstRenderTarget, &params);
api.commit(swapChain);
ImageExpectations expectations(api);
{
RenderFrame frame(api);
EXPECT_IMAGE(dstRenderTarget, expectations,
ScreenshotParams(kDstTexWidth, kDstTexHeight, "Blit2DTextureArray",
0x8de7d55b));
api.commit(swapChain);
}
flushAndWait();
}
// 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(BackendTest, BlitRegion) {
TEST_F(BlitTest, BlitRegion) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
mCleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 1024;
constexpr int kSrcTexHeight = 1024;
@@ -529,47 +480,48 @@ TEST_F(BackendTest, BlitRegion) {
constexpr int kDstLevel = 0;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
auto swapChain = mCleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> srcTexture = mCleanup.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 = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> dstTexture = mCleanup.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 =
cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR, srcRect.width,
srcRect.height, 1, 0, {srcTexture, kSrcLevel, 0}, {}, {}));
mCleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR, srcRect.width,
srcRect.height, 1, 0, { srcTexture, kSrcLevel, 0 }, {}, {}));
Handle<HwRenderTarget> dstRenderTarget =
cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR, kDstTexWidth >> kDstLevel,
kDstTexHeight >> kDstLevel, 1, 0, {dstTexture, kDstLevel, 0}, {}, {}));
mCleanup.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.
@@ -578,33 +530,25 @@ TEST_F(BackendTest, BlitRegion) {
api.commit(swapChain);
}
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegion.png" };
{
RenderFrame frame(api);
dumpScreenshot(api, dstRenderTarget, &params);
api.commit(swapChain);
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();
}
// 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(BackendTest, BlitRegionToSwapChain) {
TEST_F(BlitTest, BlitRegionToSwapChain) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
mCleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 1024;
constexpr int kSrcTexHeight = 1024;
@@ -614,15 +558,15 @@ TEST_F(BackendTest, BlitRegionToSwapChain) {
constexpr int kNumLevels = 3;
// Create a SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
auto swapChain = mCleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
Handle<HwRenderTarget> dstRenderTarget = cleanup.add(api.createDefaultRenderTarget());
Handle<HwRenderTarget> dstRenderTarget = mCleanup.add(api.createDefaultRenderTarget());
// Create a source texture.
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> srcTexture = mCleanup.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);
@@ -630,42 +574,46 @@ TEST_F(BackendTest, BlitRegionToSwapChain) {
// Create a RenderTarget for each texture's miplevel.
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
for (uint8_t level = 0; level < kNumLevels; level++) {
srcRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {}));
srcRenderTargets[level] = mCleanup.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,
};
{
RenderFrame frame(api);
ImageExpectations expectations(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);
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain.png" };
dumpScreenshot(api, dstRenderTarget, &params);
api.commit(swapChain);
}
api.commit(swapChain);
// 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();
}
// Wait for the ReadPixels result to come back.
api.finish();
executeCommands();
getDriver().purge();
}
} // namespace test
+70 -114
View File
@@ -16,6 +16,8 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "Shader.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -78,8 +80,28 @@ 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
};
TEST_F(BackendTest, VertexBufferUpdate) {
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) {
const bool largeBuffers = false;
// If updateIndices is true, then even-numbered triangles will have their indices set to
@@ -90,31 +112,15 @@ TEST_F(BackendTest, VertexBufferUpdate) {
// executeCommands().
{
auto& api = getDriverApi();
Cleanup cleanup(api);
// Create a platform-specific SwapChain and make it current.
auto swapChain = createSwapChain();
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
// 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));
Shader shader = createShader();
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);
auto defaultRenderTarget = cleanup.add(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.
@@ -128,38 +134,29 @@ TEST_F(BackendTest, VertexBufferUpdate) {
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState state;
state.program = program;
state.pipelineLayout.setLayout[1] = { descriptorSetLayout };
state.program = shader.getProgram();
state.pipelineLayout.setLayout[1] = { shader.getDescriptorSetLayout() };
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 = 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 = cleanup.add(api.createBufferObject(sizeof(MaterialParams) + 64,
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
api.updateDescriptorSetBuffer(descriptorSet, 0, ubuffer, 0, sizeof(MaterialParams) + 64);
api.bindDescriptorSet(descriptorSet, 1, {});
shader.bindUniform<MaterialParams>(api, ubuffer, kBindingConfig);
api.startCapture(0);
// 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);
}
// 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 }
});
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
@@ -202,13 +199,6 @@ TEST_F(BackendTest, 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();
@@ -216,62 +206,38 @@ TEST_F(BackendTest, VertexBufferUpdate) {
// This test renders two triangles in two separate draw calls. Between the draw calls, a uniform
// buffer object is partially updated.
TEST_F(BackendTest, BufferObjectUpdateWithOffset) {
TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
auto& api = getDriverApi();
Cleanup cleanup(api);
// Create a platform-specific SwapChain and make it current.
auto swapChain = createSwapChain();
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
// 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));
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
{{
DescriptorType::UNIFORM_BUFFER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}});
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
Shader shader = createShader();
// 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 = api.createBufferObject(sizeof(MaterialParams) + 64,
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams) + 64,
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
api.updateDescriptorSetBuffer(descriptorSet, 0, ubuffer, 0, sizeof(MaterialParams) + 64);
api.bindDescriptorSet(descriptorSet, 1, {});
shader.bindUniform<MaterialParams>(api, ubuffer, kBindingConfig);
// Create a render target.
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}}, {}, {});
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}}, {}, {}));
// Upload uniforms for the first triangle.
{
MaterialParams params {
// 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, 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;
@@ -280,29 +246,27 @@ TEST_F(BackendTest, BufferObjectUpdateWithOffset) {
params.flags.discardEnd = TargetBufferFlags::NONE;
params.viewport.height = 512;
params.viewport.width = 512;
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
renderTarget, swapChain, program, params);
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
renderTarget, swapChain, shader.getProgram(), 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.
{
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));
}
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 }
);
params.flags.clear = TargetBufferFlags::NONE;
params.flags.discardStart = TargetBufferFlags::NONE;
renderTriangle({{ DescriptorSetLayoutHandle{}, descriptorSetLayout }},
renderTarget, swapChain, program, params);
renderTriangle({{ DescriptorSetLayoutHandle{}, shader.getDescriptorSetLayout() }},
renderTarget, swapChain, shader.getProgram(), params);
static const uint32_t expectedHash = 91322442;
readPixelsAndAssertHash(
@@ -312,14 +276,6 @@ TEST_F(BackendTest, 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();
+7 -3
View File
@@ -16,6 +16,8 @@
#include "BackendTest.h"
#include "Lifetimes.h"
using namespace filament;
using namespace filament::backend;
@@ -23,13 +25,14 @@ 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 = createSwapChain();
auto swapChain = cleanup.add(createSwapChain());
Handle<HwRenderTarget> renderTarget = api.createDefaultRenderTarget();
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createDefaultRenderTarget());
int callbackCountA = 0;
api.setFrameScheduledCallback(swapChain, nullptr, [&callbackCountA](PresentCallable callable) {
@@ -79,9 +82,10 @@ TEST_F(BackendTest, FrameScheduledCallback) {
TEST_F(BackendTest, FrameCompletedCallback) {
auto& api = getDriverApi();
Cleanup cleanup(api);
// Create a SwapChain.
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
int callbackCountA = 0;
api.setFrameCompletedCallback(swapChain, nullptr,
+18 -27
View File
@@ -16,6 +16,7 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -129,12 +130,13 @@ 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 = createSwapChain();
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
// Create a program.
@@ -155,10 +157,10 @@ TEST_F(BackendTest, FeedbackLoops) {
{ "test_tex", DescriptorType::SAMPLER, 0 },
{ "Params", DescriptorType::UNIFORM_BUFFER, 1 }
});
program = api.createProgram(std::move(prog));
program = cleanup.add(api.createProgram(std::move(prog)));
}
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
{{
DescriptorType::SAMPLER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
@@ -168,27 +170,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 = api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kTexFormat, 1, kTexWidth, kTexHeight, 1, usage);
Handle<HwTexture> const texture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kTexFormat, 1, kTexWidth, kTexHeight, 1, usage));
// Create ubo
auto ubuffer = api.createBufferObject(sizeof(MaterialParams),
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
auto ubuffer = cleanup.add(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] = api.createRenderTarget( TargetBufferFlags::COLOR,
kTexWidth >> level, kTexHeight >> level, 1, 0, { texture, level, 0 }, {}, {});
renderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kTexWidth >> level, kTexHeight >> level, 1, 0, { texture, level, 0 }, {}, {}));
}
// Fill the base level of the texture with interesting colors.
@@ -226,12 +228,13 @@ 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 = api.createTextureView(texture, sourceLevel, 1);
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
auto textureView = passCleanup.add(api.createTextureView(texture, sourceLevel, 1));
DescriptorSetHandle descriptorSet = passCleanup.add(api.createDescriptorSet(descriptorSetLayout));
api.updateDescriptorSetTexture(descriptorSet, 0, textureView, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
@@ -247,8 +250,6 @@ 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
@@ -256,12 +257,13 @@ 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 = api.createTextureView(texture, sourceLevel, 1);
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
auto textureView = passCleanup.add(api.createTextureView(texture, sourceLevel, 1));
DescriptorSetHandle descriptorSet = passCleanup.add(api.createDescriptorSet(descriptorSetLayout));
api.updateDescriptorSetTexture(descriptorSet, 0, textureView, {
.filterMag = SamplerMagFilter::LINEAR,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST
@@ -277,8 +279,6 @@ 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,15 +296,6 @@ 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;
+12 -16
View File
@@ -16,9 +16,10 @@
#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>
@@ -295,10 +296,12 @@ 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 = createSwapChain();
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
auto defaultRenderTarget = api.createDefaultRenderTarget(0);
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
// Create a program.
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
@@ -315,21 +318,21 @@ TEST_F(BackendTest, UpdateImage2D) {
Program prog = shaderGen.getProgram(api);
prog.descriptorBindings(1, {{"test_tex", DescriptorType::SAMPLER, 0}});
ProgramHandle const program = api.createProgram(std::move(prog));
ProgramHandle const program = cleanup.add(api.createProgram(std::move(prog)));
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
{{
DescriptorType::SAMPLER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}});
}}}));
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
// Create a Texture.
auto usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
Handle<HwTexture> const texture = api.createTexture(SamplerType::SAMPLER_2D, 1,
t.textureFormat, 1, 512, 512, 1u, usage);
Handle<HwTexture> const texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
t.textureFormat, 1, 512, 512, 1u, usage));
// Upload some pixel data.
if (t.uploadSubregions) {
@@ -359,13 +362,6 @@ 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,6 +16,7 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -60,25 +61,28 @@ 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 = createSwapChain();
getDriverApi().makeCurrent(swapChain, swapChain);
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(getDriverApi());
auto program = getDriverApi().createProgram(std::move(p));
Program p = shaderGen.getProgram(api);
auto program = cleanup.add(api.createProgram(std::move(p)));
TrianglePrimitive triangle(getDriverApi());
TrianglePrimitive triangle(api);
auto defaultRenderTarget = getDriverApi().createDefaultRenderTarget(0);
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
// Create two Textures.
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
Handle<HwTexture> textureA = getDriverApi().createTexture(
Handle<HwTexture> textureA = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, // target
1, // levels
TextureFormat::RGBA8, // format
@@ -86,8 +90,8 @@ TEST_F(BackendTest, MRT) {
512, // width
512, // height
1, // depth
usage); // usage
Handle<HwTexture> textureB = getDriverApi().createTexture(
usage)); // usage
Handle<HwTexture> textureB = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, // target
1, // levels
TextureFormat::RGBA8, // format
@@ -95,10 +99,10 @@ TEST_F(BackendTest, MRT) {
512, // width
512, // height
1, // depth
usage); // usage
usage)); // usage
// Create a RenderTarget with two attachments.
Handle<HwRenderTarget> renderTarget = getDriverApi().createRenderTarget(
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::COLOR1,
// The width and height must match the width and height of the respective mip
// level (at least for OpenGL).
@@ -108,7 +112,7 @@ TEST_F(BackendTest, MRT) {
0, // layerCount
{{textureA },{textureB }}, // color
{}, // depth
{}); // stencil
{})); // stencil
RenderPassParams params = {};
fullViewport(params);
@@ -124,25 +128,21 @@ TEST_F(BackendTest, MRT) {
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
getDriverApi().startCapture(0);
api.startCapture(0);
getDriverApi().makeCurrent(swapChain, swapChain);
getDriverApi().beginFrame(0, 0, 0);
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
// Draw a triangle.
getDriverApi().beginRenderPass(renderTarget, params);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().endRenderPass();
api.beginRenderPass(renderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
getDriverApi().flush();
getDriverApi().commit(swapChain);
getDriverApi().endFrame(0);
api.flush();
api.commit(swapChain);
api.endFrame(0);
getDriverApi().stopCapture(0);
getDriverApi().destroyProgram(program);
getDriverApi().destroySwapChain(swapChain);
getDriverApi().destroyRenderTarget(defaultRenderTarget);
api.stopCapture(0);
}
executeCommands();
+17 -27
View File
@@ -17,9 +17,10 @@
#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>
@@ -78,12 +79,13 @@ 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 = createSwapChain();
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
// Create a program that draws only white.
@@ -92,7 +94,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 = api.createProgram(std::move(p));
whiteProgram = cleanup.add(api.createProgram(std::move(p)));
}
// Create a program that samples a texture.
@@ -106,19 +108,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 = api.createProgram(std::move(p));
textureProgram = cleanup.add(api.createProgram(std::move(p)));
}
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
{{
DescriptorType::SAMPLER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}});
}}}));
DescriptorSetHandle descriptorSet[2];
descriptorSet[0] = api.createDescriptorSet(descriptorSetLayout);
descriptorSet[1] = api.createDescriptorSet(descriptorSetLayout);
descriptorSet[0] = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
descriptorSet[1] = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
// Create a texture that has 4 mip levels. Each level is a different color.
// Level 0: 128x128 (red)
@@ -127,9 +129,9 @@ TEST_F(BackendTest, TextureViewLod) {
// Level 3: 16x16 (yellow)
const size_t kTextureSize = 128;
const size_t kMipLevels = 4;
Handle<HwTexture> texture = api.createTexture(SamplerType::SAMPLER_2D, kMipLevels,
Handle<HwTexture> texture = cleanup.add(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;
@@ -163,13 +165,13 @@ TEST_F(BackendTest, TextureViewLod) {
// Level 1: 64x64 (green) <-- base
// Level 2: 32x32 (blue) <--- white triangle rendered
// Level 3: 16x16 (yellow) <-- max
auto texture13 = api.createTextureView(texture, 1, 3);
auto texture13 = cleanup.add(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 = api.createRenderTarget(
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR, 32, 32, 1, 0,
{texture, 2 /* level */, 0 /* layer */}, {}, {});
{texture, 2 /* level */, 0 /* layer */}, {}, {}));
{
RenderPassParams params = {};
fullViewport(params);
@@ -185,7 +187,7 @@ TEST_F(BackendTest, TextureViewLod) {
api.endRenderPass();
}
backend::Handle<HwRenderTarget> defaultRenderTarget = api.createDefaultRenderTarget(0);
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
RenderPassParams params = {};
fullViewport(params);
@@ -217,7 +219,7 @@ TEST_F(BackendTest, TextureViewLod) {
api.endRenderPass();
// Adjust the base mip to 2.
auto texture22 = api.createTextureView(texture, 2, 2);
auto texture22 = cleanup.add(api.createTextureView(texture, 2, 2));
api.updateDescriptorSetTexture(descriptorSet[1], 0, texture22, {
.filterMag = SamplerMagFilter::NEAREST,
@@ -244,18 +246,6 @@ 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,6 +16,7 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -70,18 +71,20 @@ 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 = createSwapChain();
getDriverApi().makeCurrent(swapChain, swapChain);
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(getDriverApi());
auto program = getDriverApi().createProgram(std::move(p));
Program p = shaderGen.getProgram(api);
auto program = cleanup.add(api.createProgram(std::move(p)));
auto defaultRenderTarget = getDriverApi().createDefaultRenderTarget(0);
auto defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
TrianglePrimitive triangle(getDriverApi());
TrianglePrimitive triangle(api);
RenderPassParams params = {};
fullViewport(params);
@@ -97,25 +100,21 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
getDriverApi().startCapture(0);
api.startCapture(0);
getDriverApi().makeCurrent(swapChain, swapChain);
getDriverApi().beginFrame(0, 0, 0);
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
// Render a triangle.
getDriverApi().beginRenderPass(defaultRenderTarget, params);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().endRenderPass();
api.beginRenderPass(defaultRenderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
getDriverApi().flush();
getDriverApi().commit(swapChain);
getDriverApi().endFrame(0);
api.flush();
api.commit(swapChain);
api.endFrame(0);
getDriverApi().stopCapture(0);
getDriverApi().destroyProgram(program);
getDriverApi().destroySwapChain(swapChain);
getDriverApi().destroyRenderTarget(defaultRenderTarget);
api.stopCapture(0);
}
executeCommands();
+5 -7
View File
@@ -16,6 +16,7 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -80,20 +81,21 @@ 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 = createSwapChain();
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
ProgramHandle program = api.createProgram(std::move(p));
ProgramHandle program = cleanup.add(api.createProgram(std::move(p)));
Handle<HwRenderTarget> renderTarget = api.createDefaultRenderTarget();
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createDefaultRenderTarget());
TrianglePrimitive triangle(api);
@@ -150,10 +152,6 @@ TEST_F(BackendTest, PushConstants) {
api.commit(swapChain);
api.endFrame(0);
// Cleanup.
api.destroySwapChain(swapChain);
api.destroyRenderTarget(renderTarget);
}
api.stopCapture(0);
+55 -59
View File
@@ -16,9 +16,10 @@
#include "BackendTest.h"
#include "BackendTestUtils.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
#include "BackendTestUtils.h"
#include <utils/Hash.h>
@@ -236,17 +237,20 @@ 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(getDriverApi());
programFloat = getDriverApi().createProgram(std::move(p));
Program p = shaderGen.getProgram(api);
programFloat = cleanup.add(api.createProgram(std::move(p)));
}
{
ShaderGenerator shaderGen(vertex, fragmentUint, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(getDriverApi());
programUint = getDriverApi().createProgram(std::move(p));
Program p = shaderGen.getProgram(api);
programUint = cleanup.add(api.createProgram(std::move(p)));
}
@@ -255,35 +259,35 @@ TEST_F(ReadPixelsTest, ReadPixels) {
// Create a platform-specific SwapChain and make it current.
Handle<HwSwapChain> swapChain;
if (t.useDefaultRT) {
swapChain = createSwapChain();
swapChain = cleanup.add(createSwapChain());
} else {
swapChain = getDriverApi().createSwapChainHeadless(t.getRenderTargetSize(),
t.getRenderTargetSize(), 0);
swapChain = cleanup.add(api.createSwapChainHeadless(t.getRenderTargetSize(),
t.getRenderTargetSize(), 0));
}
getDriverApi().makeCurrent(swapChain, swapChain);
api.makeCurrent(swapChain, swapChain);
// Create a Texture and RenderTarget to render into.
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
Handle<HwTexture> const texture = getDriverApi().createTexture(SamplerType::SAMPLER_2D,
Handle<HwTexture> const texture = cleanup.add(api.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 = getDriverApi().createDefaultRenderTarget();
renderTarget = cleanup.add(api.createDefaultRenderTarget());
} else {
// The width and height must match the width and height of the respective mip
// level (at least for OpenGL).
renderTarget = getDriverApi().createRenderTarget(
renderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR, t.getRenderTargetSize(),
t.getRenderTargetSize(), t.samples, 0, {{ texture, uint8_t(t.mipLevel) }}, {},
{});
{}));
}
TrianglePrimitive const triangle(getDriverApi());
TrianglePrimitive const triangle(api);
RenderPassParams params = {};
fullViewport(params);
@@ -294,11 +298,11 @@ TEST_F(ReadPixelsTest, ReadPixels) {
params.viewport.height = t.getRenderTargetSize();
params.viewport.width = t.getRenderTargetSize();
getDriverApi().makeCurrent(swapChain, swapChain);
getDriverApi().beginFrame(0, 0, 0);
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
// Render a white triangle over blue.
getDriverApi().beginRenderPass(renderTarget, params);
api.beginRenderPass(renderTarget, params);
PipelineState state;
state.program = programFloat;
@@ -309,21 +313,21 @@ TEST_F(ReadPixelsTest, ReadPixels) {
state.rasterState.depthWrite = false;
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().endRenderPass();
api.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 = getDriverApi().createRenderTarget(
Handle<HwRenderTarget> mipLevelOneRT = localCleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR, renderTargetBaseSize, renderTargetBaseSize, 1, 0,
{{ texture }}, {}, {});
{{ texture }}, {}, {}));
p.clearColor = {1.f, 0.f, 0.f, 1.f};
getDriverApi().beginRenderPass(mipLevelOneRT, p);
getDriverApi().endRenderPass();
getDriverApi().destroyRenderTarget(mipLevelOneRT);
api.beginRenderPass(mipLevelOneRT, p);
api.endRenderPass();
}
// Read pixels.
@@ -347,26 +351,19 @@ TEST_F(ReadPixelsTest, ReadPixels) {
free(buffer);
}, (void*) &t);
getDriverApi().readPixels(renderTarget, t.readRect.x, t.readRect.y, t.readRect.width,
api.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};
getDriverApi().beginRenderPass(renderTarget, params);
getDriverApi().endRenderPass();
api.beginRenderPass(renderTarget, params);
api.endRenderPass();
getDriverApi().commit(swapChain);
getDriverApi().endFrame(0);
getDriverApi().destroySwapChain(swapChain);
getDriverApi().destroyRenderTarget(renderTarget);
getDriverApi().destroyTexture(texture);
api.commit(swapChain);
api.endFrame(0);
}
getDriverApi().destroyProgram(programFloat);
getDriverApi().destroyProgram(programUint);
// This ensures all driver commands have finished before exiting the test.
flushAndWait();
}
@@ -375,18 +372,21 @@ 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 = getDriverApi().createSwapChainHeadless(renderTargetSize, renderTargetSize, 0);
getDriverApi().makeCurrent(swapChain, swapChain);
auto swapChain = cleanup.add(api.createSwapChainHeadless(renderTargetSize, renderTargetSize, 0));
api.makeCurrent(swapChain, swapChain);
// Create a program.
ShaderGenerator shaderGen(vertex, fragmentFloat, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(getDriverApi());
auto program = getDriverApi().createProgram(std::move(p));
Program p = shaderGen.getProgram(api);
auto program = cleanup.add(api.createProgram(std::move(p)));
// Create a Texture and RenderTarget to render into.
auto usage = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
Handle<HwTexture> texture = getDriverApi().createTexture(
Handle<HwTexture> texture = cleanup.add(api.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 = getDriverApi().createRenderTarget(
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR,
renderTargetSize, // width
renderTargetSize, // height
@@ -404,9 +404,9 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
0, // layerCount
{{ texture }}, // color
{}, // depth
{}); // stencil
{})); // stencil
TrianglePrimitive triangle(getDriverApi());
TrianglePrimitive triangle(api);
RenderPassParams params = {};
fullViewport(params);
@@ -433,13 +433,13 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
printf("Executing test %d / %d\n", iteration, iterationCount);
}
getDriverApi().makeCurrent(swapChain, swapChain);
getDriverApi().beginFrame(0, 0, 0);
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0, 0);
// Render some content, just so we don't read back uninitialized data.
getDriverApi().beginRenderPass(renderTarget, params);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().endRenderPass();
api.beginRenderPass(renderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.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);
getDriverApi().readPixels(renderTarget, 0, 0, renderTargetSize, renderTargetSize, std::move(descriptor));
getDriverApi().commit(swapChain);
getDriverApi().endFrame(0);
api.readPixels(renderTarget, 0, 0, renderTargetSize, renderTargetSize, std::move(descriptor));
api.commit(swapChain);
api.endFrame(0);
flushAndWait();
getDriver().purge();
@@ -460,11 +460,7 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
free(buffer);
getDriverApi().destroyProgram(program);
getDriverApi().destroySwapChain(swapChain);
getDriverApi().destroyRenderTarget(renderTarget);
getDriverApi().destroyTexture(texture);
getDriverApi().finish();
api.finish();
executeCommands();
}
@@ -16,6 +16,7 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -66,10 +67,11 @@ 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 = createSwapChain();
auto swapChain = cleanup.add(createSwapChain());
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH, false };
@@ -82,22 +84,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 = api.createProgram(std::move(p));
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
backend::Handle<HwProgram> program = cleanup.add(api.createProgram(std::move(p)));
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
{{
DescriptorType::SAMPLER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}});
}}}));
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
backend::Handle<HwRenderTarget> defaultRenderTarget = api.createDefaultRenderTarget(0);
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(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 = api.createTexture(
backend::Handle<HwTexture> texture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_EXTERNAL, // target
1, // levels
TextureFormat::RGBA8, // format
@@ -105,7 +107,7 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
view.width, // width
view.height, // height
1, // depth
usage); // usage
usage)); // usage
RenderPassParams params = {};
fullViewport(params);
@@ -140,15 +142,6 @@ 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();
@@ -156,10 +149,11 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
TEST_F(BackendTest, RenderExternalImage) {
auto& api = getDriverApi();
Cleanup cleanup(api);
TrianglePrimitive triangle(api);
auto swapChain = createSwapChain();
auto swapChain = cleanup.add(createSwapChain());
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
SamplerType::SAMPLER_EXTERNAL, SamplerFormat::FLOAT, Precision::HIGH, false };
@@ -172,18 +166,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 = api.createProgram(std::move(p));
auto program = cleanup.add(api.createProgram(std::move(p)));
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
{{
DescriptorType::SAMPLER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}});
}}}));
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
backend::Handle<HwRenderTarget> defaultRenderTarget = api.createDefaultRenderTarget(0);
backend::Handle<HwRenderTarget> defaultRenderTarget = cleanup.add(api.createDefaultRenderTarget(0));
// require users to create two Filament textures and have two material parameters
// add a "plane" parameter to setExternalImage
@@ -227,8 +221,8 @@ TEST_F(BackendTest, RenderExternalImage) {
api.setupExternalImage(pixBuffer);
backend::Handle<HwTexture> texture =
api.createTextureExternalImage(SamplerType::SAMPLER_EXTERNAL,
TextureFormat::RGBA8, 1024, 1024, usage, pixBuffer);
cleanup.add(api.createTextureExternalImage(SamplerType::SAMPLER_EXTERNAL,
TextureFormat::RGBA8, 1024, 1024, usage, pixBuffer));
// We're now free to release the buffer.
CVBufferRelease(pixBuffer);
@@ -269,15 +263,6 @@ 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();
+19 -26
View File
@@ -16,6 +16,7 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -55,6 +56,7 @@ 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
// +----------------------------------------+ +------------------------------------------+
@@ -80,21 +82,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 = api.createSwapChainHeadless(256, 256, 0);
auto swapChain = cleanup.add(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 = api.createProgram(std::move(p));
ProgramHandle program = cleanup.add(api.createProgram(std::move(p)));
// Create source color and depth textures.
Handle<HwTexture> srcTexture = api.createTexture(SamplerType::SAMPLER_2D, kNumLevels,
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, kNumLevels,
kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
Handle<HwTexture> depthTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
Handle<HwTexture> depthTexture = cleanup.add(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 = {
@@ -112,13 +114,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 = api.createRenderTarget(
Handle<HwRenderTarget> srcRenderTarget = cleanup.add(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 = api.createRenderTarget(TargetBufferFlags::COLOR,
Handle<HwRenderTarget> fullRenderTarget = cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR,
kSrcTexHeight >> kSrcLevel, kSrcTexWidth >> kSrcLevel, 1, 0,
{srcTexture, kSrcLevel, 0}, {}, {});
{srcTexture, kSrcLevel, 0}, {}, {}));
TrianglePrimitive triangle(api);
@@ -150,11 +152,6 @@ 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.
@@ -169,23 +166,24 @@ 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 = api.createSwapChainHeadless(256, 256, 0);
auto swapChain = cleanup.add(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 = api.createProgram(std::move(p));
ProgramHandle program = cleanup.add(api.createProgram(std::move(p)));
// Create a source color textures.
Handle<HwTexture> srcTexture = api.createTexture(SamplerType::SAMPLER_2D, 1,
Handle<HwTexture> srcTexture = cleanup.add(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
@@ -207,9 +205,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 = api.createRenderTarget(
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR, 512, 512, 1, 0,
{srcTexture, 0, 0}, {}, {});
{srcTexture, 0, 0}, {}, {}));
TrianglePrimitive triangle(api);
@@ -249,11 +247,6 @@ 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.
+38 -47
View File
@@ -16,6 +16,7 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -63,20 +64,23 @@ namespace test {
class BasicStencilBufferTest : public BackendTest {
public:
Handle<HwSwapChain> swapChain;
ProgramHandle program;
Handle<HwSwapChain> mSwapChain;
ProgramHandle mProgram;
Cleanup mCleanup;
BasicStencilBufferTest() : mCleanup(getDriverApi()) {}
void SetUp() override {
auto& api = getDriverApi();
// Create a platform-specific SwapChain and make it current.
swapChain = createSwapChain();
api.makeCurrent(swapChain, swapChain);
mSwapChain = mCleanup.add(createSwapChain());
api.makeCurrent(mSwapChain, mSwapChain);
// Create a program.
ShaderGenerator shaderGen(vertex, fragment, sBackend, sIsMobilePlatform);
Program p = shaderGen.getProgram(api);
program = api.createProgram(std::move(p));
mProgram = mCleanup.add(api.createProgram(std::move(p)));
}
void RunTest(Handle<HwRenderTarget> renderTarget) {
@@ -103,13 +107,13 @@ public:
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState ps = {};
ps.program = program;
ps.program = mProgram;
ps.rasterState.colorWrite = false;
ps.rasterState.depthWrite = false;
ps.stencilState.stencilWrite = true;
ps.stencilState.front.stencilOpDepthStencilPass = StencilOperation::INCR;
api.makeCurrent(swapChain, swapChain);
api.makeCurrent(mSwapChain, mSwapChain);
api.beginFrame(0, 0, 0);
api.beginRenderPass(renderTarget, params);
@@ -129,29 +133,27 @@ public:
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
api.commit(swapChain);
api.commit(mSwapChain);
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 = 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(
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(
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
{{colorTexture}}, {}, {{stencilTexture}});
{{colorTexture}}, {}, {{stencilTexture}}));
RunTest(renderTarget);
@@ -159,23 +161,20 @@ 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 = 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(
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(
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
{{colorTexture}}, {depthStencilTexture}, {{depthStencilTexture}});
{{colorTexture}}, {depthStencilTexture}, {{depthStencilTexture}}));
RunTest(renderTarget);
@@ -183,30 +182,27 @@ 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 = 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(
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(
TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
{{}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA});
auto renderTarget1 = getDriverApi().createRenderTarget(
{{}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA}));
auto renderTarget1 = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
{{colorTexture}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA});
{{colorTexture}}, {depthStencilTextureMSAA}, {depthStencilTextureMSAA}));
api.startCapture(0);
@@ -230,13 +226,13 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState ps = {};
ps.program = program;
ps.program = mProgram;
ps.rasterState.colorWrite = false;
ps.rasterState.depthWrite = false;
ps.stencilState.stencilWrite = true;
ps.stencilState.front.stencilOpDepthStencilPass = StencilOperation::INCR;
api.makeCurrent(swapChain, swapChain);
api.makeCurrent(mSwapChain, mSwapChain);
api.beginFrame(0, 0, 0);
api.beginRenderPass(renderTarget0, params);
@@ -258,7 +254,7 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
api.commit(swapChain);
api.commit(mSwapChain);
api.stopCapture(0);
api.endFrame(0);
@@ -266,11 +262,6 @@ 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,6 +25,7 @@
#include <backend/BufferDescriptor.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <stdint.h>
#include <stddef.h>
@@ -90,9 +91,21 @@ 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.
-9
View File
@@ -317,15 +317,6 @@ public:
*/
size_t metalUploadBufferSizeBytes = 512 * 1024;
/**
* The action to take if a Drawable cannot be acquired.
*
* Each frame rendered requires a CAMetalDrawable texture, which is
* presented on-screen at the completion of each frame. These are
* limited and provided round-robin style by the system.
*/
bool metalDisablePanicOnDrawableFailure = false;
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL and Metal backends.
+7
View File
@@ -20,6 +20,7 @@
#include <utils/compiler.h>
#include <utils/PrivateImplementation.h>
#include <utils/CString.h>
#include <utils/StaticString.h>
#include <stddef.h>
@@ -61,11 +62,17 @@ 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,6 +26,7 @@
#include <backend/BufferDescriptor.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <stdint.h>
#include <stddef.h>
@@ -95,9 +96,21 @@ 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,6 +21,7 @@
#include <filament/Engine.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <math/mathfwd.h>
@@ -82,9 +83,21 @@ 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,6 +22,7 @@
#include <filament/Engine.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <math/mathfwd.h>
@@ -75,9 +76,21 @@ 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,6 +22,7 @@
#include <filament/RenderableManager.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <math/mathfwd.h>
@@ -81,9 +82,21 @@ 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,6 +23,7 @@
#include <backend/CallbackHandler.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <math/mat3.h>
@@ -150,9 +151,21 @@ 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.
*
+14 -1
View File
@@ -26,6 +26,7 @@
#include <backend/Platform.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <utility>
@@ -229,8 +230,20 @@ 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.
*/
Builder& name(const char* UTILS_NONNULL name, size_t len) noexcept;
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;
/**
* Creates an external texture. The content must be set using setExternalImage().
+13
View File
@@ -26,6 +26,7 @@
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/StaticString.h>
#include <stddef.h>
#include <stdint.h>
@@ -170,9 +171,21 @@ public:
* @param name A string to identify this VertexBuffer
* @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 VertexBuffer 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 VertexBuffer
* @return This Builder, for chaining calls.
*/
Builder& name(utils::StaticString const& name) noexcept;
/**
* Creates the VertexBuffer object and returns a pointer to it.
*
+13 -3
View File
@@ -2341,6 +2341,9 @@ void PostProcessManager::customResolvePrepareSubpass(DriverApi& driver, CustomRe
}
void PostProcessManager::customResolveSubpass(DriverApi& driver) noexcept {
bindPostProcessDescriptorSet(driver);
FEngine const& engine = mEngine;
Handle<HwRenderPrimitive> const& fullScreenRenderPrimitive = engine.getFullScreenRenderPrimitive();
auto const& material = getPostProcessMaterial("customResolveAsSubpass");
@@ -3036,7 +3039,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleBilinear(FrameGraph&
FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleSGSR1(FrameGraph& fg, bool sourceHasLuminance,
DynamicResolutionOptions dsrOptions, FrameGraphId<FrameGraphTexture> const input,
filament::Viewport const&, FrameGraphTexture::Descriptor const& outDesc) noexcept {
filament::Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc) noexcept {
struct QuadBlitData {
FrameGraphId<FrameGraphTexture> input;
@@ -3052,7 +3055,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleSGSR1(FrameGraph& fg,
.attachments = { .color = { data.output } },
.clearFlags = TargetBufferFlags::DEPTH });
},
[this, sourceHasLuminance](FrameGraphResources const& resources,
[this, vp, sourceHasLuminance](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
bindPostProcessDescriptorSet(driver);
@@ -3078,10 +3081,17 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleSGSR1(FrameGraph& fg,
});
mi->setParameter("viewport", float4{
float(vp.left) / inputDesc.width,
float(vp.bottom) / inputDesc.height,
float(vp.width) / inputDesc.width,
float(vp.height) / inputDesc.height
});
mi->setParameter("viewportInfo", float4{
1.0f / inputDesc.width,
1.0f / inputDesc.height,
float(inputDesc.width),
float(inputDesc.height),
float(inputDesc.height)
});
mi->commit(driver);
+5
View File
@@ -21,6 +21,7 @@
#include "FilamentAPI-impl.h"
#include <utils/CString.h>
#include <utils/StaticString.h>
namespace filament {
@@ -51,6 +52,10 @@ BufferObject::Builder& BufferObject::Builder::name(const char* name, size_t cons
return BuilderNameMixin::name(name, len);
}
BufferObject::Builder& BufferObject::Builder::name(utils::StaticString const& name) noexcept {
return BuilderNameMixin::name(name);
}
BufferObject* BufferObject::Builder::build(Engine& engine) {
return downcast(engine).createBufferObject(*this);
}
-2
View File
@@ -138,7 +138,6 @@ Engine* FEngine::create(Builder const& builder) {
.forceGLES2Context = instance->getConfig().forceGLES2Context,
.stereoscopicType = instance->getConfig().stereoscopicType,
.assertNativeWindowIsValid = instance->features.backend.opengl.assert_native_window_is_valid,
.metalDisablePanicOnDrawableFailure = instance->getConfig().metalDisablePanicOnDrawableFailure,
};
instance->mDriver = platform->createDriver(sharedContext, driverConfig);
@@ -734,7 +733,6 @@ int FEngine::loop() {
.forceGLES2Context = mConfig.forceGLES2Context,
.stereoscopicType = mConfig.stereoscopicType,
.assertNativeWindowIsValid = features.backend.opengl.assert_native_window_is_valid,
.metalDisablePanicOnDrawableFailure = mConfig.metalDisablePanicOnDrawableFailure,
};
mDriver = mPlatform->createDriver(mSharedGLContext, driverConfig);
+5
View File
@@ -21,6 +21,7 @@
#include "FilamentAPI-impl.h"
#include <utils/CString.h>
#include <utils/StaticString.h>
namespace filament {
@@ -51,6 +52,10 @@ IndexBuffer::Builder& IndexBuffer::Builder::name(const char* name, size_t const
return BuilderNameMixin::name(name, len);
}
IndexBuffer::Builder& IndexBuffer::Builder::name(utils::StaticString const& name) noexcept {
return BuilderNameMixin::name(name);
}
IndexBuffer* IndexBuffer::Builder::build(Engine& engine) {
return downcast(engine).createIndexBuffer(*this);
}
+6
View File
@@ -21,6 +21,8 @@
#include "FilamentAPI-impl.h"
#include <utils/StaticString.h>
#include <math/mat3.h>
#include <math/vec3.h>
@@ -54,6 +56,10 @@ InstanceBuffer::Builder& InstanceBuffer::Builder::name(const char* name, size_t
return BuilderNameMixin::name(name, len);
}
InstanceBuffer::Builder& InstanceBuffer::Builder::name(utils::StaticString const& name) noexcept {
return BuilderNameMixin::name(name);
}
InstanceBuffer* InstanceBuffer::Builder::build(Engine& engine) {
FILAMENT_CHECK_PRECONDITION(mImpl->mInstanceCount >= 1) << "instanceCount must be >= 1.";
FILAMENT_CHECK_PRECONDITION(mImpl->mInstanceCount <= engine.getMaxAutomaticInstances())
@@ -26,6 +26,7 @@
#include <math/norm.h>
#include <utils/CString.h>
#include <utils/StaticString.h>
namespace filament {
@@ -59,6 +60,10 @@ MorphTargetBuffer::Builder& MorphTargetBuffer::Builder::name(const char* name, s
return BuilderNameMixin::name(name, len);
}
MorphTargetBuffer::Builder& MorphTargetBuffer::Builder::name(utils::StaticString const& name) noexcept {
return BuilderNameMixin::name(name);
}
MorphTargetBuffer* MorphTargetBuffer::Builder::build(Engine& engine) {
return downcast(engine).createMorphTargetBuffer(*this);
}
+5
View File
@@ -26,6 +26,7 @@
#include <math/mat4.h>
#include <utils/CString.h>
#include <utils/StaticString.h>
#include <string.h>
#include <stddef.h>
@@ -64,6 +65,10 @@ SkinningBuffer::Builder& SkinningBuffer::Builder::name(const char* name, size_t
return BuilderNameMixin::name(name, len);
}
SkinningBuffer::Builder& SkinningBuffer::Builder::name(utils::StaticString const& name) noexcept {
return BuilderNameMixin::name(name);
}
SkinningBuffer* SkinningBuffer::Builder::build(Engine& engine) {
return downcast(engine).createSkinningBuffer(*this);
}
+5
View File
@@ -24,6 +24,7 @@
#include <backend/PixelBufferDescriptor.h>
#include <utils/CString.h>
#include <utils/StaticString.h>
#include <utils/Panic.h>
#include <filament/Stream.h>
@@ -65,6 +66,10 @@ Stream::Builder& Stream::Builder::name(const char* name, size_t const len) noexc
return BuilderNameMixin::name(name, len);
}
Stream::Builder& Stream::Builder::name(utils::StaticString const& name) noexcept {
return BuilderNameMixin::name(name);
}
Stream* Stream::Builder::build(Engine& engine) {
return downcast(engine).createStream(*this);
}
+6
View File
@@ -41,6 +41,8 @@
#include <utils/algorithm.h>
#include <utils/BitmaskEnum.h>
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/StaticString.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Panic.h>
@@ -152,6 +154,10 @@ Texture::Builder& Texture::Builder::name(const char* name, size_t const len) noe
return BuilderNameMixin::name(name, len);
}
Texture::Builder& Texture::Builder::name(StaticString const& name) noexcept {
return BuilderNameMixin::name(name);
}
Texture* Texture::Builder::build(Engine& engine) {
if (mImpl->mTarget != SamplerType::SAMPLER_EXTERNAL) {
FILAMENT_CHECK_PRECONDITION(Texture::isTextureFormatSupported(engine, mImpl->mFormat))
+5
View File
@@ -30,6 +30,7 @@
#include <utils/bitset.h>
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/StaticString.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/ostream.h>
@@ -152,6 +153,10 @@ VertexBuffer::Builder& VertexBuffer::Builder::name(const char* name, size_t cons
return BuilderNameMixin::name(name, len);
}
VertexBuffer::Builder& VertexBuffer::Builder::name(utils::StaticString const& name) noexcept {
return BuilderNameMixin::name(name);
}
VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
FILAMENT_CHECK_PRECONDITION(mImpl->mVertexCount > 0) << "vertexCount cannot be 0";
FILAMENT_CHECK_PRECONDITION(mImpl->mBufferCount > 0) << "bufferCount cannot be 0";
+5
View File
@@ -88,7 +88,12 @@ void minLeftMaxRight(inout vec4 a, inout vec4 b, inout vec4 c, inout vec4 d, ino
// - we can't use a function because offset must be a constant expression
// - we can't use line continuation because that's not supported on desktop
#ifdef TARGET_WEBGPU_ENVIRONMENT
//WebGPU can not handle texelFetchOffset without an error about "ConstOffset is only permitted...".
#define makeSample(ij, o) vec4(texelFetch(materialParams_dof, ij+o, 0).rgb, texelFetch(materialParams_alpha, ij+o, 0).r)
#else
#define makeSample(ij, o) vec4(texelFetchOffset(materialParams_dof, ij, 0, o).rgb, texelFetchOffset(materialParams_alpha, ij, 0, o).r)
#endif
vec4 median(ivec2 ij) {
vec4 v[9];
+9 -4
View File
@@ -10,6 +10,11 @@ material {
type : float4,
name : viewport,
precision: high
},
{
type : float4,
name : viewportInfo,
precision: high
}
],
variables : [
@@ -22,8 +27,8 @@ material {
vertex {
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
postProcess.normalizedUV = uvToRenderTargetUV(postProcess.normalizedUV);
postProcess.vertex.xy = postProcess.normalizedUV;
postProcess.vertex.xy = materialParams.viewport.xy + postProcess.normalizedUV * materialParams.viewport.zw;
postProcess.vertex.xy = uvToRenderTargetUV(postProcess.vertex.xy);
}
}
@@ -38,7 +43,7 @@ fragment {
#define gather textureGather
#else
vec4 gather(const mediump sampler2D color, highp vec2 p, const int comp) {
highp ivec2 i = ivec2(p * materialParams.viewport.zw - 0.5);
highp ivec2 i = ivec2(p * materialParams.viewportInfo.zw - 0.5);
vec4 d;
d[0] = texelFetchOffset(color, i, 0, ivec2(0, 1))[comp];
d[1] = texelFetchOffset(color, i, 0, ivec2(1, 1))[comp];
@@ -74,7 +79,7 @@ fragment {
#include "sgsr1_shader_mobile.fs"
void postProcess(inout PostProcessInputs postProcess) {
highp vec4 vp[1] = vec4[1]( materialParams.viewport );
highp vec4 vp[1] = vec4[1]( materialParams.viewportInfo );
postProcess.color = sgsr(materialParams_color, variable_vertex.xy, vp);
}
}
+13 -5
View File
@@ -31,13 +31,17 @@ if (WIN32)
set(SRCS ${SRCS} src/BlueGLCoreWindowsImpl.S)
endif()
elseif (APPLE AND NOT IOS)
set(SRCS ${SRCS} src/BlueGLDarwin.cpp)
if (FILAMENT_SUPPORTS_OSMESA)
set(SRCS ${SRCS} src/BlueGLOSMesa.cpp)
else()
set(SRCS ${SRCS} src/BlueGLDarwin.cpp)
endif()
set(SRCS ${SRCS} src/BlueGLCoreDarwinUniversalImpl.S)
elseif(LINUX)
if(FILAMENT_SUPPORTS_EGL_ON_LINUX)
if (FILAMENT_SUPPORTS_EGL_ON_LINUX)
set(SRCS ${SRCS} src/BlueGLLinuxEGL.cpp)
elseif(FILAMENT_SUPPORTS_OSMESA)
set(SRCS ${SRCS} src/BlueGLLinuxOSMesa.cpp)
elseif (FILAMENT_SUPPORTS_OSMESA)
set(SRCS ${SRCS} src/BlueGLOSMesa.cpp)
else()
set(SRCS ${SRCS} src/BlueGLLinux.cpp)
endif()
@@ -53,7 +57,11 @@ include_directories(${PUBLIC_HDR_DIR})
add_library(${TARGET} STATIC ${PUBLIC_HDRS} ${SRCS})
if(FILAMENT_SUPPORTS_OSMESA)
target_compile_options(${TARGET} PRIVATE -I${FILAMENT_OSMESA_PATH}/include/GL)
if (APPLE)
target_compile_options(${TARGET} PRIVATE -I${FILAMENT_OSMESA_PATH}/include)
else()
target_compile_options(${TARGET} PRIVATE -I${FILAMENT_OSMESA_PATH}/include/GL)
endif()
endif()
# specify where the public headers of this library are
@@ -17,42 +17,64 @@
#include <dlfcn.h>
#include <string.h>
#if defined(__linux__)
#include <osmesa.h>
// This is to ensure that linking during compilation will not fail even if
// OSMesaGetProcAddress is not linked.
__attribute__((weak)) OSMESAproc OSMesaGetProcAddress(char const*);
#elif defined(__APPLE__)
#include <GL/osmesa.h>
#endif // __linux__
#if defined(__linux__)
#endif
namespace bluegl {
namespace {
using ProcAddressFunc = void*(*)(char const* funcName);
}
// This is to ensure that linking during compilation will not fail even if
// OSMesaGetProcAddress is not linked.
__attribute__((weak)) OSMESAproc OSMesaGetProcAddress(char const*);
struct Driver {
ProcAddressFunc OSMesaGetProcAddress;
void* library;
} g_driver = {nullptr, nullptr};
bool initBinder() {
constexpr char const* libraryNames[] = {"libOSMesa.so", "libosmesa.so"};
static constexpr char const* libraryNames[] = {
#if defined(__linux__)
"libOSMesa.so",
"libosmesa.so",
#elif defined(__APPLE__)
"libOSMesa.dylib",
#endif
};
for (char const* name: libraryNames) {
g_driver.library = dlopen(name, RTLD_GLOBAL | RTLD_NOW);
if (g_driver.library) {
break;
}
}
if (!g_driver.library) {
// The library has been linked explicitly during compile.
g_driver.OSMesaGetProcAddress = (ProcAddressFunc) dlsym(RTLD_LOCAL, "OSMesaGetProcAddress");
} else {
if (g_driver.library) {
// Linking against a libosmesa.so.
g_driver.OSMesaGetProcAddress =
(ProcAddressFunc) dlsym(g_driver.library, "OSMesaGetProcAddress");
}
#if defined(__linux__)
else {
// If Filament was built as a dynamic library.
g_driver.OSMesaGetProcAddress = (ProcAddressFunc) dlsym(RTLD_LOCAL, "OSMesaGetProcAddress");
}
if (!g_driver.OSMesaGetProcAddress) {
// If statically linking OSMesa.
g_driver.OSMesaGetProcAddress = (ProcAddressFunc) OSMesaGetProcAddress;
}
#endif
return g_driver.OSMesaGetProcAddress;
}
@@ -28,7 +28,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 58;
static constexpr size_t MATERIAL_VERSION = 59;
/**
* Supported shading models
@@ -203,7 +203,7 @@ enum class ReflectionMode : uint8_t {
// can't really use std::underlying_type<AttributeIndex>::type because the driver takes a uint32_t
using AttributeBitset = utils::bitset32;
static constexpr size_t MATERIAL_PROPERTIES_COUNT = 29;
static constexpr size_t MATERIAL_PROPERTIES_COUNT = 30;
enum class Property : uint8_t {
BASE_COLOR, //!< float4, all shading models
ROUGHNESS, //!< float, lit shading models only
@@ -234,6 +234,7 @@ enum class Property : uint8_t {
BENT_NORMAL, //!< float3, all shading models only, except unlit
SPECULAR_FACTOR, //!< float, lit shading models only, except subsurface and cloth
SPECULAR_COLOR_FACTOR, //!< float3, lit shading models only, except subsurface and cloth
SHADOW_STRENGTH, //!< float, [0, 1] strength of shadows received by this material
// when adding new Properties, make sure to update MATERIAL_PROPERTIES_COUNT
};
@@ -217,12 +217,13 @@ public:
MaterialBuilder(MaterialBuilder&& rhs) noexcept = default;
MaterialBuilder& operator=(MaterialBuilder&& rhs) noexcept = default;
static constexpr size_t MATERIAL_VARIABLES_COUNT = 4;
static constexpr size_t MATERIAL_VARIABLES_COUNT = 5;
enum class Variable : uint8_t {
CUSTOM0,
CUSTOM1,
CUSTOM2,
CUSTOM3
CUSTOM3,
CUSTOM4, // CUSTOM4 is only available if the vertex attribute `color` is not required.
// when adding more variables, make sure to update MATERIAL_VARIABLES_COUNT
};
+2 -1
View File
@@ -49,7 +49,8 @@ std::unordered_map<std::string, Property> Enums::mStringToProperty = {
{ "microThickness", Property::MICRO_THICKNESS },
{ "bentNormal", Property::BENT_NORMAL },
{ "specularFactor", Property::SPECULAR_FACTOR },
{ "specularColorFactor", Property::SPECULAR_COLOR_FACTOR }
{ "specularColorFactor", Property::SPECULAR_COLOR_FACTOR },
{ "shadowStrength", Property::SHADOW_STRENGTH }
};
template <>
+111 -20
View File
@@ -518,28 +518,113 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl,
}
}
void GLSLPostProcessor::rebindImageSamplerForWGSL(std::vector<uint32_t>& spirv) {
constexpr size_t HEADER_SIZE = 5;
size_t const dataSize = spirv.size();
uint32_t* data = spirv.data();
struct Descriptor {
uint32_t binding = 0xFFFFFFFF;
bool fixed = false;
};
std::unordered_map<uint32_t, Descriptor> descriptors;
auto getDesc = [&](uint32_t var) -> Descriptor& {
using itertype = std::unordered_map<uint32_t, Descriptor>::iterator;
itertype itr;
if (itr = descriptors.find(var); itr == descriptors.end()) {
itr = descriptors.insert({var, {}}).first;
}
return itr->second;
};
auto pass = [&](uint32_t targetOp, std::function<void(uint32_t)> f) {
for (uint32_t cursor = HEADER_SIZE, cursorEnd = dataSize; cursor < cursorEnd;) {
uint32_t const firstWord = data[cursor];
uint32_t const wordCount = firstWord >> 16;
uint32_t const op = firstWord & 0x0000FFFF;
if (targetOp == op) {
f(cursor+1);
}
cursor += wordCount;
}
};
uint32_t samplerTypeId = 0;
// First we find the ID used to identify the sampler type and the image type.
pass(spv::Op::OpTypeSampler, [&](uint32_t pos) {
samplerTypeId = data[pos];
});
// Next we identify all the descriptors and record their set id and binding.
pass(spv::Op::OpDecorate, [&](uint32_t pos) {
uint32_t const type = data[pos + 1];
if (type == spv::Decoration::DecorationBinding) {
uint32_t const targetVar = data[pos];
uint32_t const binding = data[pos + 2];
auto& desc = getDesc(targetVar);
desc.binding = binding;
// Note these decorations do not need to be written to the output.
} else if (type == spv::Decoration::DecorationDescriptorSet) {
uint32_t const targetVar = data[pos];
uint32_t const set = data[pos + 2];
auto& desc = getDesc(targetVar);
}
});
// Offset samplers by 2n+1, and everything else by 2n
pass(spv::Op::OpLoad, [&](uint32_t pos) {
uint32_t const retType = data[pos];
uint32_t const targetVar = data[pos + 2];
auto& desc = getDesc(targetVar);
if (retType == samplerTypeId) {
if (!desc.fixed) {
//desc.binding = (desc.binding * 2) +1;
desc.binding = (desc.binding ) +100;
desc.fixed = true;
}
}
else {
if (!desc.fixed) {
//desc.binding *=2;
// desc.fixed = true;
}
}
});
// Write out the offsetted bindings
pass(spv::Op::OpDecorate, [&](uint32_t pos) {
uint32_t const type = data[pos + 1];
if (type == spv::Decoration::DecorationBinding) {
uint32_t const targetVar = data[pos];
auto const &desc = getDesc(targetVar);
data[pos + 2] = desc.binding;
}
});
}
bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
#if FILAMENT_SUPPORTS_WEBGPU
// We need to remove dead code for our variant-filter workaround for WebGPU to work
// This is especially relevant for removing the push constants that morphing uses when not disabled
spv::spirvbin_t remapper(0);
remapper.remap(*spirv, spv::spirvbin_base_t::DCE_ALL);
//We need to run some opt-passes at all times to transpile to WGSL
auto optimizer = createEmptyOptimizer();
optimizer->RegisterPass(CreateSplitCombinedImageSamplerPass());
optimizeSpirv(optimizer, *spirv);
//Currently no options we want to use
const tint::spirv::reader::Options readerOpts{};
//After splitting the image samplers, we need to remap the bindings to separate them.
rebindImageSamplerForWGSL(*spirv);
//Allow non-uniform derivitives due to our nested shaders. See https://github.com/gpuweb/gpuweb/issues/3479
const tint::spirv::reader::Options readerOpts{true};
tint::wgsl::writer::Options writerOpts{};
tint::Program tintRead = tint::spirv::reader::Read(*spirv, readerOpts);
if (tintRead.Diagnostics().ContainsErrors()) {
//TODO remove this if block once combined image sampler conversion works.
if (tintRead.Diagnostics().Str().rfind("error: WGSL does not support combined image-samplers") != std::string::npos) {
slog.w << "This tint reader error is currently ignored during WebGPU bringup: " << tintRead.Diagnostics().Str() << io::endl;
}
else {
slog.e << "Tint Reader Error: " << tintRead.Diagnostics().Str() << io::endl;
return false;
}
slog.e << "Tint Reader Error: " << tintRead.Diagnostics().Str() << io::endl;
return false;
}
tint::Result<tint::wgsl::writer::Output> wgslOut = tint::wgsl::writer::Generate(tintRead,writerOpts);
@@ -887,10 +972,8 @@ bool GLSLPostProcessor::fullOptimization(const TShader& tShader,
return true;
}
std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createOptimizer(
MaterialBuilder::Optimization optimization, Config const& config) {
std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createEmptyOptimizer() {
auto optimizer = std::make_shared<spvtools::Optimizer>(SPV_ENV_UNIVERSAL_1_3);
optimizer->SetMessageConsumer([](spv_message_level_t level,
const char* source, const spv_position_t& position, const char* message) {
if (!filterSpvOptimizerMessage(level)) {
@@ -899,6 +982,12 @@ std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createOptimizer(
slog.e << stringifySpvOptimizerMessage(level, source, position, message)
<< io::endl;
});
return optimizer;
}
std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createOptimizer(
MaterialBuilder::Optimization optimization, Config const& config) {
auto optimizer = createEmptyOptimizer();
if (optimization == MaterialBuilder::Optimization::SIZE) {
// When optimizing for size, we don't run the SPIR-V through any size optimization passes
@@ -923,7 +1012,7 @@ std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createOptimizer(
return optimizer;
}
void GLSLPostProcessor::optimizeSpirv(OptimizerPtr optimizer, SpirvBlob& spirv) const {
void GLSLPostProcessor::optimizeSpirv(OptimizerPtr optimizer, SpirvBlob& spirv) {
if (!optimizer->Run(spirv.data(), spirv.size(), &spirv)) {
slog.e << "SPIR-V optimizer pass failed" << io::endl;
return;
@@ -978,7 +1067,8 @@ void GLSLPostProcessor::registerPerformancePasses(Optimizer& optimizer, Config c
RegisterPass(CreateWrapOpKillPass());
RegisterPass(CreateDeadBranchElimPass());
RegisterPass(CreateMergeReturnPass(), MaterialBuilder::TargetApi::METAL);
RegisterPass(CreateInlineExhaustivePass());
//TODO Do not run inline pass without merge return pass until we sort out issues
RegisterPass(CreateInlineExhaustivePass(), MaterialBuilder::TargetApi::ALL & ~MaterialBuilder::TargetApi::WEBGPU);
RegisterPass(CreateAggressiveDCEPass());
RegisterPass(CreatePrivateToLocalPass());
RegisterPass(CreateLocalSingleBlockLoadStoreElimPass());
@@ -1022,7 +1112,8 @@ void GLSLPostProcessor::registerSizePasses(Optimizer& optimizer, Config const& c
RegisterPass(CreateWrapOpKillPass());
RegisterPass(CreateDeadBranchElimPass());
RegisterPass(CreateInlineExhaustivePass());
//TODO Do not run inline pass without merge return pass until we sort out issues
RegisterPass(CreateInlineExhaustivePass(), MaterialBuilder::TargetApi::ALL & ~MaterialBuilder::TargetApi::WEBGPU);
RegisterPass(CreateEliminateDeadFunctionsPass());
RegisterPass(CreatePrivateToLocalPass());
RegisterPass(CreateScalarReplacementPass(0));
+7 -1
View File
@@ -114,14 +114,20 @@ private:
* Retrieve an optimizer instance tuned for the given optimization level and shader configuration.
*/
using OptimizerPtr = std::shared_ptr<spvtools::Optimizer>;
static OptimizerPtr createOptimizer(
MaterialBuilder::Optimization optimization,
Config const& config);
static OptimizerPtr createEmptyOptimizer();
static void registerSizePasses(spvtools::Optimizer& optimizer, Config const& config);
static void registerPerformancePasses(spvtools::Optimizer& optimizer, Config const& config);
void optimizeSpirv(OptimizerPtr optimizer, SpirvBlob& spirv) const;
static void optimizeSpirv(OptimizerPtr optimizer, SpirvBlob &spirv);
static void rebindImageSamplerForWGSL(std::vector<uint32_t>& spirv);
void fixupClipDistance(SpirvBlob& spirv, GLSLPostProcessor::Config const& config) const;
+11
View File
@@ -252,6 +252,7 @@ MaterialBuilder& MaterialBuilder::variable(Variable v, const char* name) noexcep
case Variable::CUSTOM1:
case Variable::CUSTOM2:
case Variable::CUSTOM3:
case Variable::CUSTOM4:
assert(size_t(v) < MATERIAL_VARIABLES_COUNT);
mVariables[size_t(v)] = { CString(name), Precision::DEFAULT, false };
break;
@@ -266,6 +267,7 @@ MaterialBuilder& MaterialBuilder::variable(Variable v,
case Variable::CUSTOM1:
case Variable::CUSTOM2:
case Variable::CUSTOM3:
case Variable::CUSTOM4:
assert(size_t(v) < MATERIAL_VARIABLES_COUNT);
mVariables[size_t(v)] = { CString(name), precision, true };
break;
@@ -1254,6 +1256,15 @@ error:
OutputTarget::COLOR, Precision::DEFAULT, OutputType::FLOAT4, "color");
}
if (mMaterialDomain == MaterialDomain::SURFACE) {
if (mRequiredAttributes[VertexAttribute::COLOR] &&
!mVariables[int(Variable::CUSTOM4)].name.empty()) {
// both the color attribute and the custom4 variable are present, that's not supported
slog.e << "Error: when the 'color' attribute is required 'Variable::CUSTOM4' is not supported." << io::endl;
goto error;
}
}
// TODO: maybe check MaterialDomain::COMPUTE has outputs
// Resolve all the #include directives within user code.
@@ -937,6 +937,15 @@ utils::io::sstream& CodeGenerator::generateSpecializationConstant(utils::io::sst
std::string const constantString = std::visit(SpecializationConstantFormatter(), value);
static const char* types[] = { "int", "float", "bool" };
// Spec constants aren't fully supported in Tint,
// workaround until https://issues.chromium.org/issues/42250586 is resolved
if (mTargetApi == TargetApi::WEBGPU) {
out << "layout (constant_id = " << id << ") const "
<< types[value.index()] << " " << name << "_hack = " << constantString << ";\n"
<< types[value.index()] << " " << name << " = " << name << "_hack;\n";
return out;
}
if (mTargetLanguage == MaterialBuilderBase::TargetLanguage::SPIRV) {
out << "layout (constant_id = " << id << ") const "
<< types[value.index()] << " " << name << " = " << constantString << ";\n";
@@ -1218,6 +1227,7 @@ char const* CodeGenerator::getConstantName(MaterialBuilder::Property property) n
case Property::BENT_NORMAL: return "BENT_NORMAL";
case Property::SPECULAR_FACTOR: return "SPECULAR_FACTOR";
case Property::SPECULAR_COLOR_FACTOR: return "SPECULAR_COLOR_FACTOR";
case Property::SHADOW_STRENGTH: return "SHADOW_STRENGTH";
}
}
+61
View File
@@ -696,6 +696,25 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerBentNormal) {
EXPECT_TRUE(PropertyListsMatch(expected, properties));
}
TEST_F(MaterialCompiler, StaticCodeAnalyzerShadowStrength) {
std::string fragmentCode(R"(
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.shadowStrength = 0.1;
}
)");
std::string shaderCode = shaderWithAllProperties(*jobSystem, ShaderStage::FRAGMENT,
fragmentCode);
GLSLTools glslTools;
MaterialBuilder::PropertyList properties{ false };
glslTools.findProperties(ShaderStage::FRAGMENT, shaderCode, properties);
MaterialBuilder::PropertyList expected{ false };
expected[size_t(filamat::MaterialBuilder::Property::SHADOW_STRENGTH)] = true;
EXPECT_TRUE(PropertyListsMatch(expected, properties));
}
TEST_F(MaterialCompiler, StaticCodeAnalyzerOutputFactor) {
std::string fragmentCode(R"(
void material(inout MaterialInputs material) {
@@ -759,6 +778,48 @@ TEST_F(MaterialCompiler, Uv0AndUv1) {
EXPECT_TRUE(result.isValid());
}
TEST_F(MaterialCompiler, FiveCustomVariables) {
filamat::MaterialBuilder builder;
builder.variable(MaterialBuilder::Variable::CUSTOM0, "custom0");
builder.variable(MaterialBuilder::Variable::CUSTOM1, "custom1");
builder.variable(MaterialBuilder::Variable::CUSTOM2, "custom2");
builder.variable(MaterialBuilder::Variable::CUSTOM3, "custom3");
builder.variable(MaterialBuilder::Variable::CUSTOM4, "custom4");
filamat::Package result = builder.build(*jobSystem);
EXPECT_TRUE(result.isValid());
}
TEST_F(MaterialCompiler, FourCustomVariablesAndColorAttribute) {
filamat::MaterialBuilder builder;
builder.require(filament::VertexAttribute::COLOR);
builder.variable(MaterialBuilder::Variable::CUSTOM0, "custom0");
builder.variable(MaterialBuilder::Variable::CUSTOM1, "custom1");
builder.variable(MaterialBuilder::Variable::CUSTOM2, "custom2");
builder.variable(MaterialBuilder::Variable::CUSTOM3, "custom3");
filamat::Package result = builder.build(*jobSystem);
EXPECT_TRUE(result.isValid());
}
TEST_F(MaterialCompiler, FiveCustomVariablesAndColorAttributeFails) {
filamat::MaterialBuilder builder;
builder.require(filament::VertexAttribute::COLOR);
builder.variable(MaterialBuilder::Variable::CUSTOM0, "custom0");
builder.variable(MaterialBuilder::Variable::CUSTOM1, "custom1");
builder.variable(MaterialBuilder::Variable::CUSTOM2, "custom2");
builder.variable(MaterialBuilder::Variable::CUSTOM3, "custom3");
builder.variable(MaterialBuilder::Variable::CUSTOM4, "custom4");
filamat::Package result = builder.build(*jobSystem);
EXPECT_FALSE(result.isValid());
}
TEST_F(MaterialCompiler, CustomVariable4AndColorAttributeFails) {
filamat::MaterialBuilder builder;
builder.require(filament::VertexAttribute::COLOR);
builder.variable(MaterialBuilder::Variable::CUSTOM4, "custom4");
filamat::Package result = builder.build(*jobSystem);
EXPECT_FALSE(result.isValid());
}
TEST_F(MaterialCompiler, Arrays) {
filamat::MaterialBuilder builder;
+1
View File
@@ -37,6 +37,7 @@ set(DIST_HDRS
${PUBLIC_HDR_DIR}/${TARGET}/PrivateImplementation-impl.h
${PUBLIC_HDR_DIR}/${TARGET}/SingleInstanceComponentManager.h
${PUBLIC_HDR_DIR}/${TARGET}/Slice.h
${PUBLIC_HDR_DIR}/${TARGET}/StaticString.h
${PUBLIC_HDR_DIR}/${TARGET}/StructureOfArrays.h
${PUBLIC_HDR_DIR}/${TARGET}/Systrace.h
${PUBLIC_HDR_DIR}/${TARGET}/sstream.h
+6 -7
View File
@@ -22,6 +22,8 @@
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <string_view>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
@@ -197,16 +199,13 @@ private:
};
int compare(const CString& rhs) const noexcept {
size_type const lhs_size = size();
size_type const rhs_size = rhs.size();
if (lhs_size < rhs_size) return -1;
if (lhs_size > rhs_size) return 1;
return strncmp(data(), rhs.data(), size());
auto const l = std::string_view{data(), size()};
auto const r = std::string_view{rhs.data(), rhs.size()};
return l.compare(r);
}
friend bool operator==(CString const& lhs, CString const& rhs) noexcept {
return (lhs.data() == rhs.data()) ||
((lhs.size() == rhs.size()) && !strncmp(lhs.data(), rhs.data(), lhs.size()));
return lhs.compare(rhs) == 0;
}
friend bool operator!=(CString const& lhs, CString const& rhs) noexcept {
return !(lhs == rhs);
+108
View File
@@ -0,0 +1,108 @@
/*
* 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_UTILS_STATICSTRING_H
#define TNT_UTILS_STATICSTRING_H
#include <type_traits>
#include <string_view>
#include <stddef.h>
namespace utils {
/**
* @brief A lightweight string class that stores a pointer to a string literal and its size, without dynamic allocation.
*
* This class is designed to efficiently represent string literals. It does not allocate any memory
* and instead relies on the compiler to manage the memory of the string literals.
*/
class UTILS_PUBLIC StaticString {
public:
using value_type = std::string_view::value_type;
using const_pointer = std::string_view::const_pointer;
using const_reference = std::string_view::const_reference;
using size_type = std::string_view::size_type;
using const_iterator = std::string_view::const_iterator;
// Constructor from string literal
template <size_t M>
constexpr StaticString(const char (&str)[M]) noexcept : mString(str, M - 1) {} // NOLINT(*-explicit-constructor)
constexpr StaticString() noexcept = default;
constexpr const_pointer c_str() const noexcept { return mString.data(); }
constexpr const_pointer data() const noexcept { return mString.data(); }
constexpr size_type size() const noexcept { return mString.size(); }
constexpr size_type length() const noexcept { return mString.size(); }
constexpr bool empty() const noexcept { return mString.empty(); }
constexpr const_iterator begin() const noexcept { return mString.begin(); }
constexpr const_iterator end() const noexcept { return mString.end(); }
constexpr const_iterator cbegin() const noexcept { return mString.begin(); }
constexpr const_iterator cend() const noexcept { return mString.end(); }
constexpr const_reference operator[](size_type const pos) const noexcept {
return mString[pos];
}
constexpr const_reference at(size_type pos) const {
return mString[pos];
}
constexpr const_reference front() const noexcept {
return mString.front();
}
constexpr const_reference back() const noexcept {
return mString.back();
}
constexpr int compare(const StaticString& rhs) const noexcept {
return mString.compare(rhs.mString);
}
private:
std::string_view mString;
friend constexpr bool operator==(const StaticString& lhs, const StaticString& rhs) noexcept {
return lhs.mString == rhs.mString;
}
friend constexpr bool operator!=(const StaticString& lhs, const StaticString& rhs) noexcept {
return lhs.mString != rhs.mString;
}
friend constexpr bool operator<(const StaticString& lhs, const StaticString& rhs) noexcept {
return lhs.mString < rhs.mString;
}
friend constexpr bool operator>(const StaticString& lhs, const StaticString& rhs) noexcept {
return lhs.mString > rhs.mString;
}
friend constexpr bool operator<=(const StaticString& lhs, const StaticString& rhs) noexcept {
return lhs.mString <= rhs.mString;
}
friend constexpr bool operator>=(const StaticString& lhs, const StaticString& rhs) noexcept {
return lhs.mString >= rhs.mString;
}
};
} // namespace utils
#endif // TNT_UTILS_STATICSTRING_H
+6
View File
@@ -20,6 +20,9 @@ struct PostProcessVertexInputs {
#ifdef VARIABLE_CUSTOM3
vec4 VARIABLE_CUSTOM3;
#endif
#ifdef VARIABLE_CUSTOM4
vec4 VARIABLE_CUSTOM4;
#endif
};
void initPostProcessMaterialVertex(out PostProcessVertexInputs inputs) {
@@ -35,4 +38,7 @@ void initPostProcessMaterialVertex(out PostProcessVertexInputs inputs) {
#ifdef VARIABLE_CUSTOM3
inputs.VARIABLE_CUSTOM3 = vec4(0.0);
#endif
#ifdef VARIABLE_CUSTOM4
inputs.VARIABLE_CUSTOM4 = vec4(0.0);
#endif
}
+3
View File
@@ -39,6 +39,9 @@ void main() {
#if defined(VARIABLE_CUSTOM3)
VARIABLE_CUSTOM_AT3 = inputs.VARIABLE_CUSTOM3;
#endif
#if defined(VARIABLE_CUSTOM4)
VARIABLE_CUSTOM_AT4 = inputs.VARIABLE_CUSTOM4;
#endif
// some PowerVR drivers crash when gl_Position is written more than once
gl_Position = inputs.position;

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