Compare commits

..
Author SHA1 Message Date
Benjamin Doherty 15424c85b5 Metal: use atomic for buffer tracking 2025-09-19 13:41:37 -07:00
Powei Feng 0dd1d3211b backend: fix scissor viewport region test (#9220)
Make sure that the rendertarget dimensions matches the texture
dimensions. Update the expected image for the test.

Also fixed usage, rendertarget parameters to make sure vk does
not have any validation errors.
2025-09-17 21:58:50 +00:00
Filament Bot 33249c6eb8 [automated] Updating /docs due to commit 59f19ae
Full commit hash is 59f19aee0e

DOCS_ALLOW_DIRECT_EDITS
2025-09-16 17:44:15 +00:00
Ben Doherty 59f19aee0e Add documentation and script to publish to Maven Central (#9216) 2025-09-16 13:41:06 -04:00
Sungun Park 06c8fc39c2 Update MATERIAL_VERSION to 64 2025-09-16 10:08:23 -07:00
Powei Feng a36ad004b7 renderdiff: add ability to diff arbitrary rendered images (#9211)
Add a way to show the difference between two rendererd image.
This is a convenience feature to see the differences between
backends.

Refactor the ExpandedFailedResult to use this new view.
2025-09-16 00:27:42 +00:00
Ivan Mikhalchuk 6ac5f26bb5 fix UB in AtomicFreeList::push and FreeList::push: access to non-static data member without proper lifetime management 2025-09-15 08:50:02 -07:00
Powei Feng bce8320f06 vk: make sure default render target is not removed (#9213)
We need to have an reference to the default render target for
the duration of the backend lifetime.  So even if the client
tries to destroy the default render target, we don't actually
destroy it.

Note that it will be destroyed on driver shutdown.
2025-09-12 22:52:21 +00:00
Powei Feng f3a4e21fe5 backend: fix blit tests for vulkan
- The rendertarget layer count should be >=1.
- Add read pixels usage bit
2025-09-12 15:27:49 -07:00
Powei Feng 6838d8602e github: fix caching for vulkansdk and mac brew (#9203)
We should be able to use ~ (tilde) to indicate the user root
directory.

Before the cache didn't work because these paths weren't found.
2025-09-12 17:54:52 +00:00
Powei Feng fe9033c7db backend: fix vulkan test with readPixels (#9202)
filament/backend/test/Workarounds.h has a specific workaround
with regards to readPixels and texture usage.  Originally this
was introduced for the WebGPU backend. This is needed
for the vulkan backend as well.

Also simplify BufferUpdatesTest.BufferObjectUpdateWithOffset
 - no need to call flush() or executeCommands(), these are
   called when the test terminates.
 - layerCount for a rendertarget should be at least 1.
2025-09-12 17:25:28 +00:00
Powei Feng 480b0878d7 vk: fix validation warnings (#9207)
- Remove extensions that have been promoted to 1.1
- Use vkCreateMetalSurfaceEXT instead of the deprecated
   vkCreateMacOSSurfaceMVK
2025-09-11 23:59:25 +00:00
Sungun Park 57d1a6a165 Add MSAA swapchain support (#9196)
Users can now create a swapchain with MSAA support, which enables direct
rendering to a multi-sampled swapchain image and automatic resolution.

This is only supported by EGL(Android). Other GL platforms (GLX, WGL,
etc) don't support it because the swapchain MSAA settings must be
configured before window creation.

Support for other backends will follow.

BUGS=[433327615]
2025-09-11 21:29:06 +00:00
Ben Doherty fe81493052 Fix g3 build (#9209) 2025-09-11 16:28:03 -04:00
Mathias Agopian 9c52ec32ff hybrid instancing improvements
Hybrid (InstanceBuffer based) instancing no longer needs a 
descriptor-set per object because we now use the same UBO for all
objects, so the "shared" per-object descriptor-set can be used.

This also implies that InstanceBuffer doesn't need to track the UBO
handle anymore, only its offset in the buffer.
2025-09-11 12:13:47 -07:00
Mathias Agopian 5cf091220c Fix descriptor set update bug
the buffer offset in descriptors wasn't updated if it was the only 
value to change when setting the descriptor.


FIXES=[443991507]
2025-09-11 12:13:47 -07:00
Powei Feng f6892a111c vk: fix VulkanDriver::finish() (#9199)
VulkanDriver::finish() seems like it is waiting for all the
commands to be processed, but in reality, the present calls
are not waited on (because commands->wait() only waited on
the command buffers).

Here we just call vkQueueWaitIdle to address that case.

This fixes a validation error that occurs when running the
backend test BackendTest.MissingRequiredAttributes.
2025-09-11 19:02:07 +00:00
Sungun Park 63f877a68e Fix: Correctly handle unsupported SwapChain flags (#9208)
FSwapChain initialization logic would filter out unsupported flags but
would incorrectly use the original, unfiltered flags when creating the
swap chain.

This commit ensures the filtered flags are passed to the driver. It also
adds the warning to log which ones are removed.

BUGS=[433327615]
2025-09-11 18:24:02 +00:00
Powei Feng 3d8fb3bcea vk: fix blitDEPRECATED implementation (#9201)
blitDEPRECATED can also be used in the context of a multisampled
texture resolve. Fix that case to use the resolve() path in the
VulkanBlitter.

Also fixed backend test BlitTest.ColorResolve to add the correct
usage bit (otherwise it'd have validation error).

FIXES=417229577
2025-09-11 16:48:50 +00:00
Anish Goyal e218eca6b0 Create exportable Syncs (fences) (#9110)
* Create exportable fences on Android

Also - create a conversion method for the driver api so that a fence can
be converted to an external handle, where available.

* Create a frontend API for external fences

Fences will be called 'Sync' on the frontend, and come with a method for
conversion to an external sync. This will typically be a file
descriptor, for use in posix systems.

* Refactor Sync to rely on opaque handles

Also - push the callback invocations into the backend, such that they
are scheduled on a worker thread, and not run on the Filament backend
thread.

* Address PR comment - minor refactor

Clear vectors after they've been consumed, and update null checks by
removing explicit reference to nullptr.

* Remove convertSyncToFd from non-android

Other platforms might have other handle types.

Also - remove unused header include

* Add logging for convert to fd

Also, remove duplicate includes

* Address PR - change fd type

As per request by rafadavi@, changed to match the Android spec for file
descriptor type, which is `int`.

* Address PR - remove vtable from sync

Also - rename derived sync classes

* Address comment - make SyncCallbackData private

It's technically implementation-specific, even though it currently
happens to be the same. Separate this into the different backends.

* Add stub for metal backend for sync

* Add stub impl for Sync in WebGPU
2025-09-11 09:02:56 -07:00
Powei Feng 17b8b9ff65 backend: fix MRT test's layercount (#9200)
layercount should be at least 1. This was causing a validation
error for the vk backend when running the test.
2025-09-10 15:34:15 -07:00
Powei Feng 49b8a17b1f 3p: remove spirv-tools targets for binaries and shared libs (#9198)
We need to modify spirv-tools existing CMakeLists.txt to make
sure that its binaries and libs are not included in the filament
installed output.

This change was present before, but overwritten when spirv-tools
updated (wrt the WebGPU project).

In addition to modifying the CMakeLists.txt, we add a script to
carry out the changes automatically when running the update script
in the future.

Fixes #9168
2025-09-10 20:01:42 +00:00
rafadevai f89c105941 VK: Fix leak in VulkanExternalImageManager (#9194)
In the cases of a descriptor set being updated
with a external sampled texture but that descriptor
set doesn't get used later on for rendering.

Their corresponding entry in VulkanExternalImageManager::mSetBindings
will never get removed until the application is terminated, causing
performance issues or a fatal crash if run long enough.

The expectation is that the amount of bindings with
external samplers is low or none, so this won't cause
any regression when not using external samplers.
2025-09-10 17:08:14 +00:00
Filament Bot 3d9da8ab3b [automated] Updating /docs due to commit da315ef
Full commit hash is da315ef9fe

DOCS_ALLOW_DIRECT_EDITS
2025-09-10 00:19:44 +00:00
Benjamin Doherty da315ef9fe Release Filament 1.65.0 2025-09-09 17:15:42 -07:00
Powei Feng c5a7c39c8a Remove unecessary assert on texture format + mipmappable (#9193)
The backend call `isTextureFormatMipmappable` indicates whether
a format can be used to generate bitmap through graphics API.

(The issue is that previously there was a check to validate
 whether a format is mipmappable and levels > 1. These two
 conditions do not need to imply one another.)
2025-09-08 17:14:32 -07:00
Yein Jo ff4be0be48 Lift the logic to resolve inclues in shader to MaterialCompiler
- Move the file check & dir resolution to MaterialCompiler
- Move DirIncluder back to matc
FIXES=440579712
2025-09-08 10:37:16 -07:00
Mathias Agopian 88e4ddeec7 View::setCamera() should accept nullptr
FIXES=[441757817]
2025-09-08 07:56:47 -07:00
Filament Bot 220119d855 [automated] Updating /docs due to commit 81a51ae
Full commit hash is 81a51ae7e7

DOCS_ALLOW_DIRECT_EDITS
2025-09-08 06:53:30 +00:00
Mathias Agopian c4f083cfba matc and MaterialBuilder workaround options NONE by default
The workaround option is now set to none by default. Use

```
matc -Wall
```

if you want to restore the previous behavior.

Current material workarounds:
- remove MergeReturnPass (except on Metal)
- remove SimplificationPass (except on Metal)

There are reason to believe these workarounds are no longer needed,
due to changes in spirv-opt.

It's possible that older devices might still need them.

These workaround affect *all* devices, this is why it is important
to disable them if not needed.
2025-09-07 23:52:14 -07:00
Sungun Park 81a51ae7e7 Update material doc for new APIs (#9191) 2025-09-07 23:47:28 -07:00
Mathias Agopian 4089484b46 add InstanceBuffer::getLocalTransform(size_t)
FIXES=[442918117]
2025-09-05 09:51:06 -07:00
Mathias Agopian 26ceaebac6 add RenderManager::getInstanceCount()
FIXES=[442916683]
2025-09-05 09:51:06 -07:00
Yrom c72ddfcf7a backend: use VirtualAlloc for creating soft CircularBuffer on Windows 2025-09-04 13:08:37 -07:00
Sungun Park b31a42a1d0 Improve getEyeFromViewMatrix() (#9184)
Make the size of PerViewUib 2KiB again.

Add overloading functions for getEyeFromViewMatrix() and
getClipFromWorldMatrix().

BUGS=[441127971]
2025-09-04 18:58:14 +00:00
Mathias Agopian 76e36cd87b try to cleanup android libraries cmake build
- make as many library deps private as possible
- try to keep the same structure between the CMakeList.txt files
- for gltfio use libgltfio_core.a
2025-09-04 11:28:28 -07:00
Mathias Agopian 60da554732 draco: fix c++20 warnings 2025-09-04 11:27:47 -07:00
Mathias Agopian 5613663686 create an update script for draco and update readme
note that updating to a recent version is still difficult because
of makefile changes. we can do that later.
2025-09-04 11:27:47 -07:00
Mathias Agopian 57e308b75f fix linear fog parameters calculation
FIXES=[442852042]
2025-09-04 11:27:27 -07:00
Mathias Agopian d07c15dfc6 zbloat: modernize script
The script uses os.system to execute external commands like nm and 
objdump. Instead we use the subprocess module, which is the recommended 
approach in modern Python. 
Properly handle potential errors if nm or objdump fail.
2025-09-04 08:37:18 -07:00
Mathias Agopian 1422116242 zbloat: fix warnings 2025-09-04 08:37:18 -07:00
Mathias Agopian 28fcbe2b77 zbloat: add support for .a 2025-09-04 08:37:18 -07:00
Ben Doherty 2535d1b5d9 Implement setFrameScheduledCallback on other backends (#9177) 2025-09-04 11:09:26 -04:00
Mathias Agopian 4c91d48ed3 Revert "use fmin/fmax for floating-point"
This reverts commit a032190b24.
2025-09-04 00:08:30 -07:00
Mathias Agopian a032190b24 use fmin/fmax for floating-point
On ARM this generates the fmin/fmax instruction, which avoids
branches.
2025-09-03 22:30:09 -07:00
Mathias Agopian 639cdc3e5c add option to disable perfetto on android
The new cmake FILAMENT_ENABLE_PERFETTO must be set to enable 
perfetto traces on Android. It is disabled by default on release
builds, enabled otherwise.

The reason for this is that the perfetto SDK adds about 800K of code
to the library. The text section goes from 2.2 to 1.4 MB
2025-09-03 16:16:46 -07:00
Sungun Park b0f8e1be7a Add getEyeFromViewMatrix() for vertex shader (#9175)
This new method, intended for the vertex shader, returns a matrix that
transforms vertices from view space (also known as head space) to the
current eye space.

BUGS=[441127971]
2025-09-03 18:05:34 +00:00
Ben Doherty 3bd6412780 Remove the setDebugTag API in favor of tagged create API (#9011) 2025-09-03 11:14:33 -04:00
Powei Feng 9fad497fa0 Enable c++20 for Android and iOS (#9150)
- Fix Android cmake files
- Change iOS samples to use c++20
2025-09-03 00:28:14 +00:00
Doris Wu ae5ebda397 Get actual backend type from the engine (#9171) 2025-09-02 22:27:35 +00:00
Ben Doherty 58dc6715c9 iOS: avoid use of typeof (#9173) 2025-09-02 19:24:22 +00:00
Mathias Agopian d3ff60af21 remove all uses of std::string in filament
- libbackend (except webgpu)
- libfilament
- libutils

std::string generates a lot of code bloat, we use CString instead.

We also update CString to be more compatible with std::string's api.
2025-09-02 11:47:58 -07:00
Filament Bot 1c2fe0ce39 [automated] Updating /docs due to commit f875dc2
Full commit hash is f875dc2c08

DOCS_ALLOW_DIRECT_EDITS
2025-09-02 18:33:11 +00:00
Powei Feng f875dc2c08 Release Filament 1.64.1 2025-09-02 11:17:37 -07:00
Powei Feng 19e1287392 Fix minor typo and styling issues (#9172) 2025-09-02 18:11:09 +00:00
Powei Feng 7d1a09773e vk: fix readpixels validation error (#9155)
The semaphore stages between command buffer didn't consider the
data buffers (UBOS, vertices, indices) being shared across command
buffers

BUG=436853814
2025-09-02 17:44:35 +00:00
Mathias Agopian 05a6198443 froxelization optimizations
- by limiting the maximum number of point light to 255 instead of 256,
  we can simplify some loops.

- hint the compiler that certain loop counters are multiple of 16 and 
non 0, which helps vectorizing.
2025-08-29 14:46:14 -07:00
Mathias Agopian 00bd30c21b zLightNear and zLightFar are now clamped
To avoid unexpected results, View::setDynamicLightingOptions() now
clamps the parameters between the camera's near and far plane.
2025-08-29 14:46:14 -07:00
Mathias Agopian 88fa99d782 The Record buffer is now dynamically sized.
The record buffer is were we store the light indices, its maximum
size is 65536 entries, but is often limited to 16384 on about 30%
of Android devices because of the 16KB UBO limit.

This PR makes the record buffer use up to 64K entries when allowed
but the h/w.

The record buffer can be a limiting factor when many lights are used
with many froxels. With the minimum of 4096 froxels and a 16KB record 
buffer we get an average of 4 lights per froxel. With our maximum of
8192 froxels and a 64KB record buffer we get 8 lights in average per
froxel.
2025-08-29 14:46:14 -07:00
Mathias Agopian b57fb8cc13 Missing includes and small cleanups 2025-08-29 14:46:14 -07:00
Mathias Agopian 9f3c0ec8ef Update all per-object/instance buffers all at once
FIXES=[433807402]
2025-08-29 14:45:36 -07:00
Mathias Agopian 6d0a47b37c add concept of offset to instancebuffer offset 2025-08-29 14:45:36 -07:00
Mathias Agopian 39bf0f0a71 move FScene::updateUBO() back to FView
it really didn't belong to FScene
2025-08-29 14:45:36 -07:00
Filament Bot a7894b5876 [automated] Updating /docs due to commit 44ed67b
Full commit hash is 44ed67bfb6

DOCS_ALLOW_DIRECT_EDITS
2025-08-29 21:22:42 +00:00
Powei Feng 44ed67bfb6 docs add performance analysis page 2025-08-29 14:19:28 -07:00
Andy Hovingh 1765f1421d additional performance analysis documentation, namely on locking GPU frequency 2025-08-29 14:26:44 -05:00
Juan Caldas 4a4c4b276f Remove temp gltf sample (#9166) 2025-08-29 18:50:26 +00:00
FireBanana 8c985607b8 Remove extra tab in PassNode 2025-08-29 11:04:13 -07:00
Juan Caldas 090941c35a webgpu: Remove temporary file (#9165) 2025-08-29 18:00:41 +00:00
Andy Hovingh 7e6d6f6635 webgpu: fix linux build with added unit tests 2025-08-29 12:39:35 -05:00
Juan Caldas a695170193 Add missing attributes to sample (#9160) 2025-08-29 12:52:12 +00:00
rafadevai eed71107c9 GL: Fix memory leak for external texture with non external sampler (#9159)
In EGLAndroid, external textures imported from an AHB
that dont use a SAMPLER_EXTERNAL are not properly destroyed,
causing a memory leak that eventually leads to an OOM.
2025-08-29 01:30:27 +00:00
Juan Caldas 74ba3eb00c webgpu: Support skinning on WebGPU (#9137)
BUGS = [436886048]
2025-08-29 00:24:14 +00:00
Powei Feng 088900e6e0 Fix bones indices and weights sampler + descriptor set (#9154)
In some places, this sampler was incorrectly assumed to be
sampler2darray, but it should be just a sampler2d.  Additionally,
sampling from it should produce unfiltered values - this has been
changed accordingly.
2025-08-28 14:05:16 -07:00
Syed Idris Shah 60ab596e39 wgpu: fix the deadlock in finish call for gltf_viewer
gltf_viewer on android calls finish before creating any resources.
This causes a hang on OnSubmittedWorkDone for the work that was never
there. Put a check in Driver::finish call to avoid the deadlock.
2025-08-28 14:42:44 -04:00
Sungun Park e03b077e9b fix: Make ZstdHelper compatible with UBSan (#9152)
In ZstdHelper::isCompressed call, `src` may not be aligned to 4 bytes,
which is violating the alignment requirement of
`UndefinedBehaviorSanitizer: misaligned-pointer-use`, thereby resuling
in a runtime failure with UBSan enabled. So reconstruct the 32-bit
integer as a workaround.
2025-08-28 18:10:09 +00:00
Juan CaldasandAndy Hovingh f5418cb866 webgpu: Handle stacked swizzles (#9149)
BUGS = [433944896]

---------

Co-authored-by: Andy Hovingh <6198728+AndyHovingh@users.noreply.github.com>
2025-08-28 17:12:44 +00:00
Syed Idris Shah 9b75b18426 wgpu: Fix linux build
webgpu build on linux was broken because of 45fcea101f.
Resolve the symbols conflict between webgpu and X11.
2025-08-28 12:06:08 -04:00
Powei Feng 8f5f007e83 vk: fix rendertarget uninitialized fields (#9153) 2025-08-27 13:38:47 -07:00
247 changed files with 5370 additions and 11926 deletions
+4 -1
View File
@@ -8,7 +8,7 @@ runs:
uses: actions/cache@v3
id: cache-vulkan-sdk
with:
path: ${{ runner.homedir }}/VulkanSDK
path: ~/VulkanSDK
key: vulkansdk-${{ env.GITHUB_VULKANSDK_VERSION }}-2-${{ runner.os }}
- name: Download Vulkan SDK
if: steps.cache-vulkan-sdk.outputs.cache-hit != 'true'
@@ -21,6 +21,9 @@ runs:
run: |
source ${{ github.workspace }}/build/common/get-vulkan-sdk.sh
unpack_vulkan_installer
shell: bash
- name: Run Vulkan SDK setup
run: |
pushd .
cd ~/VulkanSDK/${GITHUB_VULKANSDK_VERSION}
sudo ./install_vulkan.py
+1 -1
View File
@@ -14,7 +14,7 @@ runs:
id: brew-cache
uses: actions/cache@v4 # Use a specific version
with:
path: $HOME/Library/Caches/Homebrew
path: ~/Library/Caches/Homebrew
key: ${{ runner.os }}-brew-20250424
- name: Install Mac Prerequisites
shell: bash
+10 -1
View File
@@ -532,6 +532,15 @@ if (FILAMENT_SUPPORTS_OPENGL)
add_definitions(-DFILAMENT_SUPPORTS_OPENGL)
endif()
if (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
option(FILAMENT_ENABLE_PERFETTO "Enable perfetto SDK on Android" OFF)
else()
option(FILAMENT_ENABLE_PERFETTO "Enable perfetto SDK on Android" ON)
endif()
if (FILAMENT_ENABLE_PERFETTO)
add_definitions(-DFILAMENT_ENABLE_PERFETTO)
endif()
# By default, build with Vulkan support on desktop platforms, although clients must request to use
# it at run time.
if (WIN32 OR WEBGL OR IOS)
@@ -633,7 +642,7 @@ endif()
# With WebGPU, Tint does not support ClipDistance which is used in Stereo. Mentioned in comment
# https://github.com/google/dawn/blob/855d17b08abdf02f9142bf5a8f14d0ea088810a4/src/tint/lang/spirv/reader/ast_parser/function.cc#L4434
if (FILAMENT_SUPPORTS_WEBGPU)
set(MATC_API_FLAGS ${MATC_API_FLAGS} -a webgpu --variant-filter=skinning,stereo)
set(MATC_API_FLAGS ${MATC_API_FLAGS} -a webgpu --variant-filter=stereo)
endif()
# Disable ESSL 1.0 code generation.
-3
View File
@@ -7,6 +7,3 @@ We are chaning the way Vulkan buffers are handled. We need to switch over to a m
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- Rename `sampler` parameter `unfilterable` to `filterable` [⚠️ **New Material Version**]
- materials: new mutable specialization constants feature. See the [materials documentation](https://google.github.io/filament/Materials.html) for details. [⚠️ **New Material Version**]
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.64.0'
implementation 'com.google.android.filament:filament-android:1.65.0'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.64.0'
pod 'Filament', '~> 1.65.0'
```
## Documentation
+12
View File
@@ -7,6 +7,18 @@ 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.65.1
- `setFrameScheduledCallback` now works on all backends (frame presentation scheduling is still only
available on Metal). Non-Metal backends can use the callback to be notified when Filament has
finished processing a frame on the CPU.
- materials: added `getEyeFromViewMatrix()` for vertex shader [⚠️ **Recompile Materials**]
- matc: make `--workarounds=none` the default [**Recompile Materials to take effect**]
## v1.65.0
lighting: the intermediate froxel record buffer is now dynamically sized [⚠️ **New Material Version**]
## v1.64.1
- Update CMake minimum version to 3.22.1
+2
View File
@@ -178,6 +178,8 @@ nexusPublishing {
repositories {
sonatype {
stagingProfileId = '9a75a224a4f17b'
nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/"))
snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/"))
}
}
}
@@ -146,7 +146,7 @@ abstract class MaterialCompiler extends TaskWithBinary {
.gradleProperty("com.google.android.filament.include-webgpu")
.forUseAtConfigurationTime().present
if (include_webgpu) {
matcArgs += ['-a', 'webgpu', '--variant-filter=skinning,stereo']
matcArgs += ['-a', 'webgpu', '--variant-filter=stereo']
}
def mat_no_opt = providers
+2
View File
@@ -1,6 +1,8 @@
cmake_minimum_required(VERSION 3.19)
project(filamat-android)
set(CMAKE_CXX_STANDARD 20)
option(FILAMENT_ENABLE_MATDBG "Enables Material debugger" OFF)
set(FILAMENT_DIR ${FILAMENT_DIST_DIR})
+19 -17
View File
@@ -7,6 +7,8 @@ option(FILAMENT_ENABLE_MATDBG "Enables Material debugger" OFF)
option(FILAMENT_DISABLE_MATOPT "Disables material optimizations" OFF)
option(FILAMENT_SUPPORTS_WEBGPU "Enables WebGPU on Android" OFF)
set(CMAKE_CXX_STANDARD 20)
set(FILAMENT_DIR ${FILAMENT_DIST_DIR})
add_library(filament STATIC IMPORTED)
@@ -130,6 +132,14 @@ add_library(filament-jni SHARED
../common/NioUtils.cpp
)
target_include_directories(filament-jni PRIVATE
..
${FILAMENT_DIR}/include
../../filament/backend/include
../../third_party/robin-map
../../third_party/perfetto
../../libs/utils/include)
# Ordering is significant in the following list. The PRIVATE qualifier prevents transitive deps.
target_link_libraries(filament-jni
PRIVATE filament-generatePrefilterMipmap
@@ -144,29 +154,21 @@ target_link_libraries(filament-jni
PRIVATE android
PRIVATE jnigraphics
PRIVATE utils
PRIVATE perfetto
PRIVATE abseil
PRIVATE perfetto # needed only when FILAMENT_ENABLE_PERFETTO is defined
PRIVATE abseil # needed only when FILAMENT_USE_ABSEIL_LOGGING is defined
PRIVATE zstd
# libgeometry is PUBLIC because gltfio uses it.
PUBLIC geometry
$<$<STREQUAL:${FILAMENT_ENABLE_FGVIEWER},ON>:fgviewer>
$<$<STREQUAL:${FILAMENT_ENABLE_MATDBG},ON>:matdbg>
$<$<STREQUAL:${FILAMENT_ENABLE_MATDBG},ON>:filamat>
$<$<STREQUAL:${FILAMENT_SUPPORTS_VULKAN},ON>:bluevk>
$<$<STREQUAL:${FILAMENT_SUPPORTS_VULKAN},ON>:vkshaders>
$<$<STREQUAL:${FILAMENT_SUPPORTS_WEBGPU},ON>:webgpu_dawn>
$<$<STREQUAL:${FILAMENT_SUPPORTS_VULKAN},ON>:smol-v>
PRIVATE $<$<STREQUAL:${FILAMENT_ENABLE_FGVIEWER},ON>:fgviewer>
PRIVATE $<$<STREQUAL:${FILAMENT_ENABLE_MATDBG},ON>:matdbg>
PRIVATE $<$<STREQUAL:${FILAMENT_ENABLE_MATDBG},ON>:filamat>
PRIVATE $<$<STREQUAL:${FILAMENT_SUPPORTS_VULKAN},ON>:bluevk>
PRIVATE $<$<STREQUAL:${FILAMENT_SUPPORTS_VULKAN},ON>:vkshaders>
PRIVATE $<$<STREQUAL:${FILAMENT_SUPPORTS_VULKAN},ON>:smol-v>
PRIVATE $<$<STREQUAL:${FILAMENT_SUPPORTS_WEBGPU},ON>:webgpu_dawn>
)
target_include_directories(filament-jni PRIVATE
..
${FILAMENT_DIR}/include
../../filament/backend/include
../../third_party/robin-map
../../third_party/perfetto
../../libs/utils/include)
# Force a relink when the version script is changed:
set_target_properties(filament-jni PROPERTIES LINK_DEPENDS ${VERSION_SCRIPT})
@@ -60,14 +60,6 @@ static void setParameter(JNIEnv* env, jlong nativeMaterialInstance, jstring name
env->ReleaseStringUTFChars(name_, name);
}
template<typename T>
static void setConstant(JNIEnv* env, jlong nativeMaterialInstance, jstring name_, T v) {
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
const char *name = env->GetStringUTFChars(name_, 0);
instance->setConstant(name, v);
env->ReleaseStringUTFChars(name_, name);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_MaterialInstance_nSetParameterBool(JNIEnv *env, jclass,
@@ -272,13 +264,6 @@ Java_com_google_android_filament_MaterialInstance_nSetParameterTexture(
env->ReleaseStringUTFChars(name_, name);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_MaterialInstance_nSetConstantBool(JNIEnv *env, jclass,
jlong nativeMaterialInstance, jstring name_, jboolean x) {
setConstant(env, nativeMaterialInstance, name_, bool(x));
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_MaterialInstance_nSetScissor(
@@ -450,6 +450,13 @@ Java_com_google_android_filament_RenderableManager_nGetPrimitiveCount(JNIEnv*, j
return (jint) rm->getPrimitiveCount((RenderableManager::Instance) i);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_RenderableManager_nGetInstanceCount(JNIEnv*, jclass,
jlong nativeRenderableManager, jint i) {
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
return (jint) rm->getInstanceCount((RenderableManager::Instance) i);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_RenderableManager_nSetMaterialInstanceAt(JNIEnv*, jclass,
jlong nativeRenderableManager, jint i, jint primitiveIndex, jlong nativeMaterialInstance) {
@@ -40,6 +40,13 @@ Java_com_google_android_filament_SwapChain_nIsSRGBSwapChainSupported(
return (jboolean)SwapChain::isSRGBSwapChainSupported(*engine);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_SwapChain_nIsMSAASwapChainSupported(
JNIEnv *, jclass, jlong nativeEngine, jint samples) {
Engine* engine = (Engine*) nativeEngine;
return (jboolean)SwapChain::isMSAASwapChainSupported(*engine, samples);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_SwapChain_nIsProtectedContentSupported(
JNIEnv *, jclass, jlong nativeEngine) {
@@ -402,16 +402,6 @@ public class MaterialInstance {
nSetParameterFloat4(getNativeObject(), name, color[0], color[1], color[2], color[3]);
}
/**
* Sets the value of a bool constant.
*
* @param name the name of the material constant
* @param x the value of the material constant
*/
public void setConstant(@NonNull String name, boolean x) {
nSetConstantBool(getNativeObject(), name, x);
}
/**
* Set-up a custom scissor rectangle; by default it is disabled.
*
@@ -931,9 +921,6 @@ public class MaterialInstance {
@NonNull String name, int element, @NonNull @Size(min = 1) float[] v,
@IntRange(from = 0) int offset, @IntRange(from = 1) int count);
private static native void nSetConstantBool(long nativeMaterialInstance,
@NonNull String name, boolean x);
private static native void nSetParameterTexture(long nativeMaterialInstance,
@NonNull String name, long nativeTexture, long sampler);
@@ -851,6 +851,14 @@ public class RenderableManager {
return nGetPrimitiveCount(mNativeObject, i);
}
/**
* Gets the immutable number of instances in the given renderable.
*/
@IntRange(from = 0)
public int getInstanceCount(@EntityInstance int i) {
return nGetInstanceCount(mNativeObject, i);
}
/**
* Changes the material instance binding for the given primitive.
*
@@ -1018,6 +1026,7 @@ public class RenderableManager {
private static native boolean nIsShadowReceiver(long nativeRenderableManager, int i);
private static native void nGetAxisAlignedBoundingBox(long nativeRenderableManager, int i, float[] center, float[] halfExtent);
private static native int nGetPrimitiveCount(long nativeRenderableManager, int i);
private static native int nGetInstanceCount(long nativeRenderableManager, int i);
private static native void nSetMaterialInstanceAt(long nativeRenderableManager, int i, int primitiveIndex, long nativeMaterialInstance);
private static native void nClearMaterialInstanceAt(long nativeRenderableManager, int i, int primitiveIndex);
private static native long nGetMaterialInstanceAt(long nativeRenderableManager, int i, int primitiveIndex);
@@ -97,6 +97,19 @@ public class SwapChain {
return nIsSRGBSwapChainSupported(engine.getNativeObject());
}
/**
* Return whether createSwapChain supports the CONFIG_MSAA_*_SAMPLES flag.
* The default implementation returns false.
*
* @param engine A reference to the filament Engine
* @param samples The number of samples
* @return true if CONFIG_MSAA_*_SAMPLES is supported, false otherwise.
* @see SwapChainFlags#CONFIG_MSAA_*_SAMPLES
*/
public static boolean isMSAASwapChainSupported(@NonNull Engine engine, int samples) {
return nIsMSAASwapChainSupported(engine.getNativeObject(), samples);
}
/**
* @return the native <code>Object</code> this <code>SwapChain</code> was created from or null
* for a headless SwapChain.
@@ -139,5 +152,6 @@ public class SwapChain {
private static native void nSetFrameCompletedCallback(long nativeSwapChain, Object handler, Runnable callback);
private static native boolean nIsSRGBSwapChainSupported(long nativeEngine);
private static native boolean nIsMSAASwapChainSupported(long nativeEngine, int samples);
private static native boolean nIsProtectedContentSupported(long nativeEngine);
}
@@ -92,6 +92,15 @@ public final class SwapChainFlags {
* The SwapChain contains protected content. Only supported when isProtectedContentSupported()
* is true.
*/
public static final long CONFIG_PROTECTED_CONTENT = 0x40;
}
public static final long CONFIG_PROTECTED_CONTENT = 0x40;
/**
* Indicates that the SwapChain is configured to use Multi-Sample Anti-Aliasing (MSAA) with the
* given sample points within each pixel. Only supported when isMSAASwapChainSupported(4) is
* true.
*
* This is only supported by EGL(Android). Other GL platforms (GLX, WGL, etc) don't support it
* because the swapchain MSAA settings must be configured before window creation.
*/
public static final long CONFIG_MSAA_4_SAMPLES = 0x80;
}
@@ -231,7 +231,7 @@ public class View {
*
* <p>
* The View does not take ownership of the Scene pointer. Before destroying a Camera, be sure
* to remove it from all assoicated Views.
* to remove it from all associated Views. If the camera isn't set, Renderer::render() will result in a no-op.
* </p>
*
* @see #getCamera
+31 -11
View File
@@ -1,6 +1,14 @@
cmake_minimum_required(VERSION 3.19)
project(filament-utils-android)
option(FILAMENT_SUPPORTS_VULKAN "Enables Vulkan on Android" OFF)
option(FILAMENT_ENABLE_FGVIEWER "Enables Frame Graph Viewer" OFF)
option(FILAMENT_ENABLE_MATDBG "Enables Material debugger" OFF)
option(FILAMENT_DISABLE_MATOPT "Disables material optimizations" OFF)
option(FILAMENT_SUPPORTS_WEBGPU "Enables WebGPU on Android" OFF)
set(CMAKE_CXX_STANDARD 20)
set(FILAMENT_DIR ${FILAMENT_DIST_DIR})
set(IMAGEIO_DIR ../../libs/imageio)
@@ -30,7 +38,16 @@ add_library(iblprefilter STATIC IMPORTED)
set_target_properties(iblprefilter PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilament-iblprefilter.a)
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/libfilament-utils-jni.map")
add_library(utils STATIC IMPORTED)
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
add_library(perfetto STATIC IMPORTED)
set_target_properties(perfetto PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libperfetto.a)
set(VERSION_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/libfilament-utils-jni.map")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${VERSION_SCRIPT}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,max-page-size=16384")
add_library(filament-utils-jni SHARED
@@ -53,22 +70,25 @@ add_library(filament-utils-jni SHARED
target_compile_definitions(filament-utils-jni PUBLIC IMAGEIO_LITE=1)
target_include_directories(filament-utils-jni PRIVATE
${FILAMENT_DIR}/include
..
${FILAMENT_DIR}/include
../../filament/backend/include
${IMAGEIO_DIR}/include
../../libs/utils/include)
set_target_properties(filament-utils-jni PROPERTIES LINK_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/libfilament-utils-jni.symbols)
set_target_properties(filament-utils-jni PROPERTIES LINK_DEPENDS ${VERSION_SCRIPT})
# The ordering in the following list is important because CMake does not have dependency information.
target_link_libraries(filament-utils-jni
gltfio-jni
civetweb
camutils
iblprefilter
image
ktxreader
viewer
PRIVATE gltfio-jni
PRIVATE civetweb
PRIVATE camutils
PRIVATE iblprefilter
PRIVATE image
PRIVATE filament-jni
PRIVATE ktxreader
PRIVATE viewer
PRIVATE log
PRIVATE utils
PRIVATE perfetto # needed only when FILAMENT_ENABLE_PERFETTO is defined
)
+37 -69
View File
@@ -1,20 +1,27 @@
cmake_minimum_required(VERSION 3.19)
project(gltfio-android)
option(FILAMENT_SUPPORTS_VULKAN "Enables Vulkan on Android" OFF)
option(FILAMENT_ENABLE_FGVIEWER "Enables Frame Graph Viewer" OFF)
option(FILAMENT_ENABLE_MATDBG "Enables Material debugger" OFF)
option(FILAMENT_DISABLE_MATOPT "Disables material optimizations" OFF)
option(FILAMENT_SUPPORTS_WEBGPU "Enables WebGPU on Android" OFF)
set(CMAKE_CXX_STANDARD 20)
set(FILAMENT_DIR ${FILAMENT_DIST_DIR})
set(GLTFIO_DIR ../../libs/gltfio)
set(DRACO_DIR ../../third_party/draco)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../filament-android ${CMAKE_CURRENT_BINARY_DIR}/filament-android)
add_library(gltfio_core STATIC IMPORTED)
set_target_properties(gltfio_core PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libgltfio_core.a)
add_library(dracodec STATIC IMPORTED)
set_target_properties(dracodec PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libdracodec.a)
add_library(meshoptimizer STATIC IMPORTED)
set_target_properties(meshoptimizer PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libmeshoptimizer.a)
add_library(ktxreader STATIC IMPORTED)
set_target_properties(ktxreader PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libktxreader.a)
@@ -27,10 +34,6 @@ add_library(basis_transcoder STATIC IMPORTED)
set_target_properties(basis_transcoder PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libbasis_transcoder.a)
add_library(zstd STATIC IMPORTED)
set_target_properties(zstd PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libzstd.a)
add_library(utils STATIC IMPORTED)
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
@@ -47,60 +50,11 @@ add_library(uberarchive STATIC IMPORTED)
set_target_properties(uberarchive PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libuberarchive.a)
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.map")
set(VERSION_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.map")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${VERSION_SCRIPT}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,max-page-size=16384")
set(GLTFIO_SRCS
${GLTFIO_DIR}/include/gltfio/Animator.h
${GLTFIO_DIR}/include/gltfio/AssetLoader.h
${GLTFIO_DIR}/include/gltfio/FilamentAsset.h
${GLTFIO_DIR}/include/gltfio/FilamentInstance.h
${GLTFIO_DIR}/include/gltfio/MaterialProvider.h
${GLTFIO_DIR}/include/gltfio/NodeManager.h
${GLTFIO_DIR}/include/gltfio/TrsTransformManager.h
${GLTFIO_DIR}/include/gltfio/ResourceLoader.h
${GLTFIO_DIR}/include/gltfio/TextureProvider.h
${GLTFIO_DIR}/include/gltfio/math.h
${GLTFIO_DIR}/src/ArchiveCache.cpp
${GLTFIO_DIR}/src/ArchiveCache.h
${GLTFIO_DIR}/src/Animator.cpp
${GLTFIO_DIR}/src/AssetLoader.cpp
${GLTFIO_DIR}/src/DependencyGraph.cpp
${GLTFIO_DIR}/src/DependencyGraph.h
${GLTFIO_DIR}/src/DracoCache.cpp
${GLTFIO_DIR}/src/DracoCache.h
${GLTFIO_DIR}/src/FFilamentAsset.h
${GLTFIO_DIR}/src/FFilamentInstance.h
${GLTFIO_DIR}/src/FilamentAsset.cpp
${GLTFIO_DIR}/src/FilamentInstance.cpp
${GLTFIO_DIR}/src/FNodeManager.h
${GLTFIO_DIR}/src/FTrsTransformManager.h
${GLTFIO_DIR}/src/GltfEnums.h
${GLTFIO_DIR}/src/Ktx2Provider.cpp
${GLTFIO_DIR}/src/MaterialProvider.cpp
${GLTFIO_DIR}/src/NodeManager.cpp
${GLTFIO_DIR}/src/TrsTransformManager.cpp
${GLTFIO_DIR}/src/ResourceLoader.cpp
${GLTFIO_DIR}/src/StbProvider.cpp
${GLTFIO_DIR}/src/TangentsJob.cpp
${GLTFIO_DIR}/src/TangentsJob.h
${GLTFIO_DIR}/src/UbershaderProvider.cpp
${GLTFIO_DIR}/src/Utility.cpp
${GLTFIO_DIR}/src/Utility.h
${GLTFIO_DIR}/src/Wireframe.cpp
${GLTFIO_DIR}/src/Wireframe.h
${GLTFIO_DIR}/src/downcast.h
${GLTFIO_DIR}/src/extended/AssetLoaderExtended.cpp
${GLTFIO_DIR}/src/extended/AssetLoaderExtended.h
${GLTFIO_DIR}/src/extended/ResourceLoaderExtended.cpp
${GLTFIO_DIR}/src/extended/ResourceLoaderExtended.h
${GLTFIO_DIR}/src/extended/TangentsJobExtended.cpp
${GLTFIO_DIR}/src/extended/TangentsJobExtended.h
${GLTFIO_DIR}/src/extended/TangentSpaceMeshWrapper.cpp
${GLTFIO_DIR}/src/extended/TangentSpaceMeshWrapper.h
src/main/cpp/Animator.cpp
src/main/cpp/AssetLoader.cpp
src/main/cpp/FilamentAsset.cpp
@@ -118,24 +72,38 @@ set(GLTFIO_SRCS
set(GLTFIO_INCLUDE_DIRS
..
${FILAMENT_DIR}/include
../../filament/backend/include
${FILAMENT_DIR}/backend/include
../../libs/gltfio/include
../../third_party/zstd/lib
../../third_party/cgltf
../../third_party/meshoptimizer/src
../../third_party/robin-map
../../third_party/stb
../../third_party/perfetto
../../libs/utils/include
../../libs/ktxreader/include
)
add_library(gltfio-jni SHARED ${GLTFIO_SRCS})
target_include_directories(gltfio-jni PRIVATE ${GLTFIO_INCLUDE_DIRS})
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.symbols)
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.map)
target_link_libraries(gltfio-jni filament-jni utils perfetto uberzlib log stb ktxreader basis_transcoder zstd uberarchive)
target_link_libraries(gltfio-jni dracodec meshoptimizer)
target_compile_definitions(gltfio-jni PUBLIC GLTFIO_DRACO_SUPPORTED=1)
target_include_directories(gltfio-jni PRIVATE ${GLTFIO_INCLUDE_DIRS})
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/src)
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/tnt)
# Force a relink when the version script is changed:
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.symbols;${VERSION_SCRIPT}")
# The ordering in the following list is important because CMake does not have dependency information.
target_link_libraries(gltfio-jni
PRIVATE gltfio_core
PRIVATE ktxreader
PUBLIC filament-jni
PRIVATE stb
PRIVATE basis_transcoder
PRIVATE uberarchive
PRIVATE dracodec
PRIVATE utils
PRIVATE uberzlib
PRIVATE perfetto # needed only when FILAMENT_ENABLE_PERFETTO is defined
PRIVATE log # needed only when perfetto above is used
)
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.64.0
VERSION_NAME=1.65.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+8 -1
View File
@@ -75,6 +75,8 @@ function print_help {
echo " -S type"
echo " Enable stereoscopic rendering where type is one of [instanced|multiview]. This is only"
echo " meant for building the samples."
echo " -P"
echo " Enable perfetto traces on Android. Disabled by default on the Release build, enabled otherwise."
echo ""
echo "Build types:"
echo " release"
@@ -204,6 +206,7 @@ MATOPT_GRADLE_OPTION=""
ASAN_UBSAN_OPTION=""
COVERAGE_OPTION=""
ENABLE_PERFETTO=""
BACKEND_DEBUG_FLAG_OPTION=""
@@ -413,6 +416,7 @@ function build_android_target {
${WEBGPU_OPTION} \
${BACKEND_DEBUG_FLAG_OPTION} \
${STEREOSCOPIC_OPTION} \
${ENABLE_PERFETTO} \
../..
ln -sf "out/cmake-android-${lc_target}-${arch}/compile_commands.json" \
../../compile_commands.json
@@ -806,7 +810,7 @@ function check_debug_release_build {
pushd "$(dirname "$0")" > /dev/null
while getopts ":hacCfgimp:q:uvWslwedtk:bVx:S:X:" opt; do
while getopts ":hacCfgimp:q:uvWslwedtk:bVx:S:X:P" opt; do
case ${opt} in
h)
print_help
@@ -954,6 +958,9 @@ while getopts ":hacCfgimp:q:uvWslwedtk:bVx:S:X:" opt; do
V) COVERAGE_OPTION="-DFILAMENT_ENABLE_COVERAGE=ON"
echo "Enabled coverage"
;;
P) ENABLE_PERFETTO="-DFILAMENT_ENABLE_PERFETTO=ON"
echo "Enabled perfetto"
;;
x) BACKEND_DEBUG_FLAG_OPTION="-DFILAMENT_BACKEND_DEBUG_FLAG=${OPTARG}"
;;
S) case $(echo "${OPTARG}" | tr '[:upper:]' '[:lower:]') in
@@ -0,0 +1,349 @@
#!/usr/bin/env python3
# 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.
"""
This script automates the process of closing a Sonatype OSSRH staging repository.
To run the script:
python3 ./close-sonatype-staging-repository.py
To run the embedded unit tests:
python3 ./close-sonatype-staging-repository.py test
"""
import sys
import os
import base64
import json
import pathlib
import unittest
import io
from unittest import mock
import urllib.request
import urllib.error
PROPERTIES_PATH = pathlib.Path.home() / ".gradle" / "gradle.properties"
API_BASE_URL = "https://ossrh-staging-api.central.sonatype.com/manual"
def get_gradle_credentials(file_path: pathlib.Path) -> (str, str):
"""
Reads sonatypeUsername and sonatypePassword from the properties file.
"""
if not file_path.exists():
print(f"Error: Properties file not found at {file_path}", file=sys.stderr)
sys.exit(1)
username = None
password = None
try:
with open(file_path, 'r') as f:
for line in f:
line = line.strip()
# Ignore blank lines and comments
if not line or line.startswith("#"):
continue
if line.startswith("sonatypeUsername="):
username = line.split("=", 1)[1].strip()
elif line.startswith("sonatypePassword="):
password = line.split("=", 1)[1].strip()
if not username or not password:
print(f"Error: 'sonatypeUsername' or 'sonatypePassword' not found in {file_path}", file=sys.stderr)
sys.exit(1)
return username, password
except Exception as e:
print(f"Error reading properties file: {e}", file=sys.stderr)
sys.exit(1)
def generate_auth_token(username: str, password: str) -> str:
"""
Generates a Base64 encoded auth token from username and password.
"""
auth_string = f"{username}:{password}"
auth_bytes = auth_string.encode('utf-8')
token_bytes = base64.b64encode(auth_bytes)
return token_bytes.decode('utf-8')
def find_staging_repository(token: str) -> str:
"""
Finds the 'open' staging repository key from the Sonatype API.
"""
print("Searching for staging repository...")
url = f"{API_BASE_URL}/search/repositories"
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/json"
}
req = urllib.request.Request(url, headers=headers, method="GET")
try:
with urllib.request.urlopen(req) as response:
response_body = response.read().decode('utf-8')
data = json.loads(response_body)
repositories = data.get("repositories", [])
open_repositories = list(filter(lambda repo: repo.get("state") == "open", repositories))
if len(open_repositories) == 0:
print("Error: No 'open' staging repositories found.", file=sys.stderr)
sys.exit(1)
if len(open_repositories) > 1:
print(f"Error: Expected 1 'open' repository, but found {len(repositories)}.", file=sys.stderr)
sys.exit(1)
repo_key = open_repositories[0].get("key")
if not repo_key:
print("Error: Repository found, but it has no 'key'.", file=sys.stderr)
sys.exit(1)
return repo_key
except urllib.error.HTTPError as e:
# Handle HTTP errors (e.g., 401 Unauthorized, 404 Not Found, 500 Server Error)
print(f"Error during repository search: HTTP {e.code} {e.reason}", file=sys.stderr)
try:
# Try to read the error response body for more context
error_body = e.read().decode('utf-8')
print(f"Response body: {error_body}", file=sys.stderr)
except Exception:
pass # Ignore if we can't read the body
sys.exit(1)
except urllib.error.URLError as e:
# Handle network errors (e.g., connection refused)
print(f"Error during repository search: {e.reason}", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError:
print("Error: Failed to decode JSON response from server.", file=sys.stderr)
sys.exit(1)
def close_staging_repository(token: str, repo_key: str):
"""
Closes (promotes) the staging repository with the given key.
"""
print(f"Attempting to close staging repository: {repo_key}")
url = f"{API_BASE_URL}/upload/repository/{repo_key}"
headers = {"Authorization": f"Bearer {token}"}
req = urllib.request.Request(url, headers=headers, method="POST")
try:
with urllib.request.urlopen(req) as response:
print(f"Successfully submitted request. Server responded with: {response.status} {response.reason}")
print("It may take a few moments to process.")
except urllib.error.HTTPError as e:
print(f"Error closing repository: HTTP {e.code} {e.reason}", file=sys.stderr)
try:
error_body = e.read().decode('utf-8')
print(f"Response body: {error_body}", file=sys.stderr)
except Exception:
pass
sys.exit(1)
except urllib.error.URLError as e:
print(f"Error closing repository: {e.reason}", file=sys.stderr)
sys.exit(1)
def main():
username, password = get_gradle_credentials(PROPERTIES_PATH)
token = generate_auth_token(username, password)
print("Successfully generated auth token.")
repo_key = find_staging_repository(token)
print(f"Found staging repository: {repo_key}")
close_staging_repository(token, repo_key)
print("\nScript completed successfully.")
# --- Unit Tests ---
class TestSonatypeScript(unittest.TestCase):
@mock.patch('pathlib.Path.exists', return_value=True)
@mock.patch('builtins.open', new_callable=mock.mock_open,
read_data="# This is a comment\nsonatypeUsername=testuser\n\nsonatypePassword=testpass\nother=data")
def test_get_gradle_credentials_success(self, mock_file, mock_exists):
"""Tests successful credential parsing, ignoring comments."""
user, pw = get_gradle_credentials(pathlib.Path("/fake/path"))
self.assertEqual(user, "testuser")
self.assertEqual(pw, "testpass")
@mock.patch('pathlib.Path.exists', return_value=False)
def test_get_gradle_credentials_no_file(self, mock_exists):
"""Tests error exit when file is missing."""
with self.assertRaises(SystemExit) as cm:
get_gradle_credentials(pathlib.Path("/fake/path"))
@mock.patch('pathlib.Path.exists', return_value=True)
@mock.patch('builtins.open', new_callable=mock.mock_open, read_data="# Only comments\nsonatypeUsername=testuser")
def test_get_gradle_credentials_missing_key(self, mock_file, mock_exists):
"""Tests error exit when a key is missing."""
with self.assertRaises(SystemExit) as cm:
get_gradle_credentials(pathlib.Path("/fake/path"))
def test_generate_auth_token(self):
"""Tests the Base64 token encoding."""
token = generate_auth_token("foobar", "abc123")
# echo -n "foobar:abc123" | base64
self.assertEqual(token, "Zm9vYmFyOmFiYzEyMw==")
@mock.patch('urllib.request.urlopen')
def test_find_repository_success(self, mock_urlopen):
"""Tests finding exactly one open repository."""
mock_response = mock.Mock()
mock_response.status = 200
mock_response.reason = "OK"
mock_response.read.return_value = json.dumps({
"repositories": [{"key": "test-repo-key-123", "state": "open"}]
}).encode('utf-8')
mock_urlopen.return_value.__enter__.return_value = mock_response
key = find_staging_repository("fake-token")
self.assertEqual(key, "test-repo-key-123")
@mock.patch('urllib.request.urlopen')
def test_find_repository_success(self, mock_urlopen):
"""Tests finding exactly one open repository."""
mock_response = mock.Mock()
mock_response.status = 200
mock_response.reason = "OK"
mock_response.read.return_value = json.dumps({
"repositories": [{"key": "test-repo-key-123", "state": "open"}, {"key": "test-repo-key-456", "state": "closed"}]
}).encode('utf-8')
mock_urlopen.return_value.__enter__.return_value = mock_response
key = find_staging_repository("fake-token")
self.assertEqual(key, "test-repo-key-123")
@mock.patch('urllib.request.urlopen')
def test_find_repository_success(self, mock_urlopen):
"""Tests error exit when only a closed repository is found."""
mock_response = mock.Mock()
mock_response.status = 200
mock_response.reason = "OK"
mock_response.read.return_value = json.dumps({
"repositories": [{"key": "test-repo-key-123", "state": "closed"}]
}).encode('utf-8')
mock_urlopen.return_value.__enter__.return_value = mock_response
with self.assertRaises(SystemExit) as cm:
key = find_staging_repository("fake-token")
@mock.patch('urllib.request.urlopen')
def test_find_repository_not_found(self, mock_urlopen):
"""Tests error exit when zero repositories are found."""
mock_response = mock.Mock()
mock_response.status = 200
mock_response.read.return_value = json.dumps({"repositories": []}).encode('utf-8')
mock_urlopen.return_value.__enter__.return_value = mock_response
with self.assertRaises(SystemExit) as cm:
find_staging_repository("fake-token")
@mock.patch('urllib.request.urlopen')
def test_find_repository_too_many(self, mock_urlopen):
"""Tests error exit when multiple open repositories are found."""
mock_response = mock.Mock()
mock_response.status = 200
mock_response.read.return_value = json.dumps({
"repositories": [{"key": "key1", "state": "open"}, {"key": "key2", "state": "open"}]
}).encode('utf-8')
mock_urlopen.return_value.__enter__.return_value = mock_response
with self.assertRaises(SystemExit) as cm:
find_staging_repository("fake-token")
@mock.patch('urllib.request.urlopen')
def test_find_repository_http_error(self, mock_urlopen):
"""Tests error exit on a network HTTP error."""
# Mock an HTTPError
mock_urlopen.side_effect = urllib.error.HTTPError(
url='http://fake.com',
code=401,
msg="Unauthorized",
hdrs={},
fp=io.BytesIO(b'{"error":"bad token"}')
)
with self.assertRaises(SystemExit) as cm:
find_staging_repository("fake-token")
@mock.patch('urllib.request.urlopen')
def test_close_repository_success(self, mock_urlopen):
"""Tests successful repository close."""
mock_response = mock.Mock()
mock_response.status = 202 # 202 Accepted is a common response for POST
mock_response.reason = "Accepted"
mock_urlopen.return_value.__enter__.return_value = mock_response
close_staging_repository("fake-token", "test-repo-key")
# Check that the request object was created correctly
called_request = mock_urlopen.call_args[0][0]
self.assertEqual(called_request.full_url, f"{API_BASE_URL}/upload/repository/test-repo-key")
self.assertEqual(called_request.method, "POST")
self.assertEqual(called_request.headers["Authorization"], "Bearer fake-token")
@mock.patch('urllib.request.urlopen')
def test_close_repository_http_error(self, mock_urlopen):
"""Tests error exit on close network failure."""
mock_urlopen.side_effect = urllib.error.HTTPError(
url='http://fake.com',
code=500,
msg="Server Error",
hdrs={},
fp=io.BytesIO(b'{"error":"failed to close"}')
)
with self.assertRaises(SystemExit) as cm:
close_staging_repository("fake-token", "test-repo-key")
def run_unit_tests():
"""
Configures and runs the unit tests.
"""
print("Running unit tests...")
# We replace sys.argv to prevent unittest.main from
# trying to parse the 'test' argument.
original_argv = sys.argv
sys.argv = [original_argv[0]] # Keep only the script name
unittest.main()
sys.argv = original_argv # Restore original args
if __name__ == '__main__':
# Check if the user wants to run tests
if len(sys.argv) > 1 and sys.argv[1].lower() == 'test':
run_unit_tests()
else:
# Run the main script logic
main()
+25 -22
View File
@@ -212,7 +212,7 @@ when using textures.
This property can dramatically change the appearance of a surface. Non-metallic surfaces have
chromatic diffuse reflection and achromatic specular reflection (reflected light does not change
color). Metallic surfaces do not have any diffuse reflection and chromatic specular reflection
(reflected light takes on the color of the surfaced as defined by `baseColor`).
(reflected light takes on the color of the surfaced as defined by `baseColor`).
The effect of `metallic` is shown in figure [metallicProperty] (click on the image to see a
larger version).
@@ -248,7 +248,7 @@ The effect of `roughness` on metallic surfaces is shown in figure [roughnessCond
When refraction through an object is enabled (using a `refractonType` of `thin` or `solid`), the
`roughness` property will also affect the refractions, as shown in figure
[roughnessRefractionProperty] (click on the image to see a larger version).
![Figure [roughnessRefractionProperty]: Refractive sphere with `roughness` varying from 0.0
(left) to 1.0 (right)](images/materials/refraction_roughness.png)
@@ -307,7 +307,7 @@ The sheen color controls the color appearance and strength of an optional sheen
base layer described by the properties above. The sheen layer always sits below the clear coat layer
if such a layer is present.
The sheen layer can be used to represent cloth and fabric materials. Please refer to
The sheen layer can be used to represent cloth and fabric materials. Please refer to
section [Cloth model] for more information about cloth and fabric materials.
The effect of `sheenColor` is shown in figure [materialSheenColor]
@@ -520,14 +520,14 @@ light to bend further away from the initial path.
Table [commonMatIOR] describes acceptable refractive indices for various types of materials.
Material | IOR
Material | IOR
--------------------------:|:-----------------
Air | 1.0
Water | 1.33
Common liquids | 1.33 to 1.5
Common gemstones | 1.58 to 2.33
Plastics, glass | 1.5 to 1.58
Other dielectric materials | 1.33 to 1.58
Water | 1.33
Common liquids | 1.33 to 1.5
Common gemstones | 1.58 to 2.33
Plastics, glass | 1.5 to 1.58
Other dielectric materials | 1.33 to 1.58
[Table [commonMatIOR]: Index of refraction of common materials]
The appearance of a refractive material will greatly depend on the `refractionType` and
@@ -995,10 +995,10 @@ Value
name must be a valid GLSL identifier. Entries have an optional `precision`, which can be
one of `default` (best precision for the platform, typically `high` on desktop, `medium` on
mobile), `low`, `medium`, `high`. The type must be one of the types described in
table [materialParamsTypes]. For Android external textures, entries also have an optional
transformName parameter to specify the name of the material parameter that will be
table [materialParamsTypes]. For Android external textures, entries also have an optional
transformName parameter to specify the name of the material parameter that will be
used to expose the transform matrix associated with the external sampler. In iOS and Vulkan,
this will always be identity.
this will always be identity.
Type | Description
:----------------------|:---------------------------------
@@ -2476,14 +2476,17 @@ type aliases:
### Matrices
Name | Type | Description
:-----------------------------------|:--------:|:------------------------------------
**getViewFromWorldMatrix()** | float4x4 | Matrix that converts from world space to view/eye space
**getWorldFromViewMatrix()** | float4x4 | Matrix that converts from view/eye space to world space
**getClipFromViewMatrix()** | float4x4 | Matrix that converts from view/eye space to clip (NDC) space
**getViewFromClipMatrix()** | float4x4 | Matrix that converts from clip (NDC) space to view/eye space
**getClipFromWorldMatrix()** | float4x4 | Matrix that converts from world to clip (NDC) space
**getWorldFromClipMatrix()** | float4x4 | Matrix that converts from clip (NDC) space to world space
Name | Type | Description
:-----------------------------------------|:--------:|:------------------------------------
**getViewFromWorldMatrix()** | float4x4 | Matrix that converts from world space to view/eye space
**getWorldFromViewMatrix()** | float4x4 | Matrix that converts from view/eye space to world space
**getClipFromViewMatrix()** | float4x4 | Matrix that converts from view/eye space to clip (NDC) space
**getViewFromClipMatrix()** | float4x4 | Matrix that converts from clip (NDC) space to view/eye space
**getEyeFromViewMatrix()** | float4x4 | Matrix that converts from view space to eye space
**getEyeFromViewMatrix(int eyeIndex)** | float4x4 | Matrix that converts from view space to eye space for the eye referred to by eyeIndex
**getClipFromWorldMatrix()** | float4x4 | Matrix that converts from world to clip (NDC) space
**getClipFromWorldMatrix(int eyeIndex)** | float4x4 | Matrix that converts from world to clip (NDC) space for the eye referred to by eyeIndex
**getWorldFromClipMatrix()** | float4x4 | Matrix that converts from clip (NDC) space to world space
### Frame constants
@@ -2639,7 +2642,7 @@ standard skybox material. It produces a list of 2 parameters, named `showSun` an
respectively a boolean and a cubemap texture.
```text
$ matc --reflect parameters filament/src/materials/skybox.mat
$ matc --reflect parameters filament/src/materials/skybox.mat
{
"parameters": [
{
@@ -2655,7 +2658,7 @@ $ matc --reflect parameters filament/src/materials/skybox.mat
}
]
}
```
```
### --variant-filter
+267
View File
@@ -0,0 +1,267 @@
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Maven Release - Filament</title>
<!-- Custom HTML head -->
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="../highlight.css">
<link rel="stylesheet" href="../tomorrow-night.css">
<link rel="stylesheet" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<!-- MathJax -->
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<!-- Provide site root to javascript -->
<script>
var path_to_root = "../";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
</script>
<!-- Start loading toc.js asap -->
<script src="../toc.js"></script>
</head>
<body>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
var sidebar = null;
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div style="display:flex;align-items:center;justify-content:center">
<img class="flogo" src="../images/filament_logo_small.png"></img>
</div>
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<!-- Filament: disable themes because the markdeep part does not look good for dark themes -->
<!--
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
-->
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Filament</h1>
<div class="right-buttons">
<a href="https://github.com/google/filament" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="maven-release"><a class="header" href="#maven-release">Maven Release</a></h1>
<h2 id="register-for-a-sonatype-account"><a class="header" href="#register-for-a-sonatype-account">Register for a Sonatype Account</a></h2>
<p>First, you'll need to register for a Sonatype account at the <a href="https://central.sonatype.org/register/central-portal/">Central
Portal</a>.</p>
<p>To publish under the <code>com.google.android</code> namespace, you'll also need to email
<code>central-support@sonatype.com</code> with your account information to request access.</p>
<p>Then, generate a user token through the Sonatype website. Navigate to
<a href="https://central.sonatype.com/account">https://central.sonatype.com/account</a> and select <strong>Generate
User Token</strong>.</p>
<p>Finally, add the generated token credentials to <code>~/.gradle/gradle.properties</code>. (Note: these are
different from the credentials used to log into your Sonatype account):</p>
<pre><code class="language-gradle"># Generated Sonatype token
sonatypeUsername=&lt;username&gt;
sonatypePassword=&lt;password&gt;
</code></pre>
<hr />
<h2 id="signing-key"><a class="header" href="#signing-key">Signing Key</a></h2>
<p>All release artifacts must be signed. You'll need to create an OpenPGP key pair. See Gradle's
documentation on <a href="https://docs.gradle.org/current/userguide/signing_plugin.html#sec:signatory_credentials">Signatory
credentials</a>
for instructions.</p>
<p>Update <code>~/.gradle/gradle.properties</code> with your new key's credentials:</p>
<pre><code class="language-gradle">signing.keyId=&lt;key id&gt;
signing.password=&lt;key password&gt;
signing.secretKeyRingFile=&lt;secret key ring file&gt;
</code></pre>
<hr />
<h2 id="build-the-android-release"><a class="header" href="#build-the-android-release">Build the Android Release</a></h2>
<p>Make sure <code>JAVA_HOME</code> is set correctly. For example:</p>
<pre><code class="language-bash">export JAVA_HOME=/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home
./build.sh -C -i -p android release
</code></pre>
<hr />
<h2 id="publish-to-sonatype"><a class="header" href="#publish-to-sonatype">Publish to Sonatype</a></h2>
<h3 id="a-note-on-the-legacy-staging-service"><a class="header" href="#a-note-on-the-legacy-staging-service">A Note on the Legacy Staging Service</a></h3>
<p>Previously, Filament was published to Maven via OSSRH (Open Source Software Repository Hosting). In
2025, this service was <a href="https://central.sonatype.org/pages/ossrh-eol/">sunsetted</a>. Now, we use
Sonatype's <a href="https://central.sonatype.org/">Central Publisher Portal</a>.</p>
<p>The new Central Publisher Portal does not officially support Gradle. However, Sonatype provides a
staging API compatibility service, which works with Filament's Gradle setup.</p>
<hr />
<h3 id="1-upload-to-the-staging-api-compatibility-service"><a class="header" href="#1-upload-to-the-staging-api-compatibility-service">1. Upload to the Staging API Compatibility Service</a></h3>
<pre><code class="language-bash">cd android
./gradlew publishToSonatype
</code></pre>
<h3 id="2-move-the-repository-to-the-central-publisher-portal"><a class="header" href="#2-move-the-repository-to-the-central-publisher-portal">2. Move the Repository to the Central Publisher Portal</a></h3>
<p>We have a script to automate this. It reads the <code>sonatypeUsername</code> and <code>sonatypePassword</code> from your
<code>~/.gradle/gradle.properties</code> file.</p>
<pre><code class="language-bash">python3 build/common/close-sonatype-staging-repository.py
</code></pre>
<h3 id="3-publish-the-release-on-sonatype"><a class="header" href="#3-publish-the-release-on-sonatype">3. Publish the Release on Sonatype</a></h3>
<p>Navigate to <a href="https://central.sonatype.com/publishing/deployments">Maven Central Repository Deployments</a>.</p>
<p>Here, you should see a new deployment with a <strong>Validated</strong> status and all your artifacts listed. Click
the <strong>Publish</strong> button to publish the artifacts. It typically takes around 5 minutes after clicking
<strong>Publish</strong> for the artifacts to go live.</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../build/windows_android.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../dup/contributing.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../build/windows_android.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../dup/contributing.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../elasticlunr.min.js"></script>
<script src="../mark.min.js"></script>
<script src="../searcher.js"></script>
<script src="../clipboard.min.js"></script>
<script src="../highlight.js"></script>
<script src="../book.js"></script>
<!-- Custom JS scripts -->
</div>
</body>
</html>
+2 -2
View File
@@ -273,7 +273,7 @@ copy filament-android\build\outputs\aar\filament-android-release.aar ..\..\out\
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../dup/contributing.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<a rel="next prefetch" href="../build/maven_release.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
@@ -287,7 +287,7 @@ copy filament-android\build\outputs\aar\filament-android-release.aar ..\..\out\
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../dup/contributing.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<a rel="next prefetch" href="../build/maven_release.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
+2 -2
View File
@@ -209,7 +209,7 @@ as possible. The current external dependencies of the runtime library include:</
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../build/windows_android.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<a rel="prev" href="../build/maven_release.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
@@ -223,7 +223,7 @@ as possible. The current external dependencies of the runtime library include:</
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../build/windows_android.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<a rel="prev" href="../build/maven_release.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
+2 -2
View File
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.64.0'
implementation 'com.google.android.filament:filament-android:1.65.0'
}
</code></pre>
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
@@ -196,7 +196,7 @@ dependencies {
</div>
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
<p>iOS projects can use CocoaPods to install the latest release:</p>
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.64.0'
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.65.0'
</code></pre>
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
<ul>
+1 -1
View File
@@ -162,7 +162,7 @@
<p>This tool makes it easy to analyze the composition of Android applications that use Filament.</p>
<ul>
<li>Filament materials are shown in the treemap if <code>resgen --json</code> was used in the build.</li>
<li>The input path can be a so file, folder, or zip archive (apk or aar).</li>
<li>The input path can be a <code>.so</code> or <code>.a</code> file, a folder, or a zip archive (apk or aar).</li>
<li>If the path is a zip or folder, interactively finds the file to analyze.</li>
<li>The generated web report is a self-contained HTML file.</li>
<li>Reports the gzipped size of all Filament materials.</li>
+4 -4
View File
@@ -178,9 +178,9 @@ content:counter(h1) "."counter(h2) "."counter(h3) "."counter(h4) "."counter(h5)
counter-increment: h6;margin-right:10px}
</style><style>.hljs{display:block;overflow-x:auto;padding:0.5em;background:#fff;color:#000;-webkit-text-size-adjust:none}.hljs-comment{color:#006a00}.hljs-keyword{color:#02E}.hljs-literal,.nginx .hljs-title{color:#aa0d91}.method,.hljs-list .hljs-title,.hljs-tag .hljs-title,.setting .hljs-value,.hljs-winutils,.tex .hljs-command,.http .hljs-title,.hljs-request,.hljs-status,.hljs-name{color:#008}.hljs-envvar,.tex .hljs-special{color:#660}.hljs-string{color:#c41a16}.hljs-tag .hljs-value,.hljs-cdata,.hljs-filter .hljs-argument,.hljs-attr_selector,.apache .hljs-cbracket,.hljs-date,.hljs-regexp{color:#080}.hljs-sub .hljs-identifier,.hljs-pi,.hljs-tag,.hljs-tag .hljs-keyword,.hljs-decorator,.ini .hljs-title,.hljs-shebang,.hljs-prompt,.hljs-hexcolor,.hljs-rule .hljs-value,.hljs-symbol,.hljs-symbol .hljs-string,.hljs-number,.css .hljs-function,.hljs-function .hljs-title,.coffeescript .hljs-attribute{color:#A0C}.hljs-function .hljs-title{font-weight:bold;color:#000}.hljs-class .hljs-title,.smalltalk .hljs-class,.hljs-type,.hljs-typename,.hljs-tag .hljs-attribute,.hljs-doctype,.hljs-class .hljs-id,.hljs-built_in,.setting,.hljs-params,.clojure .hljs-attribute{color:#5c2699}.hljs-variable{color:#3f6e74}.css .hljs-tag,.hljs-rule .hljs-property,.hljs-pseudo,.hljs-subst{color:#000}.css .hljs-class,.css .hljs-id{color:#9b703f}.hljs-value .hljs-important{color:#ff7700;font-weight:bold}.hljs-rule .hljs-keyword{color:#c5af75}.hljs-annotation,.apache .hljs-sqbracket,.nginx .hljs-built_in{color:#9b859d}.hljs-preprocessor,.hljs-preprocessor *,.hljs-pragma{color:#643820}.tex .hljs-formula{background-color:#eee;font-style:italic}.diff .hljs-header,.hljs-chunk{color:#808080;font-weight:bold}.diff .hljs-change{background-color:#bccff9}.hljs-addition{background-color:#baeeba}.hljs-deletion{background-color:#ffc8bd}.hljs-comment .hljs-doctag{font-weight:bold}.method .hljs-id{color:#000}</style><style>div.title { padding-top: 40px; } div.afterTitles { height: 15px; }</style><meta charset="utf-8"></p>
<style>img { max-width: 100%; }</style>
<script type="text/x-mathjax-config">MathJax.Hub.Config({ TeX: { equationNumbers: {autoNumber: "AMS"} } });</script><span style="display:none">$$\newcommand{\n}{\hat{n}}\newcommand{\thetai}{\theta_\mathrm{i}}\newcommand{\thetao}{\theta_\mathrm{o}}\newcommand{\d}[1]{\mathrm{d}#1}\newcommand{\w}{\hat{\omega}}\newcommand{\wi}{\w_\mathrm{i}}\newcommand{\wo}{\w_\mathrm{o}}\newcommand{\wh}{\w_\mathrm{h}}\newcommand{\Li}{L_\mathrm{i}}\newcommand{\Lo}{L_\mathrm{o}}\newcommand{\Le}{L_\mathrm{e}}\newcommand{\Lr}{L_\mathrm{r}}\newcommand{\Lt}{L_\mathrm{t}}\newcommand{\O}{\mathrm{O}}\newcommand{\degrees}{{^{\large\circ}}}\newcommand{\T}{\mathsf{T}}\newcommand{\mathset}[1]{\mathbb{#1}}\newcommand{\Real}{\mathset{R}}\newcommand{\Integer}{\mathset{Z}}\newcommand{\Boolean}{\mathset{B}}\newcommand{\Complex}{\mathset{C}}\newcommand{\un}[1]{\,\mathrm{#1}}$$
<p><span style="display:none">$$\newcommand{\n}{\hat{n}}\newcommand{\thetai}{\theta_\mathrm{i}}\newcommand{\thetao}{\theta_\mathrm{o}}\newcommand{\d}[1]{\mathrm{d}#1}\newcommand{\w}{\hat{\omega}}\newcommand{\wi}{\w_\mathrm{i}}\newcommand{\wo}{\w_\mathrm{o}}\newcommand{\wh}{\w_\mathrm{h}}\newcommand{\Li}{L_\mathrm{i}}\newcommand{\Lo}{L_\mathrm{o}}\newcommand{\Le}{L_\mathrm{e}}\newcommand{\Lr}{L_\mathrm{r}}\newcommand{\Lt}{L_\mathrm{t}}\newcommand{\O}{\mathrm{O}}\newcommand{\degrees}{{^{\large\circ}}}\newcommand{\T}{\mathsf{T}}\newcommand{\mathset}[1]{\mathbb{#1}}\newcommand{\Real}{\mathset{R}}\newcommand{\Integer}{\mathset{Z}}\newcommand{\Boolean}{\mathset{B}}\newcommand{\Complex}{\mathset{C}}\newcommand{\un}[1]{,\mathrm{#1}}$$
</span>
<span class="md"><p><title>Physically Based Rendering in Filament</title><div class="title"> Physically Based Rendering in Filament </div>
<span class="md"><p><title>Physically Based Rendering in Filament</title><div class="title"> Physically Based Rendering in Filament </div></p>
<div class="afterTitles"></div>
</p><p>
</p>
@@ -2060,7 +2060,7 @@ In practice only 4 or 9 coefficients (i.e.: 2 or 3 bands) are enough for \(\cosT
In practice we pre-convolve \(\Lt\) with \(\cosTheta\) and pre-scale these coefficients by the basis scaling factors \(K_l^m\) so that the reconstruction code is as simple as possible in the shader:
</p><a class="target" name="listing_irradiancesh">&nbsp;</a><pre class="listing tilde"><code><span class="line"></span>vec3 irradianceSH(vec3 n) {
<span class="line"></span> // uniform vec3 sphericalHarmonics<span class="hljs-selector-attr">[9]</span>
<span class="line"></span> // We can use only the first <span class="hljs-number">2</span> bands for better performance
<span class="line"></span> // We can <span class="hljs-selector-tag">use</span> only the first <span class="hljs-number">2</span> bands for better performance
<span class="line"></span> return
<span class="line"></span> sphericalHarmonics<span class="hljs-selector-attr">[0]</span>
<span class="line"></span> + sphericalHarmonics<span class="hljs-selector-attr">[1]</span> * (n<span class="hljs-selector-class">.y</span>)
@@ -4395,7 +4395,7 @@ For Fast Realistic Image Display. <em class="asterisk">SIGGRAPH '00 Proceedings
</div><div class="bib">[<a class="target" name="citation-schlick94">&nbsp;</a><b>Schlick94</b>] Christophe Schlick. 1994. An Inexpensive BRDF Model for Physically-Based Rendering. <em class="asterisk">Computer Graphics Forum</em>, 13 (3), 233–246.
</div><div class="bib">[<a class="target" name="citation-walter07">&nbsp;</a><b>Walter07</b>] Bruce Walter et al. 2007. Microfacet Models for Refraction through Rough Surfaces. <em class="asterisk">Proceedings of the Eurographics Symposium on Rendering</em>.
</div>
</sample></float></sample></float></float></p></span><div id="mdContextMenu" style="visibility:hidden"></div><div class="markdeepFooter"><i>formatted by <a href="https://casual-effects.com/markdeep" style="color:#999">Markdeep&nbsp;1.18&nbsp;&nbsp;</a></i><div style="display:inline-block;font-size:13px;font-family:'Times New Roman',serif;vertical-align:middle;transform:translate(-3px,-1px)rotate(135deg);">&#x2712;</div></div><script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.6/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</sample></float></sample></float></float></p></span><div id="mdContextMenu" style="visibility:hidden"></div><div class="markdeepFooter"><i>formatted by <a href="https://casual-effects.com/markdeep" style="color:#999">Markdeep&nbsp;1.18&nbsp;&nbsp;</a></i><div style="display:inline-block;font-size:13px;font-family:'Times New Roman',serif;vertical-align:middle;transform:translate(-3px,-1px)rotate(135deg);">&#x2712;</div></div><script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
+12 -9
View File
@@ -178,9 +178,9 @@ content:counter(h1) "."counter(h2) "."counter(h3) "."counter(h4) "."counter(h5)
counter-increment: h6;margin-right:10px}
</style><style>.hljs{display:block;overflow-x:auto;padding:0.5em;background:#fff;color:#000;-webkit-text-size-adjust:none}.hljs-comment{color:#006a00}.hljs-keyword{color:#02E}.hljs-literal,.nginx .hljs-title{color:#aa0d91}.method,.hljs-list .hljs-title,.hljs-tag .hljs-title,.setting .hljs-value,.hljs-winutils,.tex .hljs-command,.http .hljs-title,.hljs-request,.hljs-status,.hljs-name{color:#008}.hljs-envvar,.tex .hljs-special{color:#660}.hljs-string{color:#c41a16}.hljs-tag .hljs-value,.hljs-cdata,.hljs-filter .hljs-argument,.hljs-attr_selector,.apache .hljs-cbracket,.hljs-date,.hljs-regexp{color:#080}.hljs-sub .hljs-identifier,.hljs-pi,.hljs-tag,.hljs-tag .hljs-keyword,.hljs-decorator,.ini .hljs-title,.hljs-shebang,.hljs-prompt,.hljs-hexcolor,.hljs-rule .hljs-value,.hljs-symbol,.hljs-symbol .hljs-string,.hljs-number,.css .hljs-function,.hljs-function .hljs-title,.coffeescript .hljs-attribute{color:#A0C}.hljs-function .hljs-title{font-weight:bold;color:#000}.hljs-class .hljs-title,.smalltalk .hljs-class,.hljs-type,.hljs-typename,.hljs-tag .hljs-attribute,.hljs-doctype,.hljs-class .hljs-id,.hljs-built_in,.setting,.hljs-params,.clojure .hljs-attribute{color:#5c2699}.hljs-variable{color:#3f6e74}.css .hljs-tag,.hljs-rule .hljs-property,.hljs-pseudo,.hljs-subst{color:#000}.css .hljs-class,.css .hljs-id{color:#9b703f}.hljs-value .hljs-important{color:#ff7700;font-weight:bold}.hljs-rule .hljs-keyword{color:#c5af75}.hljs-annotation,.apache .hljs-sqbracket,.nginx .hljs-built_in{color:#9b859d}.hljs-preprocessor,.hljs-preprocessor *,.hljs-pragma{color:#643820}.tex .hljs-formula{background-color:#eee;font-style:italic}.diff .hljs-header,.hljs-chunk{color:#808080;font-weight:bold}.diff .hljs-change{background-color:#bccff9}.hljs-addition{background-color:#baeeba}.hljs-deletion{background-color:#ffc8bd}.hljs-comment .hljs-doctag{font-weight:bold}.method .hljs-id{color:#000}</style><style>div.title { padding-top: 40px; } div.afterTitles { height: 15px; }</style><meta charset="utf-8"></p>
<style>img { max-width: 100%; }</style>
<script type="text/x-mathjax-config">MathJax.Hub.Config({ TeX: { equationNumbers: {autoNumber: "AMS"} } });</script><span style="display:none">$$\newcommand{\n}{\hat{n}}\newcommand{\thetai}{\theta_\mathrm{i}}\newcommand{\thetao}{\theta_\mathrm{o}}\newcommand{\d}[1]{\mathrm{d}#1}\newcommand{\w}{\hat{\omega}}\newcommand{\wi}{\w_\mathrm{i}}\newcommand{\wo}{\w_\mathrm{o}}\newcommand{\wh}{\w_\mathrm{h}}\newcommand{\Li}{L_\mathrm{i}}\newcommand{\Lo}{L_\mathrm{o}}\newcommand{\Le}{L_\mathrm{e}}\newcommand{\Lr}{L_\mathrm{r}}\newcommand{\Lt}{L_\mathrm{t}}\newcommand{\O}{\mathrm{O}}\newcommand{\degrees}{{^{\large\circ}}}\newcommand{\T}{\mathsf{T}}\newcommand{\mathset}[1]{\mathbb{#1}}\newcommand{\Real}{\mathset{R}}\newcommand{\Integer}{\mathset{Z}}\newcommand{\Boolean}{\mathset{B}}\newcommand{\Complex}{\mathset{C}}\newcommand{\un}[1]{\,\mathrm{#1}}$$
<p><span style="display:none">$$\newcommand{\n}{\hat{n}}\newcommand{\thetai}{\theta_\mathrm{i}}\newcommand{\thetao}{\theta_\mathrm{o}}\newcommand{\d}[1]{\mathrm{d}#1}\newcommand{\w}{\hat{\omega}}\newcommand{\wi}{\w_\mathrm{i}}\newcommand{\wo}{\w_\mathrm{o}}\newcommand{\wh}{\w_\mathrm{h}}\newcommand{\Li}{L_\mathrm{i}}\newcommand{\Lo}{L_\mathrm{o}}\newcommand{\Le}{L_\mathrm{e}}\newcommand{\Lr}{L_\mathrm{r}}\newcommand{\Lt}{L_\mathrm{t}}\newcommand{\O}{\mathrm{O}}\newcommand{\degrees}{{^{\large\circ}}}\newcommand{\T}{\mathsf{T}}\newcommand{\mathset}[1]{\mathbb{#1}}\newcommand{\Real}{\mathset{R}}\newcommand{\Integer}{\mathset{Z}}\newcommand{\Boolean}{\mathset{B}}\newcommand{\Complex}{\mathset{C}}\newcommand{\un}[1]{,\mathrm{#1}}$$
</span>
<span class="md"><p><title>Filament Materials Guide</title><div class="title"> Filament Materials Guide </div>
<span class="md"><p><title>Filament Materials Guide</title><div class="title"> Filament Materials Guide </div></p>
<div class="afterTitles"></div>
</p><p>
</p>
@@ -504,7 +504,7 @@ when using textures.</p>
This property can dramatically change the appearance of a surface. Non-metallic surfaces have
chromatic diffuse reflection and achromatic specular reflection (reflected light does not change
color). Metallic surfaces do not have any diffuse reflection and chromatic specular reflection
(reflected light takes on the color of the surfaced as defined by <code>baseColor</code>).
(reflected light takes on the color of the surfaced as defined by <code>baseColor</code>).
</p><p>
The effect of <code>metallic</code> is shown in <a href="#figure_metallicproperty" target="_self">figure&nbsp;1</a> (click on the image to see a
larger version).
@@ -597,7 +597,7 @@ indices.
base layer described by the properties above. The sheen layer always sits below the clear coat layer
if such a layer is present.</p>
</p><p>
The sheen layer can be used to represent cloth and fabric materials. Please refer to
The sheen layer can be used to represent cloth and fabric materials. Please refer to
section <a href="#toc3.3" target="_self">3.3</a> for more information about cloth and fabric materials.
</p><p>
The effect of <code>sheenColor</code> is shown in <a href="#figure_materialsheencolor" target="_self">figure&nbsp;7</a>
@@ -1220,10 +1220,10 @@ non-shader data.</p>
name must be a valid GLSL identifier. Entries have an optional <code>precision</code>, which can be
one of <code>default</code> (best precision for the platform, typically <code>high</code> on desktop, <code>medium</code> on
mobile), <code>low</code>, <code>medium</code>, <code>high</code>. The type must be one of the types described in
<a href="#table_materialparamstypes" target="_self">table&nbsp;14</a>. For Android external textures, entries also have an optional
transformName parameter to specify the name of the material parameter that will be
<a href="#table_materialparamstypes" target="_self">table&nbsp;14</a>. For Android external textures, entries also have an optional
transformName parameter to specify the name of the material parameter that will be
used to expose the transform matrix associated with the external sampler. In iOS and Vulkan,
this will always be identity.
this will always be identity.
</p></dd></dl><div class='table'>
<a class="target" name="table_materialparamstypes">&nbsp;</a><table class="table longtable"><tr><th style="text-align:left"> Type </th><th style="text-align:left"> Description </th></tr>
<tr><td style="text-align:left"> bool </td><td style="text-align:left"> Single boolean </td></tr>
@@ -2361,7 +2361,10 @@ type aliases:</p>
<tr><td style="text-align:left"> <strong class="asterisk">getWorldFromViewMatrix()</strong> </td><td style="text-align:center"> float4&times;4 </td><td style="text-align:left"> Matrix that converts from view/eye space to world space </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">getClipFromViewMatrix()</strong> </td><td style="text-align:center"> float4&times;4 </td><td style="text-align:left"> Matrix that converts from view/eye space to clip (NDC) space </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">getViewFromClipMatrix()</strong> </td><td style="text-align:center"> float4&times;4 </td><td style="text-align:left"> Matrix that converts from clip (NDC) space to view/eye space </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">getEyeFromViewMatrix()</strong> </td><td style="text-align:center"> float4&times;4 </td><td style="text-align:left"> Matrix that converts from view space to eye space </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">getEyeFromViewMatrix(int eyeIndex)</strong> </td><td style="text-align:center"> float4&times;4 </td><td style="text-align:left"> Matrix that converts from view space to eye space for the eye referred to by eyeIndex </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">getClipFromWorldMatrix()</strong> </td><td style="text-align:center"> float4&times;4 </td><td style="text-align:left"> Matrix that converts from world to clip (NDC) space </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">getClipFromWorldMatrix(int eyeIndex)</strong> </td><td style="text-align:center"> float4&times;4 </td><td style="text-align:left"> Matrix that converts from world to clip (NDC) space for the eye referred to by eyeIndex </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">getWorldFromClipMatrix()</strong> </td><td style="text-align:center"> float4&times;4 </td><td style="text-align:left"> Matrix that converts from clip (NDC) space to world space </td></tr>
</table></div>
</p>
@@ -2501,7 +2504,7 @@ a good compromise between runtime performance and size.</p>
metadata in JSON format. The example below prints out the list of parameters defined in Filament's
standard skybox material. It produces a list of 2 parameters, named <code>showSun</code> and <code>skybox</code>,
respectively a boolean and a cubemap texture.</p>
</p><pre class="listing backtick"><code><span class="line"></span>$ matc --reflect parameters filament/src/materials/skybox.mat
</p><pre class="listing backtick"><code><span class="line"></span>$ matc --reflect parameters filament/src/materials/skybox.mat
<span class="line"></span>{
<span class="line"></span> &quot;parameters&quot;: [
<span class="line"></span> {
@@ -2600,7 +2603,7 @@ Finally if <code>variantFilter</code> contains the <code>fog</code> filter, an e
</p><p>
Be aware that <code>external</code> samplers account for 2 regular samplers.</div>
</p><p>
</p></span><div id="mdContextMenu" style="visibility:hidden"></div><div class="markdeepFooter"><i>formatted by <a href="https://casual-effects.com/markdeep" style="color:#999">Markdeep&nbsp;1.18&nbsp;&nbsp;</a></i><div style="display:inline-block;font-size:13px;font-family:'Times New Roman',serif;vertical-align:middle;transform:translate(-3px,-1px)rotate(135deg);">&#x2712;</div></div><script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.6/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</p></span><div id="mdContextMenu" style="visibility:hidden"></div><div class="markdeepFooter"><i>formatted by <a href="https://casual-effects.com/markdeep" style="color:#999">Markdeep&nbsp;1.18&nbsp;&nbsp;</a></i><div style="display:inline-block;font-size:13px;font-family:'Times New Roman',serif;vertical-align:middle;transform:translate(-3px,-1px)rotate(135deg);">&#x2712;</div></div><script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
+2 -2
View File
@@ -250,7 +250,7 @@ execution profile data.</p>
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../notes/framegraph.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<a rel="next prefetch" href="../notes/performance_analysis.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
@@ -264,7 +264,7 @@ execution profile data.</p>
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../notes/framegraph.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<a rel="next prefetch" href="../notes/performance_analysis.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
+2 -2
View File
@@ -331,7 +331,7 @@ nodes</li>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../notes/coverage.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<a rel="prev" href="../notes/performance_analysis.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
@@ -345,7 +345,7 @@ nodes</li>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../notes/coverage.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<a rel="prev" href="../notes/performance_analysis.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
+352
View File
@@ -0,0 +1,352 @@
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Performance analysis - Filament</title>
<!-- Custom HTML head -->
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="../highlight.css">
<link rel="stylesheet" href="../tomorrow-night.css">
<link rel="stylesheet" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<!-- MathJax -->
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<!-- Provide site root to javascript -->
<script>
var path_to_root = "../";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
</script>
<!-- Start loading toc.js asap -->
<script src="../toc.js"></script>
</head>
<body>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
var sidebar = null;
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div style="display:flex;align-items:center;justify-content:center">
<img class="flogo" src="../images/filament_logo_small.png"></img>
</div>
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<!-- Filament: disable themes because the markdeep part does not look good for dark themes -->
<!--
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
-->
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Filament</h1>
<div class="right-buttons">
<a href="https://github.com/google/filament" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="performance-analysis"><a class="header" href="#performance-analysis">Performance Analysis</a></h1>
<h2 id="android"><a class="header" href="#android">Android</a></h2>
<h3 id="prerequisites"><a class="header" href="#prerequisites">Prerequisites</a></h3>
<ul>
<li>Download and install Android GPU Inspector (AGI). See https://developer.android.com/agi.</li>
</ul>
<hr />
<h4 id="profiling"><a class="header" href="#profiling">Profiling</a></h4>
<ol>
<li>
<p>Before profiling the application or analyzing the performance in a consistent way, ideally
the <strong>GPU frequency on the target hardware should get locked</strong>. In order to do this, you need to do the following:</p>
<ol>
<li>Ensure your device is OEM unlocked. If your phone is carrier-locked, you may need
to wait a period, such as 60 or 90 days after activation to be eligible for unlocking.
Some phones don't support this at all. You will need to enable developer options
(e.g. Settings &gt; About Phone and tap the Build number 7 times). You need to go to
Settings &gt; System &gt; Developer options and toggle on "OEM unlocking".</li>
<li>Next, you need to unlock the phone. You will need to install Android SDK Platform
Tools on you computer, enable "USB debugging" in your phone's Settings &gt; System &gt; Developer options.</li>
<li>Connect the phone to the computer via a USB cable and run from the command line:
<pre><code class="language-shell">adb reboot bootloader
# once the phone is in bootloader mode, run the following to begin the unlocking
# process:
fastboot flashing unlock
</code></pre>
A warning will appear on your phone's screen. Use the volume buttons to navigate and
the power button to select the "Unlock the bootloader" option. The phone will perform
a factory data reset and reboot with an unlocked bootloader.</li>
<li>You would need to flash an image to the phone with root permissions, such as a *-userdebug or *-eng
build. One way to do this is with the <a href="https://flash.android.com/">Android Flash Tool</a>. Connect the
tool to your device and find a build to flash ending in <code>-userdebug</code> or <code>-eng</code>. Once you have that
selected run <code>Install build</code>.</li>
<li>Shell into your device as root and configure the gpu frequency to be locked, e.g.:
<pre><code class="language-shell">adb shell
su
# navigate to the system GPU directory. this varies on different phones. One phone might have
# it at /sys/class/kgsl/kgsl-3d0 and another might be in something similar, maybe with "mali" instead
# of "kgsl". At the time of writing this for the device at hand it was /sys/devices/platform/1f000000.mali
cd /sys/devices/platform/1f000000.mali
# get the current available GPU governors and frequencies.
# note that some systems may have these at gpu_available_governors and gpu_available_frequencies, but
# the system at the time of writing this had available_governors and available_frequences
cat available_governors
cat available_frequencies
# depending on the governors, you may want to set it prioritize performance over other things like
# battery. Some systems allow you to do this with something like (although, for the device used at the
# time of writing this did not have an equivalent option):
echo performance &gt; gpu_governor
# finally, lock your frequency in, usually to something high like 897 MHz. Some systems may have you
# pipe the value to gpu_min_freq and gpu_max_freq, but the system used at the time of writing this had:
echo 940000 &gt; hint_min_freq
echo 940000 &gt; hint_max_freq
# you can typically verify the GPU is running at that frequency consistently by running something like
# the following a few times over time, which should show the frequency you want to lock the device to:
cat cur_freq
</code></pre>
</li>
<li>You may need to re-apply the <code>hint_min_freq</code> just before starting the profiling trace and check before and after
that the frequency remained at the value expected. Some systems may adjust the frequency on you, but you
may want to ensure the frequency remains the same through the analysis.</li>
<li>The GPU frequency settings should be undone after restarting the device, but after you have done your app profiling,
you can revert the state of the device, such as the OS build image, back to the way you had it
initially, as needed.</li>
</ol>
</li>
<li>
<p>Build a release build of Filament with the applicable backend(s) enabled
<em>(+ any special flags for enabling sys strace. Nothing special is needed for Vulkan or WebGPU aside from
building a release build with no flags)</em> <em>(debug builds for this are useless)</em></p>
<pre><code class="language-shell"># the following command assumes you are in the root filament directory
# and ANDROID_HOME is exported (and possibly also CC and CXX on linux as needed)
#
# NOTE: to build with WebGPU support you need to explicitly include the -W flag
# (it doesn't get compiled in by default), e.g.:
# ./build.sh -W -p android,desktop -i release
#
# Note that you can speed this up a bit (and reduce disk space usage) by limiting the target to just
# the ABI you plan on testing with the -q flag, e.g. -q arm64-v8a. If you do this, you
# will need to update the android/gradle.properties file to specify the ABI(s) you are targeting
# with the com.google.android.filament.abis property.
# Thus, a build command that would target BOTH Vulkan AND WebGPU AND only target the ARM64 ABI would look something
# like:
# ./build.sh -W -q arm64-v8a -p android,desktop -i release
./build.sh -p android,desktop -i release
</code></pre>
</li>
<li>
<p>Connect your Android device to your computer via a USB cord with USB debugging enabled and configure
the system property to default to the desired backend, e.g.
<em>(to determine how these numbers map to the backends, see the <code>enum class Backend</code>
definition in <a href="../../../../filament/backend/include/backend/DriverEnums.h">filament/filament/backend/include/backend/DriverEnums.h</a>)</em>:</p>
<pre><code class="language-shell"># to set the backend to Vulkan:
adb shell setprop debug.filament.backend 2
# to set the backend to WebGPU:
adb shell setprop debug.filament.backend 4
# to view the current property:
adb shell getprop debug.filament.backend
</code></pre>
</li>
<li>
<p>Build and run a sample, e.g. sample-gltf-viewer, on your Android device
<em>(<a href="https://developer.android.com/studio">Android Studio</a> recommended)</em>.</p>
</li>
<li>
<p>Run <a href="https://developer.android.com/agi">AGI</a> and follow instructions to profile the app/system
with trace capture(s). See https://developer.android.com/agi/start for more details to
get started.</p>
<ul>
<li>We typically only run "Capture System Profiler trace" <em>(not necessarily "Capture Frame Profiler trace")</em></li>
<li>When configuring the trace:
<ul>
<li>For both the WebGPU and Vulkan backends configure the profiler for the Vulkan API
<em>(since WebGPU should be using Vulkan under the hood as well)</em></li>
<li>Running for ~1 seconds should suffice</li>
<li>Hit the "Configure" button in Trace objects, select "Switch to advanced mode" and add:
<pre><code>data_sources {
config {
name: "track_event"
track_event_config {
disabled_categories: "*"
enabled_categories: "filament/filament"
enabled_categories: "filament/jobsystem"
enabled_categories: "filament/gltfio"
}
}
}
</code></pre>
</li>
</ul>
</li>
<li>One you open the trace, zoom into a series of frames to get a sense of generally how long they typically take
<em>(use <code>W</code>, <code>S</code>, <code>A</code> and <code>D</code> keys and mouse wheel for navigation)</em> and find a representative one.
We are most interested in the performance of the
<code>FEngine::loop</code> thread, how long it takes, overlap in activities/processes/commands, reduction in queue submissions,
etc. Similarly, we can view GPU timeline as it relates to that. We want to see overlapping
shader invocations and non-interrupted fragment shader runs.</li>
</ul>
</li>
</ol>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../notes/coverage.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../notes/framegraph.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../notes/coverage.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../notes/framegraph.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../elasticlunr.min.js"></script>
<script src="../mark.min.js"></script>
<script src="../searcher.js"></script>
<script src="../clipboard.min.js"></script>
<script src="../highlight.js"></script>
<script src="../book.js"></script>
<!-- Custom JS scripts -->
</div>
</body>
</html>
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+27 -31
View File
@@ -995,10 +995,10 @@ Value
name must be a valid GLSL identifier. Entries have an optional `precision`, which can be
one of `default` (best precision for the platform, typically `high` on desktop, `medium` on
mobile), `low`, `medium`, `high`. The type must be one of the types described in
table [materialParamsTypes]. For Android external textures, entries also have an optional
transformName parameter to specify the name of the material parameter that will be
table [materialParamsTypes]. For Android external textures, entries also have an optional
transformName parameter to specify the name of the material parameter that will be
used to expose the transform matrix associated with the external sampler. In iOS and Vulkan,
this will always be identity.
this will always be identity.
Type | Description
:----------------------|:---------------------------------
@@ -1096,37 +1096,30 @@ Value
`bool` or `number`, depending on the `type` of the constant. The type must be one of the types
described in table [materialConstantsTypes].
Constants may also be specified as mutable by setting the `mutable` property to `true`. Only
`bool` spec constants may be specified as mutable.
| Type | Description | Default | May be mutable? |
|:------|:------------------------------|:--------|-----------------|
| int | A signed, 32 bit GLSL int | | no |
| float | A single-precision GLSL float | 0.0 | no |
| bool | A GLSL bool | false | yes |
Type | Description | Default
:----------------------|:-----------------------------------------|:------------------
int | A signed, 32 bit GLSL int | 0
float | A single-precision GLSL float | 0.0
bool | A GLSL bool | false
[Table [materialConstantsTypes]: Material constants types]
Description
: Lists the constant parameters accepted by your material. These constants can be set, or
"specialized", at runtime when loading a material package. Multiple materials can be loaded from
the same material package with differing constant parameter specializations. If a constant
parameter is specialized as mutable, it may be changed at any time on a per-instance basis via
`MaterialInstance::setConstant(name, value)`. Otherwise, once a material is loaded from a
material package, its constant parameters cannot be changed.
Compared to regular parameters, constant parameters allow the compiler to generate more
efficient code. Access constant parameters from the shader by prefixing the name with
`materialConstant_`. For example, a constant parameter named `myConstant` is accessed in the
shader as `materialConstant_myConstant`. If a constant parameter is not set at runtime, the
default is used.
the same material package with differing constant parameter specializations. Once a material is
loaded from a material package, its constant parameters cannot be changed. Compared to regular
parameters, constant parameters allow the compiler to generate more efficient code. Access
constant parameters from the shader by prefixing the name with `materialConstant_`. For example,
a constant parameter named `myConstant` is accessed in the shader as
`materialConstant_myConstant`. If a constant parameter is not set at runtime, the default is
used.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON
material {
constants : [
{
name : overrideAlpha,
type : bool,
mutable : true,
type : bool
},
{
name : customAlpha,
@@ -2483,14 +2476,17 @@ type aliases:
### Matrices
Name | Type | Description
:-----------------------------------|:--------:|:------------------------------------
**getViewFromWorldMatrix()** | float4x4 | Matrix that converts from world space to view/eye space
**getWorldFromViewMatrix()** | float4x4 | Matrix that converts from view/eye space to world space
**getClipFromViewMatrix()** | float4x4 | Matrix that converts from view/eye space to clip (NDC) space
**getViewFromClipMatrix()** | float4x4 | Matrix that converts from clip (NDC) space to view/eye space
**getClipFromWorldMatrix()** | float4x4 | Matrix that converts from world to clip (NDC) space
**getWorldFromClipMatrix()** | float4x4 | Matrix that converts from clip (NDC) space to world space
Name | Type | Description
:-----------------------------------------|:--------:|:------------------------------------
**getViewFromWorldMatrix()** | float4x4 | Matrix that converts from world space to view/eye space
**getWorldFromViewMatrix()** | float4x4 | Matrix that converts from view/eye space to world space
**getClipFromViewMatrix()** | float4x4 | Matrix that converts from view/eye space to clip (NDC) space
**getViewFromClipMatrix()** | float4x4 | Matrix that converts from clip (NDC) space to view/eye space
**getEyeFromViewMatrix()** | float4x4 | Matrix that converts from view space to eye space
**getEyeFromViewMatrix(int eyeIndex)** | float4x4 | Matrix that converts from view space to eye space for the eye referred to by eyeIndex
**getClipFromWorldMatrix()** | float4x4 | Matrix that converts from world to clip (NDC) space
**getClipFromWorldMatrix(int eyeIndex)** | float4x4 | Matrix that converts from world to clip (NDC) space for the eye referred to by eyeIndex
**getWorldFromClipMatrix()** | float4x4 | Matrix that converts from clip (NDC) space to world space
### Frame constants
+2
View File
@@ -3,6 +3,7 @@
- [Introduction](./dup/intro.md)
- [Build](./dup/building.md)
- [Build for Android on Windows](./build/windows_android.md)
- [Maven Release](./build/maven_release.md)
- [Contribute](./dup/contributing.md)
- [Coding Style](./dup/code_style.md)
- [Core Concepts](./main/README.md)
@@ -24,6 +25,7 @@
- [Running with ASAN and UBSAN](./notes/asan_ubsan.md)
- [Using Instruments on macOS](./notes/instruments.md)
- [Code coverage analysis](./notes/coverage.md)
- [Performance analysis](./notes/performance_analysis.md)
- [Framegraph](./notes/framegraph.md)
- [Libraries](./notes/libs.md)
- [bluegl](./dup/bluegl.md)
@@ -0,0 +1,89 @@
# Maven Release
## Register for a Sonatype Account
First, you'll need to register for a Sonatype account at the [Central
Portal](https://central.sonatype.org/register/central-portal/).
To publish under the `com.google.android` namespace, you'll also need to email
`central-support@sonatype.com` with your account information to request access.
Then, generate a user token through the Sonatype website. Navigate to
[https://central.sonatype.com/account](https://central.sonatype.com/account) and select **Generate
User Token**.
Finally, add the generated token credentials to `~/.gradle/gradle.properties`. (Note: these are
different from the credentials used to log into your Sonatype account):
```gradle
# Generated Sonatype token
sonatypeUsername=<username>
sonatypePassword=<password>
```
-----
## Signing Key
All release artifacts must be signed. You'll need to create an OpenPGP key pair. See Gradle's
documentation on [Signatory
credentials](https://docs.gradle.org/current/userguide/signing_plugin.html#sec:signatory_credentials)
for instructions.
Update `~/.gradle/gradle.properties` with your new key's credentials:
```gradle
signing.keyId=<key id>
signing.password=<key password>
signing.secretKeyRingFile=<secret key ring file>
```
-----
## Build the Android Release
Make sure `JAVA_HOME` is set correctly. For example:
```bash
export JAVA_HOME=/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home
./build.sh -C -i -p android release
```
-----
## Publish to Sonatype
### A Note on the Legacy Staging Service
Previously, Filament was published to Maven via OSSRH (Open Source Software Repository Hosting). In
2025, this service was [sunsetted](https://central.sonatype.org/pages/ossrh-eol/). Now, we use
Sonatype's [Central Publisher Portal](https://central.sonatype.org/).
The new Central Publisher Portal does not officially support Gradle. However, Sonatype provides a
staging API compatibility service, which works with Filament's Gradle setup.
-----
### 1\. Upload to the Staging API Compatibility Service
```bash
cd android
./gradlew publishToSonatype
```
### 2\. Move the Repository to the Central Publisher Portal
We have a script to automate this. It reads the `sonatypeUsername` and `sonatypePassword` from your
`~/.gradle/gradle.properties` file.
```bash
python3 build/common/close-sonatype-staging-repository.py
```
### 3\. Publish the Release on Sonatype
Navigate to [Maven Central Repository Deployments](https://central.sonatype.com/publishing/deployments).
Here, you should see a new deployment with a **Validated** status and all your artifacts listed. Click
the **Publish** button to publish the artifacts. It typically takes around 5 minutes after clicking
**Publish** for the artifacts to go live.
@@ -6,12 +6,64 @@
- Download and install Android GPU Inspector (AGI). See https://developer.android.com/agi.
### Android with the Vulkan Backend
---
#### Profiling
1. Before profiling the application or analyzing the performance in a consistent way, ideally
the **GPU frequency on the target hardware should get locked**. In order to do this, you need to do the following:
1. Ensure your device is OEM unlocked. If your phone is carrier-locked, you may need
to wait a period, such as 60 or 90 days after activation to be eligible for unlocking.
Some phones don't support this at all. You will need to enable developer options
(e.g. Settings > About Phone and tap the Build number 7 times). You need to go to
Settings > System > Developer options and toggle on "OEM unlocking".
1. Next, you need to unlock the phone. You will need to install Android SDK Platform
Tools on you computer, enable "USB debugging" in your phone's Settings > System > Developer options.
1. Connect the phone to the computer via a USB cable and run from the command line:
```shell
adb reboot bootloader
# once the phone is in bootloader mode, run the following to begin the unlocking
# process:
fastboot flashing unlock
```
A warning will appear on your phone's screen. Use the volume buttons to navigate and
the power button to select the "Unlock the bootloader" option. The phone will perform
a factory data reset and reboot with an unlocked bootloader.
1. You would need to flash an image to the phone with root permissions, such as a *-userdebug or *-eng
build. One way to do this is with the [Android Flash Tool](https://flash.android.com/). Connect the
tool to your device and find a build to flash ending in `-userdebug` or `-eng`. Once you have that
selected run `Install build`.
1. Shell into your device as root and configure the gpu frequency to be locked, e.g.:
```shell
adb shell
su
# navigate to the system GPU directory. this varies on different phones. One phone might have
# it at /sys/class/kgsl/kgsl-3d0 and another might be in something similar, maybe with "mali" instead
# of "kgsl". At the time of writing this for the device at hand it was /sys/devices/platform/1f000000.mali
cd /sys/devices/platform/1f000000.mali
# get the current available GPU governors and frequencies.
# note that some systems may have these at gpu_available_governors and gpu_available_frequencies, but
# the system at the time of writing this had available_governors and available_frequences
cat available_governors
cat available_frequencies
# depending on the governors, you may want to set it prioritize performance over other things like
# battery. Some systems allow you to do this with something like (although, for the device used at the
# time of writing this did not have an equivalent option):
echo performance > gpu_governor
# finally, lock your frequency in, usually to something high like 897 MHz. Some systems may have you
# pipe the value to gpu_min_freq and gpu_max_freq, but the system used at the time of writing this had:
echo 940000 > hint_min_freq
echo 940000 > hint_max_freq
# you can typically verify the GPU is running at that frequency consistently by running something like
# the following a few times over time, which should show the frequency you want to lock the device to:
cat cur_freq
```
1. You may need to re-apply the `hint_min_freq` just before starting the profiling trace and check before and after
that the frequency remained at the value expected. Some systems may adjust the frequency on you, but you
may want to ensure the frequency remains the same through the analysis.
1. The GPU frequency settings should be undone after restarting the device, but after you have done your app profiling,
you can revert the state of the device, such as the OS build image, back to the way you had it
initially, as needed.
1. Build a release build of Filament with the applicable backend(s) enabled
_(+ any special flags for enabling sys strace. Nothing special is needed for Vulkan or WebGPU aside from
building a release build with no flags)_ _(debug builds for this are useless)_
+3
View File
@@ -40,6 +40,7 @@ set(PUBLIC_HDRS
include/filament/Skybox.h
include/filament/Stream.h
include/filament/SwapChain.h
include/filament/Sync.h
include/filament/Texture.h
include/filament/TextureSampler.h
include/filament/ToneMapper.h
@@ -103,6 +104,7 @@ set(SRCS
src/Skybox.cpp
src/Stream.cpp
src/SwapChain.cpp
src/Sync.cpp
src/Texture.cpp
src/ToneMapper.cpp
src/TransformManager.cpp
@@ -132,6 +134,7 @@ set(SRCS
src/details/Skybox.cpp
src/details/Stream.cpp
src/details/SwapChain.cpp
src/details/Sync.cpp
src/details/Texture.cpp
src/details/VertexBuffer.cpp
src/details/View.cpp
+11 -4
View File
@@ -295,6 +295,7 @@ if (FILAMENT_SUPPORTS_WEBGPU)
src/webgpu/WebGPUStrings.h
src/webgpu/WebGPUSwapChain.cpp
src/webgpu/WebGPUSwapChain.h
src/webgpu/WebGPUSync.h
src/webgpu/WebGPUTexture.cpp
src/webgpu/WebGPUTexture.h
src/webgpu/WebGPUTextureHelpers.h
@@ -494,9 +495,6 @@ else()
-Wover-aligned -Wreorder-init-list
-Werror
)
if (CMAKE_CXX_STANDARD EQUAL 20)
list(APPEND FILAMENT_WARNINGS -Wno-deprecated-this-capture)
endif()
endif()
if (APPLE)
@@ -580,7 +578,9 @@ if (APPLE OR LINUX)
test/test_Template.cpp
)
if (FILAMENT_SUPPORTS_WEBGPU)
list(APPEND BACKEND_TEST_SRC test/test_WebGPUAsyncTaskCounter.cpp)
list(APPEND BACKEND_TEST_SRC
test/test_WebGPUAsyncTaskCounter.cpp
test/test_WebGPUComposeSwizzle.cpp)
endif()
if (APPLE)
# Metal-specific tests
@@ -629,6 +629,10 @@ if (APPLE AND NOT IOS)
target_link_libraries(backend_test PRIVATE image imageio)
list(APPEND BACKEND_TEST_DEPS image)
endif()
if (FILAMENT_SUPPORTS_WEBGPU)
target_link_libraries(backend_test PRIVATE webgpu_dawn dawncpp_headers)
list(APPEND BACKEND_TEST_DEPS webgpu_dawn dawncpp_headers)
endif()
set(BACKEND_TEST_COMBINED_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/libbackendtest_combined.a")
combine_static_libs(backend_test "${BACKEND_TEST_COMBINED_OUTPUT}" "${BACKEND_TEST_DEPS}")
@@ -660,6 +664,9 @@ if (LINUX)
add_executable(backend_test_linux test/linux_runner.cpp ${BACKEND_TEST_SRC})
target_link_libraries(backend_test_linux PRIVATE ${BACKEND_TEST_LIBS})
set_target_properties(backend_test_linux PROPERTIES FOLDER Tests)
if (FILAMENT_SUPPORTS_WEBGPU)
target_link_libraries(backend_test_linux PRIVATE webgpu_dawn dawncpp_headers)
endif()
endif()
# ==================================================================================================
@@ -101,6 +101,16 @@ static constexpr uint64_t SWAP_CHAIN_HAS_STENCIL_BUFFER = SWAP_CHAIN_CON
*/
static constexpr uint64_t SWAP_CHAIN_CONFIG_PROTECTED_CONTENT = 0x40;
/**
* Indicates that the SwapChain is configured to use Multi-Sample Anti-Aliasing (MSAA) with the
* given sample points within each pixel. Only supported when isMSAASwapChainSupported(4) is
* true.
*
* This is only supported by EGL(Android). Other GL platforms (GLX, WGL, etc) don't support it
* because the swapchain MSAA settings must be configured before window creation.
*/
static constexpr uint64_t SWAP_CHAIN_CONFIG_MSAA_4_SAMPLES = 0x80;
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_SAMPLER_COUNT = 62; // Maximum needed at feature level 3.
static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects.
@@ -629,6 +639,18 @@ enum class PrimitiveType : uint8_t {
TRIANGLE_STRIP = 5 //!< triangle strip
};
[[nodiscard]] constexpr bool isStripPrimitiveType(const PrimitiveType type) {
switch (type) {
case PrimitiveType::POINTS:
case PrimitiveType::LINES:
case PrimitiveType::TRIANGLES:
return false;
case PrimitiveType::LINE_STRIP:
case PrimitiveType::TRIANGLE_STRIP:
return true;
}
}
/**
* Supported uniform types
*/
@@ -38,6 +38,7 @@ struct HwRenderPrimitive;
struct HwRenderTarget;
struct HwStream;
struct HwSwapChain;
struct HwSync;
struct HwTexture;
struct HwTimerQuery;
struct HwVertexBufferInfo;
@@ -151,6 +152,7 @@ using RenderPrimitiveHandle = Handle<HwRenderPrimitive>;
using RenderTargetHandle = Handle<HwRenderTarget>;
using StreamHandle = Handle<HwStream>;
using SwapChainHandle = Handle<HwSwapChain>;
using SyncHandle = Handle<HwSync>;
using TextureHandle = Handle<HwTexture>;
using TimerQueryHandle = Handle<HwTimerQuery>;
using VertexBufferHandle = Handle<HwVertexBuffer>;
@@ -29,6 +29,7 @@
namespace filament::backend {
class CallbackHandler;
class Driver;
/**
@@ -42,6 +43,9 @@ public:
struct SwapChain {};
struct Fence {};
struct Stream {};
struct Sync {};
using SyncCallback = void(*)(Sync* UTILS_NONNULL sync, void* UTILS_NULLABLE userData);
class ExternalImageHandle;
@@ -27,12 +27,12 @@ namespace filament::backend {
* A PresentCallable is a callable object that, when called, schedules a frame for presentation on
* a SwapChain.
*
* Typically, Filament's backend is responsible scheduling a frame's presentation. However, there
* are certain cases where the application might want to control when a frame is scheduled for
* Typically, Filament's backend is responsible for scheduling a frame's presentation. However,
* there are certain cases where the application might want to control when a frame is scheduled for
* presentation.
*
* For example, on iOS, UIKit elements can be synchronized to 3D content by scheduling a present
* within a CATransation:
* within a CATransaction:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* void myFrameScheduledCallback(PresentCallable presentCallable, void* user) {
@@ -55,12 +55,13 @@ namespace filament::backend {
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @remark Only Filament's Metal backend supports PresentCallables and frame callbacks. Other
* backends ignore the callback (which will never be called) and proceed normally.
* @remark The PresentCallable mechanism for user-controlled presentation is only supported by
* Filament's Metal backend. On other backends, the FrameScheduledCallback is still invoked, but the
* PresentCallable passed to it is a no-op and calling it has no effect.
*
* Applications *must* call each PresentCallable they receive. Each PresentCallable represents a
* frame that is waiting to be presented. If an application fails to call a PresentCallable, a
* memory leak could occur. To "cancel" the presentation of a frame, pass false to the
* When using the Metal backend, applications *must* call each PresentCallable they receive. Each
* PresentCallable represents a frame that is waiting to be presented, and failing to call it
* will result in a memory leak. To "cancel" the presentation of a frame, pass false to the
* PresentCallable, which will cancel the presentation of the frame and release associated memory.
*
* @see Renderer, SwapChain::setFrameScheduledCallback
@@ -69,6 +70,7 @@ class UTILS_PUBLIC PresentCallable {
public:
using PresentFn = void(*)(bool presentFrame, void* user);
static void noopPresent(bool, void*) {}
PresentCallable(PresentFn fn, void* user) noexcept;
~PresentCallable() noexcept = default;
+1 -4
View File
@@ -20,7 +20,6 @@
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Invocable.h>
#include <utils/bitset.h>
#include <backend/DriverEnums.h>
@@ -67,7 +66,6 @@ public:
using DescriptorBindingsInfo = utils::FixedCapacityVector<Descriptor>;
using DescriptorSetInfo = std::array<DescriptorBindingsInfo, MAX_DESCRIPTOR_SET_COUNT>;
using SpecializationConstantsInfo = utils::FixedCapacityVector<SpecializationConstant>;
using MutableSpecConstantsInfo = utils::bitset8;
using ShaderBlob = utils::FixedCapacityVector<uint8_t>;
using ShaderSource = std::array<ShaderBlob, SHADER_TYPE_COUNT>;
@@ -105,8 +103,7 @@ public:
Program& descriptorBindings(backend::descriptor_set_t set,
DescriptorBindingsInfo descriptorBindings) noexcept;
Program& specializationConstants(SpecializationConstantsInfo specConstants,
uint32_t firstMutableId, MutableSpecConstantsInfo mutableSpecConstants) noexcept;
Program& specializationConstants(SpecializationConstantsInfo specConstants) noexcept;
struct PushConstant {
utils::CString name;
@@ -85,6 +85,15 @@ public:
*/
virtual bool isSRGBSwapChainSupported() const noexcept;
/**
* Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_MSAA_*_SAMPLES flag.
* The default implementation returns false.
*
* @param samples The number of samples
* @return true if SWAP_CHAIN_CONFIG_MSAA_*_SAMPLES is supported, false otherwise.
*/
virtual bool isMSAASwapChainSupported(uint32_t samples) const noexcept;
/**
* Return whether protected contexts are supported by this backend.
* If protected context are supported, the SWAP_CHAIN_CONFIG_PROTECTED_CONTENT flag can be
@@ -276,6 +285,26 @@ public:
*/
virtual backend::FenceStatus waitFence(Fence* UTILS_NONNULL fence, uint64_t timeout) noexcept;
// --------------------------------------------------------------------------------------------
// Sync support
/**
* Creates a Sync. These can be used for frame synchronization externally
* (certain platform implementations can be exported to handles that can
* be used in other processes).
*
* @return A Sync object.
*/
virtual Platform::Sync* UTILS_NONNULL createSync() noexcept;
/**
* Destroys a sync. If called with a sync not created by this platform
* object, this will lead to undefined behavior.
*
* @param sync The sync to destroy, that was created by this platform
* instance.
*/
virtual void destroySync(Platform::Sync* UTILS_NONNULL sync) noexcept;
// --------------------------------------------------------------------------------------------
// Streaming support
@@ -98,8 +98,9 @@ protected:
void terminate() noexcept override;
bool isProtectedContextSupported() const noexcept override;
bool isSRGBSwapChainSupported() const noexcept override;
bool isMSAASwapChainSupported(uint32_t samples) const noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
@@ -58,6 +58,17 @@ public:
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(ExternalImageHandle externalImage) noexcept;
/**
* Converts a sync to an external file descriptor, if possible. Accepts an
* opaque handle to a sync, as well as a pointer to where the fd should be
* stored.
* @param sync The sync to be converted to a file descriptor.
* @param fd A pointer to where the file descriptor should be stored.
* @return `true` on success, `false` on failure. The default implementation
* returns `false`.
*/
bool convertSyncToFd(Platform::Sync* sync, int* fd) noexcept;
protected:
// --------------------------------------------------------------------------------------------
// Platform Interface
@@ -74,6 +85,10 @@ protected:
// --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface
struct SyncEGLAndroid : public Platform::Sync {
EGLSyncKHR sync;
};
void terminate() noexcept override;
void beginFrame(
@@ -89,9 +104,10 @@ protected:
*/
void setPresentationTime(int64_t presentationTimeInNanosecond) noexcept override;
Stream* createStream(void* nativeStream) noexcept override;
void destroyStream(Stream* stream) noexcept override;
Platform::Sync* createSync() noexcept override;
void destroySync(Platform::Sync* sync) noexcept override;
void attach(Stream* stream, intptr_t tname) noexcept override;
void detach(Stream* stream) noexcept override;
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept override;
@@ -17,8 +17,9 @@
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H
#include <backend/Platform.h>
#include <backend/CallbackHandler.h>
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include <bluevk/BlueVK.h>
@@ -29,9 +30,9 @@
#include <cstddef>
#include <functional>
#include <string>
#include <tuple>
#include <unordered_set>
#include <string>
#include <stddef.h>
#include <stdint.h>
@@ -45,6 +46,10 @@ using SwapChain = Platform::SwapChain;
*/
struct VulkanPlatformPrivate;
// Forward declare the fence status that will be maintained by the command
// buffer manager.
struct VulkanCmdFence;
/**
* A Platform interface that creates a Vulkan backend.
*/
@@ -229,6 +234,25 @@ public:
virtual SwapChainPtr createSwapChain(void* nativeWindow, uint64_t flags = 0,
VkExtent2D extent = {0, 0});
/**
* Creates a Platform::Sync object, which tracks a fence and its status,
* and allows conversion to an external sync.
* @param fence The underlying VkFence to use for synchronization.
* @param fenceStatus An object tracking the fence's state
* @return A Platform::Sync object tracking the provided fence.
*/
virtual Platform::Sync* createSync(VkFence fence,
std::shared_ptr<VulkanCmdFence> fenceStatus) noexcept;
/**
* Destroys a sync. If called with a sync not created by this platform
* object, this will lead to undefined behavior.
*
* @param sync The sync to destroy, which was created by this platform
* instance.
*/
virtual void destroySync(Platform::Sync* sync) noexcept;
/**
* Allows implementers to provide instance extensions that they'd like to include in the
* instance creation.
@@ -408,12 +432,19 @@ public:
}
protected:
struct VulkanSync : public Platform::Sync {
VkFence fence;
std::shared_ptr<VulkanCmdFence> fenceStatus;
};
virtual ExtensionSet getSwapchainInstanceExtensions() const;
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept;
virtual VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept;
private:
// Platform dependent helper methods
static ExtensionSet getSwapchainInstanceExtensionsImpl();
@@ -44,6 +44,17 @@ public:
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const override;
/**
* Converts a sync to an external file descriptor, if possible. Accepts an
* opaque handle to a sync, as well as a pointer to where the fd should be
* stored.
* @param sync The sync to be converted to a file descriptor.
* @param fd A pointer to where the file descriptor should be stored.
* @return `true` on success, `false` on failure. The default implementation
* returns `false`.
*/
bool convertSyncToFd(Platform::Sync* sync, int* fd) const noexcept;
protected:
virtual ExtensionSet getSwapchainInstanceExtensions() const override;
@@ -51,6 +62,8 @@ protected:
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept override;
virtual VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept override;
private:
struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
AHardwareBuffer* aHardwareBuffer = nullptr;
@@ -19,6 +19,16 @@
#include <backend/Platform.h>
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_X11)
// Resolve the conflicts between webgpu_cpp.h and X11 defines
#undef Always
#undef Success
#undef None
#undef True
#undef False
#undef Status
#undef Bool
#endif
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
@@ -107,6 +107,10 @@
#define DECL_DRIVER_API_R_N(R, N, ...) \
DECL_DRIVER_API_RETURN(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__), PAIR_ARGS_N(PARAM, ##__VA_ARGS__))
#define DECL_DRIVER_API_TAGGED_R_N(R, N, ...) \
DECL_DRIVER_API_RETURN(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__, utils::CString, tag = {}), \
PAIR_ARGS_N(PARAM, ##__VA_ARGS__, utils::CString, tag))
#define DECL_DRIVER_API_SYNCHRONOUS_N(R, N, ...) \
DECL_DRIVER_API_SYNCHRONOUS(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__), PAIR_ARGS_N(PARAM, ##__VA_ARGS__))
@@ -165,35 +169,31 @@ DECL_DRIVER_API_0(finish)
// reset state tracking, if the driver does any state tracking (e.g. GL)
DECL_DRIVER_API_0(resetState)
DECL_DRIVER_API_N(setDebugTag,
backend::HandleBase::HandleId, handleId,
utils::CString, tag)
/*
* Creating driver objects
* -----------------------
*/
DECL_DRIVER_API_R_N(backend::VertexBufferInfoHandle, createVertexBufferInfo,
DECL_DRIVER_API_TAGGED_R_N(backend::VertexBufferInfoHandle, createVertexBufferInfo,
uint8_t, bufferCount,
uint8_t, attributeCount,
backend::AttributeArray, attributes)
DECL_DRIVER_API_R_N(backend::VertexBufferHandle, createVertexBuffer,
DECL_DRIVER_API_TAGGED_R_N(backend::VertexBufferHandle, createVertexBuffer,
uint32_t, vertexCount,
backend::VertexBufferInfoHandle, vbih)
DECL_DRIVER_API_R_N(backend::IndexBufferHandle, createIndexBuffer,
DECL_DRIVER_API_TAGGED_R_N(backend::IndexBufferHandle, createIndexBuffer,
backend::ElementType, elementType,
uint32_t, indexCount,
backend::BufferUsage, usage)
DECL_DRIVER_API_R_N(backend::BufferObjectHandle, createBufferObject,
DECL_DRIVER_API_TAGGED_R_N(backend::BufferObjectHandle, createBufferObject,
uint32_t, byteCount,
backend::BufferObjectBinding, bindingType,
backend::BufferUsage, usage)
DECL_DRIVER_API_R_N(backend::TextureHandle, createTexture,
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTexture,
backend::SamplerType, target,
uint8_t, levels,
backend::TextureFormat, format,
@@ -203,19 +203,19 @@ DECL_DRIVER_API_R_N(backend::TextureHandle, createTexture,
uint32_t, depth,
backend::TextureUsage, usage)
DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureView,
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTextureView,
backend::TextureHandle, texture,
uint8_t, baseLevel,
uint8_t, levelCount)
DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureViewSwizzle,
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTextureViewSwizzle,
backend::TextureHandle, texture,
backend::TextureSwizzle, r,
backend::TextureSwizzle, g,
backend::TextureSwizzle, b,
backend::TextureSwizzle, a)
DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureExternalImage2,
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTextureExternalImage2,
backend::SamplerType, target,
backend::TextureFormat, format,
uint32_t, width,
@@ -223,7 +223,7 @@ DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureExternalImage2,
backend::TextureUsage, usage,
backend::Platform::ExternalImageHandleRef, image)
DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureExternalImage,
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTextureExternalImage,
backend::SamplerType, target,
backend::TextureFormat, format,
uint32_t, width,
@@ -231,7 +231,7 @@ DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureExternalImage,
backend::TextureUsage, usage,
void*, image)
DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureExternalImagePlane,
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTextureExternalImagePlane,
backend::TextureFormat, format,
uint32_t, width,
uint32_t, height,
@@ -239,7 +239,7 @@ DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureExternalImagePlane,
void*, image,
uint32_t, plane)
DECL_DRIVER_API_R_N(backend::TextureHandle, importTexture,
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, importTexture,
intptr_t, id,
backend::SamplerType, target,
uint8_t, levels,
@@ -250,17 +250,17 @@ DECL_DRIVER_API_R_N(backend::TextureHandle, importTexture,
uint32_t, depth,
backend::TextureUsage, usage)
DECL_DRIVER_API_R_N(backend::RenderPrimitiveHandle, createRenderPrimitive,
DECL_DRIVER_API_TAGGED_R_N(backend::RenderPrimitiveHandle, createRenderPrimitive,
backend::VertexBufferHandle, vbh,
backend::IndexBufferHandle, ibh,
backend::PrimitiveType, pt)
DECL_DRIVER_API_R_N(backend::ProgramHandle, createProgram,
DECL_DRIVER_API_TAGGED_R_N(backend::ProgramHandle, createProgram,
backend::Program&&, program)
DECL_DRIVER_API_R_0(backend::RenderTargetHandle, createDefaultRenderTarget)
DECL_DRIVER_API_TAGGED_R_N(backend::RenderTargetHandle, createDefaultRenderTarget)
DECL_DRIVER_API_R_N(backend::RenderTargetHandle, createRenderTarget,
DECL_DRIVER_API_TAGGED_R_N(backend::RenderTargetHandle, createRenderTarget,
backend::TargetBufferFlags, targetBufferFlags,
uint32_t, width,
uint32_t, height,
@@ -270,23 +270,25 @@ DECL_DRIVER_API_R_N(backend::RenderTargetHandle, createRenderTarget,
backend::TargetBufferInfo, depth,
backend::TargetBufferInfo, stencil)
DECL_DRIVER_API_R_0(backend::FenceHandle, createFence)
DECL_DRIVER_API_TAGGED_R_N(backend::FenceHandle, createFence)
DECL_DRIVER_API_R_N(backend::SwapChainHandle, createSwapChain,
DECL_DRIVER_API_TAGGED_R_N(backend::SyncHandle, createSync)
DECL_DRIVER_API_TAGGED_R_N(backend::SwapChainHandle, createSwapChain,
void*, nativeWindow,
uint64_t, flags)
DECL_DRIVER_API_R_N(backend::SwapChainHandle, createSwapChainHeadless,
DECL_DRIVER_API_TAGGED_R_N(backend::SwapChainHandle, createSwapChainHeadless,
uint32_t, width,
uint32_t, height,
uint64_t, flags)
DECL_DRIVER_API_R_0(backend::TimerQueryHandle, createTimerQuery)
DECL_DRIVER_API_TAGGED_R_N(backend::TimerQueryHandle, createTimerQuery)
DECL_DRIVER_API_R_N(backend::DescriptorSetLayoutHandle, createDescriptorSetLayout,
DECL_DRIVER_API_TAGGED_R_N(backend::DescriptorSetLayoutHandle, createDescriptorSetLayout,
backend::DescriptorSetLayout&&, info)
DECL_DRIVER_API_R_N(backend::DescriptorSetHandle, createDescriptorSet,
DECL_DRIVER_API_TAGGED_R_N(backend::DescriptorSetHandle, createDescriptorSet,
backend::DescriptorSetLayoutHandle, dslh)
DECL_DRIVER_API_N(updateDescriptorSetBuffer,
@@ -328,6 +330,7 @@ DECL_DRIVER_API_N(destroySwapChain, backend::SwapChainHandle, sch)
DECL_DRIVER_API_N(destroyStream, backend::StreamHandle, sh)
DECL_DRIVER_API_N(destroyTimerQuery, backend::TimerQueryHandle, sh)
DECL_DRIVER_API_N(destroyFence, backend::FenceHandle, fh)
DECL_DRIVER_API_N(destroySync, backend::SyncHandle, sh)
DECL_DRIVER_API_N(destroyDescriptorSetLayout, backend::DescriptorSetLayoutHandle, dslh)
DECL_DRIVER_API_N(destroyDescriptorSet, backend::DescriptorSetHandle, dsh)
@@ -337,13 +340,15 @@ DECL_DRIVER_API_N(destroyDescriptorSet, backend::DescriptorSetHandle, ds
*/
DECL_DRIVER_API_SYNCHRONOUS_0(void, terminate)
DECL_DRIVER_API_SYNCHRONOUS_N(backend::StreamHandle, createStreamNative, void*, stream)
DECL_DRIVER_API_SYNCHRONOUS_0(backend::StreamHandle, createStreamAcquired)
DECL_DRIVER_API_SYNCHRONOUS_N(backend::StreamHandle, createStreamNative, void*, stream, utils::CString, tag)
DECL_DRIVER_API_SYNCHRONOUS_N(backend::StreamHandle, createStreamAcquired, utils::CString, tag)
DECL_DRIVER_API_SYNCHRONOUS_N(void, setAcquiredImage, backend::StreamHandle, stream, void*, image, const math::mat3f&, transform, backend::CallbackHandler*, handler, backend::StreamCallback, cb, void*, userData)
DECL_DRIVER_API_SYNCHRONOUS_N(void, setStreamDimensions, backend::StreamHandle, stream, uint32_t, width, uint32_t, height)
DECL_DRIVER_API_SYNCHRONOUS_N(int64_t, getStreamTimestamp, backend::StreamHandle, stream)
DECL_DRIVER_API_SYNCHRONOUS_N(void, updateStreams, backend::DriverApi*, driver)
DECL_DRIVER_API_SYNCHRONOUS_N(backend::FenceStatus, getFenceStatus, backend::FenceHandle, fh)
DECL_DRIVER_API_SYNCHRONOUS_N(void, getPlatformSync, backend::SyncHandle, sh,
backend::CallbackHandler*, handler, backend::Platform::SyncCallback, cb, void*, userData)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isTextureFormatSupported, backend::TextureFormat, format)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isTextureSwizzleSupported)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isTextureFormatMipmappable, backend::TextureFormat, format)
@@ -353,6 +358,7 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameBufferFetchMultiSampleSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameTimeSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isAutoDepthResolveSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isSRGBSwapChainSupported)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isMSAASwapChainSupported, uint32_t, samples)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedContentSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isStereoSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported)
@@ -249,8 +249,9 @@ public:
}
void associateTagToHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
// TODO: for now, only pool handles check for use-after-free, so we only keep tags for
// those
if (tag.empty()) {
return;
}
uint32_t key = id;
if (UTILS_LIKELY(isPoolHandle(id))) {
// Truncate the age to get the debug tag
+24
View File
@@ -38,6 +38,11 @@
#include <stdlib.h>
#include <string.h>
#if defined(WIN32)
# include <windows.h> // for VirtualAlloc, VirtualProtect, VirtualFree
# include <utils/unwindows.h>
#endif
using namespace utils;
namespace filament::backend {
@@ -134,6 +139,21 @@ void* CircularBuffer::alloc(size_t size) {
mprotect(guard, BLOCK_SIZE, PROT_NONE);
}
return data;
#elif defined(WIN32)
size_t const BLOCK_SIZE = getBlockSize();
// On Windows, use VirtualAlloc instead of malloc to allocate virtual memory.
// This allows us to set page protection by VirtualProtect.
void* data = VirtualAlloc(nullptr, size * 2 + BLOCK_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
FILAMENT_CHECK_POSTCONDITION(data != nullptr)
<< "couldn't allocate " << (size * 2 / 1024)
<< " KiB of virtual address space for the command buffer";
// guard page at the end
void* guard = (void*)(uintptr_t(data) + size * 2);
DWORD oldProtect = 0;
BOOL ok = VirtualProtect(guard, BLOCK_SIZE, PAGE_NOACCESS, &oldProtect);
FILAMENT_CHECK_POSTCONDITION(ok) << "VirtualProtect failed to set guard page";
return data;
#else
return ::malloc(2 * size);
#endif
@@ -150,6 +170,10 @@ void CircularBuffer::dealloc() noexcept {
mAshmemFd = -1;
}
}
#elif defined(WIN32)
if (mData) {
VirtualFree(mData, 0, MEM_RELEASE);
}
#else
::free(mData);
#endif
+5 -5
View File
@@ -57,7 +57,7 @@ static void printParameterPack(io::ostream& out, const FIRST& first, const REMAI
printParameterPack(out, rest...);
}
static UTILS_NOINLINE UTILS_UNUSED std::string extractMethodName(std::string& command) noexcept {
static UTILS_NOINLINE UTILS_UNUSED std::string_view extractMethodName(std::string_view command) noexcept {
constexpr const char startPattern[] = "::Command<&filament::backend::Driver::";
auto pos = command.rfind(startPattern);
auto end = command.rfind('(');
@@ -133,9 +133,9 @@ template<std::size_t... I>
void CommandType<void (Driver::*)(ARGS...)>::Command<METHOD>::log(std::index_sequence<I...>) noexcept {
#if DEBUG_COMMAND_STREAM
static_assert(UTILS_HAS_RTTI, "DEBUG_COMMAND_STREAM can only be used with RTTI");
std::string command = utils::CallStack::demangleTypeName(typeid(Command).name()).c_str();
DLOG(INFO) << extractMethodName(command) << " : size=" << sizeof(Command);
utils::io::sstream parameterPack;
CString command = CallStack::demangleTypeName(typeid(Command).name());
DLOG(INFO) << extractMethodName({command.data(), command.size()}) << " : size=" << sizeof(Command);
io::sstream parameterPack;
printParameterPack(parameterPack, std::get<I>(mArgs)...);
DLOG(INFO) << "\t" << parameterPack.c_str();
#endif
@@ -164,7 +164,7 @@ void CommandType<void (Driver::*)(ARGS...)>::Command<METHOD>::log() noexcept {
// ------------------------------------------------------------------------------------------------
void CustomCommand::execute(Driver&, CommandBase* base, intptr_t* next) {
*next = CustomCommand::align(sizeof(CustomCommand));
*next = align(sizeof(CustomCommand));
static_cast<CustomCommand*>(base)->mCommand();
static_cast<CustomCommand*>(base)->~CustomCommand();
}
+4
View File
@@ -140,6 +140,10 @@ struct HwFence : public HwBase {
Platform::Fence* fence = nullptr;
};
struct HwSync : public HwBase {
Platform::Sync* sync = nullptr;
};
struct HwSwapChain : public HwBase {
Platform::SwapChain* swapChain = nullptr;
};
+7 -6
View File
@@ -21,7 +21,8 @@
#include <utils/ostream.h>
#ifndef NDEBUG
# include <string>
# include <utils/CString.h>
# include <string_view>
#endif
#include <stddef.h>
@@ -36,11 +37,11 @@ static char const * const kOurNamespace = "filament::backend::";
// removes all occurrences of "what" from "str"
UTILS_NOINLINE
static std::string& removeAll(std::string& str, const std::string& what) noexcept {
static CString& removeAll(CString& str, const std::string_view what) noexcept {
if (!what.empty()) {
const std::string empty;
const CString empty;
size_t pos = 0;
while ((pos = str.find(what, pos)) != std::string::npos) {
while ((pos = std::string_view{ str.data(), str.size() }.find(what, pos)) != std::string_view::npos) {
str.replace(pos, what.length(), empty);
}
}
@@ -49,13 +50,13 @@ static std::string& removeAll(std::string& str, const std::string& what) noexcep
template <typename T>
UTILS_NOINLINE
static io::ostream& logHandle(io::ostream& out, std::string& typeName, T id) noexcept {
static io::ostream& logHandle(io::ostream& out, CString& typeName, T id) noexcept {
return out << removeAll(typeName, kOurNamespace) << " @ " << id;
}
template <typename T>
io::ostream& operator<<(io::ostream& out, const Handle<T>& h) noexcept {
std::string s(CallStack::typeName<Handle<T>>().c_str());
CString s{ CallStack::typeName<Handle<T>>() };
return logHandle(out, s, h.getId());
}
+2 -13
View File
@@ -82,19 +82,8 @@ Program& Program::attributes(AttributesInfo attributes) noexcept {
return *this;
}
Program& Program::specializationConstants(SpecializationConstantsInfo specConstants,
uint32_t firstMutableId, MutableSpecConstantsInfo mutableSpecConstants) noexcept {
// String the two lists together.
mSpecializationConstants = SpecializationConstantsInfo(
specConstants.size() + mutableSpecConstants.size());
std::uninitialized_move(specConstants.begin(), specConstants.end(),
mSpecializationConstants.begin());
for (uint32_t i = 0; i < mutableSpecConstants.size(); i++) {
mSpecializationConstants[i + specConstants.size()] = SpecializationConstant {
.id = i + firstMutableId,
.value = mutableSpecConstants[i],
};
}
Program& Program::specializationConstants(SpecializationConstantsInfo specConstants) noexcept {
mSpecializationConstants = std::move(specConstants);
return *this;
}
+1 -1
View File
@@ -153,7 +153,7 @@ private:
Type mType = Type::NONE;
static PlatformMetal* platform;
static std::array<uint64_t, TypeCount> aliveBuffers;
static std::array<std::atomic<uint64_t>, TypeCount> aliveBuffers;
};
class MetalBuffer {
+1 -1
View File
@@ -24,7 +24,7 @@
namespace filament {
namespace backend {
std::array<uint64_t, TrackedMetalBuffer::TypeCount> TrackedMetalBuffer::aliveBuffers = { 0 };
std::array<std::atomic<uint64_t>, TrackedMetalBuffer::TypeCount> TrackedMetalBuffer::aliveBuffers = { 0 };
PlatformMetal* TrackedMetalBuffer::platform = nullptr;
PlatformMetal* ScopedAllocationTimer::platform = nullptr;
+85 -50
View File
@@ -418,49 +418,40 @@ void MetalDriver::finish(int) {
}
void MetalDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes) {
uint8_t attributeCount, AttributeArray attributes, utils::CString tag) {
construct_handle<MetalVertexBufferInfo>(vbih, *mContext,
bufferCount, attributeCount, attributes);
mHandleAllocator.associateTagToHandle(vbih.getId(), std::move(tag));
}
void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih) {
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih, utils::CString tag) {
MetalVertexBufferInfo const* const vbi = handle_cast<const MetalVertexBufferInfo>(vbih);
construct_handle<MetalVertexBuffer>(vbh, *mContext, vertexCount, vbi->bufferCount, vbih);
mHandleAllocator.associateTagToHandle(vbh.getId(), std::move(tag));
// No actual GPU memory is allocated here, so no need to check for allocation success.
}
void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) {
uint32_t indexCount, BufferUsage usage, utils::CString tag) {
auto elementSize = (uint8_t)getElementTypeSize(elementType);
auto* indexBuffer =
construct_handle<MetalIndexBuffer>(ibh, *mContext, usage, elementSize, indexCount);
auto& buffer = indexBuffer->buffer;
// If the allocation was not successful, postpone the error message until the next tick, to give
// Filament a chance to call setDebugTag on the handle; this way we get a nicer error message.
if (UTILS_UNLIKELY(!buffer.wasAllocationSuccessful())) {
const size_t byteCount = buffer.getSize();
runAtNextTick([byteCount, this, ibh]() {
FILAMENT_CHECK_POSTCONDITION(false)
<< "Could not allocate Metal index buffer of size " << byteCount
<< ", tag=" << mHandleAllocator.getHandleTag(ibh.getId()).c_str_safe();
});
}
FILAMENT_CHECK_POSTCONDITION(buffer.wasAllocationSuccessful())
<< "Could not allocate Metal index buffer of size " << buffer.getSize()
<< ", tag=" << tag.c_str_safe();
mHandleAllocator.associateTagToHandle(ibh.getId(), std::move(tag));
}
void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {
BufferObjectBinding bindingType, BufferUsage usage, utils::CString tag) {
auto* bufferObject =
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
// If the allocation was not successful, postpone the error message until the next tick, to give
// Filament a chance to call setDebugTag on the handle; this way we get a nicer error message.
if (UTILS_UNLIKELY(!bufferObject->getBuffer()->wasAllocationSuccessful())) {
runAtNextTick([byteCount, this, boh]() {
FILAMENT_CHECK_POSTCONDITION(false)
<< "Could not allocate Metal buffer of size " << byteCount
<< ", tag=" << mHandleAllocator.getHandleTag(boh.getId()).c_str_safe();
});
}
FILAMENT_CHECK_POSTCONDITION(bufferObject->getBuffer()->wasAllocationSuccessful())
<< "Could not allocate Metal buffer of size " << byteCount
<< ", tag=" << tag.c_str_safe();
mHandleAllocator.associateTagToHandle(boh.getId(), std::move(tag));
}
// fixme: TextureUsage is a bitfield
@@ -495,7 +486,7 @@ inline const char* stringify(SamplerType samplerType) {
void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height,
uint32_t depth, TextureUsage usage) {
uint32_t depth, TextureUsage usage, utils::CString tag) {
// Clamp sample count to what the device supports.
auto& sc = mContext->sampleCountLookup;
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
@@ -507,55 +498,60 @@ void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8
"createTextureR(th = %d, target = %s, levels = %d, format = ?, samples = %d, width = "
"%d, height = %d, depth = %d, usage = %s)\n",
th.getId(), stringify(target), levels, samples, width, height, depth, stringify(usage));
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
}
void MetalDriver::createTextureViewR(
Handle<HwTexture> th, Handle<HwTexture> srch, uint8_t baseLevel, uint8_t levelCount) {
void MetalDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
uint8_t baseLevel, uint8_t levelCount, utils::CString tag) {
MetalTexture const* src = handle_cast<MetalTexture>(srch);
mContext->textures.insert(
construct_handle<MetalTexture>(th, *mContext, src, baseLevel, levelCount));
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
}
void MetalDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
backend::TextureSwizzle a) {
backend::TextureSwizzle a, utils::CString tag) {
MetalTexture const* src = handle_cast<MetalTexture>(srch);
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext, src, r, g, b, a));
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
}
void MetalDriver::createTextureExternalImage2R(Handle<HwTexture> th,
backend::SamplerType target,
backend::TextureFormat format,
uint32_t width, uint32_t height, backend::TextureUsage usage,
Platform::ExternalImageHandleRef image) {
Platform::ExternalImageHandleRef image, utils::CString tag) {
// FIXME: implement createTextureExternalImage2R
}
void MetalDriver::createTextureExternalImageR(Handle<HwTexture> th,
backend::SamplerType target,
backend::TextureFormat format,
uint32_t width, uint32_t height, backend::TextureUsage usage, void* image) {
void MetalDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, utils::CString tag) {
mContext->textures.insert(construct_handle<MetalTexture>(
th, *mContext, format, width, height, usage, (CVPixelBufferRef)image));
// This release matches the retain call in setupExternalImage. The MetalTexture will have
// retained the buffer by now.
CVPixelBufferRelease((CVPixelBufferRef)image);
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
}
void MetalDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, uint32_t plane) {
void* image, uint32_t plane, utils::CString tag) {
mContext->textures.insert(construct_handle<MetalTexture>(
th, *mContext, format, width, height, usage, (CVPixelBufferRef)image, plane));
// This release matches the retain call in setupExternalImage. The MetalTexture will have
// retained the buffer by now.
CVPixelBufferRelease((CVPixelBufferRef)image);
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
}
void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height,
uint32_t depth, TextureUsage usage) {
uint32_t depth, TextureUsage usage, utils::CString tag) {
id<MTLTexture> metalTexture = (id<MTLTexture>) CFBridgingRelease((void*) i);
FILAMENT_CHECK_PRECONDITION(metalTexture.width == width)
<< "Imported id<MTLTexture> width (" << metalTexture.width
@@ -572,32 +568,36 @@ void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
<< ") != Filament texture type (" << filamentMetalType << ")";
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext,
target, levels, format, samples, width, height, depth, usage, metalTexture));
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
}
void MetalDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
PrimitiveType pt) {
PrimitiveType pt, utils::CString tag) {
construct_handle<MetalRenderPrimitive>(rph);
MetalDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh);
mHandleAllocator.associateTagToHandle(rph.getId(), std::move(tag));
}
void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program) {
void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program, utils::CString tag) {
#if FILAMENT_METAL_DEBUG_LOG
auto handleId = rph.getId();
DEBUG_LOG("createProgramR(rph = %d, program = ", handleId);
DLOG(INFO) << program;
#endif
construct_handle<MetalProgram>(rph, *mContext, std::move(program));
mHandleAllocator.associateTagToHandle(rph.getId(), std::move(tag));
}
void MetalDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int dummy) {
void MetalDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils::CString tag) {
construct_handle<MetalRenderTarget>(rth, mContext);
mHandleAllocator.associateTagToHandle(rth.getId(), std::move(tag));
}
void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
uint8_t samples, uint8_t layerCount, MRT color,
TargetBufferInfo depth, TargetBufferInfo stencil) {
TargetBufferInfo depth, TargetBufferInfo stencil, utils::CString tag) {
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
<< "createRenderTarget must be called outside of a render pass.";
// Clamp sample count to what the device supports.
@@ -640,14 +640,20 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
construct_handle<MetalRenderTarget>(rth, mContext, width, height, samples,
colorAttachments, depthAttachment, stencilAttachment);
mHandleAllocator.associateTagToHandle(rth.getId(), std::move(tag));
}
void MetalDriver::createFenceR(Handle<HwFence> fh, int dummy) {
void MetalDriver::createFenceR(Handle<HwFence> fh, utils::CString tag) {
auto* fence = handle_cast<MetalFence>(fh);
fence->encode();
mHandleAllocator.associateTagToHandle(fh.getId(), std::move(tag));
}
void MetalDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
void MetalDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags,
utils::CString tag) {
// TODO: support MSAA swapchain
if (UTILS_UNLIKELY(flags & SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER)) {
CVPixelBufferRef pixelBuffer = (CVPixelBufferRef) nativeWindow;
construct_handle<MetalSwapChain>(sch, *mContext, mPlatform, pixelBuffer, flags);
@@ -658,15 +664,24 @@ void MetalDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
auto* metalLayer = (__bridge CAMetalLayer*) nativeWindow;
construct_handle<MetalSwapChain>(sch, *mContext, mPlatform, metalLayer, flags);
}
mHandleAllocator.associateTagToHandle(sch.getId(), std::move(tag));
}
void MetalDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
uint32_t width, uint32_t height, uint64_t flags) {
uint32_t width, uint32_t height, uint64_t flags, utils::CString tag) {
construct_handle<MetalSwapChain>(sch, *mContext, mPlatform, width, height, flags);
mHandleAllocator.associateTagToHandle(sch.getId(), std::move(tag));
}
void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
void MetalDriver::createSyncR(Handle<HwSync> sh, utils::CString tag) {
// TODO: Ensure sync is active, and then invoke and clear all pending
// callbacks.
mHandleAllocator.associateTagToHandle(sh.getId(), std::move(tag));
}
void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::CString tag) {
// nothing to do, timer query was constructed in createTimerQueryS
mHandleAllocator.associateTagToHandle(tqh.getId(), std::move(tag));
}
UTILS_UNUSED
@@ -703,7 +718,7 @@ const char* toString(DescriptorFlags flags) {
}
void MetalDriver::createDescriptorSetLayoutR(
Handle<HwDescriptorSetLayout> dslh, DescriptorSetLayout&& info) {
Handle<HwDescriptorSetLayout> dslh, DescriptorSetLayout&& info, utils::CString tag) {
#if FILAMENT_METAL_DEBUG_LOG == 1
const char* labelStr = "";
std::visit([&labelStr](auto&& arg) {
@@ -724,13 +739,15 @@ void MetalDriver::createDescriptorSetLayoutR(
DEBUG_LOG("})\n");
#endif
construct_handle<MetalDescriptorSetLayout>(dslh, std::move(info));
mHandleAllocator.associateTagToHandle(dslh.getId(), std::move(tag));
}
void MetalDriver::createDescriptorSetR(
Handle<HwDescriptorSet> dsh, Handle<HwDescriptorSetLayout> dslh) {
Handle<HwDescriptorSet> dsh, Handle<HwDescriptorSetLayout> dslh, utils::CString tag) {
DEBUG_LOG("createDescriptorSetR(dsh = %d, dslh = %d)\n", dsh.getId(), dslh.getId());
MetalDescriptorSetLayout* layout = handle_cast<MetalDescriptorSetLayout>(dslh);
construct_handle<MetalDescriptorSet>(dsh, layout);
mHandleAllocator.associateTagToHandle(dsh.getId(), std::move(tag));
}
Handle<HwVertexBufferInfo> MetalDriver::createVertexBufferInfoS() noexcept {
@@ -807,6 +824,10 @@ Handle<HwSwapChain> MetalDriver::createSwapChainHeadlessS() noexcept {
return alloc_handle<MetalSwapChain>();
}
Handle<HwSync> MetalDriver::createSyncS() noexcept {
return alloc_handle<MetalSync>();
}
Handle<HwTimerQuery> MetalDriver::createTimerQueryS() noexcept {
// The handle must be constructed here, as a synchronous call to getTimerQueryValue might happen
// before createTimerQueryR is executed.
@@ -969,11 +990,11 @@ ShaderLanguage MetalDriver::getShaderLanguage() const noexcept {
return ShaderLanguage::MSL;
}
Handle<HwStream> MetalDriver::createStreamNative(void* stream) {
Handle<HwStream> MetalDriver::createStreamNative(void* stream, utils::CString tag) {
return {};
}
Handle<HwStream> MetalDriver::createStreamAcquired() {
Handle<HwStream> MetalDriver::createStreamAcquired(utils::CString tag) {
return {};
}
@@ -1008,6 +1029,19 @@ FenceStatus MetalDriver::getFenceStatus(Handle<HwFence> fh) {
return fence->wait(0);
}
void MetalDriver::destroySync(Handle<HwSync> sh) {
if (sh) {
destruct_handle<MetalSync>(sh);
}
}
void MetalDriver::getPlatformSync(Handle<HwSync> sh, CallbackHandler* handler,
Platform::SyncCallback cb, void* userData) {
// TODO: If the sync has been inserted into the command stream, execute
// the callback. Otherwise, enqueue it.
}
bool MetalDriver::isTextureFormatSupported(TextureFormat format) {
return MetalTexture::decidePixelFormat(mContext, format) != MTLPixelFormatInvalid;
}
@@ -1089,6 +1123,11 @@ bool MetalDriver::isSRGBSwapChainSupported() {
return false;
}
bool MetalDriver::isMSAASwapChainSupported(uint32_t) {
// TODO: support MSAA swapchain
return false;
}
bool MetalDriver::isProtectedContentSupported() {
// the SWAP_CHAIN_CONFIG_PROTECTED_CONTENT flag is not supported
return false;
@@ -2135,10 +2174,6 @@ void MetalDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
void MetalDriver::resetState(int) {
}
void MetalDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
mHandleAllocator.associateTagToHandle(handleId, std::move(tag));
}
void MetalDriver::runAtNextTick(const std::function<void()>& fn) noexcept {
mTickOps.push_back(fn);
}
@@ -437,6 +437,10 @@ private:
uint64_t value;
};
// TODO: Provide implementation for MetalSync.
class MetalSync : public HwSync {
};
struct MetalTimerQuery : public HwTimerQuery {
MetalTimerQuery() : status(std::make_shared<Status>()) {}
+14 -7
View File
@@ -16,12 +16,11 @@
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/Platform.h>
#include "noop/NoopDriver.h"
#include "CommandStreamDispatcher.h"
#include <backend/Handle.h>
#include <stdint.h>
namespace filament::backend {
@@ -116,6 +115,9 @@ void NoopDriver::destroySwapChain(Handle<HwSwapChain> sch) {
void NoopDriver::destroyStream(Handle<HwStream> sh) {
}
void NoopDriver::destroySync(Handle<HwSync> sh) {
}
void NoopDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
}
@@ -125,11 +127,11 @@ void NoopDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh) {
void NoopDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) {
}
Handle<HwStream> NoopDriver::createStreamNative(void* nativeStream) {
Handle<HwStream> NoopDriver::createStreamNative(void* nativeStream, utils::CString tag) {
return {};
}
Handle<HwStream> NoopDriver::createStreamAcquired() {
Handle<HwStream> NoopDriver::createStreamAcquired(utils::CString tag) {
return {};
}
@@ -147,6 +149,10 @@ int64_t NoopDriver::getStreamTimestamp(Handle<HwStream> sh) {
void NoopDriver::updateStreams(CommandStream* driver) {
}
void NoopDriver::getPlatformSync(Handle<HwSync> sh, CallbackHandler* handler,
Platform::SyncCallback cb, void* userData) {
}
void NoopDriver::destroyFence(Handle<HwFence> fh) {
}
@@ -192,6 +198,10 @@ bool NoopDriver::isSRGBSwapChainSupported() {
return false;
}
bool NoopDriver::isMSAASwapChainSupported(uint32_t) {
return false;
}
bool NoopDriver::isProtectedContentSupported() {
return false;
}
@@ -415,7 +425,4 @@ void NoopDriver::bindDescriptorSet(
backend::DescriptorSetOffsetArray&& offsets) {
}
void NoopDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
}
} // namespace filament
+148 -34
View File
@@ -149,6 +149,18 @@ using namespace utils;
namespace filament::backend {
namespace {
static CallbackHandler::Callback syncCallbackWrapper = [](void* userData) {
std::unique_ptr<OpenGLDriver::GLSyncFence::CallbackData> cbData(
static_cast<OpenGLDriver::GLSyncFence::CallbackData*>(userData));
// This assumes the sync has not yet been destroyed. If it has, this will be
// undefined behavior.
cbData->cb(cbData->sync, cbData->userData);
};
} // namespace
Driver* OpenGLDriverFactory::create(
OpenGLPlatform* platform,
void* sharedGLContext,
@@ -615,6 +627,10 @@ Handle<HwFence> OpenGLDriver::createFenceS() noexcept {
return initHandle<GLFence>();
}
Handle<HwSync> OpenGLDriver::createSyncS() noexcept {
return initHandle<GLSyncFence>();
}
Handle<HwSwapChain> OpenGLDriver::createSwapChainS() noexcept {
return initHandle<GLSwapChain>();
}
@@ -639,24 +655,29 @@ void OpenGLDriver::createVertexBufferInfoR(
Handle<HwVertexBufferInfo> vbih,
uint8_t bufferCount,
uint8_t attributeCount,
AttributeArray attributes) {
AttributeArray attributes,
utils::CString tag) {
DEBUG_MARKER()
construct<GLVertexBufferInfo>(vbih, bufferCount, attributeCount, attributes);
mHandleAllocator.associateTagToHandle(vbih.getId(), std::move(tag));
}
void OpenGLDriver::createVertexBufferR(
Handle<HwVertexBuffer> vbh,
uint32_t vertexCount,
Handle<HwVertexBufferInfo> vbih) {
Handle<HwVertexBufferInfo> vbih,
utils::CString tag) {
DEBUG_MARKER()
construct<GLVertexBuffer>(vbh, vertexCount, vbih);
mHandleAllocator.associateTagToHandle(vbh.getId(), std::move(tag));
}
void OpenGLDriver::createIndexBufferR(
Handle<HwIndexBuffer> ibh,
ElementType elementType,
uint32_t indexCount,
BufferUsage usage) {
BufferUsage usage,
utils::CString tag) {
DEBUG_MARKER()
auto& gl = mContext;
@@ -668,10 +689,11 @@ void OpenGLDriver::createIndexBufferR(
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->gl.buffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, size, nullptr, getBufferUsage(usage));
CHECK_GL_ERROR()
mHandleAllocator.associateTagToHandle(ibh.getId(), std::move(tag));
}
void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh,
uint32_t byteCount, BufferObjectBinding bindingType, BufferUsage usage) {
void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage, utils::CString tag) {
DEBUG_MARKER()
assert_invariant(byteCount > 0);
@@ -693,11 +715,12 @@ void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh,
}
CHECK_GL_ERROR()
mHandleAllocator.associateTagToHandle(boh.getId(), std::move(tag));
}
void OpenGLDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
PrimitiveType pt) {
PrimitiveType pt, utils::CString tag) {
DEBUG_MARKER()
auto& gl = mContext;
@@ -730,9 +753,10 @@ void OpenGLDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->gl.buffer);
CHECK_GL_ERROR()
mHandleAllocator.associateTagToHandle(rph.getId(), std::move(tag));
}
void OpenGLDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
void OpenGLDriver::createProgramR(Handle<HwProgram> ph, Program&& program, utils::CString tag) {
DEBUG_MARKER()
@@ -760,6 +784,7 @@ void OpenGLDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
construct<OpenGLProgram>(ph, *this, std::move(program));
CHECK_GL_ERROR()
mHandleAllocator.associateTagToHandle(ph.getId(), std::move(tag));
}
UTILS_NOINLINE
@@ -864,7 +889,7 @@ void OpenGLDriver::textureStorage(GLTexture* t,
void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage usage) {
TextureUsage usage, utils::CString tag) {
DEBUG_MARKER()
GLenum internalFormat = getInternalFormat(format);
@@ -953,10 +978,11 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
}
CHECK_GL_ERROR()
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
}
void OpenGLDriver::createTextureViewR(Handle<HwTexture> th,
Handle<HwTexture> srch, uint8_t baseLevel, uint8_t levelCount) {
Handle<HwTexture> srch, uint8_t baseLevel, uint8_t levelCount, utils::CString tag) {
DEBUG_MARKER()
GLTexture const* const src = handle_cast<GLTexture const*>(srch);
@@ -999,10 +1025,12 @@ void OpenGLDriver::createTextureViewR(Handle<HwTexture> th,
ref->count++;
CHECK_GL_ERROR()
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
}
void OpenGLDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) {
TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a,
utils::CString tag) {
DEBUG_MARKER()
GLTexture const* const src = handle_cast<GLTexture const*>(srch);
@@ -1062,11 +1090,12 @@ void OpenGLDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwText
ref->count++;
CHECK_GL_ERROR()
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
}
void OpenGLDriver::createTextureExternalImage2R(Handle<HwTexture> th, SamplerType target,
TextureFormat format, uint32_t width, uint32_t height, TextureUsage usage,
Platform::ExternalImageHandleRef image) {
Platform::ExternalImageHandleRef image, utils::CString tag) {
DEBUG_MARKER()
usage |= TextureUsage::SAMPLEABLE;
@@ -1113,10 +1142,12 @@ void OpenGLDriver::createTextureExternalImage2R(Handle<HwTexture> th, SamplerTyp
t->gl.target = t->externalTexture->target;
t->gl.id = t->externalTexture->id;
}
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
}
void OpenGLDriver::createTextureExternalImageR(Handle<HwTexture> th, SamplerType target,
TextureFormat format, uint32_t width, uint32_t height, TextureUsage usage, void* image) {
TextureFormat format, uint32_t width, uint32_t height, TextureUsage usage, void* image,
utils::CString tag) {
DEBUG_MARKER()
usage |= TextureUsage::SAMPLEABLE;
@@ -1162,17 +1193,18 @@ void OpenGLDriver::createTextureExternalImageR(Handle<HwTexture> th, SamplerType
t->gl.target = t->externalTexture->target;
t->gl.id = t->externalTexture->id;
}
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
}
void OpenGLDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
TextureFormat format, uint32_t width, uint32_t height, TextureUsage usage,
void* image, uint32_t plane) {
void* image, uint32_t plane, utils::CString tag) {
// not relevant for the OpenGL backend
}
void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
uint32_t w, uint32_t h, uint32_t depth, TextureUsage usage) {
uint32_t w, uint32_t h, uint32_t depth, TextureUsage usage, utils::CString tag) {
DEBUG_MARKER()
auto& gl = mContext;
@@ -1225,6 +1257,7 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
}
CHECK_GL_ERROR()
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
}
void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer const* vb) {
@@ -1626,7 +1659,7 @@ void OpenGLDriver::renderBufferStorage(GLuint rbo, GLenum internalformat, uint32
}
void OpenGLDriver::createDefaultRenderTargetR(
Handle<HwRenderTarget> rth, int) {
Handle<HwRenderTarget> rth, utils::CString tag) {
DEBUG_MARKER()
construct<GLRenderTarget>(rth, 0, 0); // FIXME: we don't know the width/height
@@ -1637,6 +1670,7 @@ void OpenGLDriver::createDefaultRenderTargetR(
rt->gl.samples = 1;
// FIXME: these flags should reflect the actual attachments present
rt->targets = TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH;
mHandleAllocator.associateTagToHandle(rth.getId(), std::move(tag));
}
void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
@@ -1647,7 +1681,8 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
uint8_t layerCount,
MRT color,
TargetBufferInfo depth,
TargetBufferInfo stencil) {
TargetBufferInfo stencil,
utils::CString tag) {
DEBUG_MARKER()
GLRenderTarget* rt = construct<GLRenderTarget>(rth, width, height);
@@ -1760,9 +1795,10 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
assert_invariant(tmin == tmax);
CHECK_GL_ERROR()
mHandleAllocator.associateTagToHandle(rth.getId(), std::move(tag));
}
void OpenGLDriver::createFenceR(Handle<HwFence> fh, int) {
void OpenGLDriver::createFenceR(Handle<HwFence> fh, utils::CString tag) {
DEBUG_MARKER()
GLFence* f = handle_cast<GLFence*>(fh);
@@ -1784,9 +1820,29 @@ void OpenGLDriver::createFenceR(Handle<HwFence> fh, int) {
});
}
#endif
mHandleAllocator.associateTagToHandle(fh.getId(), std::move(tag));
}
void OpenGLDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
void OpenGLDriver::createSyncR(Handle<HwSync> sh, utils::CString tag) {
DEBUG_MARKER()
GLSyncFence* s = handle_cast<GLSyncFence*>(sh);
{
std::lock_guard<std::mutex> guard(s->lock);
s->sync = mPlatform.createSync();
}
for (auto& cbData : s->conversionCallbacks) {
cbData->sync = s->sync;
scheduleCallback(cbData->handler, cbData.release(), syncCallbackWrapper);
}
s->conversionCallbacks.clear();
mHandleAllocator.associateTagToHandle(sh.getId(), std::move(tag));
}
void OpenGLDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags,
utils::CString tag) {
DEBUG_MARKER()
GLSwapChain* sc = handle_cast<GLSwapChain*>(sch);
@@ -1803,10 +1859,12 @@ void OpenGLDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
sc->rec709 = ((flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) &&
!mPlatform.isSRGBSwapChainSupported());
}
mHandleAllocator.associateTagToHandle(sch.getId(), std::move(tag));
}
void OpenGLDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
uint32_t width, uint32_t height, uint64_t flags) {
uint32_t width, uint32_t height, uint64_t flags, utils::CString tag) {
DEBUG_MARKER()
GLSwapChain* sc = handle_cast<GLSwapChain*>(sch);
@@ -1824,25 +1882,30 @@ void OpenGLDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
sc->rec709 = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE &&
!mPlatform.isSRGBSwapChainSupported());
}
mHandleAllocator.associateTagToHandle(sch.getId(), std::move(tag));
}
void OpenGLDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
void OpenGLDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::CString tag) {
DEBUG_MARKER()
GLTimerQuery* tq = handle_cast<GLTimerQuery*>(tqh);
mContext.createTimerQuery(tq);
mHandleAllocator.associateTagToHandle(tqh.getId(), std::move(tag));
}
void OpenGLDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
DescriptorSetLayout&& info) {
DescriptorSetLayout&& info, utils::CString tag) {
DEBUG_MARKER()
construct<GLDescriptorSetLayout>(dslh, std::move(info));
mHandleAllocator.associateTagToHandle(dslh.getId(), std::move(tag));
}
void OpenGLDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
Handle<HwDescriptorSetLayout> dslh) {
Handle<HwDescriptorSetLayout> dslh, utils::CString tag) {
DEBUG_MARKER()
GLDescriptorSetLayout const* dsl = handle_cast<GLDescriptorSetLayout*>(dslh);
construct<GLDescriptorSet>(dsh, mContext, dslh, dsl);
mHandleAllocator.associateTagToHandle(dslh.getId(), std::move(tag));
}
// ------------------------------------------------------------------------------------------------
@@ -1948,7 +2011,7 @@ void OpenGLDriver::destroyTexture(Handle<HwTexture> th) {
if (UTILS_UNLIKELY(t->hwStream)) {
detachStream(t);
}
if (UTILS_UNLIKELY(t->target == SamplerType::SAMPLER_EXTERNAL)) {
if (UTILS_UNLIKELY(t->externalTexture)) {
mPlatform.destroyExternalImageTexture(t->externalTexture);
} else {
glDeleteTextures(1, &t->gl.id);
@@ -2051,6 +2114,16 @@ void OpenGLDriver::destroyStream(Handle<HwStream> sh) {
}
}
void OpenGLDriver::destroySync(Handle<HwSync> sh) {
DEBUG_MARKER()
if (sh) {
GLSyncFence* s = handle_cast<GLSyncFence*>(sh);
mPlatform.destroySync(s->sync);
destruct(sh, s);
}
}
void OpenGLDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
DEBUG_MARKER()
@@ -2088,13 +2161,17 @@ void OpenGLDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
// These are called on the application's thread
// ------------------------------------------------------------------------------------------------
Handle<HwStream> OpenGLDriver::createStreamNative(void* nativeStream) {
Handle<HwStream> OpenGLDriver::createStreamNative(void* nativeStream, utils::CString tag) {
Platform::Stream* stream = mPlatform.createStream(nativeStream);
return initHandle<GLStream>(stream);
auto handle = initHandle<GLStream>(stream);
mHandleAllocator.associateTagToHandle(handle.getId(), std::move(tag));
return handle;
}
Handle<HwStream> OpenGLDriver::createStreamAcquired() {
return initHandle<GLStream>();
Handle<HwStream> OpenGLDriver::createStreamAcquired(utils::CString tag) {
auto handle = initHandle<GLStream>();
mHandleAllocator.associateTagToHandle(handle.getId(), std::move(tag));
return handle;
}
// Stashes an acquired external image and a release callback. The image is not bound to OpenGL until
@@ -2235,6 +2312,33 @@ FenceStatus OpenGLDriver::getFenceStatus(Handle<HwFence> fh) {
return FenceStatus::ERROR;
}
void OpenGLDriver::getPlatformSync(Handle<HwSync> sh, CallbackHandler* handler,
Platform::SyncCallback cb, void* userData) {
if (!sh) {
return;
}
GLSyncFence* s = handle_cast<GLSyncFence*>(sh);
auto cbData = std::make_unique<GLSyncFence::CallbackData>();
cbData->handler = handler;
cbData->cb = cb;
cbData->userData = userData;
// If we haven't already set the handle, toss the conversion callback in the
// back of the list.
{
std::lock_guard guard(s->lock);
if (s->sync == nullptr) {
s->conversionCallbacks.push_back(std::move(cbData));
return;
}
}
// Otherwise, go ahead and schedule the callback now.
cbData->sync = s->sync;
scheduleCallback(cbData->handler, cbData.release(), syncCallbackWrapper);
}
bool OpenGLDriver::isTextureFormatSupported(TextureFormat format) {
const auto& ext = mContext.ext;
if (isETC2Compression(format)) {
@@ -2400,6 +2504,10 @@ bool OpenGLDriver::isSRGBSwapChainSupported() {
return mPlatform.isSRGBSwapChainSupported();
}
bool OpenGLDriver::isMSAASwapChainSupported(uint32_t samples) {
return mPlatform.isMSAASwapChainSupported(samples);
}
bool OpenGLDriver::isProtectedContentSupported() {
return mPlatform.isProtectedContextSupported();
}
@@ -2514,13 +2622,20 @@ size_t OpenGLDriver::getMaxArrayTextureLayers() {
// Swap chains
// ------------------------------------------------------------------------------------------------
void OpenGLDriver::commit(Handle<HwSwapChain> sch) {
DEBUG_MARKER()
GLSwapChain* sc = handle_cast<GLSwapChain*>(sch);
mPlatform.commit(sc->swapChain);
auto& fs = sc->frameScheduled;
if (fs.callback) {
scheduleCallback(fs.handler, [callback = std::move(fs.callback)]() {
PresentCallable noop = PresentCallable(PresentCallable::noopPresent, nullptr);
callback(noop);
});
}
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
if (UTILS_UNLIKELY(!mFrameCompleteOps.empty())) {
whenGpuCommandsComplete([ops = std::move(mFrameCompleteOps)]() {
@@ -2586,10 +2701,6 @@ void OpenGLDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain>
// Updating driver objects
// ------------------------------------------------------------------------------------------------
void OpenGLDriver::setDebugTag(HandleBase::HandleId handleId, CString tag) {
mHandleAllocator.associateTagToHandle(handleId, std::move(tag));
}
void OpenGLDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh,
uint32_t index, Handle<HwBufferObject> boh) {
DEBUG_MARKER()
@@ -3717,9 +3828,12 @@ void OpenGLDriver::beginFrame(
}
}
void OpenGLDriver::setFrameScheduledCallback(Handle<HwSwapChain>, CallbackHandler*,
FrameScheduledCallback&& /*callback*/, uint64_t /*flags*/) {
void OpenGLDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
FrameScheduledCallback&& callback, uint64_t /*flags*/) {
DEBUG_MARKER()
GLSwapChain* sc = handle_cast<GLSwapChain*>(sch);
sc->frameScheduled.handler = handler;
sc->frameScheduled.callback = std::move(callback);
}
void OpenGLDriver::setFrameCompletedCallback(Handle<HwSwapChain>,
@@ -28,6 +28,7 @@
#include "ShaderCompilerService.h"
#include <backend/AcquiredImage.h>
#include <backend/CallbackHandler.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/PipelineState.h>
@@ -99,6 +100,10 @@ public:
struct GLSwapChain : public HwSwapChain {
using HwSwapChain::HwSwapChain;
bool rec709 = false;
struct {
CallbackHandler* handler = nullptr;
FrameScheduledCallback callback;
} frameScheduled;
};
struct GLVertexBufferInfo : public HwVertexBufferInfo {
@@ -193,6 +198,19 @@ public:
std::shared_ptr<State> state{ std::make_shared<State>() };
};
// Note: named "GLSyncFence" to avoid confusion with the GL handle,
// "GLsync" (lowercase S)
struct GLSyncFence : public HwSync {
struct CallbackData {
CallbackHandler* handler;
Platform::SyncCallback cb;
Platform::Sync* sync;
void* userData;
};
std::mutex lock;
std::vector<std::unique_ptr<CallbackData>> conversionCallbacks;
};
OpenGLDriver(OpenGLDriver const&) = delete;
OpenGLDriver& operator=(OpenGLDriver const&) = delete;
@@ -76,6 +76,10 @@ bool OpenGLPlatform::isSRGBSwapChainSupported() const noexcept {
return false;
}
bool OpenGLPlatform::isMSAASwapChainSupported(uint32_t) const noexcept {
return false;
}
uint32_t OpenGLPlatform::getDefaultFramebufferObject() noexcept {
return 0;
}
@@ -117,6 +121,15 @@ FenceStatus OpenGLPlatform::waitFence(
return FenceStatus::ERROR;
}
Platform::Sync* OpenGLPlatform::createSync() noexcept {
return new Platform::Sync();
}
void OpenGLPlatform::destroySync(Platform::Sync* sync) noexcept {
// sync must be a Platform::Sync, since it was created by this platform
// object.
delete sync;
}
Platform::Stream* OpenGLPlatform::createStream(
UTILS_UNUSED void* nativeStream) noexcept {
@@ -46,7 +46,6 @@
#include <cctype>
#include <mutex>
#include <memory>
#include <string>
#include <string_view>
#include <thread>
#include <utility>
@@ -67,9 +66,9 @@ constexpr uint32_t MAX_NUM_SYNCHRONOUS_PROGRAMS_PER_FRAME = 1;
// ------------------------------------------------------------------------------------------------
static std::string to_string(bool const b) { return b ? "true" : "false"; }
static std::string to_string(int const i) { return std::to_string(i); }
static std::string to_string(float const f) { return "float(" + std::to_string(f) + ")"; }
static CString to_string(bool const b) { return CString{ b ? "true" : "false" }; }
static CString to_string(int const i) { return utils::to_string(i); }
static CString to_string(float const f) { return "float(" + utils::to_string(f) + ")"; }
static void logCompilationError(ShaderStage shaderType, const char* name, GLuint shaderId,
Program::ShaderBlob const& sourceCode) noexcept;
@@ -696,14 +695,14 @@ void ShaderCompilerService::cancelPendingSynchronousProgram(program_token_t cons
bool multiview, program_token_t const& token) noexcept {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
auto const appendSpecConstantString = +[](std::string& s, Program::SpecializationConstant const& sc) {
s += "#define SPIRV_CROSS_CONSTANT_ID_" + std::to_string(sc.id) + ' ';
auto const appendSpecConstantString = +[](CString& s, Program::SpecializationConstant const& sc) {
s += "#define SPIRV_CROSS_CONSTANT_ID_" + utils::to_string(sc.id) + ' ';
s += std::visit([](auto&& arg) { return to_string(arg); }, sc.value);
s += '\n';
return s;
};
std::string specializationConstantString;
CString specializationConstantString;
int32_t numViews = 2;
for (auto const& sc: specializationConstants) {
appendSpecConstantString(specializationConstantString, sc);
@@ -766,8 +765,10 @@ void ShaderCompilerService::cancelPendingSynchronousProgram(program_token_t cons
}
std::array<std::string_view, 5> sources = {
version, prolog, specializationConstantString, packingFunctions,
{ body.data(), body.size() - 1 }// null-terminated
version, prolog,
{ specializationConstantString.data(), specializationConstantString.size() },
packingFunctions,
{ body.data(), body.size() - 1 } // null-terminated
};
// Some of the sources may be zero-length. Remove them as to avoid passing lengths of
@@ -966,16 +967,18 @@ UTILS_NOINLINE
sourceCode.size() };
size_t lc = 1;
size_t start = 0;
std::string line;
CString line;
while (true) {
size_t const end = shader.find('\n', start);
if (end == std::string::npos) {
line = shader.substr(start);
if (end == std::string_view::npos) {
auto sv = shader.substr(start);
line = { sv.data(), sv.size() };
} else {
line = shader.substr(start, end - start);
auto sv = shader.substr(start, end - start);
line = { sv.data(), sv.size() };
}
LOG(ERROR) << lc++ << ": " << line.c_str();
if (end == std::string::npos) {
if (end == std::string_view::npos) {
break;
}
start = end + 1;
@@ -448,6 +448,11 @@ EGLConfig PlatformEGL::findSwapChainConfig(uint64_t flags, bool window, bool pbu
configAttribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE;
}
if (flags & SWAP_CHAIN_CONFIG_MSAA_4_SAMPLES) {
configAttribs[EGL_SAMPLE_BUFFERS] = 1;
configAttribs[EGL_SAMPLES] = 4;
}
if (UTILS_UNLIKELY(
!eglChooseConfig(mEGLDisplay, configAttribs.data(), &config, 1, &configsCount))) {
logEglError("eglChooseConfig");
@@ -481,11 +486,48 @@ bool PlatformEGL::isSRGBSwapChainSupported() const noexcept {
return ext.egl.KHR_gl_colorspace;
}
bool PlatformEGL::isMSAASwapChainSupported(uint32_t samples) const noexcept {
if (samples <= 1) {
return true;
}
Config configAttribs = {
{ EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT },
{ EGL_RED_SIZE, 8 },
{ EGL_GREEN_SIZE, 8 },
{ EGL_BLUE_SIZE, 8 },
{ EGL_DEPTH_SIZE, 24 },
{ EGL_SAMPLE_BUFFERS, 1 },
{ EGL_SAMPLES, samples },
};
if (!ext.egl.KHR_no_config_context) {
if (isOpenGL()) {
configAttribs[EGL_RENDERABLE_TYPE] = EGL_OPENGL_BIT;
} else {
configAttribs[EGL_RENDERABLE_TYPE] = EGL_OPENGL_ES2_BIT;
if (ext.egl.KHR_create_context) {
configAttribs[EGL_RENDERABLE_TYPE] |= EGL_OPENGL_ES3_BIT_KHR;
}
}
}
EGLConfig config = EGL_NO_CONFIG_KHR;
EGLint configsCount;
if (!eglChooseConfig(mEGLDisplay, configAttribs.data(), &config, 1, &configsCount)) {
return false;
}
return configsCount > 0;
}
Platform::SwapChain* PlatformEGL::createSwapChain(
void* nativeWindow, uint64_t flags) noexcept {
Config attribs;
if (ext.egl.KHR_gl_colorspace) {
// Remove flags for unsupported features.
if (isSRGBSwapChainSupported()) {
if (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) {
attribs[EGL_GL_COLORSPACE_KHR] = EGL_GL_COLORSPACE_SRGB_KHR;
}
@@ -493,7 +535,7 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
flags &= ~SWAP_CHAIN_CONFIG_SRGB_COLORSPACE;
}
if (ext.egl.EXT_protected_content) {
if (isProtectedContextSupported()) {
if (flags & SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) {
attribs[EGL_PROTECTED_CONTENT_EXT] = EGL_TRUE;
}
@@ -501,6 +543,13 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
flags &= ~SWAP_CHAIN_CONFIG_PROTECTED_CONTENT;
}
if (flags & SWAP_CHAIN_CONFIG_MSAA_4_SAMPLES) {
if (!isMSAASwapChainSupported(4)) {
flags &= ~SWAP_CHAIN_CONFIG_MSAA_4_SAMPLES;
}
}
// Retrieve a config for the given flags.
EGLConfig config = EGL_NO_CONFIG_KHR;
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
config = findSwapChainConfig(flags, true, false);
@@ -83,6 +83,7 @@ UTILS_PRIVATE PFNEGLGETCOMPOSITORTIMINGANDROIDPROC eglGetCompositorTimingANDROID
UTILS_PRIVATE PFNEGLGETNEXTFRAMEIDANDROIDPROC eglGetNextFrameIdANDROID = {};
UTILS_PRIVATE PFNEGLGETFRAMETIMESTAMPSUPPORTEDANDROIDPROC eglGetFrameTimestampSupportedANDROID = {};
UTILS_PRIVATE PFNEGLGETFRAMETIMESTAMPSANDROIDPROC eglGetFrameTimestampsANDROID = {};
UTILS_PRIVATE PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID = {};
}
using namespace glext;
@@ -226,6 +227,8 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
"eglGetFrameTimestampSupportedANDROID");
eglGetFrameTimestampsANDROID = (PFNEGLGETFRAMETIMESTAMPSANDROIDPROC)eglGetProcAddress(
"eglGetFrameTimestampsANDROID");
eglDupNativeFenceFDANDROID =
(PFNEGLDUPNATIVEFENCEFDANDROIDPROC) eglGetProcAddress("eglDupNativeFenceFDANDROID");
}
mAssertNativeWindowIsValid = driverConfig.assertNativeWindowIsValid;
@@ -395,6 +398,31 @@ void PlatformEGLAndroid::destroyStream(Platform::Stream* stream) noexcept {
mExternalStreamManager.release(stream);
}
Platform::Sync* PlatformEGLAndroid::createSync() noexcept {
auto sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
return new SyncEGLAndroid{.sync = sync};
}
bool PlatformEGLAndroid::convertSyncToFd(Platform::Sync* sync, int* fd) noexcept {
assert_invariant(sync && fd);
SyncEGLAndroid& eglSync = static_cast<SyncEGLAndroid&>(*sync);
*fd = eglDupNativeFenceFDANDROID(mEGLDisplay, eglSync.sync);
// In the case where there was no native FD, -1 is returned. Return false
// to indicate there was an error in this case.
if (*fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
LOG(ERROR) << "Failed to convert sync to fd: " << eglGetError();
return false;
}
return true;
}
void PlatformEGLAndroid::destroySync(Platform::Sync* sync) noexcept {
assert_invariant(sync);
SyncEGLAndroid& eglSync = static_cast<SyncEGLAndroid&>(*sync);
eglDestroySyncKHR(mEGLDisplay, eglSync.sync);
delete sync;
}
void PlatformEGLAndroid::attach(Stream* stream, intptr_t tname) noexcept {
mExternalStreamManager.attach(stream, tname);
}
@@ -20,6 +20,7 @@
#include <bluevk/BlueVK.h>
#include "DriverBase.h"
#include "backend/Platform.h"
#include "vulkan/memory/Resource.h"
@@ -52,6 +53,19 @@ struct VulkanFence : public HwFence, fvkmemory::ThreadSafeResource {
std::shared_ptr<VulkanCmdFence> fence;
};
struct VulkanSync : fvkmemory::ThreadSafeResource, public HwSync {
struct CallbackData {
CallbackHandler* handler;
Platform::SyncCallback cb;
Platform::Sync* sync;
void* userData;
};
VulkanSync() {}
std::mutex lock;
std::vector<std::unique_ptr<CallbackData>> conversionCallbacks;
};
struct VulkanTimerQuery : public HwTimerQuery, fvkmemory::ThreadSafeResource {
VulkanTimerQuery(uint32_t startingIndex, uint32_t stoppingIndex)
: mStartingQueryIndex(startingIndex),
@@ -96,7 +96,6 @@ inline void resolveFast(VulkanCommandBuffer* commands, VkImageAspectFlags aspect
VulkanLayout oldSrcLayout = src.getLayout();
VulkanLayout oldDstLayout = dst.getLayout();
src.texture->transitionLayout(commands, srcRange, VulkanLayout::TRANSFER_SRC);
dst.texture->transitionLayout(commands, dstRange, VulkanLayout::TRANSFER_DST);
assert_invariant(
@@ -109,10 +108,9 @@ inline void resolveFast(VulkanCommandBuffer* commands, VkImageAspectFlags aspect
.extent = { src.getExtent2D().width, src.getExtent2D().height, 1 },
}};
vkCmdResolveImage(cmdbuffer,
src.getImage(), fvkutils::getVkLayout(VulkanLayout::TRANSFER_SRC),
src.getImage(), fvkutils::getVkLayout(src.getLayout()),
dst.getImage(), fvkutils::getVkLayout(VulkanLayout::TRANSFER_DST),
1, resolveRegions);
if (oldSrcLayout == VulkanLayout::UNDEFINED) {
oldSrcLayout = src.texture->getDefaultLayout();
}
+31 -12
View File
@@ -24,6 +24,7 @@
#include "VulkanConstants.h"
#include "VulkanContext.h"
#include <utils/CString.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/debug.h>
@@ -57,26 +58,26 @@ VkCommandBuffer createCommandBuffer(VkDevice device, VkCommandPool pool) {
} // anonymous namespace
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
void VulkanGroupMarkers::push(std::string const& marker, Timestamp start) noexcept {
void VulkanGroupMarkers::push(CString const& marker, Timestamp start) noexcept {
mMarkers.push_back({marker,
start.time_since_epoch().count() > 0.0
? start
: std::chrono::high_resolution_clock::now()});
}
std::pair<std::string, Timestamp> VulkanGroupMarkers::pop() noexcept {
std::pair<CString, Timestamp> VulkanGroupMarkers::pop() noexcept {
auto ret = mMarkers.back();
mMarkers.pop_back();
return ret;
}
std::pair<std::string, Timestamp> VulkanGroupMarkers::pop_bottom() noexcept {
std::pair<CString, Timestamp> VulkanGroupMarkers::pop_bottom() noexcept {
auto ret = mMarkers.front();
mMarkers.pop_front();
return ret;
}
std::pair<std::string, Timestamp> const& VulkanGroupMarkers::top() const {
std::pair<CString, Timestamp> const& VulkanGroupMarkers::top() const {
assert_invariant(!empty());
return mMarkers.back();
}
@@ -102,6 +103,12 @@ VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext const& context, VkDevice
vkCreateSemaphore(mDevice, &sci, VKALLOC, &mSubmission);
VkFenceCreateInfo fenceCreateInfo{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
VkExportFenceCreateInfo exportFenceCreateInfo{
.sType = VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO,
.handleTypes = context.getFenceExportFlags()
};
fenceCreateInfo.pNext = &exportFenceCreateInfo;
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &mFence);
}
@@ -211,7 +218,9 @@ VkSemaphore VulkanCommandBuffer::submit() {
FVK_LOGI << "Submitting cmdbuffer=" << mBuffer
<< " wait=(";
for (size_t s = 0, count = mWaitSemaphores.size(); s < count; ++s) {
FVK_LOGI << mWaitSemaphores[s] << " ";
FVK_LOGI << "\tsemaphore=" << mWaitSemaphores[s] << "|stage="
<< mWaitSemaphoreStages[s]
<< (s < mWaitSemaphores.size() - 1 ? "\n" : "");
}
FVK_LOGI << ") "
<< " signal=" << mSubmission
@@ -351,7 +360,7 @@ void CommandBufferPool::waitFor(VkSemaphore previousAction, VkPipelineStageFlags
}
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
std::string CommandBufferPool::topMarker() const {
CString CommandBufferPool::topMarker() const {
if (!mGroupMarkers || mGroupMarkers->empty()) {
return "";
}
@@ -362,11 +371,11 @@ void CommandBufferPool::pushMarker(char const* marker, VulkanGroupMarkers::Times
if (!mGroupMarkers) {
mGroupMarkers = std::make_unique<VulkanGroupMarkers>();
}
mGroupMarkers->push(marker, timestamp);
mGroupMarkers->push(CString{ marker }, timestamp);
getRecording().pushMarker(marker);
}
std::pair<std::string, VulkanGroupMarkers::Timestamp> CommandBufferPool::popMarker() {
std::pair<CString, VulkanGroupMarkers::Timestamp> CommandBufferPool::popMarker() {
assert_invariant(mGroupMarkers && !mGroupMarkers->empty());
auto ret = mGroupMarkers->pop();
@@ -422,6 +431,9 @@ bool VulkanCommands::flush() {
VkSemaphore lastSubmit = mLastSubmit;
bool hasFlushed = false;
VkFence flushedFence = VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> flushedFenceStatus;
// Note that we've ordered it so that the non-protected commands are followed by the protected
// commands. This assumes that the protected commands will be that one doing the rendering into
// the protected memory (i.e. protected render target).
@@ -434,12 +446,17 @@ bool VulkanCommands::flush() {
}
if (lastSubmit != VK_NULL_HANDLE) {
// Note that the stage we're waiting on is the fragment shader stage. This assumes
// that the subsequent command buffer will only dependent on the fragment output of the
// previous buffer, allowing for vertex work to proceed (more overlapping
// that the subsequent command buffer will only depend on
// 1) fragment output of the previous command buffer
// 2) reading/writing of buffers (i.e. UBO) of the previous command buffer
// Restricting the wait stages will allow for vertex work to proceed (more overlapping
// vertex/fragment work).
pool->waitFor(lastSubmit, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
pool->waitFor(lastSubmit,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT);
lastSubmit = VK_NULL_HANDLE;
}
flushedFence = pool->getRecording().getVkFence();
flushedFenceStatus = pool->getRecording().getFenceStatus();
dependency = pool->flush();
hasFlushed = true;
}
@@ -447,6 +464,8 @@ bool VulkanCommands::flush() {
if (hasFlushed) {
mInjectedDependency = VK_NULL_HANDLE;
mLastSubmit = dependency;
mLastFence = flushedFence;
mLastFenceStatus = flushedFenceStatus;
}
return true;
@@ -523,7 +542,7 @@ void VulkanCommands::insertEventMarker(char const* str, uint32_t len) {
}
}
std::string VulkanCommands::getTopGroupMarker() const {
CString VulkanCommands::getTopGroupMarker() const {
if (mProtectedPool) {
return mProtectedPool->topMarker();
}
+21 -10
View File
@@ -28,6 +28,7 @@
#include "vulkan/utils/StaticVector.h"
#include <utils/Condition.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Mutex.h>
@@ -35,7 +36,6 @@
#include <chrono>
#include <list>
#include <string>
#include <utility>
namespace filament::backend {
@@ -47,14 +47,14 @@ class VulkanGroupMarkers {
public:
using Timestamp = std::chrono::time_point<std::chrono::high_resolution_clock>;
void push(std::string const& marker, Timestamp start = {}) noexcept;
std::pair<std::string, Timestamp> pop() noexcept;
std::pair<std::string, Timestamp> pop_bottom() noexcept;
std::pair<std::string, Timestamp> const& top() const;
void push(utils::CString const& marker, Timestamp start = {}) noexcept;
std::pair<utils::CString, Timestamp> pop() noexcept;
std::pair<utils::CString, Timestamp> pop_bottom() noexcept;
std::pair<utils::CString, Timestamp> const& top() const;
bool empty() const noexcept;
private:
std::list<std::pair<std::string, Timestamp>> mMarkers;
std::list<std::pair<utils::CString, Timestamp>> mMarkers;
};
#endif // FVK_DEBUG_GROUP_MARKERS
@@ -148,9 +148,9 @@ struct CommandBufferPool {
void waitFor(VkSemaphore previousAction, VkPipelineStageFlags waitStage);
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
std::string topMarker() const;
utils::CString topMarker() const;
void pushMarker(char const* marker, VulkanGroupMarkers::Timestamp timestamp);
std::pair<std::string, VulkanGroupMarkers::Timestamp> popMarker();
std::pair<utils::CString, VulkanGroupMarkers::Timestamp> popMarker();
void insertEvent(char const* marker);
#endif
@@ -223,11 +223,19 @@ public:
// it from the existing dependency chain. This is especially useful for setting up
// vkQueuePresentKHR.
VkSemaphore acquireFinishedSignal() {
VkSemaphore ret= mLastSubmit;
VkSemaphore ret = mLastSubmit;
mLastSubmit = VK_NULL_HANDLE;
return ret;
}
VkFence getMostRecentFence() {
return mLastFence;
}
std::shared_ptr<VulkanCmdFence> getMostRecentFenceStatus() {
return mLastFenceStatus;
}
// Takes a semaphore that signals when the next flush can occur. Only one injected
// semaphore is allowed per flush. Useful after calling vkAcquireNextImageKHR.
// waitStage
@@ -249,7 +257,7 @@ public:
void pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp = {});
void popGroupMarker();
void insertEventMarker(char const* string, uint32_t len);
std::string getTopGroupMarker() const;
utils::CString getTopGroupMarker() const;
#endif
private:
@@ -265,6 +273,9 @@ private:
VkSemaphore mInjectedDependency = VK_NULL_HANDLE;
VkSemaphore mLastSubmit = VK_NULL_HANDLE;
VkFence mLastFence = VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> mLastFenceStatus;
VkPipelineStageFlags mInjectedDependencyWaitStage = 0;
};
@@ -106,6 +106,10 @@ public:
return mPhysicalDeviceProperties.properties.vendorID;
}
inline VkExternalFenceHandleTypeFlags getFenceExportFlags() const noexcept {
return mFenceExportFlags;
}
inline bool isImageCubeArraySupported() const noexcept {
return mPhysicalDeviceFeatures.features.imageCubeArray == VK_TRUE;
}
@@ -168,6 +172,9 @@ private:
// non-conformant vulkan implementation).
.imageView2DOn3DImage = VK_TRUE,
};
VkExternalFenceHandleTypeFlags mFenceExportFlags = {};
bool mDebugMarkersSupported = false;
bool mDebugUtilsSupported = false;
bool mLazilyAllocatedMemorySupported = false;
+166 -42
View File
@@ -35,6 +35,7 @@
#include <backend/DriverEnums.h>
#include <backend/platforms/VulkanPlatform.h>
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/Panic.h>
@@ -136,6 +137,13 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFla
}
#endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
static CallbackHandler::Callback syncCallbackWrapper = [](void* userData) {
std::unique_ptr<VulkanSync::CallbackData> cbData(
static_cast<VulkanSync::CallbackData*>(userData));
// This assumes the sync has not yet been destroyed. If it has, this will be
// undefined behavior.
cbData->cb(cbData->sync, cbData->userData);
};
}// anonymous namespace
@@ -232,6 +240,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context,
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache,
&mDescriptorSetLayoutCache),
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
mIsMSAASwapChainSupported(false), // TODO: support MSAA swapchain
mStereoscopicType(driverConfig.stereoscopicType) {
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
@@ -416,6 +425,8 @@ void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
FrameScheduledCallback&& callback, uint64_t flags) {
auto swapChain = resource_ptr<VulkanSwapChain>::cast(&mResourceManager, sch);
swapChain->setFrameScheduledCallback(handler, std::move(callback));
}
void VulkanDriver::setFrameCompletedCallback(Handle<HwSwapChain> sch,
@@ -474,6 +485,16 @@ void VulkanDriver::finish(int dummy) {
FVK_SYSTRACE_SCOPE();
endCommandRecording();
// It's not enough to wait on the fences of the buffers submitted. Present calls are
// submitted to this same queue, so the more correct option is to call vkQueueWaitIdle.
vkQueueWaitIdle(mPlatform->getGraphicsQueue());
if (auto protectedQueue = mPlatform->getProtectedGraphicsQueue();
UTILS_UNLIKELY(protectedQueue != VK_NULL_HANDLE)) {
vkQueueWaitIdle(protectedQueue);
}
// This wait() is still necessary to update the fence status.
mCommands.wait();
mReadPixels.runUntilComplete();
@@ -481,12 +502,13 @@ void VulkanDriver::finish(int dummy) {
void VulkanDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
PrimitiveType pt) {
PrimitiveType pt, utils::CString tag) {
FVK_SYSTRACE_SCOPE();
auto vb = resource_ptr<VulkanVertexBuffer>::cast(&mResourceManager, vbh);
auto ib = resource_ptr<VulkanIndexBuffer>::cast(&mResourceManager, ibh);
auto ptr = resource_ptr<VulkanRenderPrimitive>::make(&mResourceManager, rph, pt, vb, ib);
ptr.inc();
mResourceManager.associateHandle(rph.getId(), std::move(tag));
}
void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
@@ -499,11 +521,12 @@ void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
}
void VulkanDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes) {
uint8_t attributeCount, AttributeArray attributes, utils::CString tag) {
FVK_SYSTRACE_SCOPE();
auto vbi = resource_ptr<VulkanVertexBufferInfo>::make(&mResourceManager, vbih, bufferCount,
attributeCount, attributes);
vbi.inc();
mResourceManager.associateHandle(vbih.getId(), std::move(tag));
}
void VulkanDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
@@ -516,12 +539,13 @@ void VulkanDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
}
void VulkanDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint32_t vertexCount,
Handle<HwVertexBufferInfo> vbih) {
Handle<HwVertexBufferInfo> vbih, utils::CString tag) {
FVK_SYSTRACE_SCOPE();
auto vbi = resource_ptr<VulkanVertexBufferInfo>::cast(&mResourceManager, vbih);
auto vb = resource_ptr<VulkanVertexBuffer>::make(&mResourceManager, vbh, mContext, mStagePool,
vertexCount, vbi);
vb.inc();
mResourceManager.associateHandle(vbh.getId(), std::move(tag));
}
void VulkanDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
@@ -534,12 +558,13 @@ void VulkanDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
}
void VulkanDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) {
uint32_t indexCount, BufferUsage usage, utils::CString tag) {
FVK_SYSTRACE_SCOPE();
auto elementSize = (uint8_t) getElementTypeSize(elementType);
auto ib = resource_ptr<VulkanIndexBuffer>::make(&mResourceManager, ibh, mContext, mAllocator,
mStagePool, mBufferCache, elementSize, indexCount);
ib.inc();
mResourceManager.associateHandle(ibh.getId(), std::move(tag));
}
void VulkanDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
@@ -552,11 +577,12 @@ void VulkanDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
}
void VulkanDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {
BufferObjectBinding bindingType, BufferUsage usage, utils::CString tag) {
FVK_SYSTRACE_SCOPE();
auto bo = resource_ptr<VulkanBufferObject>::make(&mResourceManager, boh, mContext, mAllocator,
mStagePool, mBufferCache, byteCount, bindingType);
bo.inc();
mResourceManager.associateHandle(boh.getId(), std::move(tag));
}
void VulkanDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
@@ -570,7 +596,7 @@ void VulkanDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
void VulkanDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage usage) {
TextureUsage usage, utils::CString tag) {
FVK_SYSTRACE_SCOPE();
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mResourceManager, &mCommands,
@@ -583,31 +609,34 @@ void VulkanDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
texture->transitionLayout(&commandsBuf, primaryViewRange, defaultLayout);
texture.inc();
mResourceManager.associateHandle(th.getId(), std::move(tag));
}
void VulkanDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
uint8_t baseLevel, uint8_t levelCount) {
uint8_t baseLevel, uint8_t levelCount, utils::CString tag) {
auto src = resource_ptr<VulkanTexture>::cast(&mResourceManager, srch);
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, src, baseLevel,
levelCount);
texture.inc();
mResourceManager.associateHandle(th.getId(), std::move(tag));
}
void VulkanDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
backend::TextureSwizzle a) {
TextureSwizzle const swizzleArray[] = {r, g, b, a};
VkComponentMapping const swizzle = fvkutils::getSwizzleMap(swizzleArray);
auto src = resource_ptr<VulkanTexture>::cast(&mResourceManager, srch);
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, src, swizzle);
texture.inc();
backend::TextureSwizzle a, utils::CString tag) {
TextureSwizzle const swizzleArray[] = { r, g, b, a };
VkComponentMapping const swizzle = fvkutils::getSwizzleMap(swizzleArray);
auto src = resource_ptr<VulkanTexture>::cast(&mResourceManager, srch);
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, src, swizzle);
texture.inc();
mResourceManager.associateHandle(th.getId(), std::move(tag));
}
void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
Platform::ExternalImageHandleRef externalImage) {
Platform::ExternalImageHandleRef externalImage, utils::CString tag) {
FVK_SYSTRACE_SCOPE();
auto metadata = mPlatform->extractExternalImageMetadata(externalImage);
@@ -653,11 +682,12 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
}
texture.inc();
mResourceManager.associateHandle(th.getId(), std::move(tag));
}
void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* externalImage) {
void* externalImage, utils::CString tag) {
FVK_SYSTRACE_SCOPE();
assert_invariant(false && "Not supported in Vulkan backend");
// not supported in this backend
@@ -665,16 +695,17 @@ void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::Sa
void VulkanDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, uint32_t plane) {
void* image, uint32_t plane, utils::CString tag) {
assert_invariant(false && "Not supported in Vulkan backend");
}
void VulkanDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage usage) {
TextureUsage usage, utils::CString tag) {
// not supported in this backend
assert_invariant(false && "Not supported in Vulkan backend");
mResourceManager.associateHandle(th.getId(), std::move(tag));
}
void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
@@ -687,11 +718,12 @@ void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
mExternalImageManager.removeExternallySampledTexture(texture);
}
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program, utils::CString tag) {
FVK_SYSTRACE_SCOPE();
auto vprogram = resource_ptr<VulkanProgram>::make(&mResourceManager, ph, mPlatform->getDevice(),
program);
vprogram.inc();
mResourceManager.associateHandle(ph.getId(), std::move(tag));
}
void VulkanDriver::destroyProgram(Handle<HwProgram> ph) {
@@ -702,18 +734,20 @@ void VulkanDriver::destroyProgram(Handle<HwProgram> ph) {
vprogram.dec();
}
void VulkanDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
void VulkanDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils::CString tag) {
assert_invariant(mDefaultRenderTarget); // Default render target should already exist.
auto renderTarget = resource_ptr<VulkanRenderTarget>::make(&mResourceManager, rth,
std::move(std::move(*mDefaultRenderTarget.get())));
mDefaultRenderTarget = renderTarget;
mResourceManager.associateHandle(rth.getId(), std::move(tag));
}
void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
TargetBufferFlags targets, uint32_t width, uint32_t height, uint8_t samples,
uint8_t layerCount, MRT color, TargetBufferInfo depth, TargetBufferInfo stencil) {
uint8_t layerCount, MRT color, TargetBufferInfo depth, TargetBufferInfo stencil,
utils::CString tag) {
FVK_SYSTRACE_SCOPE();
@@ -774,6 +808,8 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
mPlatform->getPhysicalDevice(), mContext, &mResourceManager, mAllocator, &mCommands,
width, height, samples, colorTargets, depthStencil, mStagePool, layerCount);
rt.inc();
mResourceManager.associateHandle(rth.getId(), std::move(tag));
}
void VulkanDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
@@ -782,15 +818,13 @@ void VulkanDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
}
auto rt = resource_ptr<VulkanRenderTarget>::cast(&mResourceManager, rth);
if (UTILS_UNLIKELY(rt == mDefaultRenderTarget)) {
// Note that this should only happen on driver shutdown.
mDefaultRenderTarget = {};
} else {
// Note that the default render target will be destroyed on shutdown
if (UTILS_LIKELY(rt != mDefaultRenderTarget)) {
rt.dec();
}
}
void VulkanDriver::createFenceR(Handle<HwFence> fh, int) {
void VulkanDriver::createFenceR(Handle<HwFence> fh, utils::CString tag) {
VulkanCommandBuffer* cmdbuf;
if (mCurrentRenderPass.commandBuffer) {
cmdbuf = mCurrentRenderPass.commandBuffer;
@@ -802,19 +836,57 @@ void VulkanDriver::createFenceR(Handle<HwFence> fh, int) {
// buffer.
auto fence = resource_ptr<VulkanFence>::cast(&mResourceManager, fh);
fence->fence = cmdbuf->getFenceStatus();
mResourceManager.associateHandle(fh.getId(), std::move(tag));
}
void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
void VulkanDriver::createSyncR(Handle<HwSync> sh, utils::CString tag) {
auto sync = resource_ptr<VulkanSync>::cast(&mResourceManager, sh);
VkFence fence = VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> fenceStatus;
if (mCurrentRenderPass.commandBuffer) {
VulkanCommandBuffer* cmdBuff = mCurrentRenderPass.commandBuffer;
fence = cmdBuff->getVkFence();
fenceStatus = cmdBuff->getFenceStatus();
// If we're currently recording, flush so that the fence only applies
// to commands already issued.
mCommands.flush();
} else {
fence = mCommands.getMostRecentFence();
fenceStatus = mCommands.getMostRecentFenceStatus();
}
{
std::lock_guard<std::mutex> guard(sync->lock);
sync->sync = mPlatform->createSync(fence, fenceStatus);
}
for (auto& cbData : sync->conversionCallbacks) {
cbData->sync = sync->sync;
scheduleCallback(cbData->handler, cbData.release(), syncCallbackWrapper);
}
sync->conversionCallbacks.clear();
mResourceManager.associateHandle(sh.getId(), std::move(tag));
}
void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags,
utils::CString tag) {
FVK_SYSTRACE_SCOPE();
// Running gc() to guard against an edge case where the old swapchains need to have been
// destroyed before the new swapchain can be created. Otherwise, we would fail
// vkCreateSwapchainKHR with VK_ERROR_NATIVE_WINDOW_IN_USE_KHR.
mResourceManager.gc();
// TODO: support MSAA swapchain
if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 && !isSRGBSwapChainSupported()) {
FVK_LOGW << "sRGB swapchain requested, but Platform does not support it";
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
}
if ((flags & backend::SWAP_CHAIN_CONFIG_MSAA_4_SAMPLES) != 0 && !isMSAASwapChainSupported(4)) {
FVK_LOGW << "MSAAx4 swapchain requested, but Platform does not support it";
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_MSAA_4_SAMPLES);
}
if (flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) {
if (!isProtectedContentSupported()) {
FVK_LOGW << "protected swapchain requested, but Platform does not support it";
@@ -823,10 +895,11 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
auto swapChain = resource_ptr<VulkanSwapChain>::make(&mResourceManager, sch, mPlatform,
mContext, &mResourceManager, mAllocator, &mCommands, mStagePool, nativeWindow, flags);
swapChain.inc();
mResourceManager.associateHandle(sch.getId(), std::move(tag));
}
void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
uint32_t height, uint64_t flags) {
uint32_t height, uint64_t flags, utils::CString tag) {
if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 && !isSRGBSwapChainSupported()) {
FVK_LOGW << "sRGB swapchain requested, but Platform does not support it";
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
@@ -836,21 +909,24 @@ void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t wi
mContext, &mResourceManager, mAllocator, &mCommands, mStagePool, nullptr, flags,
VkExtent2D{width, height});
swapChain.inc();
mResourceManager.associateHandle(sch.getId(), std::move(tag));
}
void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::CString tag) {
// nothing to do, timer query was constructed in createTimerQueryS
mResourceManager.associateHandle(tqh.getId(), std::move(tag));
}
void VulkanDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
backend::DescriptorSetLayout&& info) {
backend::DescriptorSetLayout&& info, utils::CString tag) {
FVK_SYSTRACE_SCOPE();
auto layout = mDescriptorSetLayoutCache.createLayout(dslh, std::move(info));
layout.inc();
mResourceManager.associateHandle(dslh.getId(), std::move(tag));
}
void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
Handle<HwDescriptorSetLayout> dslh) {
Handle<HwDescriptorSetLayout> dslh, utils::CString tag) {
FVK_SYSTRACE_SCOPE();
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout =
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
@@ -860,6 +936,8 @@ void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
if (layout->hasExternalSamplers()) {
mAppState.hasExternalSamplerLayouts = true;
}
mResourceManager.associateHandle(dsh.getId(), std::move(tag));
}
Handle<HwVertexBufferInfo> VulkanDriver::createVertexBufferInfoS() noexcept {
@@ -929,6 +1007,13 @@ Handle<HwFence> VulkanDriver::createFenceS() noexcept {
return handle;
}
Handle<HwSync> VulkanDriver::createSyncS() noexcept {
auto handle = mResourceManager.allocHandle<VulkanSync>();
auto sync = resource_ptr<VulkanSync>::make(&mResourceManager, handle);
sync.inc();
return handle;
}
Handle<HwSwapChain> VulkanDriver::createSwapChainS() noexcept {
return mResourceManager.allocHandle<VulkanSwapChain>();
}
@@ -967,6 +1052,17 @@ void VulkanDriver::destroySwapChain(Handle<HwSwapChain> sch) {
void VulkanDriver::destroyStream(Handle<HwStream> sh) {
}
void VulkanDriver::destroySync(Handle<HwSync> sh) {
if (!sh) {
return;
}
FVK_SYSTRACE_SCOPE();
auto sync = resource_ptr<VulkanSync>::cast(&mResourceManager, sh);
mPlatform->destroySync(sync->sync);
sync.dec();
}
void VulkanDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
if (!tqh) {
return;
@@ -985,16 +1081,16 @@ void VulkanDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
set.dec();
if (mAppState.hasExternalSamplers() && set->getExternalSamplerVkSet() != VK_NULL_HANDLE) {
if (mAppState.hasExternalSamplers()) {
mExternalImageManager.removeDescriptorSet(set);
}
}
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream) {
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream, utils::CString tag) {
return {};
}
Handle<HwStream> VulkanDriver::createStreamAcquired() {
Handle<HwStream> VulkanDriver::createStreamAcquired(utils::CString tag) {
return {};
}
@@ -1042,6 +1138,29 @@ FenceStatus VulkanDriver::getFenceStatus(Handle<HwFence> fh) {
return FenceStatus::TIMEOUT_EXPIRED;
}
void VulkanDriver::getPlatformSync(Handle<HwSync> sh, CallbackHandler* handler,
Platform::SyncCallback cb, void* userData) {
auto sync = resource_ptr<VulkanSync>::cast(&mResourceManager, sh);
auto cbData = std::make_unique<VulkanSync::CallbackData>();
cbData->handler = handler;
cbData->cb = cb;
cbData->userData = userData;
// If we haven't already set the handle, toss the conversion callback in the
// back of the list.
{
std::lock_guard guard(sync->lock);
if (sync->sync == nullptr) {
sync->conversionCallbacks.push_back(std::move(cbData));
return;
}
}
// Otherwise, go ahead and schedule the callback now.
cbData->sync = sync->sync;
scheduleCallback(cbData->handler, cbData.release(), syncCallbackWrapper);
}
// We create all textures using VK_IMAGE_TILING_OPTIMAL, so our definition of "supported" is that
// the GPU supports the given texture format with non-zero optimal tiling features.
bool VulkanDriver::isTextureFormatSupported(TextureFormat format) {
@@ -1104,6 +1223,10 @@ bool VulkanDriver::isSRGBSwapChainSupported() {
return mIsSRGBSwapChainSupported;
}
bool VulkanDriver::isMSAASwapChainSupported(uint32_t) {
return mIsMSAASwapChainSupported;
}
bool VulkanDriver::isProtectedContentSupported() {
return mContext.isProtectedMemorySupported();
}
@@ -1607,7 +1730,7 @@ void VulkanDriver::commit(Handle<HwSwapChain> sch) {
auto swapChain = resource_ptr<VulkanSwapChain>::cast(&mResourceManager, sch);
// Present the backbuffer after the most recent command buffer submission has finished.
swapChain->present();
swapChain->present(*this);
}
void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
@@ -1788,9 +1911,14 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
VkOffset3D const srcOffsets[2] = { { srcLeft, srcTop, 0 }, { srcRight, srcBottom, 1 }};
VkOffset3D const dstOffsets[2] = { { dstLeft, dstTop, 0 }, { dstRight, dstBottom, 1 }};
mBlitter.blit(vkfilter,
dstTarget->getColor0(), dstOffsets,
srcTarget->getColor0(), srcOffsets);
auto const& dstAttachment = dstTarget->getColor0();
auto const& srcAttachment = srcTarget->getColor0();
if (srcAttachment.texture->samples > 1) {
mBlitter.resolve(dstAttachment, srcAttachment);
} else {
mBlitter.blit(vkfilter, dstAttachment, dstOffsets, srcAttachment, srcOffsets);
}
}
void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
@@ -2079,10 +2207,6 @@ void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, cons
void VulkanDriver::resetState(int) {
}
void VulkanDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
mResourceManager.associateHandle(handleId, std::move(tag));
}
void VulkanDriver::endCommandRecording() {
mCommands.flush();
mPipelineCache.resetBoundPipeline();
@@ -187,6 +187,7 @@ private:
} mAppState;
bool const mIsSRGBSwapChainSupported;
bool const mIsMSAASwapChainSupported;
backend::StereoscopicType const mStereoscopicType;
};
@@ -32,6 +32,7 @@
#include <utils/compiler.h> // UTILS_FALLTHROUGH
#include <utils/Panic.h> // ASSERT_POSTCONDITION
#include <utils/CString.h>
using namespace bluevk;
@@ -285,7 +286,7 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
<< " error=" << static_cast<int32_t>(result);
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
std::string name{ builder.getName().c_str(), builder.getName().size() };
utils::CString name{ builder.getName().c_str(), builder.getName().size() };
switch (static_cast<ShaderStage>(i)) {
case ShaderStage::VERTEX:
name += "_vs";
+6 -1
View File
@@ -308,7 +308,10 @@ struct VulkanRenderTarget : private HwRenderTarget, fvkmemory::Resource {
// Creates a special "default" render target (i.e. associated with the swap chain)
explicit VulkanRenderTarget();
VulkanRenderTarget(VulkanRenderTarget&& target) {
VulkanRenderTarget(VulkanRenderTarget&& target)
: HwRenderTarget(0, 0),
mOffscreen(false),
mProtected(false) {
swap(std::move(target));
}
@@ -365,6 +368,8 @@ struct VulkanRenderTarget : private HwRenderTarget, fvkmemory::Resource {
private:
void swap(VulkanRenderTarget&& target) {
std::swap(width, target.width);
std::swap(height, target.height);
std::swap(mOffscreen, target.mOffscreen);
std::swap(mProtected, target.mProtected);
std::swap(mInfo, target.mInfo);
@@ -93,7 +93,7 @@ void VulkanSwapChain::update() {
mLayerCount = bundle.layerCount;
}
void VulkanSwapChain::present() {
void VulkanSwapChain::present(DriverBase& driver) {
if (!mHeadless && mTransitionSwapChainImageLayoutForPresent) {
VulkanCommandBuffer& commands = mCommands->get();
VkImageSubresourceRange const subresources{
@@ -120,6 +120,14 @@ void VulkanSwapChain::present() {
// We presented the last acquired buffer.
mAcquired = false;
mIsFirstRenderPass = true;
if (frameScheduled.callback) {
driver.scheduleCallback(frameScheduled.handler,
[callback = std::move(frameScheduled.callback)]() {
PresentCallable noop = PresentCallable(PresentCallable::noopPresent, nullptr);
callback(noop);
});
}
}
void VulkanSwapChain::acquire(bool& resized) {
+14 -1
View File
@@ -47,7 +47,7 @@ struct VulkanSwapChain : public HwSwapChain, fvkmemory::Resource {
~VulkanSwapChain();
void present();
void present(DriverBase& driver);
// Acquire a new image from the swapchain. If the image is not available it would wait until it
// is.
@@ -79,6 +79,13 @@ struct VulkanSwapChain : public HwSwapChain, fvkmemory::Resource {
inline bool isProtected() noexcept {
return mPlatform->isProtected(swapChain);
}
inline void setFrameScheduledCallback(CallbackHandler* handler,
FrameScheduledCallback&& callback) noexcept {
frameScheduled.handler = handler;
frameScheduled.callback = std::move(callback);
}
private:
static constexpr int IMAGE_READY_SEMAPHORE_COUNT = FVK_MAX_COMMAND_BUFFERS;
@@ -94,6 +101,12 @@ private:
bool const mFlushAndWaitOnResize;
bool const mTransitionSwapChainImageLayoutForPresent;
// These fields store a callback to notify the client that Filament is commiting a frame.
struct {
CallbackHandler* handler = nullptr;
FrameScheduledCallback callback;
} frameScheduled;
// We create VulkanTextures based on VkImages. VulkanTexture has facilities for doing layout
// transitions, which are useful here.
utils::FixedCapacityVector<fvkmemory::resource_ptr<VulkanTexture>> mColors;
@@ -106,7 +106,7 @@ VkComponentMapping composeSwizzle(VkComponentMapping const& prev, VkComponentMap
return { vals[0], vals[1], vals[2], vals[3] };
};
// We make sure all all identities are mapped into respective channels so that actual channel
// We make sure all identities are mapped into respective channels so that actual channel
// mapping will be passed onto the output.
VkComponentMapping const prevExplicit = identityToChannel(prev);
VkComponentMapping const nextExplicit = identityToChannel(next);
@@ -38,6 +38,7 @@ template ResourceType getTypeEnum<VulkanDescriptorSetLayout>() noexcept;
template ResourceType getTypeEnum<VulkanDescriptorSet>() noexcept;
template ResourceType getTypeEnum<VulkanFence>() noexcept;
template ResourceType getTypeEnum<VulkanBuffer>() noexcept;
template ResourceType getTypeEnum<VulkanSync>() noexcept;
template<typename D>
ResourceType getTypeEnum() noexcept {
@@ -92,10 +93,13 @@ ResourceType getTypeEnum() noexcept {
if constexpr (std::is_same_v<D, VulkanBuffer>) {
return ResourceType::VULKAN_BUFFER;
}
if constexpr (std::is_same_v<D, VulkanSync>) {
return ResourceType::SYNC;
}
return ResourceType::UNDEFINED_TYPE;
}
std::string getTypeStr(ResourceType type) {
std::string_view getTypeStr(ResourceType type) {
switch (type) {
case ResourceType::BUFFER_OBJECT:
return "BufferObject";
@@ -131,6 +135,8 @@ std::string getTypeStr(ResourceType type) {
return "Fence";
case ResourceType::VULKAN_BUFFER:
return "VulkanBuffer";
case ResourceType::SYNC:
return "Sync";
case ResourceType::UNDEFINED_TYPE:
return "";
}
@@ -52,13 +52,14 @@ enum class ResourceType : uint8_t {
VULKAN_BUFFER = 14,
STAGE_SEGMENT = 15,
STAGE_IMAGE = 16,
UNDEFINED_TYPE = 17, // Must be the last enum because we use it for iterating over the enums.
SYNC = 17,
UNDEFINED_TYPE = 18, // Must be the last enum because we use it for iterating over the enums.
};
template<typename D>
ResourceType getTypeEnum() noexcept;
std::string getTypeStr(ResourceType type);
std::string_view getTypeStr(ResourceType type);
inline bool isThreadSafeType(ResourceType type) {
return type == ResourceType::FENCE || type == ResourceType::TIMER_QUERY;
@@ -114,6 +114,9 @@ void ResourceManager::destroyWithType(ResourceType type, HandleId id) {
case ResourceType::VULKAN_BUFFER:
destruct<VulkanBuffer>(Handle<VulkanBuffer>(id));
break;
case ResourceType::SYNC:
destruct<VulkanSync>(Handle<VulkanSync>(id));
break;
case ResourceType::UNDEFINED_TYPE:
break;
}
@@ -183,7 +183,6 @@ ExtensionSet getInstanceExtensions(ExtensionSet const& externallyRequiredExts =
ExtensionSet const TARGET_EXTS = {
// Request all cross-platform extensions.
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
// Request these if available.
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
@@ -236,9 +235,6 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
#if defined(__APPLE__)
VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,
#endif
VK_KHR_MAINTENANCE1_EXTENSION_NAME,
VK_KHR_MAINTENANCE2_EXTENSION_NAME,
VK_KHR_MAINTENANCE3_EXTENSION_NAME,
VK_KHR_MULTIVIEW_EXTENSION_NAME,
};
ExtensionSet exts;
@@ -920,6 +916,8 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
context.mBlittableDepthStencilFormats =
findBlittableDepthStencilFormats(mImpl->mPhysicalDevice);
context.mFenceExportFlags = getFenceExportFlags();
assert_invariant(context.mDepthStencilFormats.size() > 0);
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
@@ -1002,6 +1000,17 @@ SwapChainPtr VulkanPlatform::createSwapChain(void* nativeWindow, uint64_t flags,
return swapchain;
}
Platform::Sync* VulkanPlatform::createSync(VkFence fence,
std::shared_ptr<VulkanCmdFence> fenceStatus) noexcept {
return new VulkanSync{.fence = fence, .fenceStatus = fenceStatus};
}
void VulkanPlatform::destroySync(Platform::Sync* sync) noexcept {
// Sync must be a VulkanSync*, since it was created by VulkanPlatform's
// createSync object.
delete sync;
}
VkInstance VulkanPlatform::getInstance() const noexcept {
return mImpl->mInstance;
}
@@ -1038,6 +1047,11 @@ VkQueue VulkanPlatform::getProtectedGraphicsQueue() const noexcept {
return mImpl->mProtectedGraphicsQueue;
}
VkExternalFenceHandleTypeFlagBits VulkanPlatform::getFenceExportFlags() const noexcept {
// By default, fences should not be exportable.
return static_cast<VkExternalFenceHandleTypeFlagBits>(0);
}
ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() const {
return getSwapchainInstanceExtensionsImpl();
}
@@ -389,6 +389,37 @@ VulkanPlatform::ImageData VulkanPlatformAndroid::createVkImageFromExternal(
};
}
bool VulkanPlatformAndroid::convertSyncToFd(Platform::Sync* sync, int* fd) const noexcept {
assert_invariant(sync && fd);
VulkanSync& vulkanSync = static_cast<VulkanSync&>(*sync);
assert_invariant(vulkanSync.fenceStatus);
if (vulkanSync.fenceStatus->getStatus() == VK_SUCCESS) {
// We've already signaled; return -1 so that operations will proceed
// immediately. Also, signal that fence conversion was successful.
*fd = -1;
return true;
}
VkFenceGetFdInfoKHR getFdInfo = {
.sType = VK_STRUCTURE_TYPE_FENCE_GET_FD_INFO_KHR,
.fence = vulkanSync.fence,
.handleType = getFenceExportFlags(),
};
VkResult res = vkGetFenceFdKHR(getDevice(), &getFdInfo, fd);
if (res != VK_SUCCESS) {
LOG(ERROR) << "Failed to convert sync to fd: " << res;
return false;
}
return true;
}
VkExternalFenceHandleTypeFlagBits VulkanPlatformAndroid::getFenceExportFlags() const noexcept {
// On android, make fences that can be exported to fd's.
return VK_EXTERNAL_FENCE_HANDLE_TYPE_SYNC_FD_BIT;
}
VulkanPlatform::ExtensionSet VulkanPlatformAndroid::getSwapchainInstanceExtensions() const {
return {
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
@@ -28,8 +28,8 @@
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
#ifndef VK_MVK_macos_surface
#error VK_MVK_macos_surface is not defined
#ifndef VK_EXT_metal_surface
#error VK_EXT_metal_surface is not defined
#endif
using namespace bluevk;
@@ -38,7 +38,7 @@ namespace filament::backend {
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl() {
ExtensionSet const ret = {
VK_MVK_MACOS_SURFACE_EXTENSION_NAME, // TODO: replace with VK_EXT_metal_surface
VK_EXT_METAL_SURFACE_EXTENSION_NAME,
};
return ret;
}
@@ -46,17 +46,18 @@ VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensionsImpl(
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHRImpl(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept {
VkSurfaceKHR surface;
NSView* nsview = (__bridge NSView*) nativeWindow;
FILAMENT_CHECK_POSTCONDITION(nsview) << "Unable to obtain Metal-backed NSView.";
CAMetalLayer* mlayer = (__bridge CAMetalLayer*) nativeWindow;
FILAMENT_CHECK_POSTCONDITION(mlayer) << "Unable to obtain Metal-backed layer.";
// Create the VkSurface.
FILAMENT_CHECK_POSTCONDITION(vkCreateMacOSSurfaceMVK)
<< "Unable to load vkCreateMacOSSurfaceMVK.";
VkMacOSSurfaceCreateInfoMVK createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK;
createInfo.pView = (__bridge void*) nsview;
VkResult result = vkCreateMacOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC,
(VkSurfaceKHR*) &surface);
FILAMENT_CHECK_POSTCONDITION(vkCreateMetalSurfaceEXT)
<< "Unable to load vkCreateMetalSurfaceEXT.";
VkMetalSurfaceCreateInfoEXT createInfo = {
.sType = VK_STRUCTURE_TYPE_METAL_SURFACE_CREATE_INFO_EXT,
.pLayer = mlayer,
};
VkResult result =
vkCreateMetalSurfaceEXT(instance, &createInfo, VKALLOC, &surface);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkCreateMacOSSurfaceMVK. error=" << static_cast<int32_t>(result);
return std::make_tuple(surface, VkExtent2D{});
File diff suppressed because it is too large Load Diff
+95 -45
View File
@@ -28,6 +28,7 @@
#include "WebGPURenderTarget.h"
#include "WebGPUStrings.h"
#include "WebGPUSwapChain.h"
#include "WebGPUSync.h"
#include "WebGPUTexture.h"
#include "WebGPUTextureHelpers.h"
#include "WebGPUVertexBuffer.h"
@@ -154,7 +155,8 @@ void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns,
void WebGPUDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch,
CallbackHandler* handler, FrameScheduledCallback&& callback, uint64_t flags) {
auto swapChain = handleCast<WebGPUSwapChain>(sch);
swapChain->setFrameScheduledCallback(handler, std::move(callback));
}
void WebGPUDriver::setFrameCompletedCallback(Handle<HwSwapChain> sch,
@@ -207,7 +209,17 @@ void WebGPUDriver::finish(int /* dummy */) {
FWGPU_SYSTRACE_SCOPE();
mDevice.Tick();
mAdapter.GetInstance().ProcessEvents();
mReadPixelMapsCounter.waitForAllToFinish();
flush();
// Some applications/samples might call finish in the very beginning of an application before
// creating any resources/tasks to flush out the system on any pending work from previous runs.
// Currently, gltf_viewer sample on android exhibit this behavior.
// Bail out early to safeguard against those cases. Otherwise, OnSubmittedWorkDone will
// hang on work that was never submitted.
if (mCommandEncoder == nullptr) {
return;
}
// Wait for all previously submitted work to finish.
std::mutex syncPoint;
std::condition_variable syncCondition;
@@ -225,7 +237,6 @@ void WebGPUDriver::finish(int /* dummy */) {
});
std::unique_lock<std::mutex> lock(syncPoint);
syncCondition.wait(lock, [&done] { return done; });
mReadPixelMapsCounter.waitForAllToFinish();
}
void WebGPUDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
@@ -367,6 +378,10 @@ Handle<HwFence> WebGPUDriver::createFenceS() noexcept {
return allocAndConstructHandle<WebGPUFence, HwFence>();
}
Handle<HwSync> WebGPUDriver::createSyncS() noexcept {
return allocHandle<WebGPUSync>();
}
Handle<HwTimerQuery> WebGPUDriver::createTimerQueryS() noexcept {
return Handle<HwTimerQuery>((Handle<HwTimerQuery>::HandleId) mNextFakeHandle++);
}
@@ -435,8 +450,11 @@ Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
// ------------------------------------------------------------------------------------------------
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
const uint64_t flags) {
const uint64_t flags, utils::CString tag) {
FWGPU_SYSTRACE_SCOPE();
// TODO: support MSAA swapchain
mNativeWindow = nativeWindow;
wgpu::Surface surface = mPlatform.createSurface(nativeWindow, flags);
@@ -457,10 +475,12 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
if (mDefaultRenderTarget) {
setDefaultTargetFlags(*mDefaultRenderTarget, *mSwapChain);
}
setDebugTag(sch.getId(), std::move(tag));
}
void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
uint32_t height, uint64_t flags) {
uint32_t height, uint64_t flags, utils::CString tag) {
wgpu::Extent2D extent = { .width = width, .height = height };
mSwapChain = constructHandle<WebGPUSwapChain>(sch, extent, mAdapter,
mDevice, flags);
@@ -468,56 +488,69 @@ void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t wi
if (mDefaultRenderTarget) {
setDefaultTargetFlags(*mDefaultRenderTarget, *mSwapChain);
}
setDebugTag(sch.getId(), std::move(tag));
}
void WebGPUDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vertexBufferInfoHandle,
const uint8_t bufferCount, const uint8_t attributeCount, const AttributeArray attributes) {
const uint8_t bufferCount, const uint8_t attributeCount, const AttributeArray attributes,
utils::CString tag) {
FWGPU_SYSTRACE_SCOPE();
constructHandle<WebGPUVertexBufferInfo>(vertexBufferInfoHandle, bufferCount, attributeCount,
attributes, mDeviceLimits);
setDebugTag(vertexBufferInfoHandle.getId(), std::move(tag));
}
void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vertexBufferHandle,
const uint32_t vertexCount, Handle<HwVertexBufferInfo> vertexBufferInfoHandle) {
const uint32_t vertexCount, Handle<HwVertexBufferInfo> vertexBufferInfoHandle,
utils::CString tag) {
FWGPU_SYSTRACE_SCOPE();
const auto vertexBufferInfo = handleCast<WebGPUVertexBufferInfo>(vertexBufferInfoHandle);
constructHandle<WebGPUVertexBuffer>(vertexBufferHandle, vertexCount,
vertexBufferInfo->bufferCount, vertexBufferInfoHandle);
setDebugTag(vertexBufferHandle.getId(), std::move(tag));
}
void WebGPUDriver::createIndexBufferR(Handle<HwIndexBuffer> indexBufferHandle,
const ElementType elementType, const uint32_t indexCount, const BufferUsage usage) {
const ElementType elementType, const uint32_t indexCount, const BufferUsage usage,
utils::CString tag) {
FWGPU_SYSTRACE_SCOPE();
const auto elementSize = static_cast<uint8_t>(getElementTypeSize(elementType));
constructHandle<WebGPUIndexBuffer>(indexBufferHandle, mDevice, elementSize, indexCount);
setDebugTag(indexBufferHandle.getId(), std::move(tag));
}
void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> bufferObjectHandle,
const uint32_t byteCount, const BufferObjectBinding bindingType, const BufferUsage usage) {
const uint32_t byteCount, const BufferObjectBinding bindingType, const BufferUsage usage,
utils::CString tag) {
FWGPU_SYSTRACE_SCOPE();
constructHandle<WebGPUBufferObject>(bufferObjectHandle, mDevice, bindingType, byteCount);
setDebugTag(bufferObjectHandle.getId(), std::move(tag));
}
void WebGPUDriver::createTextureR(Handle<HwTexture> textureHandle, const SamplerType target,
const uint8_t levels, const TextureFormat format, const uint8_t samples,
const uint32_t width, const uint32_t height, const uint32_t depth,
const TextureUsage usage) {
const TextureUsage usage, utils::CString tag) {
FWGPU_SYSTRACE_SCOPE();
constructHandle<WebGPUTexture>(textureHandle, target, levels, format, samples, width, height,
depth, usage, mDevice);
setDebugTag(textureHandle.getId(), std::move(tag));
}
void WebGPUDriver::createTextureViewR(Handle<HwTexture> textureHandle,
Handle<HwTexture> sourceTextureHandle, const uint8_t baseLevel, const uint8_t levelCount) {
Handle<HwTexture> sourceTextureHandle, const uint8_t baseLevel, const uint8_t levelCount,
utils::CString tag) {
auto source = handleCast<WebGPUTexture>(sourceTextureHandle);
constructHandle<WebGPUTexture>(textureHandle, source, baseLevel, levelCount);
setDebugTag(textureHandle.getId(), std::move(tag));
}
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> textureHandle,
Handle<HwTexture> sourceTextureHandle, const backend::TextureSwizzle r,
const backend::TextureSwizzle g, const backend::TextureSwizzle b,
const backend::TextureSwizzle a) {
const backend::TextureSwizzle a, utils::CString tag) {
if (!isTextureSwizzleSupported()) {
FWGPU_LOGW << "WebGPUDriver::createTextureViewSwizzleR called while texture swizzling is "
@@ -527,37 +560,24 @@ void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> textureHandle,
auto sourceTexture{ handleCast<WebGPUTexture>(sourceTextureHandle) };
assert_invariant(sourceTexture);
wgpu::TextureComponentSwizzle swizzle
{
wgpu::TextureComponentSwizzle const nextSwizzle{
.r = toWGPUComponentSwizzle(r),
.g = toWGPUComponentSwizzle(g),
.b = toWGPUComponentSwizzle(b),
.a = toWGPUComponentSwizzle(a),
};
wgpu::TextureComponentSwizzleDescriptor swizzleDesc {};
swizzleDesc.swizzle = swizzle;
// The WebGPUTexture constructor for swizzled views will handle composing the swizzle and
// creating the new texture view.
constructHandle<WebGPUTexture>(textureHandle, sourceTexture, nextSwizzle);
const wgpu::TextureViewDescriptor viewDesc {
.nextInChain = &swizzleDesc,
.label = "swizzled_texture_view",
.format = sourceTexture->getTexture().GetFormat(),
.dimension = sourceTexture->getViewDimension(),
.baseMipLevel = 0,
.mipLevelCount = sourceTexture->getTexture().GetMipLevelCount(),
.baseArrayLayer = 0,
.arrayLayerCount = sourceTexture->getTexture().GetDepthOrArrayLayers(),
};
wgpu::TextureView swizzledView{ sourceTexture->getTexture().CreateView(&viewDesc) };
FILAMENT_CHECK_POSTCONDITION(swizzledView) << "Failed to create swizzled Texture view";
constructHandle<WebGPUTexture>(textureHandle, sourceTexture, swizzledView);
setDebugTag(textureHandle.getId(), std::move(tag));
}
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> textureHandle,
const backend::SamplerType target, const backend::TextureFormat format,
const uint32_t width, const uint32_t height, const backend::TextureUsage usage,
Platform::ExternalImageHandleRef externalImage) {
Platform::ExternalImageHandleRef externalImage, utils::CString tag) {
FWGPU_SYSTRACE_SCOPE();
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
}
@@ -565,27 +585,27 @@ void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> textureHandle,
void WebGPUDriver::createTextureExternalImageR(Handle<HwTexture> textureHandle,
const backend::SamplerType target, const backend::TextureFormat format,
const uint32_t width, const uint32_t height, const backend::TextureUsage usage,
void* externalImage) {
void* externalImage, utils::CString tag) {
FWGPU_SYSTRACE_SCOPE();
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
}
void WebGPUDriver::createTextureExternalImagePlaneR(Handle<HwTexture> textureHandle,
const backend::TextureFormat format, const uint32_t width, const uint32_t height,
const backend::TextureUsage usage, void* image, const uint32_t plane) {
const backend::TextureUsage usage, void* image, const uint32_t plane, utils::CString tag) {
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
}
void WebGPUDriver::importTextureR(Handle<HwTexture> textureHandle, const intptr_t id,
const SamplerType target, const uint8_t levels, const TextureFormat format,
const uint8_t samples, const uint32_t width, const uint32_t height, const uint32_t depth,
const TextureUsage usage) {
const TextureUsage usage, utils::CString tag) {
PANIC_POSTCONDITION("Import WebGPU Texture is not supported");
}
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> renderPrimitiveHandle,
Handle<HwVertexBuffer> vertexBufferHandle, Handle<HwIndexBuffer> indexBufferHandle,
const PrimitiveType primitiveType) {
const PrimitiveType primitiveType, utils::CString tag) {
FWGPU_SYSTRACE_SCOPE();
assert_invariant(mDevice);
const auto renderPrimitive = constructHandle<WebGPURenderPrimitive>(renderPrimitiveHandle);
@@ -594,27 +614,31 @@ void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> renderPrimit
renderPrimitive->vertexBuffer = vertexBuffer;
renderPrimitive->indexBuffer = indexBuffer;
renderPrimitive->type = primitiveType;
setDebugTag(renderPrimitiveHandle.getId(), std::move(tag));
}
void WebGPUDriver::createProgramR(Handle<HwProgram> programHandle, Program&& program) {
void WebGPUDriver::createProgramR(Handle<HwProgram> programHandle, Program&& program,
utils::CString tag) {
FWGPU_SYSTRACE_SCOPE();
constructHandle<WebGPUProgram>(programHandle, mDevice, program);
setDebugTag(programHandle.getId(), std::move(tag));
}
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> renderTargetHandle,
const int /* dummy */) {
utils::CString tag) {
assert_invariant(!mDefaultRenderTarget);
mDefaultRenderTarget = constructHandle<WebGPURenderTarget>(renderTargetHandle);
assert_invariant(mDefaultRenderTarget);
if (mSwapChain) {
setDefaultTargetFlags(*mDefaultRenderTarget, *mSwapChain);
}
setDebugTag(renderTargetHandle.getId(), std::move(tag));
}
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> renderTargetHandle,
const TargetBufferFlags targetFlags, const uint32_t width, const uint32_t height,
const uint8_t samples, const uint8_t layerCount, const MRT color,
const TargetBufferInfo depth, const TargetBufferInfo stencil) {
const TargetBufferInfo depth, const TargetBufferInfo stencil, utils::CString tag) {
FWGPU_SYSTRACE_SCOPE();
constructHandle<WebGPURenderTarget>(
renderTargetHandle, width, height, samples, layerCount, color, depth, stencil,
@@ -623,39 +647,49 @@ void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> renderTargetHandle
return handleCast<WebGPUTexture>(textureHandle);
},
mDevice);
setDebugTag(renderTargetHandle.getId(), std::move(tag));
}
void WebGPUDriver::createFenceR(Handle<HwFence> fenceHandle, const int /* dummy */) {
void WebGPUDriver::createFenceR(Handle<HwFence> fenceHandle, utils::CString tag) {
// The handle is constructed synchronously in createFenceS.
const auto fence = handleCast<WebGPUFence>(fenceHandle);
assert_invariant(mQueue);
fence->addMarkerToQueueState(mQueue);
setDebugTag(fenceHandle.getId(), std::move(tag));
}
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}
void WebGPUDriver::createSyncR(Handle<HwSync> syncHandle, utils::CString tag) {
// TODO: Ensure sync is active, and then invoke and clear all pending
// callbacks.
setDebugTag(syncHandle.getId(), std::move(tag));
}
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::CString tag) {}
void WebGPUDriver::createDescriptorSetLayoutR(
Handle<HwDescriptorSetLayout> descriptorSetLayoutHandle,
backend::DescriptorSetLayout&& info) {
backend::DescriptorSetLayout&& info, utils::CString tag) {
FWGPU_SYSTRACE_SCOPE();
constructHandle<WebGPUDescriptorSetLayout>(descriptorSetLayoutHandle, std::move(info), mDevice);
setDebugTag(descriptorSetLayoutHandle.getId(), std::move(tag));
}
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> descriptorSetHandle,
Handle<HwDescriptorSetLayout> descriptorSetLayoutHandle) {
Handle<HwDescriptorSetLayout> descriptorSetLayoutHandle, utils::CString tag) {
FWGPU_SYSTRACE_SCOPE();
auto layout = handleCast<WebGPUDescriptorSetLayout>(descriptorSetLayoutHandle);
constructHandle<WebGPUDescriptorSet>(descriptorSetHandle, layout->getLayout(),
layout->getBindGroupEntries());
setDebugTag(descriptorSetHandle.getId(), std::move(tag));
}
Handle<HwStream> WebGPUDriver::createStreamNative(void* nativeStream) {
Handle<HwStream> WebGPUDriver::createStreamNative(void* nativeStream, utils::CString tag) {
return {
//todo
};
}
Handle<HwStream> WebGPUDriver::createStreamAcquired() {
Handle<HwStream> WebGPUDriver::createStreamAcquired(utils::CString tag) {
return {
//todo
};
@@ -698,6 +732,18 @@ FenceStatus WebGPUDriver::getFenceStatus(Handle<HwFence> fenceHandle) {
return fence->getStatus();
}
void WebGPUDriver::destroySync(Handle<HwSync> syncHandle) {
if (syncHandle) {
destructHandle<WebGPUSync>(syncHandle);
}
}
void WebGPUDriver::getPlatformSync(Handle<HwSync> syncHandle, CallbackHandler* handler,
Platform::SyncCallback cb, void* userData) {
// TODO: If the sync has been inserted into the command stream, execute
// the callback. Otherwise, enqueue it.
}
bool WebGPUDriver::isTextureFormatSupported(const TextureFormat format) {
return toWGPUTextureFormat(format) != wgpu::TextureFormat::Undefined;
}
@@ -747,6 +793,10 @@ bool WebGPUDriver::isSRGBSwapChainSupported() {
return false;
}
bool WebGPUDriver::isMSAASwapChainSupported(uint32_t) {
return false;
}
bool WebGPUDriver::isProtectedContentSupported() {
return false;
}
@@ -1353,7 +1403,7 @@ void WebGPUDriver::commit(Handle<HwSwapChain> swapChainHandle) {
if (UTILS_VERY_UNLIKELY(!mCommandEncoder)) {
// nothing to submit
if (UTILS_LIKELY(mSwapChain)) {
mSwapChain->present();
mSwapChain->present(*this);
}
return;
}
@@ -1387,7 +1437,7 @@ void WebGPUDriver::commit(Handle<HwSwapChain> swapChainHandle) {
}
mTextureView = nullptr;
assert_invariant(mSwapChain);
mSwapChain->present();
mSwapChain->present(*this);
}
void WebGPUDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
@@ -68,6 +68,7 @@ private:
[[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final;
[[nodiscard]] wgpu::Sampler makeSampler(SamplerParams const& params);
[[nodiscard]] static wgpu::AddressMode fWrapModeToWAddressMode(const filament::backend::SamplerWrapMode& fUsage);
void setDebugTag(HandleBase::HandleId handleId, utils::CString tag);
// The platform (e.g. OS) specific aspects of the WebGPU backend are strictly only
// handled in the WebGPUPlatform.
@@ -293,10 +293,11 @@ wgpu::RenderPipeline WebGPUPipelineCache::createRenderPipeline(
},
.primitive = {
.topology = toWebGPU(request.primitiveType),
// TODO should we assume some constant format here or is there a way to get
// this from PipelineState somehow or elsewhere?
// Perhaps, cache/assert format from index buffers as they are requested?
.stripIndexFormat = wgpu::IndexFormat::Undefined,
// TODO This won't always be the case for the primitives bound. But this particular field
// in the pipeline struct is only meant for restarting the strip, which we don't need so far.
.stripIndexFormat =
isStripPrimitiveType(request.primitiveType)? wgpu::IndexFormat::Uint16 :
wgpu::IndexFormat::Undefined,
.frontFace = request.rasterState.inverseFrontFaces ? wgpu::FrontFace::CW : wgpu::FrontFace::CCW,
.cullMode = toWebGPU(request.rasterState.culling),
// TODO no depth clamp in WebGPU supported directly. unclippedDepth is close, so we are
@@ -312,12 +313,10 @@ wgpu::RenderPipeline WebGPUPipelineCache::createRenderPipeline(
},
.fragment = nullptr // will add below if fragment module is included
};
// TODO:
if (pipelineDescriptor.primitive.topology == wgpu::PrimitiveTopology::LineStrip ||
pipelineDescriptor.primitive.topology == wgpu::PrimitiveTopology::TriangleStrip) {
PANIC_POSTCONDITION("stripIndexFormat must be set for strip topologies. "
"This needs to be plumbed through from the RenderPrimitive.");
}
FILAMENT_CHECK_POSTCONDITION(!isStripPrimitiveType(request.primitiveType) ||
pipelineDescriptor.primitive.stripIndexFormat != wgpu::IndexFormat::Undefined)
<< "If the topology is a strip format, e.g. TriangleStrip, the stripIndexFormat cannot "
"be Undefined.";
wgpu::FragmentState fragmentState = {};
const wgpu::BlendState blendState {
@@ -161,7 +161,7 @@ private:
// render targets... //
uint8_t multisampleCount{ 0 }; // 1 : 313
uint8_t colorFormatCount{ 0 }; // 1 : 314
uint8_t padding[5]{ 0 }; // 5 : 319
uint8_t padding[5]{ 0 }; // 5 : 315
TargetBufferFlags targetRenderFlags{ TargetBufferFlags::NONE }; // 4 : 320
wgpu::TextureFormat depthStencilFormat { wgpu::TextureFormat::Undefined }; // 4 : 324
wgpu::TextureFormat colorFormats[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT]{ //
@@ -386,10 +386,17 @@ wgpu::Texture WebGPUSwapChain::getCurrentTexture() {
}
void WebGPUSwapChain::present() {
void WebGPUSwapChain::present(DriverBase& driver) {
if (!isHeadless()) {
mSurface.Present();
}
if (frameScheduled.callback) {
driver.scheduleCallback(frameScheduled.handler,
[callback = std::move(frameScheduled.callback)]() {
PresentCallable noop = PresentCallable(PresentCallable::noopPresent, nullptr);
callback(noop);
});
}
}
}// namespace filament::backend
+11 -1
View File
@@ -49,7 +49,12 @@ public:
[[nodiscard]] bool isHeadless() const { return mType == SwapChainType::HEADLESS; }
void present();
void present(DriverBase& driver);
void setFrameScheduledCallback(CallbackHandler* handler, FrameScheduledCallback&& callback) {
frameScheduled.handler = handler;
frameScheduled.callback = std::move(callback);
}
private:
@@ -75,6 +80,11 @@ private:
uint8_t mHeadlessBufferIndex = 0;
std::array<wgpu::Texture, mHeadlessBufferCount> mRenderTargetTextures;
std::array<wgpu::TextureView, mHeadlessBufferCount> mRenderTargetViews;
struct {
CallbackHandler* handler = nullptr;
FrameScheduledCallback callback;
} frameScheduled;
};
} // namespace filament::backend
+30
View File
@@ -0,0 +1,30 @@
/*
* 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_WEBGPUSYNC_H
#define TNT_FILAMENT_BACKEND_WEBGPUSYNC_H
#include "DriverBase.h"
namespace filament::backend {
// TODO: provide a more complete implementation of Sync.
class WebGPUSync final : public HwSync {
};
}
#endif // TNT_FILAMENT_BACKEND_WEBGPUSYNC_H
+44 -18
View File
@@ -216,7 +216,9 @@ WebGPUTexture::WebGPUTexture(const SamplerType samplerType, const uint8_t levels
mBlockWidth{ filament::backend::getBlockWidth(format) },
mBlockHeight{ filament::backend::getBlockHeight(format) },
mDefaultMipLevel{ 0 },
mDefaultBaseArrayLayer{ 0 } {
mDefaultBaseArrayLayer{ 0 },
// an undefined mSwizzle means that is not a swizzle view
mSwizzle{} {
assert_invariant(
samples == 1 ||
samples == 4 &&
@@ -279,11 +281,36 @@ WebGPUTexture::WebGPUTexture(WebGPUTexture const* src, const uint8_t baseLevel,
mTexture{ src->mTexture },
mDefaultMipLevel{ baseLevel },
mDefaultBaseArrayLayer{ src->mArrayLayerCount },
mDefaultTextureView{ makeTextureView(mDefaultMipLevel, levelCount, 0, mDefaultBaseArrayLayer,
toWebGPUTextureViewDimension(src->target)) },
mMsaaSidecarTexture{ src->mMsaaSidecarTexture } {}
mMsaaSidecarTexture{ src->mMsaaSidecarTexture },
mSwizzle{ src->mSwizzle } {
const bool swizzleUndefined{ (src->mSwizzle.r == wgpu::ComponentSwizzle::Undefined) &&
(src->mSwizzle.g == wgpu::ComponentSwizzle::Undefined) &&
(src->mSwizzle.b == wgpu::ComponentSwizzle::Undefined) &&
(src->mSwizzle.a == wgpu::ComponentSwizzle::Undefined) };
if (swizzleUndefined) {
mDefaultTextureView = makeTextureView(mDefaultMipLevel, levelCount, 0,
mDefaultBaseArrayLayer, src->getViewDimension());
} else {
wgpu::TextureComponentSwizzleDescriptor swizzleDesc{};
swizzleDesc.swizzle = mSwizzle;
const wgpu::TextureViewDescriptor viewDesc{
.nextInChain = &swizzleDesc,
.label = "swizzled_texture_view",
.format = mTexture.GetFormat(),
.dimension = src->getViewDimension(),
.baseMipLevel = mDefaultMipLevel,
.mipLevelCount = levelCount,
.baseArrayLayer = 0,
.arrayLayerCount = mDefaultBaseArrayLayer,
};
mDefaultTextureView = mTexture.CreateView(&viewDesc);
FILAMENT_CHECK_POSTCONDITION(mDefaultTextureView)
<< "Failed to create swizzled Texture view";
}
}
WebGPUTexture::WebGPUTexture(const WebGPUTexture* src, const wgpu::TextureView view) noexcept
WebGPUTexture::WebGPUTexture(const WebGPUTexture* src,
const wgpu::TextureComponentSwizzle nextSwizzle) noexcept
: HwTexture{ src->target, src->levels, src->samples, src->width, src->height, src->depth,
src->format, src->usage},
mViewFormat{ src->mViewFormat },
@@ -298,20 +325,19 @@ WebGPUTexture::WebGPUTexture(const WebGPUTexture* src, const wgpu::TextureView v
mTexture{ src->mTexture },
mDefaultMipLevel{ src->mDefaultMipLevel },
mDefaultBaseArrayLayer{ 0 },
mDefaultTextureView{ view },
mMsaaSidecarTexture{src->mMsaaSidecarTexture}{}
mMsaaSidecarTexture{src->mMsaaSidecarTexture},
mSwizzle{ composeSwizzle(src->getSwizzle(), nextSwizzle) } {
wgpu::TextureComponentSwizzleDescriptor swizzleDesc{};
swizzleDesc.swizzle = mSwizzle;
wgpu::Texture const& WebGPUTexture::getMsaaSidecarTexture(const uint8_t sampleCount) const {
if (mMsaaSidecarTexture == nullptr) {
return mMsaaSidecarTexture; // nullptr (no such sidecar)
}
FILAMENT_CHECK_PRECONDITION(sampleCount == mMsaaSidecarTexture.GetSampleCount())
<< "The MSAA sidecar texture has a different sample count ("
<< mMsaaSidecarTexture.GetSampleCount() << ") than requested (" << +sampleCount
<< "). Note that this restriction was written when WebGPU only supported msaa "
"textures with 4 samples. If that has changed, this implementation should be "
"updated (e.g. map of sidecar textures by sampleCount or something).";
return mMsaaSidecarTexture;
const wgpu::TextureViewDescriptor viewDesc{
.nextInChain = &swizzleDesc,
.label = "swizzled_texture_view",
.format = mTexture.GetFormat(),
.dimension = src->getViewDimension(),
};
mDefaultTextureView = mTexture.CreateView(&viewDesc);
FILAMENT_CHECK_POSTCONDITION(mDefaultTextureView) << "Failed to create swizzled Texture view";
}
bool WebGPUTexture::supportsMultipleMipLevelsViaStorageBinding(const wgpu::TextureFormat format) {

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