Compare commits

..

91 Commits

Author SHA1 Message Date
Run Yu
5f14ebba87 Use MapAsync and AllowProcessEvents for callback 2025-11-19 11:19:27 -05:00
Doris Wu
aa4e2c56b5 buffer update opt: Skip releaseFreeSlots when no frees are pending (#9437) 2025-11-15 10:23:27 +08:00
Powei Feng
aa4f1910b8 android: add static annotation for Utils.init() (#9435)
This allows for static call from java, e.g.:

```
static {
    Utils.init();
}
```
2025-11-13 22:01:49 +00:00
Mathias Agopian
223a4b18a8 Fix several problems with RenderPass descriptor sets (#9431)
The root of the problem is that in the main rendering loop we need
to set the correct per-view descriptor-set based on the material and
variants.

But we have two cases, either the descriptor set is always constant, 
which is the case with ssr, structure, shadows and postfx passes, or
it need to be dynamically changed based on the material & variant.

In the 2nd case, where was a problem where the postfx descriptor set
could be used, which is wrong (e.g. while rendering shadows we need
the shadow UBO, even with a postfx material), and would corrupt the
correct descriptor set, which would never be set back.

Another issue was related to running a custom command, it could 
change the state without updating the local copy, causing corruptions
or validation errors.

In this CL we:
- use the same descriptorsetlayout for both postfx and depth
- invalidate the state after a custom command
- only switch to postfx descriptor set in dynamic mode (color pass)


This fixes setChannelDepthClearEnabled() which could cause 
validation error, corruption or crashes.

FIXES=[459567258]
2025-11-13 12:07:16 -08:00
rafadevai
57ef534acd VK: Add support for getting pipeline creation stats (#9422)
* VK: Add support for getting pipeline creation stats

When debugging is pipeline compilation related hitches
is useful to know more information about what happened
when building a new pipeline.

By levering the VK_EXT_pipeline_creation_feedback
extension, we can know if the pipeline creation was
speed up because of a cache hit, how much time it spent
creating it and also the stats related to each
pipeline stage.

This is only available when using the debug flag
FVK_DEBUG_SHADER_MODULE.

* Fix the unused build error
2025-11-13 09:01:02 -08:00
rafadevai
719914fb84 VK: Better support for renderdoc captures (#9423)
Doing a renderdoc capture when using transient memory
or importing AHardwareBuffers into Vulkan, makes it fail,
crash or not replay properly.

To improve the capture support, added a new constant to let the
backend know when renderdoc is expected to be used and configure
things appropietly.

- In the case of transient attachments that uses lazy allocated
memory, it will be disabled.
- In the case of AHardwareBuffers, a more robust memory heap
selection algorithm will be used.

Also a new VulkanPlatform function will be added, to let other
subclasses know that the transient attachments are enabled or not.
2025-11-13 07:40:51 -08:00
Powei Feng
080f958da3 android: remove checked in asset (#9426)
For sample-gltf-viewer, an asset was checked into the source tree.
But other assets are generated or copied from asset directories.

We remove the checked in gltf and copy the asset over during
build (as with other existing files).
2025-11-13 06:29:45 +00:00
Doris Wu
7547aa3807 buffer update opt: Integrate UboManager into the engine (#9397) 2025-11-13 02:19:48 +00:00
Sungun Park
67a0c6e0e1 fix a typo in VertexBuffer (#9427) 2025-11-12 15:31:55 -08:00
Ben Doherty
37cb842993 Fix possible null string crash inside TextWriter (#9428) 2025-11-12 12:07:16 -08:00
Doris Wu
ad27d48fd3 buffer update opt: refactor UboManager and introduce FenceManager (#9418) 2025-11-11 16:56:06 +08:00
Mathias Agopian
3a503976c8 add View::getLastDynamicResolutionScale() (#9419)
* add View::getLastDynamicResolutionScale()

This method return the last dynamic resolution scaling factor used
by the view.

FIXES=[457753622]
2025-11-10 14:49:23 -08:00
Mathias Agopian
ce6fa82026 improve camera precondition checks and default state (#9420)
* improve camera precondition checks and default state

- Camera is now initialized with a default projection matrix with
left/right, top/bottom set to (-1,1) and near/far set to (0.1,1).

- setLensProjection() and setProjection(FOV) now check the near/far
precondition (near>0 and far>near).

- sanitize user's light near/far to avoid infs and nans during
  froxelization

- finally froxelizer asserts that near>0 and far>near on debug builds.

FIXES=[458030186]
2025-11-10 14:48:48 -08:00
Mathias Agopian
e8349ab5cc AndroidNativeWindow must also work on 32-bits architectures (#9424) 2025-11-10 14:44:52 -08:00
yein
4773fc4647 Move the include resolution functionality to matp (#9414)
* Move the include resolution functionality to matp

- matp::MaterialParser now has a function resolveIncludes that returns a pair of status and resolve string.
- all the include resolution classes are moved to matp private src.
- matp::resolveIncludes is renamed to matp::resolveIncludesRecursively and only used internally.
- added insertLineDirectives and insertLineDirectiveChecks in Config; add those in the CommandlineConfig.
- moved output format to public use.

Note: MaterialParser::resolveIncludes could take a includer instead of the materialFilePath, but i decided
to go with the materialFilePath because the most common use case is resolving from the file's directory.
This allows the parser to just create the default DirIncluder internally, and we don't need to expose it publicly.

* add const to the buffer param
2025-11-07 15:23:54 -08:00
Rafael Marinho de Araújo
d47a69a529 add overload of loadTexture that loads bitmap from ByteArray (#9416)
Co-authored-by: rma6 <rafaelmarinhoa@gmail.com>
2025-11-07 10:11:23 -08:00
Powei Feng
d3b74e96b4 vk: fix thread-safety wrt queryFrameTimestamps() (#9415)
queryFrameTimestamps() and queryCompositorTiming() are both
synchronous APIs, but VuklanSwapchain is not a thread-safe
ref-counted handle (i.e. fvkmemory::ThreadSafeResource). We move
the Platform::SwapChain* pointers to a thread-safe map that is
mapped by the handle id.
2025-11-07 18:00:01 +00:00
Mathias Agopian
209d3f7550 Fix a race condition when tearing down FrameInfo (#9413)
* HandleAllocator::deallocate() was unsafe

It needs to know the concrete type to call the proper destructor, so
if it was given a base type handle (e.g. Handle<HwFoo>) it would not
destroy it properly.

* add AsyncJobQueue::cancelAll()

* Fix a race condition when tearing down FrameInfo

It is actually invalid to destroy a Handle<HwFence> while inside
fenceWait().

Updated the HwFence implementations so that they don't pretend they can
handle being destroyed during fenceWait(), they can't.

FrameInfo now cancels all the pending callbacks and waits for the 
currently executing one to terminate, *before* destroying the
handles.

Reenable the gpuFrameComplete metric, as it should be working now.
2025-11-06 16:50:08 -08:00
Mathias Agopian
92e65cb3fd Properly use SDK 26+ APIs (#9417)
With minSDK less than 26, it's unsafe to use APIs in nativewindow.h 
that appeared at API 26 or more, even when using the weak symbol
technique. This is because pre 26, libnativewindow.h didn't exist and
therefore the build system cannot link against it.

Currently Filament's minSdk is 21, which means we should never use
nativewindow.h API after 26.

In this PR we chose the strategy at compile time; either the more
modern weak symbol or the old way with dlsym based on __ANDROID_API__.
2025-11-06 16:49:28 -08:00
Powei Feng
a4945939de vk: fix leaking swapchains again (#9410)
- Adjust order of destroy call in both headless and platform
   swapchains.  We need to be careful of the order due to
   assumptions made by the base class's destroy().
 - remove `=0` since VulkanPlatformSwapChainBase::destroy() has
   an implementation.

Fixes #9403
2025-11-05 22:10:05 -08:00
Benjamin Doherty
086760b307 Add missing mutex header 2025-11-05 15:35:29 -08:00
Powei Feng
ce1b63ce38 gltf_viewer: fix accidentally removed FilamentAppVulkanPlatform 2025-11-05 15:26:47 -08:00
Mathias Agopian
b7b8983653 add a feature flag for FrameInfo::gpuFrameComplete (#9409)
There are several race conditions caused by this feature. So we add
this flag and disable it by default, for now.
2025-11-05 15:22:43 -08:00
Powei Feng
d52fb1f4fd vk: refactor to use polymorphism for platform specialization (#9398)
We refactor VulkanPlatform so that getSwapchainInstanceExtensions()
and createVkSurfaceKHR() are virtual functions that are implemented
by each platform. This will enable the use of polymorphism for
platform-dependent bits.
2025-11-05 19:44:39 +00:00
Powei Feng
901c87761b gltfio: add null check for when image is not provided (#9407)
Fixes #9402
2025-11-05 19:26:24 +00:00
Benjamin Doherty
081fe6a434 Bump MATERIAL_VERSION to 67 2025-11-05 10:35:14 -08:00
Benjamin Doherty
b84c6ace8d Release Filament 1.67.0 2025-11-05 10:33:15 -08:00
yein
2113e04aba Support returning a .mat file from matc (#9399) 2025-11-05 10:17:55 -08:00
Doris Wu
84fdf36ed9 buffer update opt: New android sample for material instance stress test (#9396) 2025-11-05 03:24:56 +00:00
Doris Wu
dc1de994e0 buffer update opt: Initialize UboManager class (#9331) 2025-11-05 02:47:28 +00:00
Mathias Agopian
7c6479020e use choreographer to access the system's expected present time (#9393)
* use choreographer to access the system's expected present time

* more Android specific code into VulkanPlatformAndroid

* Fix race-condition between init() and terminate()

it was possible for the thread to not have been ready by the time
we call terminate.

* fix build and minor fixes
2025-11-04 14:17:50 -08:00
yein
a5f949d30c Move resolving #includes from MaterialBuilder to MaterialCompiler (#9374)
* Move resolving #includes from MaterialBuilder to MaterialCompiler, before parsing the material.

- resolving #includes was happening after parsing, now moving before parsing.
this is because we could offload this resolution at build time for RuntimeMaterialCompiler
- filamat::resolveIncludes used to have an assumption where the given text was already
a shader block. this assumption is now broken so it finds the line offset internally.
thus the line offset field is removed from IncludeResult.

* Move include related classes to matc
2025-11-04 14:14:43 -08:00
haroonq
6c867c692b Add flags for supporting externally defined dependencies. (#9355) 2025-11-04 13:36:01 -08:00
Benn Herrera
bd735fbab8 updated cgltf to 1.15 and broke out commands from README into utility script. (#9369) 2025-11-04 13:35:21 -08:00
Benn Herrera
b4c4ce9e17 Add Optional Inclusion of libwebp in Third Party Libaries (#9371)
* adds third_party/libwebp and cmake option to enable building it.

* disable simd and default disable pthreads for webgl builds.

* ensure non-empty config var value

* Added notes on libwebp change to NEW_RELEASE_NOTES.md

* Update NEW_RELEASE_NOTES.md
2025-11-04 13:34:57 -08:00
rafadevai
138cc55d3b Don't always associate transformName to a stream (#9404)
Before the transformName field was expected to be
used together with the stream API, that is not the
case anymore.

In the scenario that isn't using the stream API,
filament will try to associate the transformName
field to a stream that doesn't exist, causing a
crash.
2025-11-04 13:34:14 -08:00
Powei Feng
3d6db313bd vk: swapchain destroy must call through to base class (#9405)
Fixes #9403
2025-11-04 20:15:52 +00:00
Powei Feng
faa565c3ff vk: remove unused shaders (#9394)
The BlitDepth shaders were no longer referenced in the blit code
and hence are removed.
2025-11-04 17:45:57 +00:00
yrcloud
2a2caad1bf webgpu: use staging buffers for updateGPUBuffer (#9360)
BUGS=450620535
2025-11-03 17:28:59 -05:00
Anish Goyal
804ee87356 Check for null fences to avoid segfault in tests (#9389)
* Check for null fences to avoid segfault in tests

In some test environments, creating a sync or fence backed by an Android
fence returns null. In order to avoid NPEs, cover this scenario with a
null check.

* Address PR - add log statement

Log that native fences are not supported when unable to create a native
sync fence.
2025-11-03 19:28:15 +00:00
Filament Bot
f5c9d973dc [automated] Updating /docs due to commit 778cbe0
Full commit hash is 778cbe09d1

DOCS_ALLOW_DIRECT_EDITS
2025-10-31 22:25:27 +00:00
Powei Feng
778cbe09d1 build: allow building tools separately (#9384)
Building tools separately is necessary for the existing
cross-complation usecase.  We generalize this by introducing
two cmake vars that enable exporting and importing
prebuilt tools.

The intended usecase is to enable ASAN-built filament without
having to run ASAN-built matc (which is prohibitively slow).

build.sh has been modified to add a `-y` flag forprebuilding
tools.
2025-10-31 22:23:15 +00:00
Mathias Agopian
bf6c51bae6 Fix GLES2 UBO emulation (#9392)
The binding cache didn't take into account the UBO offset.

FIXES=[456802974]
2025-10-31 14:28:45 -07:00
Powei Feng
cb3933b349 Guard MonotonicRingMap.MonotonicityDeathTest with GTEST_HAS_DEATH_TEST (#9388) 2025-10-31 20:45:32 +00:00
Mathias Agopian
4e9d691d9d fix transparent picking (#9390)
FIXES=[456489383]
2025-10-31 13:39:31 -07:00
Mathias Agopian
fe5f8547f7 Add frame history support to the Vulkan backend on Android (#9386) 2025-10-31 13:13:48 -07:00
Powei Feng
3207c31721 renderdiff: disable webgpu + bloom due to flake (#9368)
WebGPU's rendering of bloom sems to be non-deterministic.  This
is not expected.  For now, we disable bloom+webgpu to avoid
hitting flakes in our CI.

This required a slight refactoring of the test configuration
parsing and execution scripts.

RDIFF_BRANCH=pf/renderdiff-disable-bloom-webgpu
2025-10-31 06:55:44 +00:00
Mathias Agopian
236d650ed7 Add display present time as well as compositor timings to FrameInfo (#9378)
* Use a custom, non-allocating map for frame ids

* use memory_order_relaxed for the id of heap allocated handles

* Added displayPresent time to FrameInfo

To do this, we added support for queryFrameTiming() to the backend,
as a synchronous API. Then FrameInfo uses it to update the
corresponding history entry.

displayPresent time can be used to detect "buffer stuffing", i.e.
when the GPU gets too much ahead of the display. Currently
the information is returned to the user only. Eventually filament will
make use of it to determine if a frame skip is mandated.

* API BREAK: rename frameTime to gpuFrameDuration

* FrameInfo now contains compositor timings

- the presentation deadline
- the refresh rate from the display
- the composition-display latency

* set FrameInfo data to INVALID if feature is not supported

report presentDeadline properly.
2025-10-30 15:33:26 -07:00
Mathias Agopian
294b79b321 vkWaitForFences() must be called with the read-lock held (#9385) 2025-10-30 15:31:05 -07:00
Ben Doherty
0dc392a760 Refactor Metal command buffer error logging (#9383) 2025-10-30 15:03:09 -07:00
Ben Doherty
06fa370491 Metal: support MSAA SwapChain flag (#9361) 2025-10-30 11:08:08 -07:00
Mathias Agopian
6fdd7398b6 fix a possible deadlock on exit (#9377)
The deadlock happened because FrameInfoManager needs to wait that all
fences have signaled before it can be destroyed. 

The fix here is simply to destroy the fence without waiting (for safety
we do wait after we destroy them).

This uncovered another problem where all backends didn't handle this 
correctly.

We now explicitly handle this when destroying a fence: we make sure it
unblocks all the waiters and returns an error.
2025-10-30 09:30:27 -07:00
Mathias Agopian
32a89a9017 Disable producer throttling on Vulkan (#9381)
This allows vkPresentKHR to not block.
2025-10-30 09:30:02 -07:00
Mathias Agopian
ddb2d89e6c creating a cubemap with mip levels was incorrect (#9382)
the miplevels where not specified with the correct size.
2025-10-30 09:29:33 -07:00
Mathias Agopian
ea608d409e fix artifacts with screen space reflections (#9380)
on mobile h/w, strong highlights generated by the lighting stages can
turn into +inf and eventually to NaN if more math is performed on them.
Some h/w will kill a while tile or even primitive when that happens,
resulting in strong visual artifacts.

We fix this by clamping to MEDIUMP_FLT_MAX.
2025-10-29 17:00:49 -07:00
rafadevai
494e454f38 GL: Fix importing non srgb using the external image API (#9343)
* GL: Fix importing non srgb using the external image API

When importing a non srgb AHB but setting the srgb as true,
makes the eglCreateImageKHR function to fail because it tries
to set the colorspace as srgb for non srgb formats.

Example:
createExternalImage(yuv_ahardwaredbuffer, /*sRGB=*/true);

The fix is to only make sure that flag is set to true
if the corresponding filament format requires sRGB and
false otherwise.

* Fix mapToFilamentFormat to return the correct srgb formats

* fix build error on android

---------

Co-authored-by: Serge Metral <sergemetral@google.com>
2025-10-29 15:50:03 -07:00
Filament Bot
a9bbb0bf3b [automated] Updating /docs due to commit 21c7fa6
Full commit hash is 21c7fa6253

DOCS_ALLOW_DIRECT_EDITS
2025-10-29 18:13:20 +00:00
Powei Feng
21c7fa6253 Release Filament 1.66.2 2025-10-29 11:09:30 -07:00
Powei Feng
df453dab89 webgpu: fix index buffer handle allocation (#9379)
Handle allocation was using HwIndexBuffer, which is much smaller
than WebGPUIndexBuffer. This can lead to memory corruption.
2025-10-29 17:27:38 +00:00
Powei Feng
629f35f5f7 gltfio: fix stack-use-after-free issue (#9376) 2025-10-29 06:50:34 +00:00
Powei Feng
6e93fc1b44 webgpu: fix two stack-use-after-free issues (#9375)
There are a few structs that are used as parameters in webgpu
calls that are scoped to the wrong block.
2025-10-29 04:38:27 +00:00
Doris Wu
379b63d11b buffer update opt: Add new API for getting uniform buffer offset alignment (#9347) 2025-10-29 11:29:31 +08:00
Mathias Agopian
bfd23fcb4f Use the new setProducerThrottling API (#9362) 2025-10-28 10:44:02 -07:00
Mathias Agopian
9c8aea115c editing a program in matdbg should not crash the target (#9373)
fix a couple assert in the OpenGL backend. these should not terminate 
the process when a program fails to compile, in MAT_DGB mode.
2025-10-28 10:42:48 -07:00
Powei Feng
ca038169c6 webgpu: fix FrameScheduled callback and test (#9370)
Fix the callback by using a shared_ptr to store the callback.
The same logic exists in all other backends.

Re-enable corresponding backend test for webgpu.

Remove viewport size check in beingRenderPass. There is no similar
check in other backends.
2025-10-28 16:16:24 +00:00
Powei Feng
0c5a0b3399 Revert "webgpu: fix FrameScheduled callback and test"
This reverts commit 896246ba7c.
2025-10-27 13:49:59 -07:00
Powei Feng
896246ba7c webgpu: fix FrameScheduled callback and test
Fix the callback by using a shared_ptr to store the callback.
The same logic exists in all other backends.

Re-enable corresponding backend test for webgpu.

Remove viewport size check in beingRenderPass. There is no similar
check in other backends.
2025-10-27 13:47:44 -07:00
Sungun Park
3021f8d0a2 Shared context support for GLX (#9359) 2025-10-27 17:33:16 +00:00
Powei Feng
9e437ea588 backend-test: fix clean up ordering (#9353)
Previously, Cleanup is scoped to the testing function, which will
go out of scope before BackendTest's destructor is called.  We
need to order the cleanup (destruction of resources) to after
the readPixels and previous commands are complete - which is
in the destructor of BackendTest.

Additionally, we need to cleanup before the destruction of the
driver.  This means we need to be able to order the destruction
of Cleanup (and not rely on RAII). This is done by having a
unique_ptr holding a Cleanup instance.
2025-10-27 17:04:15 +00:00
Filament Bot
556da0ed0b [automated] Updating /docs due to commit ee1921a
Full commit hash is ee1921ab3a

DOCS_ALLOW_DIRECT_EDITS
2025-10-24 22:20:19 +00:00
Benjamin Doherty
ee1921ab3a Update Sonatype link 2025-10-24 15:17:06 -07:00
Mathias Agopian
2b8fe35a70 new frame and compositor timing APIs for Platform (#9354)
* Add Compositor Timing APIs to Platform

These APIs are currently modeled after Android capabilities, but a lot of
the concepts are generic enough to map to other system with a compositor.

This adds Platfrom::queryCompositorTiming() which is currently only
implementd by PlaftormEGLAndroid. Vulkan and WebGPU will come later.
The functionality is implement via EGL, but a new NativeWindow
helper is provided to access the same data from non-EGL backends.
Also to be on the safe side, PlatformEGLAndroid reverts to using
the ANativeWindow if EGL fails.

This CL also cleans-up PlatformEGLAndroid to accomodate those changes.

* Add queryFrameTimestamps() API to Platform.

This API allows to query very precise timing information of
past frames on a given swapchain. This information is provided
by the Android framework via EGL or ANativeWindow (albeit
through private APIs).
Currently only implemented by PlatformEGLAndroid.
2025-10-24 18:03:38 +00:00
Mathias Agopian
624b05f838 cleanup PlatformEGL swapchain (#9349)
SwapChainEGL is now implemented more idiomatically. It has a
constructor that does the initialization. This allows subclasses
to be implemented properly and safely.

PlatformEGL::createSwapChain() now just constructs SwapChainEGL and
does nothing else, which allows a subclass of PlatformEGL to "add" to
the PlatformEGL swapchain, buy simply subclassing from it and
constructing it normally.

The SwapChain doesn't need to have a vtable because polymorphism
is handled by the concrete Platform implementation.

Here we also implement a no-op SwapChainEGLAndroid to show how easy
it is to extend the swapchain. This functionality will be used soon.
2025-10-24 10:41:10 -07:00
Filament Bot
8a23ed91c0 [automated] Updating /docs due to commit db477e0
Full commit hash is db477e0549

DOCS_ALLOW_DIRECT_EDITS
2025-10-24 16:51:10 +00:00
Powei Feng
db477e0549 docs: update remote UI (#9358)
- Also modify update script
   - Renamed to update_remote_ui
   - Repair path inside sciprt
 - Ensure update script is not copied over to /docs
 - Add requirements.txt (for python/pip dependencies)
2025-10-24 16:47:40 +00:00
Doris Wu
ef3c391978 buffer update opt: Add material instances stress test sample (#9345) 2025-10-24 03:04:00 +00:00
Powei Feng
4547b69ccc backend-test: fix breakages on linux (#9350)
- On webgpu, do not error on linux when we're building headless
   only (without platform support for surface).
- For test/backend/test.sh, enable path for building on
   Linux + 64-bit x86
2025-10-23 21:12:25 +00:00
rafadevai
326bc4c311 Fix comments for TextureSampler::setAnisotropy (#9357)
The default value for no anisotropy must be 1
instead of 0.
2025-10-23 11:14:33 -07:00
haroonq
44ceee1f90 Disable unsupported flags when compiling with GCC. (#9356) 2025-10-23 09:56:40 -07:00
Mathias Agopian
b646700ccc fix a rare swapchain leak in PlatformEGL (#9348)
If createSwapChain() fails, a SwapChain object is always returned
(this might itself be a problem, but we can address that later),
however in that case, destroySwapChain() would leak the EGLSwapChain
structure.
2025-10-22 10:25:20 -07:00
Powei Feng
6caf88d3aa github: add backend tests to presbumit (gl/vk) (#9344)
This commit enables running the backend tests for opengl and
vulkan on github CI. The script is located in test/backend.

We also add exceptions (SKIP_TEST) for the currently failing
tests.  Issues have been filed to track addressing these failed
tests.

We also disable a series of broken webgpu tests.
2025-10-22 10:21:55 -07:00
Doris Wu
8d6e8b8b3c buffer update opt: Add engine config for default shared Ubo size (#9340) 2025-10-22 03:48:18 +00:00
rafadevai
9da29e3ab7 Add new public API to query a sampler transform name field. (#9320)
* Add new public API to query a sampler transform name field.

This new API will let filament users query a Material object
the value of the `transformName` field of a specified sampler
parameter.

The transformName is an optional field, so if its not defined
by the user, it will return a nullptr value.

- A new test was added to test_filamat to validate the serialization.
- A new parameter was added to the test sandboxLit material to
validate the parsing a material with the new field.

* Addressing review comments

- Add java and js bindings for the new API
- Tests for querying the getParameterTransformName

* Use utils::ImmutableCString for transformName

* Updating release notes

* Review comments

* Addressing more review comments

- Fix comments
- For the java binding return an empty string when the
transform is not present.
2025-10-21 23:55:25 +00:00
haroonq
e60280e79d Add a FILAMENT_ENABLE_EXPERIMENTAL_GCC_SUPPORT cmake flag. (#9339)
Adds support for compiling filament libraries with GCC. Since GCC is not
officially supported, we prefer to disable warnings (incl. not treating
warnings as errors) rather than updating the code in order to compile
cleanly.

This flag is disabled by default.
2025-10-21 16:34:04 -07:00
Powei Feng
70db8e241b vk: use virutal methods in platform Swapchain implementation (#9337) 2025-10-21 21:21:13 +00:00
Filament Bot
7f43cf2f23 [automated] Updating /docs due to commit d3f4e91
Full commit hash is d3f4e91be2

DOCS_ALLOW_DIRECT_EDITS
2025-10-21 19:59:20 +00:00
Sungun Park
d3f4e91be2 Release Filament 1.66.1 2025-10-21 12:56:30 -07:00
Powei Feng
3602864c43 vk & webgpu: Fix timepoint overflow (#9342)
nanosecond is long long and `timeout` is uint64_t.  This creates
an overflow when timeout is too large (for example
timeout = std::numeric_limits<uint64_t>::max() when waiting
forver).
2025-10-21 12:51:26 -07:00
Sungun Park
ff3bd4dc4d Fix TSAN for VulkanAsyncHandles (#9338) 2025-10-20 19:21:14 -07:00
haroonq
b950598d11 Various fixes to support gcc compilation. (#9323)
* Add missing includes.

* Add parenthesis around operators to suppress compiler warning.

* Remove duplicate definition of is_supported_aux_t.

* Explicitly create descriptions.

* Remove usage of anonymous struct with non-trivially constructible members.

This is an error on some compilers (e.g. gcc).

* Remove unnecessary rvalue-reference on pointer type.

* Explicitly construct SamplerParams to suppress compiler warnings.

* Place attribute specifier before declaration.

* Remove usage of anonymous struct with a non-trivially constructible member.

Replace the `array` union member with an `operator[]` to provide similar
functionality.

Some compilers (e.g. gcc) do not support this non-standard use-case.

* Use same warning settings as main filament project.
2025-10-20 09:10:08 -07:00
Powei Feng
0e1e5fb381 renderdiff: disable SSR for transmission (#9336)
SSR is not required for transmission tests, and it seems to be
flaky.

RDIFF_BRANCH=pf/renderdiff-disable-transmission
2025-10-17 13:51:35 -07:00
636 changed files with 135747 additions and 2302 deletions

View File

@@ -179,7 +179,7 @@ jobs:
fetch-depth: 0
- uses: ./.github/actions/linux-prereq
- name: Run build script
run: ./build.sh -W debug test_filamat filament
run: ./build.sh -W debug test_filamat filament gltf_viewer
- name: Run test
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
@@ -203,3 +203,23 @@ jobs:
./build.sh -p desktop debug gltf_viewer
- name: Run test
run: bash test/code-correctness/test.sh
test-backend:
name: test-backend
runs-on: macos-14-xlarge
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/mac-prereq
- uses: ./.github/actions/get-mesa
- uses: ./.github/actions/get-vulkan-sdk
- name: Prerequisites
run: |
# Must have at least clang-16 for a webgpu/dawn build.
sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
shell: bash
- name: Run backend tests
shell: bash
run: |
bash test/backend/test.sh

View File

@@ -62,6 +62,11 @@ force a clean build by adding the `-c` flag in that case.
To install the libraries and executables in `out/debug/` and `out/release/`, add the `-i` flag.
The script offers more features described by executing `build.sh -h`.
For more specialized options, please also consider the following pages:
- `-d`: [`matdbg`](https://google.github.io/filament/dup/matdbg.html)
- `-t`: [`fgviewer`](https://google.github.io/filament/dup/fgviewer.html)
- `-b` and `-y`: [ASAN/UBSAN builds](https://google.github.io/filament/notes/asan_ubsan.html)
### Filament-specific CMake Options
The following CMake options are boolean options specific to Filament:

View File

@@ -56,6 +56,10 @@ option(FILAMENT_ENABLE_FGVIEWER "Enable the frame graph viewer" OFF)
option(FILAMENT_USE_ABSEIL_LOGGING "Use Abseil to log, may increase binary size" OFF)
option(FILAMENT_ENABLE_EXPERIMENTAL_GCC_SUPPORT "Enable GCC support (unsupported)" OFF)
option(FILAMENT_SUPPORTS_WEBP_TEXTURES "Enable webp texture support for Filament (builds libwebp when ON)" OFF)
# This is to disable GTAO for the short-term while we investigate a way to better manage size increases.
# On the regular filament build (where size is of less concern), we enable GTAO by default.
option(FILAMENT_DISABLE_GTAO "Disable GTAO" OFF)
@@ -295,15 +299,10 @@ set(TOOLS ${CMAKE_CURRENT_SOURCE_DIR}/tools)
# ==================================================================================================
set(MIN_CLANG_VERSION "6.0")
if (CMAKE_C_COMPILER_ID MATCHES "Clang")
if (CMAKE_C_COMPILER_VERSION VERSION_LESS MIN_CLANG_VERSION)
message(FATAL_ERROR "Detected C compiler Clang ${CMAKE_C_COMPILER_VERSION} < ${MIN_CLANG_VERSION}")
endif()
elseif (NOT MSVC)
message(FATAL_ERROR "Detected C compiler ${CMAKE_C_COMPILER_ID} is unsupported")
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(FILAMENT_USING_GCC OFF)
if (CMAKE_C_COMPILER_ID MATCHES "GNU" AND FILAMENT_ENABLE_EXPERIMENTAL_GCC_SUPPORT)
set(FILAMENT_USING_GCC ON)
elseif (CMAKE_C_COMPILER_ID MATCHES "Clang")
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS MIN_CLANG_VERSION)
message(FATAL_ERROR "Detected CXX compiler Clang ${CMAKE_CXX_COMPILER_VERSION} < ${MIN_CLANG_VERSION}")
endif()
@@ -344,7 +343,11 @@ if (MSVC)
set(CXX_STANDARD "/std:c++latest")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} /W0 /Zc:__cplusplus")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} -fstrict-aliasing -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations")
if(FILAMENT_USING_GCC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} -Wno-changes-meaning -Wno-return-type -Wno-attributes -Wno-unknown-pragmas -Wno-class-memaccess -Wno-multichar -Wno-deprecated-declarations -Wno-subobject-linkage -Wno-invalid-constexpr")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} -fstrict-aliasing -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations")
endif()
if (APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-nullability-extension")
endif()
@@ -365,10 +368,14 @@ endif()
if (LINUX)
option(USE_STATIC_LIBCXX "Link against the static runtime libraries." ON)
if (${USE_STATIC_LIBCXX})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
link_libraries("-static-libgcc -static-libstdc++")
link_libraries(libc++.a)
link_libraries(libc++abi.a)
if (FILAMENT_USING_GCC)
link_libraries("-static-libgcc -static-libstdc++")
else ()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
link_libraries("-static-libgcc -static-libstdc++")
link_libraries(libc++.a)
link_libraries(libc++abi.a)
endif()
endif()
# Only linux, clang doesn't want to use a shared library that is not PIC.
@@ -393,8 +400,8 @@ if (MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE")
endif()
# Add colors to ninja builds
if (UNIX AND CMAKE_GENERATOR STREQUAL "Ninja")
# Add colors to ninja builds (when not using gcc)
if (UNIX AND CMAKE_GENERATOR STREQUAL "Ninja" AND NOT FILAMENT_USING_GCC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcolor-diagnostics")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics")
endif()
@@ -748,12 +755,61 @@ function(combine_static_libs TARGET OUTPUT DEPS)
endfunction()
# ==================================================================================================
# Configuration for CMAKE_CROSSCOMPILING.
# Configuration importing/exporting prebuilt "/tools" executables
# ==================================================================================================
# In certain cases, we want the ability to separate the build type/flags (release, debug) of tools
# (matc, resgen, etc...) from filament. Cross compilation is one such case (e.g. building material
# with matc running on host while building filament for Android) [1]. Another example is when client
# wants to enable ASAN for filament but not the tool [2].
#
# Here are the varibles that are used to determine behavior is such flows:
# - CMAKE_CROSSCOMPILING : Set by cmake to indicate the build's target platform is different
# from the host platform.
# - IMPORT_EXECUTABLES_DIR : This is the directory containing the /ImportExecutables-type.cmake
# which are then included in other CMakeLists.txt to enable finding targets that have been
# prebuilt. This is set by the client in their `cmake` invocation.
# - IMPORT_EXECUTABLES : Path to a file to for 1) exporting the prebuilt targets or 2)
# importing a previously exported targets. (Used internally and should not be set by client).
# - FILAMENT_EXPORT_PREBUILT_EXECUTABLES_DIR : A path set by the client to indicate that
# they wish to export the tools as prebuilts, and the corresponding targets will be recorded
# in a cmake file (i.e IMPORT_EXECUTABLES). IMPORT_EXECUTABLES will be set relative to the the
# given path. This will set FILAMENT_EXPORT_PREBUILT_EXECUTABLES=ON.
# - FILAMENT_IMPORT_PREBUILT_EXECUTABLES_DIR : A path set by the client to indicate that they wish
# to import the tools as prebuilts from a .cmake file. The location (IMPORT_EXECUTABLES) of the
# file is relative to the path given. This will set FILAMENT_IMPORT_PREBUILT_EXECUTABLES=ON.
# - IMPORT_PREBUILT_EXECUTABLES and EXPORT_PREBUILT_EXECUTABLES are internal booleans set to
# ON based on the prescence of FILAMENT_IMPORT_PREBUILT_EXECUTABLES_DIR and
# FILAMENT_EXPORT_PREBUILT_EXECUTABLES_DIR.
#
# In conclusion, for cases
# - [1] (crosscompiling), the client must set IMPORT_EXECUTABLES_DIR for both when they are building
# the tools and when they are building the target-platform filament.
# - [2] (all other instances), the client must set FILAMENT_EXPORT_PREBUILT_EXECUTABLES_DIR in
# the prebuilt exporting step, and set FILAMENT_IMPORT_PREBUILT_EXECUTABLES_DIR in the importing
# step.
# TODO: Fold the cross compilation case into the more general FILAMENT_IMPORT/EXPORT variables.
if (FILAMENT_EXPORT_PREBUILT_EXECUTABLES_DIR)
set(FILAMENT_EXPORT_PREBUILT_EXECUTABLES ON)
set(IMPORT_EXECUTABLES_DIR ${FILAMENT_EXPORT_PREBUILT_EXECUTABLES_DIR})
endif()
if (FILAMENT_IMPORT_PREBUILT_EXECUTABLES_DIR)
set(FILAMENT_IMPORT_PREBUILT_EXECUTABLES ON)
set(IMPORT_EXECUTABLES_DIR ${FILAMENT_IMPORT_PREBUILT_EXECUTABLES_DIR})
endif()
if (WEBGL)
set(IMPORT_EXECUTABLES ${FILAMENT}/${IMPORT_EXECUTABLES_DIR}/ImportExecutables-Release.cmake)
else()
set(IMPORT_EXECUTABLES ${FILAMENT}/${IMPORT_EXECUTABLES_DIR}/ImportExecutables-${CMAKE_BUILD_TYPE}.cmake)
if (FILAMENT_EXPORT_PREBUILT_EXECUTABLES OR FILAMENT_IMPORT_PREBUILT_EXECUTABLES)
set(IMPORT_EXECUTABLES_BUILD_TYPE Prebuilt)
else()
set(IMPORT_EXECUTABLES_BUILD_TYPE ${CMAKE_BUILD_TYPE})
endif()
set(IMPORT_EXECUTABLES ${FILAMENT}/${IMPORT_EXECUTABLES_DIR}/ImportExecutables-${IMPORT_EXECUTABLES_BUILD_TYPE}.cmake)
endif()
# ==================================================================================================
@@ -832,6 +888,7 @@ add_subdirectory(${EXTERNAL}/getopt)
add_subdirectory(${EXTERNAL}/perfetto/tnt)
add_subdirectory(${EXTERNAL}/zstd/tnt)
# Note that this has to be placed after mikktspace in order for combine_static_libs to work.
add_subdirectory(${LIBRARIES}/geometry)
@@ -854,6 +911,11 @@ if (FILAMENT_ENABLE_FGVIEWER)
add_subdirectory(${LIBRARIES}/fgviewer)
endif()
if (FILAMENT_SUPPORTS_WEBP_TEXTURES)
add_subdirectory(${EXTERNAL}/libwebp/tnt)
add_definitions(-DFILAMENT_SUPPORTS_WEBP_TEXTURES)
endif()
if (FILAMENT_SUPPORTS_VULKAN)
add_subdirectory(${LIBRARIES}/bluevk)
add_subdirectory(${EXTERNAL}/vkmemalloc/tnt)
@@ -912,6 +974,6 @@ if (IS_HOST_PLATFORM)
endif()
# Generate exported executables for cross-compiled builds (Android, WebGL, and iOS)
if (NOT CMAKE_CROSSCOMPILING)
if ((NOT CMAKE_CROSSCOMPILING AND NOT FILAMENT_IMPORT_PREBUILT_EXECUTABLES) OR FILAMENT_EXPORT_PREBUILT_EXECUTABLES)
export(TARGETS matc cmgen filamesh mipgen resgen uberz glslminifier FILE ${IMPORT_EXECUTABLES})
endif()

View File

@@ -6,3 +6,7 @@
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- engine: add `View::getLastDynamicResolutionScale()` (b/457753622)
- materials: Make Material Instances' UBO descriptor use dynamic offsets. [⚠️ **Recompile Materials**]

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.66.0'
implementation 'com.google.android.filament:filament-android:1.67.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.66.0'
pod 'Filament', '~> 1.67.0'
```
## Documentation

View File

@@ -7,6 +7,21 @@ 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.67.1
- Metal: Add support for the `SwapChain::CONFIG_MSAA_4_SAMPLES` flag.
- third_party: Optionally add libwebp to build
- controlled by cmake flag FILAMENT_SUPPORTS_WEBP_TEXTURES, defaults to OFF
- actual webp texture support for libs/gltfio coming in subsequent change
## v1.67.0
- materials: Add a new API getParameterTransformName that will return the value of the transformName field of a sampler
parameter. [⚠️ **Recompile Materials**]
## v1.66.2
## v1.66.1
- filamat: Removed a dependency on Glslang's deprecated SPIR-V remapper.

View File

@@ -51,10 +51,6 @@ add_library(bluevk STATIC IMPORTED)
set_target_properties(bluevk PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libbluevk.a)
add_library(vkshaders STATIC IMPORTED)
set_target_properties(vkshaders PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libvkshaders.a)
if (FILAMENT_SUPPORTS_WEBGPU)
add_library(webgpu_dawn STATIC IMPORTED)
set_target_properties(webgpu_dawn PROPERTIES IMPORTED_LOCATION
@@ -165,7 +161,6 @@ target_link_libraries(filament-jni
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>
)

View File

@@ -557,7 +557,8 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
jboolean disableHandleUseAfterFreeCheck,
jint preferredShaderLanguage,
jboolean forceGLES2Context, jboolean assertNativeWindowIsValid,
jint gpuContextPriority) {
jint gpuContextPriority,
jlong sharedUboInitialSizeInBytes) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
Engine::Config config = {
.commandBufferSizeMB = (uint32_t) commandBufferSizeMB,
@@ -576,6 +577,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
.forceGLES2Context = (bool) forceGLES2Context,
.assertNativeWindowIsValid = (bool) assertNativeWindowIsValid,
.gpuContextPriority = (Engine::GpuContextPriority) gpuContextPriority,
.sharedUboInitialSizeInBytes = (uint32_t) sharedUboInitialSizeInBytes,
};
builder->config(&config);
}

View File

@@ -279,6 +279,17 @@ Java_com_google_android_filament_Material_nHasParameter(JNIEnv* env, jclass,
return (jboolean) hasParameter;
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_google_android_filament_Material_nGetParameterTransformName(JNIEnv* env, jclass,
jlong nativeMaterial, jstring samplerName_) {
Material* material = (Material*) nativeMaterial;
const char* samplerName = env->GetStringUTFChars(samplerName_, 0);
const char* transformName = material->getParameterTransformName(samplerName);
jstring transformName_ = env->NewStringUTF(transformName ? transformName : "");
env->ReleaseStringUTFChars(samplerName_, samplerName);
return transformName_;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_Material_nCompile(JNIEnv *env, jclass clazz,

View File

@@ -147,6 +147,15 @@ Java_com_google_android_filament_View_nSetDynamicResolutionOptions(JNIEnv*, jcla
view->setDynamicResolutionOptions(options);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nGetLastDynamicResolutionScale(JNIEnv *env, jclass, jlong nativeView, jfloatArray out_) {
jfloat* out = env->GetFloatArrayElements(out_, nullptr);
View *view = (View *) nativeView;
math::float2 result = view->getLastDynamicResolutionScale();
std::copy_n(result.v, 2, out);
env->ReleaseFloatArrayElements(out_, out, 0);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nSetShadowType(JNIEnv*, jclass, jlong nativeView, jint type) {
View* view = (View*) nativeView;

View File

@@ -99,6 +99,15 @@ final class Asserts {
}
}
@NonNull @Size(min = 2)
static float[] assertFloat2(@Nullable float[] out) {
if (out == null) out = new float[2];
else if (out.length < 2) {
throw new ArrayIndexOutOfBoundsException("Array length must be at least 2");
}
return out;
}
@NonNull @Size(min = 4)
static float[] assertFloat4(@Nullable float[] out) {
if (out == null) out = new float[4];

View File

@@ -264,7 +264,8 @@ public class Engine {
config.disableHandleUseAfterFreeCheck,
config.preferredShaderLanguage.ordinal(),
config.forceGLES2Context, config.assertNativeWindowIsValid,
config.gpuContextPriority.ordinal());
config.gpuContextPriority.ordinal(),
config.sharedUboInitialSizeInBytes);
return this;
}
@@ -525,6 +526,16 @@ public class Engine {
* GPU context priority level. Controls GPU work scheduling and preemption.
*/
public GpuContextPriority gpuContextPriority = GpuContextPriority.DEFAULT;
/**
* The initial size in bytes of the shared uniform buffer used for material instance batching.
*
* If the buffer runs out of space during a frame, it will be automatically reallocated
* with a larger capacity. Setting an appropriate initial size can help avoid runtime
* reallocations, which can cause a minor performance stutter, at the cost of higher
* initial memory usage.
*/
public long sharedUboInitialSizeInBytes = 256 * 64;
}
private Engine(long nativeEngine, Config config) {
@@ -1529,7 +1540,8 @@ public class Engine {
boolean disableHandleUseAfterFreeCheck,
int preferredShaderLanguage,
boolean forceGLES2Context, boolean assertNativeWindowIsValid,
int gpuContextPriority);
int gpuContextPriority,
long sharedUboInitialSizeInBytes);
private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal);
private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext);
private static native void nSetBuilderPaused(long nativeBuilder, boolean paused);

View File

@@ -787,6 +787,21 @@ public class Material {
return nHasParameter(getNativeObject(), name);
}
/**
*
* Returns the name of the transform parameter associated with the given sampler parameter.
* In the case the parameter doesn't have a transform name field, it will return an empty string.
*
* @param samplerName the name of the sampler parameter to query.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/general:parameters">
* General: parameters</a>
*/
public String getParameterTransformName(@NonNull String samplerName) {
return nGetParameterTransformName(getNativeObject(), samplerName);
}
/**
* Sets the value of a bool parameter on this material's default instance.
*
@@ -1111,4 +1126,5 @@ public class Material {
private static native int nGetRequiredAttributes(long nativeMaterial);
private static native boolean nHasParameter(long nativeMaterial, @NonNull String name);
private static native String nGetParameterTransformName(long nativeMaterial, @NonNull String samplerName);
}

View File

@@ -294,8 +294,8 @@ public class TextureSampler {
/**
* This controls anisotropic filtering.
*
* @param anisotropy Amount of anisotropy, should be a power-of-two. The default is 0.
* The maximum permissible value is 7.
* @param anisotropy Amount of anisotropy, should be a power-of-two. The default is 1.
* The maximum permissible value is 128.
*/
public void setAnisotropy(float anisotropy) {
mSampler = nSetAnisotropy(mSampler, anisotropy);

View File

@@ -25,7 +25,6 @@ import java.util.EnumSet;
import static com.google.android.filament.Asserts.assertFloat3In;
import static com.google.android.filament.Asserts.assertFloat4In;
import static com.google.android.filament.Colors.LinearColor;
import com.google.android.filament.proguard.UsedByNative;
@@ -674,6 +673,21 @@ public class View {
return mDynamicResolution;
}
/**
* Returns the last dynamic resolution scale factor used by this view. This value is updated
* when Renderer::render(View*) is called
* @param out A 2-float array where the value will be stored, or null in which case the array is
* allocated.
* @return A 2-float array containing the horizontal and the vertical scale factors
* @see Renderer#render(View)
*/
@NonNull @Size(min = 2)
public float[] getLastDynamicResolutionScale(@Nullable @Size(min = 2) float[] out) {
out = Asserts.assertFloat2(out);
nGetLastDynamicResolutionScale(getNativeObject(), out);
return out;
}
/**
* Sets the rendering quality for this view (e.g. color precision).
*
@@ -1317,6 +1331,7 @@ public class View {
private static native void nSetDithering(long nativeView, int dithering);
private static native int nGetDithering(long nativeView);
private static native void nSetDynamicResolutionOptions(long nativeView, boolean enabled, boolean homogeneousScaling, float minScale, float maxScale, float sharpness, int quality);
private static native void nGetLastDynamicResolutionScale(long nativeView, float[] out);
private static native void nSetRenderQuality(long nativeView, int hdrColorBufferQuality);
private static native void nSetDynamicLightingOptions(long nativeView, float zLightNear, float zLightFar);
private static native void nSetShadowType(long nativeView, int type);

View File

@@ -40,7 +40,21 @@ fun loadTexture(engine: Engine, resources: Resources, resourceId: Int, type: Tex
options.inPremultiplied = type == TextureType.COLOR
val bitmap = BitmapFactory.decodeResource(resources, resourceId, options)
return buildTexture(engine, bitmap, type)
}
fun loadTexture(engine: Engine, bytes: ByteArray, type: TextureType, offset: Int = 0, length: Int = bytes.size): Texture {
val options = BitmapFactory.Options()
// Color is the only type of texture we want to pre-multiply with the alpha channel
// Pre-multiplication is the default behavior, so we need to turn it off here
options.inPremultiplied = type == TextureType.COLOR
val bitmap = BitmapFactory.decodeByteArray(bytes, offset, length, options)
return buildTexture(engine, bitmap, type)
}
private fun buildTexture(engine: Engine, bitmap: Bitmap, type: TextureType): Texture {
val texture = Texture.Builder()
.width(bitmap.width)
.height(bitmap.height)

View File

@@ -22,6 +22,7 @@ object Utils {
/**
* Initializes the utils JNI layer. Must be called before using any utils functionality.
*/
@JvmStatic
fun init() {
// Load Filament first to ensure that the NioUtils Java class is available in the JNIEnv.
Filament.init()

View File

@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.66.0
VERSION_NAME=1.67.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.

View File

@@ -17,12 +17,20 @@ filamentTools {
}
// don't forget to update MainACtivity.kt when/if changing this.
tasks.register('copyMesh', Copy) {
tasks.register('copyDamagedHelmetGltf', Copy) {
from file("../../../third_party/models/DamagedHelmet/DamagedHelmet.glb")
into file("src/main/assets/models")
rename {String fileName -> "helmet.glb"}
}
// don't forget to update MainACtivity.kt when/if changing this.
tasks.register('copyBusterGltf', Copy) {
from "../../../third_party/models/BusterDrone"
into "src/main/assets/models"
}
preBuild.dependsOn copyMesh
preBuild.dependsOn copyDamagedHelmetGltf
preBuild.dependsOn copyBusterGltf
clean.doFirst {
delete "src/main/assets"

View File

@@ -0,0 +1,12 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
/.idea/caches
/.idea/gradle.xml
.DS_Store
/build
/captures
/src/main/assets
.externalNativeBuild

View File

@@ -0,0 +1,52 @@
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'filament-tools-plugin'
}
project.ext.isSample = true
kotlin {
jvmToolchain(versions.jdk)
}
filamentTools {
materialInputDir = project.layout.projectDirectory.dir("src/main/materials")
materialOutputDir = project.layout.projectDirectory.dir("src/main/assets/materials")
}
clean.doFirst {
delete "src/main/assets"
}
android {
namespace 'com.google.android.filament.materialinstancestress'
compileSdkVersion versions.compileSdk
defaultConfig {
applicationId "com.google.android.filament.materialinstancestress"
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
}
// NOTE: This is a workaround required because the AGP task collectReleaseDependencies
// is not configuration-cache friendly yet; this is only useful for Play publication
dependenciesInfo {
includeInApk = false
}
// We use the .filamat extension for materials compiled with matc
// Telling aapt to not compress them allows to load them efficiently
aaptOptions {
noCompress 'filamat', 'ktx'
}
compileOptions {
sourceCompatibility versions.jdk
targetCompatibility versions.jdk
}
}
dependencies {
implementation deps.kotlin
implementation project(':filament-android')
}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:exported="true"
android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,532 @@
/*
* 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.
*/
package com.google.android.filament.materialinstancestress
import android.animation.ValueAnimator
import android.app.Activity
import android.opengl.Matrix
import android.os.Bundle
import android.view.Choreographer
import android.view.Surface
import android.view.SurfaceView
import android.view.animation.LinearInterpolator
import com.google.android.filament.*
import com.google.android.filament.RenderableManager.*
import com.google.android.filament.VertexBuffer.*
import com.google.android.filament.android.DisplayHelper
import com.google.android.filament.android.FilamentHelper
import com.google.android.filament.android.UiHelper
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.channels.Channels
import kotlin.math.cos
import kotlin.math.sin
class MainActivity : Activity() {
// Make sure to initialize Filament first
// This loads the JNI library needed by most API calls
companion object {
init {
Filament.init()
}
private const val NUM_CUBES = 1000
private const val GRID_SIZE = 10
private const val SPACING = 2.5f
}
// The View we want to render into
private lateinit var surfaceView: SurfaceView
// UiHelper is provided by Filament to manage SurfaceView and SurfaceTexture
private lateinit var uiHelper: UiHelper
// DisplayHelper is provided by Filament to manage the display
private lateinit var displayHelper: DisplayHelper
// Choreographer is used to schedule new frames
private lateinit var choreographer: Choreographer
// Engine creates and destroys Filament resources
// Each engine must be accessed from a single thread of your choosing
// Resources cannot be shared across engines
private lateinit var engine: Engine
// A renderer instance is tied to a single surface (SurfaceView, TextureView, etc.)
private lateinit var renderer: Renderer
// A scene holds all the renderable, lights, etc. to be drawn
private lateinit var scene: Scene
// A view defines a viewport, a scene and a camera for rendering
private lateinit var view: View
// Should be pretty obvious :)
private lateinit var camera: Camera
private lateinit var material: Material
private lateinit var vertexBuffer: VertexBuffer
private lateinit var indexBuffer: IndexBuffer
// Filament entity representing a renderable object
@Entity private val renderables = IntArray(NUM_CUBES)
private val materialInstances = arrayOfNulls<MaterialInstance>(NUM_CUBES)
private val translationMatrices = Array(NUM_CUBES) { FloatArray(16) }
@Entity private var light = 0
// A swap chain is Filament's representation of a surface
private var swapChain: SwapChain? = null
private var cameraRadius = 42.7f // Initial distance (calculated from original 15Y, 40Z)
private var cameraTheta = 0.0f // Azimuth (horizontal) in radians
private var cameraPhi = 0.358f // Elevation (vertical) in radians (from original 15Y, 40Z)
private var mLastX = 0.0f
private var mLastY = 0.0f
private val DRAG_SPEED = 0.005f
// Performs the rendering and schedules new frames
private val frameScheduler = FrameCallback()
private val animator = ValueAnimator.ofFloat(0.0f, 360.0f)
private val animatorListener = object : ValueAnimator.AnimatorUpdateListener {
// Pre-allocate matrix to avoid GC churn
private val transformMatrix = FloatArray(16)
override fun onAnimationUpdate(a: ValueAnimator) {
val tcm = engine.transformManager
val time = a.animatedFraction // 0.0 to 1.0
// Calculate the sine wave scale factor.
// sin(time * 2 * PI) oscillates between -1.0 and 1.0.
// We multiply by 0.5f (so it's -0.5 to 0.5) and add 1.0f.
// This makes the final 'scale' oscillate between 0.5 and 1.5.
val scale = 1.0f + sin(time * 2.0 * Math.PI).toFloat() * 0.5f
val localScale = (scale - 0.5f) * 0.5f + 0.5f
for (i in 0 until NUM_CUBES) {
val inst = tcm.getInstance(renderables[i])
val materialInst = materialInstances[i] ?: continue
// Get the cube's base position from its stored matrix
// The translation is in elements 12 (x), 13 (y), and 14 (z)
val baseX = translationMatrices[i][12]
val baseY = translationMatrices[i][13]
val baseZ = translationMatrices[i][14]
// Set the transformMatrix to a new translation matrix,
// scaled from the center (0,0,0)
Matrix.setIdentityM(transformMatrix, 0)
Matrix.translateM(transformMatrix, 0,
baseX * scale,
baseY * scale,
baseZ * scale)
Matrix.scaleM(transformMatrix, 0, localScale, localScale, localScale)
tcm.setTransform(inst, transformMatrix)
// Vary roughness
val roughness = 0.5f + 0.5f * sin(time * 2.0 * Math.PI + i * 0.1).toFloat()
materialInst.setParameter("roughness", roughness)
// Vary color (using linear RGB)
val r = 0.5f + 0.5f * cos(time * 2.0 * Math.PI + i * 0.5).toFloat()
val g = 0.5f + 0.5f * sin(time * 2.0 * Math.PI + i * 0.3).toFloat()
val b = 0.5f + 0.5f * cos(time * 2.0 * Math.PI + i * 0.1).toFloat()
materialInst.setParameter("baseColor", Colors.RgbType.LINEAR, r, g, b)
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
surfaceView = SurfaceView(this)
setContentView(surfaceView)
surfaceView.setOnTouchListener(touchListener)
choreographer = Choreographer.getInstance()
displayHelper = DisplayHelper(this)
setupSurfaceView()
setupFilament()
setupView()
setupScene()
}
private fun setupSurfaceView() {
uiHelper = UiHelper(UiHelper.ContextErrorPolicy.DONT_CHECK)
uiHelper.renderCallback = SurfaceCallback()
uiHelper.attachTo(surfaceView)
}
private fun setupFilament() {
engine = Engine.create()
renderer = engine.createRenderer()
scene = engine.createScene()
view = engine.createView()
camera = engine.createCamera(engine.entityManager.create())
}
private fun setupView() {
scene.skybox = Skybox.Builder().color(0.035f, 0.035f, 0.035f, 1.0f).build(engine)
view.camera = camera
view.scene = scene
}
private fun setupScene() {
loadMaterial()
createMesh()
val tcm = engine.transformManager
val center = (GRID_SIZE - 1) * 0.5f
for (i in 0 until NUM_CUBES) {
// Calculate grid position
val x = (i % GRID_SIZE) - center
val y = ((i / GRID_SIZE) % GRID_SIZE) - center
val z = (i / (GRID_SIZE * GRID_SIZE)) - center
val posX = x * SPACING
val posY = y * SPACING
val posZ = z * SPACING
// Create material instance for this cube
materialInstances[i] = material.createInstance()
// Set initial parameters (will be overwritten by animator, but good practice)
materialInstances[i]?.setParameter("baseColor", Colors.RgbType.SRGB, 1.0f, 0.85f, 0.57f)
materialInstances[i]?.setParameter("metallic", 0.0f)
materialInstances[i]?.setParameter("roughness", 0.3f)
// Create the renderable entity
renderables[i] = EntityManager.get().create()
// Create the renderable component
RenderableManager.Builder(1)
.boundingBox(Box(0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f))
.geometry(0, PrimitiveType.TRIANGLES, vertexBuffer, indexBuffer, 0, 6 * 6)
.material(0, materialInstances[i]!!)
.build(engine, renderables[i])
// Add to scene
scene.addEntity(renderables[i])
// Set its initial transform
// Store the base translation matrix
Matrix.setIdentityM(translationMatrices[i], 0)
Matrix.translateM(translationMatrices[i], 0, posX, posY, posZ)
// Get the transform component instance
val inst = tcm.getInstance(renderables[i])
tcm.setTransform(inst, translationMatrices[i])
}
// We now need a light, let's create a directional light
light = EntityManager.get().create()
val (r, g, b) = Colors.cct(5_500.0f)
LightManager.Builder(LightManager.Type.DIRECTIONAL)
.color(r, g, b)
.intensity(110_000.0f)
.direction(0.0f, -0.5f, -1.0f)
.castShadows(true)
.build(engine, light)
scene.addEntity(light)
camera.setExposure(16.0f, 1.0f / 125.0f, 100.0f)
updateCamera()
startAnimation()
}
private fun loadMaterial() {
readUncompressedAsset("materials/lit.filamat").let {
material = Material.Builder().payload(it, it.remaining()).build(engine)
}
}
private fun createMesh() {
val floatSize = 4
val shortSize = 2
val vertexSize = 3 * floatSize + 4 * floatSize
// A vertex is a position + a tangent frame:
// 3 floats for XYZ position, 4 floats for normal+tangents (quaternion)
@Suppress("ArrayInDataClass")
data class Vertex(val x: Float, val y: Float, val z: Float, val tangents: FloatArray)
fun ByteBuffer.put(v: Vertex): ByteBuffer {
putFloat(v.x)
putFloat(v.y)
putFloat(v.z)
v.tangents.forEach { putFloat(it) }
return this
}
// 6 faces, 4 vertices per face
val vertexCount = 6 * 4
// Create tangent frames, one per face
val tfPX = FloatArray(4)
val tfNX = FloatArray(4)
val tfPY = FloatArray(4)
val tfNY = FloatArray(4)
val tfPZ = FloatArray(4)
val tfNZ = FloatArray(4)
MathUtils.packTangentFrame( 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f, 0.0f, tfPX)
MathUtils.packTangentFrame( 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, -1.0f, -1.0f, 0.0f, 0.0f, tfNX)
MathUtils.packTangentFrame(-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, tfPY)
MathUtils.packTangentFrame(-1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 0.0f, tfNY)
MathUtils.packTangentFrame( 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, tfPZ)
MathUtils.packTangentFrame( 0.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, tfNZ)
val vertexData = ByteBuffer.allocate(vertexCount * vertexSize)
// It is important to respect the native byte order
.order(ByteOrder.nativeOrder())
// Face -Z
.put(Vertex(-1.0f, -1.0f, -1.0f, tfNZ))
.put(Vertex(-1.0f, 1.0f, -1.0f, tfNZ))
.put(Vertex( 1.0f, 1.0f, -1.0f, tfNZ))
.put(Vertex( 1.0f, -1.0f, -1.0f, tfNZ))
// Face +X
.put(Vertex( 1.0f, -1.0f, -1.0f, tfPX))
.put(Vertex( 1.0f, 1.0f, -1.0f, tfPX))
.put(Vertex( 1.0f, 1.0f, 1.0f, tfPX))
.put(Vertex( 1.0f, -1.0f, 1.0f, tfPX))
// Face +Z
.put(Vertex(-1.0f, -1.0f, 1.0f, tfPZ))
.put(Vertex( 1.0f, -1.0f, 1.0f, tfPZ))
.put(Vertex( 1.0f, 1.0f, 1.0f, tfPZ))
.put(Vertex(-1.0f, 1.0f, 1.0f, tfPZ))
// Face -X
.put(Vertex(-1.0f, -1.0f, 1.0f, tfNX))
.put(Vertex(-1.0f, 1.0f, 1.0f, tfNX))
.put(Vertex(-1.0f, 1.0f, -1.0f, tfNX))
.put(Vertex(-1.0f, -1.0f, -1.0f, tfNX))
// Face -Y
.put(Vertex(-1.0f, -1.0f, 1.0f, tfNY))
.put(Vertex(-1.0f, -1.0f, -1.0f, tfNY))
.put(Vertex( 1.0f, -1.0f, -1.0f, tfNY))
.put(Vertex( 1.0f, -1.0f, 1.0f, tfNY))
// Face +Y
.put(Vertex(-1.0f, 1.0f, -1.0f, tfPY))
.put(Vertex(-1.0f, 1.0f, 1.0f, tfPY))
.put(Vertex( 1.0f, 1.0f, 1.0f, tfPY))
.put(Vertex( 1.0f, 1.0f, -1.0f, tfPY))
// Make sure the cursor is pointing in the right place in the byte buffer
.flip()
// Declare the layout of our mesh
vertexBuffer = VertexBuffer.Builder()
.bufferCount(1)
.vertexCount(vertexCount)
// Because we interleave position and color data we must specify offset and stride
// We could use de-interleaved data by declaring two buffers and giving each
// attribute a different buffer index
.attribute(VertexAttribute.POSITION, 0, AttributeType.FLOAT3, 0, vertexSize)
.attribute(VertexAttribute.TANGENTS, 0, AttributeType.FLOAT4, 3 * floatSize, vertexSize)
.build(engine)
// Feed the vertex data to the mesh
// We only set 1 buffer because the data is interleaved
vertexBuffer.setBufferAt(engine, 0, vertexData)
// Create the indices
val indexData = ByteBuffer.allocate(6 * 2 * 3 * shortSize)
.order(ByteOrder.nativeOrder())
repeat(6) {
val i = (it * 4).toShort()
indexData
.putShort(i).putShort((i + 1).toShort()).putShort((i + 2).toShort())
.putShort(i).putShort((i + 2).toShort()).putShort((i + 3).toShort())
}
indexData.flip()
// 6 faces, 2 triangles per face,
indexBuffer = IndexBuffer.Builder()
.indexCount(vertexCount * 2)
.bufferType(IndexBuffer.Builder.IndexType.USHORT)
.build(engine)
indexBuffer.setBuffer(engine, indexData)
}
private fun updateCamera() {
// Calculate eye position from spherical coordinates
val eyeY = cameraRadius * sin(cameraPhi)
val horizontalRadius = cameraRadius * cos(cameraPhi)
val eyeX = horizontalRadius * sin(cameraTheta)
val eyeZ = horizontalRadius * cos(cameraTheta)
// Point the camera at the center (0,0,0)
camera.lookAt(eyeX.toDouble(), eyeY.toDouble(), eyeZ.toDouble(), // eye
0.0, 0.0, 0.0, // center
0.0, 1.0, 0.0) // up
}
private val touchListener = object : android.view.View.OnTouchListener {
override fun onTouch(v: android.view.View?, event: android.view.MotionEvent?): Boolean {
if (event == null) return false
when (event.action) {
android.view.MotionEvent.ACTION_DOWN -> {
mLastX = event.x
mLastY = event.y
return true
}
android.view.MotionEvent.ACTION_MOVE -> {
val dx = event.x - mLastX
val dy = event.y - mLastY
cameraTheta -= dx * DRAG_SPEED
cameraPhi += dy * DRAG_SPEED
// Clamp vertical angle to avoid flipping over
cameraPhi = cameraPhi.coerceIn(-1.5f, 1.5f)
updateCamera()
mLastX = event.x
mLastY = event.y
return true
}
}
return v?.onTouchEvent(event) ?: false
}
}
private fun startAnimation() {
animator.interpolator = LinearInterpolator()
animator.duration = 6000
animator.repeatMode = ValueAnimator.RESTART
animator.repeatCount = ValueAnimator.INFINITE
animator.addUpdateListener(animatorListener)
animator.start()
}
override fun onResume() {
super.onResume()
choreographer.postFrameCallback(frameScheduler)
animator.start()
}
override fun onPause() {
super.onPause()
choreographer.removeFrameCallback(frameScheduler)
animator.cancel()
}
override fun onDestroy() {
super.onDestroy()
// Stop the animation and any pending frame
choreographer.removeFrameCallback(frameScheduler)
animator.cancel();
// Always detach the surface before destroying the engine
uiHelper.detach()
// Cleanup all resources
engine.destroyEntity(light)
// Destroy all 1000 entities and material instances
for (i in 0 until NUM_CUBES) {
engine.destroyEntity(renderables[i])
materialInstances[i]?.let { engine.destroyMaterialInstance(it) }
}
engine.destroyRenderer(renderer)
engine.destroyVertexBuffer(vertexBuffer)
engine.destroyIndexBuffer(indexBuffer)
engine.destroyMaterial(material)
engine.destroyView(view)
engine.destroyScene(scene)
engine.destroyCameraComponent(camera.entity)
// Engine.destroyEntity() destroys Filament related resources only
// (components), not the entity itself
val entityManager = EntityManager.get()
entityManager.destroy(light)
for (entity in renderables) {
entityManager.destroy(entity)
}
entityManager.destroy(camera.entity)
// Destroying the engine will free up any resource you may have forgotten
// to destroy, but it's recommended to do the cleanup properly
engine.destroy()
}
inner class FrameCallback : Choreographer.FrameCallback {
override fun doFrame(frameTimeNanos: Long) {
// Schedule the next frame
choreographer.postFrameCallback(this)
// This check guarantees that we have a swap chain
if (uiHelper.isReadyToRender) {
// If beginFrame() returns false you should skip the frame
// This means you are sending frames too quickly to the GPU
if (renderer.beginFrame(swapChain!!, frameTimeNanos)) {
renderer.render(view)
renderer.endFrame()
}
}
}
}
inner class SurfaceCallback : UiHelper.RendererCallback {
override fun onNativeWindowChanged(surface: Surface) {
swapChain?.let { engine.destroySwapChain(it) }
swapChain = engine.createSwapChain(surface)
displayHelper.attach(renderer, surfaceView.display)
}
override fun onDetachedFromSurface() {
displayHelper.detach()
swapChain?.let {
engine.destroySwapChain(it)
// Required to ensure we don't return before Filament is done executing the
// destroySwapChain command, otherwise Android might destroy the Surface
// too early
engine.flushAndWait()
swapChain = null
}
}
override fun onResized(width: Int, height: Int) {
val aspect = width.toDouble() / height.toDouble()
camera.setProjection(45.0, aspect, 0.1, 100.0, Camera.Fov.VERTICAL)
view.viewport = Viewport(0, 0, width, height)
FilamentHelper.synchronizePendingFrames(engine)
}
}
private fun readUncompressedAsset(assetName: String): ByteBuffer {
assets.openFd(assetName).use { fd ->
val input = fd.createInputStream()
val dst = ByteBuffer.allocate(fd.length.toInt())
val src = Channels.newChannel(input)
src.read(dst)
src.close()
return dst.apply { rewind() }
}
}
}

View File

@@ -0,0 +1,53 @@
// Simple lit material that defines 3 parameters:
// - baseColor
// - roughness
// - metallic
//
// These parameters can be used by the application to change the appearance of the material.
//
// This source material must be compiled to a binary material using the matc tool.
// The command used to compile this material is:
// matc -p mobile -a opengl -o app/src/main/assets/lit.filamat app/src/materials/lit.mat
//
// See build.gradle for an example of how to compile materials automatically
// Please refer to the documentation for more information about matc and the materials system.
material {
name : lit,
// Dynamic lighting is enabled on this material
shadingModel : lit,
// We don't need to declare a "requires" array, lit materials
// always requires the "tangents" vertex attribute (the normal
// is required for lighting, tangent/bitangent for normal mapping
// and anisotropy)
// List of parameters exposed by this material
parameters : [
// The color must be passed in linear space, not sRGB
{
type : float3,
name : baseColor
},
{
type : float,
name : roughness
},
{
type : float,
name : metallic
}
],
}
fragment {
void material(inout MaterialInputs material) {
prepareMaterial(material);
// Nothing fancy here, we simply copy the parameters
material.baseColor.rgb = materialParams.baseColor;
material.roughness = materialParams.roughness;
material.metallic = materialParams.metallic;
}
}

View File

@@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillType="evenOdd"
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
android:strokeColor="#00000000"
android:strokeWidth="1">
<aapt:attr name="android:fillColor">
<gradient
android:endX="78.5885"
android:endY="90.9159"
android:startX="48.7653"
android:startY="61.0927"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0"/>
<item
android:color="#00000000"
android:offset="1.0"/>
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
android:strokeColor="#00000000"
android:strokeWidth="1"/>
</vector>

View File

@@ -0,0 +1,171 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportHeight="108"
android:viewportWidth="108">
<path
android:fillColor="#26A69A"
android:pathData="M0,0h108v108h-108z"/>
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeColor="#33FFFFFF"
android:strokeWidth="0.8"/>
</vector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background"/>
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#3F51B5</color>
<color name="colorPrimaryDark">#303F9F</color>
<color name="colorAccent">#FF4081</color>
</resources>

View File

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Material Instance Stress</string>
</resources>

View File

@@ -0,0 +1,8 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="android:Theme.Material.Light.DarkActionBar">
<!-- Customize your theme here. -->
</style>
</resources>

View File

@@ -12,6 +12,7 @@ include ':samples:sample-image-based-lighting'
include ':samples:sample-lit-cube'
include ':samples:sample-live-wallpaper'
include ':samples:sample-material-builder'
include ':samples:sample-material-instance-stress'
include ':samples:sample-multi-view'
include ':samples:sample-page-curl'
include ':samples:sample-stream-test'

View File

@@ -77,6 +77,10 @@ function print_help {
echo " meant for building the samples."
echo " -P"
echo " Enable perfetto traces on Android. Disabled by default on the Release build, enabled otherwise."
echo " -y build_type"
echo " Build the filament dependent tools (matc, resgen) separately from the project. This will set"
echo " the tools as prebuilts that filament target will then use to build. The built_type option"
echo " (debug|release) is meant to indicate the type of build of the resulting prebuilts."
echo ""
echo "Build types:"
echo " release"
@@ -217,6 +221,11 @@ OSMESA_OPTION=""
IOS_BUILD_SIMULATOR=false
BUILD_UNIVERSAL_LIBRARIES=false
ISSUE_SPLIT_BUILD=false
SPLIT_BUILD_TYPE=""
PREBUILT_TOOLS_DIR=""
IMPORT_EXECUTABLES_DIR_OPTION="-DIMPORT_EXECUTABLES_DIR=out"
BUILD_GENERATOR=Ninja
BUILD_COMMAND=ninja
BUILD_CUSTOM_TARGETS=
@@ -242,6 +251,37 @@ function build_clean_aggressive {
git clean -qfX android
}
function build_tools_for_split_build {
local build_type_arg=$1
local lc_build_type=$(echo "${build_type_arg}" | tr '[:upper:]' '[:lower:]')
PREBUILT_TOOLS_DIR="out/prebuilt-tools-${lc_build_type}"
echo "Building tools for split build (${lc_build_type}) in ${PREBUILT_TOOLS_DIR}..."
mkdir -p "${PREBUILT_TOOLS_DIR}"
pushd "${PREBUILT_TOOLS_DIR}" > /dev/null
local lc_name=$(echo "${UNAME}" | tr '[:upper:]' '[:lower:]')
local architectures=""
if [[ "${lc_name}" == "darwin" ]]; then
if [[ "${BUILD_UNIVERSAL_LIBRARIES}" == "true" ]]; then
architectures="-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64"
fi
fi
cmake \
-G "${BUILD_GENERATOR}" \
-DFILAMENT_EXPORT_PREBUILT_EXECUTABLES_DIR=${PREBUILT_TOOLS_DIR} \
-DCMAKE_BUILD_TYPE="${build_type_arg}" \
${WEBGPU_OPTION} \
${architectures} \
../..
${BUILD_COMMAND} ${WEB_HOST_TOOLS}
popd > /dev/null
}
function build_desktop_target {
local lc_target=$(echo "$1" | tr '[:upper:]' '[:lower:]')
local build_targets=$2
@@ -265,7 +305,7 @@ function build_desktop_target {
if [[ ! -d "CMakeFiles" ]] || [[ "${ISSUE_CMAKE_ALWAYS}" == "true" ]]; then
cmake \
-G "${BUILD_GENERATOR}" \
-DIMPORT_EXECUTABLES_DIR=out \
${IMPORT_EXECUTABLES_DIR_OPTION} \
-DCMAKE_BUILD_TYPE="$1" \
-DCMAKE_INSTALL_PREFIX="../${lc_target}/filament" \
${EGL_ON_LINUX_OPTION} \
@@ -331,7 +371,7 @@ function build_webgl_with_target {
source "${EMSDK}/emsdk_env.sh"
cmake \
-G "${BUILD_GENERATOR}" \
-DIMPORT_EXECUTABLES_DIR=out \
${IMPORT_EXECUTABLES_DIR_OPTION} \
-DCMAKE_TOOLCHAIN_FILE="${EMSDK}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake" \
-DCMAKE_BUILD_TYPE="$1" \
-DCMAKE_INSTALL_PREFIX="../webgl-${lc_target}/filament" \
@@ -404,7 +444,7 @@ function build_android_target {
if [[ ! -d "CMakeFiles" ]] || [[ "${ISSUE_CMAKE_ALWAYS}" == "true" ]]; then
cmake \
-G "${BUILD_GENERATOR}" \
-DIMPORT_EXECUTABLES_DIR=out \
${IMPORT_EXECUTABLES_DIR_OPTION} \
-DCMAKE_BUILD_TYPE="$1" \
-DFILAMENT_NDK_VERSION="${FILAMENT_NDK_VERSION}" \
-DCMAKE_INSTALL_PREFIX="../android-${lc_target}/filament" \
@@ -638,7 +678,7 @@ function build_ios_target {
if [[ ! -d "CMakeFiles" ]] || [[ "${ISSUE_CMAKE_ALWAYS}" == "true" ]]; then
cmake \
-G "${BUILD_GENERATOR}" \
-DIMPORT_EXECUTABLES_DIR=out \
${IMPORT_EXECUTABLES_DIR_OPTION} \
-DCMAKE_BUILD_TYPE="$1" \
-DCMAKE_INSTALL_PREFIX="../ios-${lc_target}/filament" \
-DIOS_ARCH="${arch}" \
@@ -810,7 +850,7 @@ function check_debug_release_build {
pushd "$(dirname "$0")" > /dev/null
while getopts ":hacCfgimp:q:uvWslwedtk:bVx:S:X:P" opt; do
while getopts ":hacCfgimp:q:uvWslwedtk:bVx:S:X:Py:" opt; do
case ${opt} in
h)
print_help
@@ -979,6 +1019,20 @@ while getopts ":hacCfgimp:q:uvWslwedtk:bVx:S:X:P" opt; do
;;
X) OSMESA_OPTION="-DFILAMENT_OSMESA_PATH=${OPTARG}"
;;
y)
ISSUE_SPLIT_BUILD=true
SPLIT_BUILD_TYPE=${OPTARG}
case $(echo "${SPLIT_BUILD_TYPE}" | tr '[:upper:]' '[:lower:]') in
debug|release)
;;
*)
echo "Unknown build type for -y: ${SPLIT_BUILD_TYPE}"
echo "Build type must be one of [debug|release]"
echo ""
exit 1
;;
esac
;;
\?)
echo "Invalid option: -${OPTARG}" >&2
echo ""
@@ -1013,6 +1067,13 @@ done
validate_build_command
if [[ "${ISSUE_SPLIT_BUILD}" == "true" ]]; then
# Capitalize first letter of SPLIT_BUILD_TYPE
SPLIT_BUILD_TYPE_CAPITALIZED="$(echo ${SPLIT_BUILD_TYPE:0:1} | tr '[:lower:]' '[:upper:]')${SPLIT_BUILD_TYPE:1}"
build_tools_for_split_build "${SPLIT_BUILD_TYPE_CAPITALIZED}"
IMPORT_EXECUTABLES_DIR_OPTION="-DFILAMENT_IMPORT_PREBUILT_EXECUTABLES_DIR=${PREBUILT_TOOLS_DIR}"
fi
if [[ "${ISSUE_CLEAN}" == "true" ]]; then
build_clean
fi

View File

@@ -165,8 +165,8 @@ 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>
<a href="https://central.sonatype.com/usertoken">https://central.sonatype.com/usertoken</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

View File

@@ -180,7 +180,7 @@ rm -r Vulkan-Headers-1.3.232 v1.3.232.zip
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../dup/filamat.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<a rel="next prefetch" href="../dup/fgviewer.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
@@ -194,7 +194,7 @@ rm -r Vulkan-Headers-1.3.232 v1.3.232.zip
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../dup/filamat.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<a rel="next prefetch" href="../dup/fgviewer.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>

View File

@@ -200,6 +200,12 @@ inside the Filament source tree.</p>
force a clean build by adding the <code>-c</code> flag in that case.</p>
<p>To install the libraries and executables in <code>out/debug/</code> and <code>out/release/</code>, add the <code>-i</code> flag.
The script offers more features described by executing <code>build.sh -h</code>.</p>
<p>For more specialized options, please also consider the following pages:</p>
<ul>
<li><code>-d</code>: <a href="https://google.github.io/filament/dup/matdbg.html"><code>matdbg</code></a></li>
<li><code>-t</code>: <a href="https://google.github.io/filament/dup/fgviewer.html"><code>fgviewer</code></a></li>
<li><code>-b</code> and <code>-y</code>: <a href="https://google.github.io/filament/notes/asan_ubsan.html">ASAN/UBSAN builds</a></li>
</ul>
<h3 id="filament-specific-cmake-options"><a class="header" href="#filament-specific-cmake-options">Filament-specific CMake Options</a></h3>
<p>The following CMake options are boolean options specific to Filament:</p>
<ul>

345
docs/dup/fgviewer.html Normal file
View File

@@ -0,0 +1,345 @@
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>fgviewer - 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="fgviewer"><a class="header" href="#fgviewer">fgviewer</a></h1>
<ol>
<li><a href="#capabilities">Capabilities</a></li>
<li><a href="#setup-for-desktop">Setup for Desktop</a></li>
<li><a href="#setup-for-android">Setup for Android</a></li>
<li><a href="#debugger-usage">Debugger Usage</a></li>
<li><a href="#architecture-overview">Architecture Overview</a></li>
<li><a href="#c-server">C++ Server</a></li>
<li><a href="#javascript-client">JavaScript Client</a></li>
<li><a href="#http-requests">HTTP Requests</a></li>
<li><a href="#wish-list">Wish List</a></li>
</ol>
<h2 id="capabilities"><a class="header" href="#capabilities">Capabilities</a></h2>
<p>fgviewer is a library and web application for real-time visualization of the frame graph in Filament.
It displays active passes and resource usage, providing insights into the rendering pipeline.</p>
<h2 id="setup-for-desktop"><a class="header" href="#setup-for-desktop">Setup for Desktop</a></h2>
<p>When using the easy build script, include the <code>-t</code> argument. For example:</p>
<pre><code>./build.sh -ft debug gltf_viewer
</code></pre>
<p>The <code>t</code> enables a CMake option called <code>FILAMENT_ENABLE_FGVIEWER</code> and the <code>f</code> ensures that CMake gets
re-run so that the option is honored.</p>
<p>Next, set an environment variable as follows. In Windows, use <code>set</code> instead of <code>export</code>.</p>
<pre><code>export FILAMENT_FGVIEWER_PORT=8050
</code></pre>
<p>Next, launch any app that links against a debug build of a Filament and point your web browser to
http://localhost:8050. Skip ahead to <strong>Debugger Usage</strong>.</p>
<h2 id="setup-for-android"><a class="header" href="#setup-for-android">Setup for Android</a></h2>
<p>Rebuild Filament for Android after enabling a CMake option called <code>FILAMENT_ENABLE_FGVIEWER</code>. Note that
CMake is invoked from several places for Android (both gradle and our easy build script), so one
pragmatic and reliable way of doing this is to simply hack <code>CMakeLists.txt</code> and
<code>filament-android/CMakeLists.txt</code> by unconditionally setting <code>FILAMENT_ENABLE_FGVIEWER</code> to <code>ON</code>.</p>
<p>After rebuilding Filament with the option enabled, ensure that internet permissions are enabled in
your app by adding the following into your manifest as a child of the <code>&lt;manifest&gt;</code> element.</p>
<pre><code>&lt;uses-permission android:name="android.permission.INTERNET" /&gt;
</code></pre>
<p>Now launch your app as usual. The Filament Engine sets up a server that is hardcoded to listen to
port <code>8085</code>. Next, you will need to forward your device's TCP port <code>8085</code> to your host port of choice.
For example, to forward the fgviewer server on your device to port <code>8085</code> on your host machine, do the
following:</p>
<pre><code>adb forward tcp:8085 tcp:8085
</code></pre>
<p>This lets you go to http://localhost:8085 in Chrome on your host machine.</p>
<h2 id="debugger-usage"><a class="header" href="#debugger-usage">Debugger Usage</a></h2>
<p>After opening the fgviewer page in your browser, you can see the active views are on the left panel.
Then you can select any of them to see the active passes and resources for that view.</p>
<p align="center">
<img width="600px" src=https://github.com/user-attachments/assets/2d31767f-fc25-4f17-8c14-528fe5c6b698>
</p>
<h2 id="architecture-overview"><a class="header" href="#architecture-overview">Architecture Overview</a></h2>
<p align="center">
<img width="450" src=https://github.com/user-attachments/assets/537ebb89-6ad0-4b93-bbeb-207d4fe9ec5a>
</p>
<p>The fgviewer library has two parts: a C++ server and a JavaScript client. The C++ server is
responsible for instancing a <a href="https://github.com/civetweb/civetweb">civetweb</a> context that handles HTTP requests. The
JavaScript client is a small web app that contains a view into an in-browser database of framegraphs.</p>
<p>When a new connection is established, the client asks the server for a list of framegraphs
in order to populate its in-browser database. If the connection is lost (e.g. if the app crashes),
then the database stays intact and the web app is still functional.</p>
<h2 id="c-server"><a class="header" href="#c-server">C++ Server</a></h2>
<p>The civetweb server is wrapped by our <code>DebugServer</code> class, which provides a public interface consisting of
a few methods invoked by the Filament engine.</p>
<p>Since each view corresponds to a frame graph, the engine should notify <code>DebugServer</code> of any changes to the views
on the engine side.</p>
<ul>
<li><strong>createView</strong> Notifies the debugger that a new view has been created.</li>
<li><strong>updateView</strong> Notifies the debugger of updates to an existing view.</li>
<li><strong>destroyView</strong> Notifies the debugger that a view is being removed.</li>
</ul>
<h2 id="javascript-client"><a class="header" href="#javascript-client">JavaScript Client</a></h2>
<p>The web app is built using LitElement, a lightweight library for creating Web Components. Our goal is to keep the code simple and modern, avoiding frameworks like React or Angular.</p>
<p>The app presents a view over a pseudo-database, which is essentially a global variable holding a dictionary that maps frame graph ids to objects following the JSON structure described below.</p>
<h2 id="http-requests"><a class="header" href="#http-requests">HTTP requests</a></h2>
<p>The server responds to the following GET requests by returning a JSON blob. The <code>{id}</code> in these
requests is a concept specific to fgviewer (not Filament) which is an 8-digit hex string for identifying frame graphs.</p>
<hr />
<p><code>/api/framegraphs</code></p>
<p>Returns an array containing all framegraphs in an app. Example:</p>
<pre><code class="language-json">[{
"fgid": "00000000",
"viewName": "Main View",
"passes": [{
"name": "shadow pass",
"reads": [],
"writes": ["0"]
}],
"resources": [{
"id": "0",
"name": "shadowmap",
"properties": [{"resolution": "256x256"}, {"is_subresource": "false"}]
}]
},
{
"fgid": "00000001",
"viewName": "UI view",
...
}]
</code></pre>
<hr />
<p><code>/api/framegraph?fg={id}</code></p>
<p>Returns a specific framegraph info. Example:</p>
<pre><code class="language-json">{
"fgid": "00000000",
"viewName": "Main View",
"passes": [{
"name": "shadow pass",
"reads": [],
"writes": ["0"]
}],
"resources": [{
"id": "0",
"name": "shadowmap",
"properties": [{"resolution": "256x256"}, {"is_subresource": "false"}]
}]
}
</code></pre>
<hr />
<p><code>/api/status</code></p>
<p>Returns one of the below:</p>
<ul>
<li><code>0</code>: first time connected</li>
<li><code>1</code>: no-op</li>
<li><code>{fgid}</code>: the corresponding frame graph has an update
<ul>
<li>Then the web view can request for the actual info using the previous api</li>
</ul>
</li>
</ul>
<p>If the request gets timeout, the web page show disconnected to the user.</p>
<hr />
<h2 id="wish-list"><a class="header" href="#wish-list">Wish List</a></h2>
<ul>
<li>Display the texture contents on the webview</li>
</ul>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../dup/bluevk.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/filamat.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="../dup/bluevk.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/filamat.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>

View File

@@ -336,7 +336,7 @@ void material(inout MaterialInputs material) {
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../dup/bluevk.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<a rel="prev" href="../dup/fgviewer.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
@@ -350,7 +350,7 @@ void material(inout MaterialInputs material) {
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../dup/bluevk.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<a rel="prev" href="../dup/fgviewer.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>

View File

@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.66.0'
implementation 'com.google.android.filament:filament-android:1.66.2'
}
</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.66.0'
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.66.2'
</code></pre>
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
<ul>

View File

@@ -160,10 +160,15 @@
<main>
<h1 id="running-with-asanubsan"><a class="header" href="#running-with-asanubsan">Running with ASAN/UBSAN</a></h1>
<h2 id="enabling"><a class="header" href="#enabling">Enabling</a></h2>
<p>When building though build.sh, pass the <code>-b</code> flag. This sets the cmake variable
<p>When building though <code>build.sh</code>, pass the <code>-b</code> flag. This sets the cmake variable
<code>FILAMENT_ENABLE_ASAN_UBSAN=ON</code> which eventually passes <code>"-fsanitize=address -fsanitize=undefined"</code>
to all compile and link operations.</p>
<p>If building through CMake directly, or an IDE like CLion that doesn't use build.sh, instead pass
<p>It might be desirable to pair the <code>-b</code> with <code>-y release</code>. The <code>-y</code> flag indicates that targets
in <code>/tools</code> will be built separately from the filament target, and the filament build depends on
the <code>/tools</code> targets as prebuilt binaries. This separation reduces ASAN/UBSAN build time
considerably. This option assumes that the user is trying to catch sanitization issues for
filament and not the tools that are used to build filament.</p>
<p>If building through CMake directly, or an IDE like CLion that doesn't use <code>build.sh</code>, instead pass
<code>-DFILAMENT_ENABLE_ASAN_UBSAN=ON</code> to cmake in order to get the same result.</p>
<h2 id="getting-memory-leak-detection-on-mac"><a class="header" href="#getting-memory-leak-detection-on-mac">Getting memory leak detection on Mac</a></h2>
<p>Memory leak detection isn't enabled by default on MacOS. There are two issues to address, first is

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -46,6 +46,9 @@
"libs/matdbg/README.md": {
"dest": "dup/matdbg.md"
},
"libs/fgviewer/README.md": {
"dest": "dup/fgviewer.md"
},
"tools/normal-blending/README.md": {
"dest": "dup/normal_blending.md"
},

View File

@@ -0,0 +1,15 @@
attrs==25.4.0
certifi==2025.10.5
h11==0.16.0
idna==3.11
outcome==1.3.0.post0
PySocks==1.7.1
selenium==4.37.0
sniffio==1.3.1
sortedcontainers==2.4.0
trio==0.31.0
trio-websocket==0.12.2
typing_extensions==4.15.0
urllib3==2.5.0
websocket-client==1.9.0
wsproto==1.2.0

View File

@@ -140,6 +140,8 @@ if __name__ == "__main__":
res, err = execute('mdbook build', cwd=MDBOOK_DIR)
assert res == 0, f"failed to execute `mdbook`. return-code={res} err=\"{err}\""
shutil.copytree(RAW_COPIES_DIR, BOOK_OUPUT_DIR, dirs_exist_ok=True)
RAW_IGNORES = shutil.ignore_patterns('update_remote_ui.sh')
shutil.copytree(RAW_COPIES_DIR, BOOK_OUPUT_DIR, ignore=RAW_IGNORES, dirs_exist_ok=True)
shutil.copy(os.path.join(MARKDEEP_DIR, FILAMENT_MD), BOOK_OUPUT_DIR)
shutil.copy(os.path.join(MARKDEEP_DIR, MATERIALS_MD), BOOK_OUPUT_DIR)

View File

@@ -32,6 +32,7 @@
- [Libraries](./notes/libs.md)
- [bluegl](./dup/bluegl.md)
- [bluevk](./dup/bluevk.md)
- [fgviewer](./dup/fgviewer.md)
- [filamat](./dup/filamat.md)
- [gltfio](./dup/gltfio.md)
- [iblprefilter](./dup/iblprefilter.md)

View File

@@ -9,8 +9,8 @@ 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**.
[https://central.sonatype.com/usertoken](https://central.sonatype.com/usertoken)
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):

View File

@@ -2,11 +2,17 @@
## Enabling
When building though build.sh, pass the `-b` flag. This sets the cmake variable
When building though `build.sh`, pass the `-b` flag. This sets the cmake variable
`FILAMENT_ENABLE_ASAN_UBSAN=ON` which eventually passes `"-fsanitize=address -fsanitize=undefined"`
to all compile and link operations.
If building through CMake directly, or an IDE like CLion that doesn't use build.sh, instead pass
It might be desirable to pair the `-b` with `-y release`. The `-y` flag indicates that targets
in `/tools` will be built separately from the filament target, and the filament build depends on
the `/tools` targets as prebuilt binaries. This separation reduces ASAN/UBSAN build time
considerably. This option assumes that the user is trying to catch sanitization issues for
filament and not the tools that are used to build filament.
If building through CMake directly, or an IDE like CLion that doesn't use `build.sh`, instead pass
`-DFILAMENT_ENABLE_ASAN_UBSAN=ON` to cmake in order to get the same result.
## Getting memory leak detection on Mac

File diff suppressed because one or more lines are too long

View File

@@ -1,17 +0,0 @@
#!/bin/bash
pushd "$(dirname "$0")" > /dev/null
curl -OL https://nightly.link/google/filament/workflows/web-continuous/main/filament-web.zip
unzip -q filament-web.zip
tar -xvzf filament-release-web.tgz
rm filament-release-web.tgz
rm filament-web.zip
rm filament.d.ts
cp ../../web/samples/remote.html index.html
popd
git status
echo ""
echo "All done! Next, make a git commit that updates docs/remote."

View File

@@ -8,10 +8,10 @@ tar -xvzf filament-release-web.tgz
rm filament-release-web.tgz
rm filament-web.zip
rm filament.d.ts
cp ../../web/samples/remote.html index.html
cp ../../../web/samples/remote.html index.html
popd
git status
echo ""
echo "All done! Next, make a git commit that updates docs/remote."
echo "All done! Next, commit the changed files"

View File

@@ -139,6 +139,7 @@ set(SRCS
src/details/SwapChain.cpp
src/details/Sync.cpp
src/details/Texture.cpp
src/details/UboManager.cpp
src/details/VertexBuffer.cpp
src/details/View.cpp
src/ds/PerViewDescriptorSetUtils.cpp
@@ -218,6 +219,7 @@ set(PRIVATE_HDRS
src/details/Stream.h
src/details/SwapChain.h
src/details/Texture.h
src/details/UboManager.h
src/details/VertexBuffer.h
src/details/View.h
src/downcast.h
@@ -363,7 +365,7 @@ add_definitions(
# Generate all .filamat: default material, skyboxes, and post-process
# ==================================================================================================
if (CMAKE_CROSSCOMPILING)
if (CMAKE_CROSSCOMPILING OR FILAMENT_IMPORT_PREBUILT_EXECUTABLES)
include(${IMPORT_EXECUTABLES})
endif()
@@ -708,6 +710,13 @@ if (MSVC)
elseif(WEBGL)
# Avoid strict-vtable-pointers here, it is broken in WebAssembly.
set(OPTIMIZATION_FLAGS -fvisibility-inlines-hidden)
elseif(FILAMENT_USING_GCC)
set(OPTIMIZATION_FLAGS
-ffast-math
-fno-finite-math-only
-ffp-contract=fast
-fvisibility-inlines-hidden
)
else()
set(OPTIMIZATION_FLAGS
-ffast-math
@@ -729,6 +738,18 @@ set(LINUX_COMPILER_FLAGS
if (MSVC)
set(FILAMENT_WARNINGS /W3)
elseif (FILAMENT_USING_GCC)
# GCC is not officially supported, so use a weaker set of warnings (incl. not using -Werror).
set(FILAMENT_WARNINGS
-Wall -Wextra-semi -Wundef
-Wno-unused-parameter -Wno-missing-field-initializers
-Wno-unused-but-set-variable -Wno-unused-variable -Wno-unused-function
-Wnon-virtual-dtor -Wimplicit-fallthrough
-Wno-strict-aliasing
-Wno-changes-meaning -Wno-return-type
-Wno-invalid-constexpr -Wno-parentheses
-Wno-sign-compare -Wno-deprecated-declarations
)
else()
set(FILAMENT_WARNINGS
-Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers

View File

@@ -92,7 +92,7 @@ Copy your platform's Makefile below into a `Makefile` inside the same directory.
### Linux
```make
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl -labseil
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -libl -labseil
CC=clang++
main: main.o
@@ -110,7 +110,7 @@ clean:
### macOS
```make
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -lvkshaders -libl -labseil
FILAMENT_LIBS=-lfilament -lbackend -lbluegl -lbluevk -lfilabridge -lfilaflat -lutils -lgeometry -lsmol-v -libl -labseil
FRAMEWORKS=-framework Cocoa -framework Metal -framework CoreVideo
CC=clang++
ARCH ?= $(shell uname -m)
@@ -140,7 +140,7 @@ used to change the run-time library version.
```make
FILAMENT_LIBS=filament.lib backend.lib bluegl.lib bluevk.lib filabridge.lib filaflat.lib \
utils.lib geometry.lib smol-v.lib ibl.lib vkshaders.lib abseil.lib
utils.lib geometry.lib smol-v.lib ibl.lib abseil.lib
CC=cl.exe
main.exe: main.obj

View File

@@ -104,6 +104,10 @@ if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3)
list(APPEND SRCS src/VirtualMachineEnv.cpp)
endif ()
if (ANDROID)
list(APPEND SRCS src/AndroidNdk.cpp)
list(APPEND SRCS src/AndroidNativeWindow.cpp)
list(APPEND SRCS src/AndroidSwapChainHelper.cpp)
list(APPEND SRCS src/AndroidFrameCallback.cpp)
list(APPEND SRCS src/opengl/platforms/ExternalStreamManagerAndroid.cpp)
list(APPEND SRCS src/opengl/platforms/PlatformEGLAndroid.cpp)
elseif (IOS)
@@ -248,10 +252,21 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/utils/Spirv.h
src/vulkan/utils/StaticVector.h
)
if (LINUX OR WIN32)
list(APPEND SRCS src/vulkan/platform/VulkanPlatformLinuxWindows.cpp)
if (LINUX)
list(APPEND SRCS
src/vulkan/platform/VulkanPlatformLinux.cpp
include/backend/platforms/VulkanPlatformLinux.h
)
elseif (WIN32)
list(APPEND SRCS
include/backend/platforms/VulkanPlatformWindows.h
src/vulkan/platform/VulkanPlatformWindows.cpp
)
elseif (APPLE OR IOS)
list(APPEND SRCS src/vulkan/platform/VulkanPlatformApple.mm)
list(APPEND SRCS
include/backend/platforms/VulkanPlatformApple.h
src/vulkan/platform/VulkanPlatformApple.mm
)
elseif (ANDROID)
list(APPEND SRCS
include/backend/platforms/VulkanPlatformAndroid.h
@@ -367,46 +382,6 @@ endif()
add_library(${TARGET}_headers INTERFACE)
target_include_directories(${TARGET}_headers INTERFACE ${PUBLIC_HDR_DIR})
# ==================================================================================================
# Build SPIRV snippets used by the Vulkan backend.
# ==================================================================================================
set(VKSHADERS_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated/vkshaders")
file(MAKE_DIRECTORY ${VKSHADERS_DIR})
get_resgen_vars(${VKSHADERS_DIR} vkshaders)
set(VKSHADER_BINS)
function(build_vkshader SOURCE TARGET_PATH)
set(SOURCE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/src/vulkan/${SOURCE}")
set(VKSHADER_BINS ${VKSHADER_BINS} ${TARGET_PATH} PARENT_SCOPE)
add_custom_command(
OUTPUT ${TARGET_PATH}
COMMAND matc --raw -o ${TARGET_PATH} ${SOURCE_PATH}
MAIN_DEPENDENCY ${SOURCE_PATH}
DEPENDS matc ${SOURCE_PATH}
COMMENT "Building SPIR-V")
endfunction()
build_vkshader("BlitDepth.vs" "BlitDepthVs.spv")
build_vkshader("BlitDepth.fs" "BlitDepthFs.spv")
add_custom_command(
OUTPUT ${RESGEN_OUTPUTS}
COMMAND resgen ${RESGEN_FLAGS} ${VKSHADER_BINS}
DEPENDS resgen ${VKSHADER_BINS}
COMMENT "Aggregating compiled VK shaders")
if (DEFINED RESGEN_SOURCE_FLAGS)
set_source_files_properties(${RESGEN_SOURCE} PROPERTIES COMPILE_FLAGS ${RESGEN_SOURCE_FLAGS})
endif()
set(DUMMY_SRC "${VKSHADERS_DIR}/dummy.c")
add_custom_command(OUTPUT ${DUMMY_SRC} COMMAND echo "//" > ${DUMMY_SRC})
add_library(vkshaders STATIC ${DUMMY_SRC} ${RESGEN_SOURCE})
set_target_properties(vkshaders PROPERTIES FOLDER Filament/Generated)
# ==================================================================================================
# Dependencies
# ==================================================================================================
@@ -440,7 +415,7 @@ if(FILAMENT_SUPPORTS_OPENGL AND NOT IOS AND NOT ANDROID AND NOT WEBGL)
endif()
if (FILAMENT_SUPPORTS_VULKAN)
target_link_libraries(${TARGET} PUBLIC bluevk vkmemalloc vkshaders smol-v)
target_link_libraries(${TARGET} PUBLIC bluevk vkmemalloc smol-v)
target_link_libraries(${TARGET} PRIVATE SPIRV-Headers)
endif()
@@ -469,6 +444,13 @@ if (MSVC)
elseif(WEBGL)
# Avoid strict-vtable-pointers here, it is broken in WebAssembly.
set(OPTIMIZATION_FLAGS -fvisibility-inlines-hidden)
elseif(FILAMENT_USING_GCC)
set(OPTIMIZATION_FLAGS
-ffast-math
-fno-finite-math-only
-ffp-contract=fast
-fvisibility-inlines-hidden
)
else()
set(OPTIMIZATION_FLAGS
-ffast-math
@@ -495,6 +477,13 @@ endif()
if (MSVC)
set(FILAMENT_WARNINGS /W3)
elseif (FILAMENT_USING_GCC)
# GCC is not officially supported, so use a weaker set of warnings (incl. not using -Werror)
set(FILAMENT_WARNINGS
-Wall -Wno-unused-parameter -Wno-missing-field-initializers
-Wdeprecated -Wnon-virtual-dtor -Wno-strict-aliasing
-Wno-invalid-constexpr -Wno-parentheses -Wno-unused-variable
)
else()
set(FILAMENT_WARNINGS
-Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers
@@ -545,7 +534,7 @@ endif()
# ==================================================================================================
set(INSTALL_TYPE ARCHIVE)
install(TARGETS ${TARGET} ${INSTALL_TYPE} DESTINATION lib/${DIST_DIR})
install(TARGETS vkshaders ${INSTALL_TYPE} DESTINATION lib/${DIST_DIR})
install(TARGETS ${INSTALL_TYPE} DESTINATION lib/${DIST_DIR})
install(DIRECTORY ${PUBLIC_HDR_DIR}/backend DESTINATION include)
if (FILAMENT_SUPPORTS_WEBGPU)
@@ -576,6 +565,7 @@ if (APPLE OR LINUX)
test/test_BufferUpdates.cpp
test/test_Callbacks.cpp
test/test_MemoryMappedBuffer.cpp
test/test_MsaaSwapChain.cpp
test/test_MRT.cpp
test/test_PushConstants.cpp
test/test_LoadImage.cpp

View File

@@ -1681,7 +1681,7 @@ static_assert(sizeof(StencilState::StencilOperations) == 5u,
static_assert(sizeof(StencilState) == 12u,
"StencilState size not what was intended");
using FrameScheduledCallback = utils::Invocable<void(backend::PresentCallable)>;
using FrameScheduledCallback = utils::Invocable<void(PresentCallable)>;
enum class Workaround : uint16_t {
// The EASU pass must split because shader compiler flattens early-exit branch
@@ -1707,7 +1707,11 @@ enum class Workaround : uint16_t {
EMULATE_SRGB_SWAPCHAIN,
};
using StereoscopicType = backend::Platform::StereoscopicType;
using StereoscopicType = Platform::StereoscopicType;
using FrameTimestamps = Platform::FrameTimestamps;
using CompositorTiming = Platform::CompositorTiming;
} // namespace filament::backend

View File

@@ -91,6 +91,113 @@ public:
using ExternalImageHandleRef = ExternalImageHandle const&;
struct CompositorTiming {
/** duration in nanosecond since epoch of std::steady_clock */
using time_point_ns = int64_t;
/** duration in nanosecond on the std::steady_clock */
using duration_ns = int64_t;
static constexpr time_point_ns INVALID = -1; //!< value not supported
/**
* The timestamp [ns] since epoch of the next time the compositor will begin composition.
* This is effectively the deadline for when the compositor must receive a newly queued
* frame.
*/
time_point_ns compositeDeadline;
/**
* The time delta [ns] between subsequent composition events.
*/
duration_ns compositeInterval;
/**
* The time delta [ns] between the start of composition and the expected present time of
* that composition. This can be used to estimate the latency of the actual present time.
*/
duration_ns compositeToPresentLatency;
/**
* The timestamp [ns] since epoch of the system's expected presentation time.
* INVALID if not supported.
*/
time_point_ns expectedPresentTime;
/**
* The timestamp [ns] since epoch of the current frame's start (i.e. vsync)
* INVALID if not supported.
*/
time_point_ns frameTime;
/**
* The timestamp [ns] since epoch of the current frame's deadline
* INVALID if not supported.
*/
time_point_ns frameTimelineDeadline;
};
struct FrameTimestamps {
/** duration in nanosecond since epoch of std::steady_clock */
using time_point_ns = int64_t;
static constexpr time_point_ns INVALID = -1; //!< value not supported
static constexpr time_point_ns PENDING = -2; //!< value not yet available
/**
* The time the application requested this frame be presented.
* If the application does not request a presentation time explicitly,
* this will correspond to buffer's queue time.
*/
time_point_ns requestedPresentTime;
/**
* The time when all the application's rendering to the surface was completed.
*/
time_point_ns acquireTime;
/**
* The time when the compositor selected this frame as the one to use for the next
* composition. This is the earliest indication that the frame was submitted in time.
*/
time_point_ns latchTime;
/**
* The first time at which the compositor began preparing composition for this frame.
* Zero if composition was handled by the display and the compositor didn't do any
* rendering.
*/
time_point_ns firstCompositionStartTime;
/**
* The last time at which the compositor began preparing composition for this frame, for
* frames composited more than once. Zero if composition was handled by the display and the
* compositor didn't do any rendering.
*/
time_point_ns lastCompositionStartTime;
/**
* The time at which the compositor's rendering work for this frame finished. This will be
* INVALID if composition was handled by the display and the compositor didn't do any
* rendering.
*/
time_point_ns gpuCompositionDoneTime;
/**
* The time at which this frame started to scan out to the physical display.
*/
time_point_ns displayPresentTime;
/**
* The time when the buffer became available for reuse as a buffer the client can target
* without blocking. This is generally the point when all read commands of the buffer have
* been submitted, but not necessarily completed.
*/
time_point_ns dequeueReadyTime;
/**
* The time at which all reads for the purpose of display/composition were completed for
* this frame.
*/
time_point_ns releaseTime;
};
/**
* The type of technique for stereoscopic rendering. (Note that the materials used will need to
* be compatible with the chosen technique.)
@@ -247,6 +354,65 @@ public:
*/
virtual bool pumpEvents() noexcept;
// --------------------------------------------------------------------------------------------
// Swapchain timing APIs
/**
* Whether this platform supports compositor timing querying.
*
* @return true if this Platform supports compositor timings, false otherwise [default]
* @see queryCompositorTiming()
* @see setPresentFrameId()
* @see queryFrameTimestamps()
*/
virtual bool isCompositorTimingSupported() const noexcept;
/**
* If compositor timing is supported, fills the provided CompositorTiming structure
* with timing information form the compositor the swapchain's native window is using.
* The swapchain'snative window must be valid (i.e. not a headless swapchain).
* @param swapchain to query the compositor timing from
* @return true on success, false otherwise (e.g. if not supported)
* @see isCompositorTimingSupported()
*/
virtual bool queryCompositorTiming(SwapChain const* UTILS_NONNULL swapchain,
CompositorTiming* UTILS_NONNULL outCompositorTiming) const noexcept;
/**
* Associate a generic frameId which must be monotonically increasing (albeit not strictly) with
* the next frame to be presented on the specified swapchain.
*
* This must be called from the backend thread.
*
* @param swapchain
* @param frameId
* @return true on success, false otherwise
* @see isCompositorTimingSupported()
* @see queryFrameTimestamps()
*/
virtual bool setPresentFrameId(SwapChain const* UTILS_NONNULL swapchain,
uint64_t frameId) noexcept;
/**
* If compositor timing is supported, fills the provided FrameTimestamps structure
* with timing information of a given frame, identified by the frame id, of the specified
* swapchain. The system only keeps a limited history of frames timings.
*
* This API is thread safe and can be called from any thread.
*
* @param swapchain swapchain to query the timestamps of
* @param frameId frame we're interested it
* @param outFrameTimestamps output structure receiving the timestamps
* @return true if successful, false otherwise
* @see isCompositorTimingSupported()
* @see setPresentFrameId()
*/
virtual bool queryFrameTimestamps(SwapChain const* UTILS_NONNULL swapchain,
uint64_t frameId, FrameTimestamps* UTILS_NONNULL outFrameTimestamps) const noexcept;
// --------------------------------------------------------------------------------------------
// Caching APIs
/**
* InsertBlobFunc is an Invocable to an application-provided function that a
* backend implementation may use to insert a key/value pair into the
@@ -339,6 +505,9 @@ public:
size_t retrieveBlob(const void* UTILS_NONNULL key, size_t keySize,
void* UTILS_NONNULL value, size_t valueSize);
// --------------------------------------------------------------------------------------------
// Debugging APIs
using DebugUpdateStatFunc = utils::Invocable<void(const char* UTILS_NONNULL key, uint64_t value)>;
/**

View File

@@ -430,7 +430,7 @@ public:
virtual bool isExtraContextSupported() const noexcept;
/**
* Creates an OpenGL context with the same configuration than the main context and makes it
* Creates an OpenGL context with the same configuration as the main context and makes it
* current to the current thread. Must not be called from the main driver thread.
* createContext() is only supported if isExtraContextSupported() returns true.
* These additional contexts will be automatically terminated in terminate.
@@ -443,7 +443,7 @@ public:
/**
* Detach and destroy the current context if any and releases all resources associated to
* this thread.
* this thread. This must be called from the same thread where createContext() was called.
*/
virtual void releaseContext() noexcept;
};

View File

@@ -25,6 +25,7 @@
#include <EGL/eglext.h>
#include <EGL/eglplatform.h>
#include <utils/compiler.h>
#include <utils/Invocable.h>
#include <initializer_list>
@@ -100,10 +101,6 @@ protected:
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) override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool isSwapChainProtected(SwapChain* swapChain) noexcept override;
ContextType getCurrentContextType() const noexcept override;
@@ -158,17 +155,9 @@ protected:
return egl.makeCurrent(context, mEGLDummySurface, mEGLDummySurface);
}
// TODO: this should probably use getters instead.
EGLDisplay mEGLDisplay = EGL_NO_DISPLAY;
EGLContext mEGLContext = EGL_NO_CONTEXT;
EGLContext mEGLContextProtected = EGL_NO_CONTEXT;
EGLSurface mEGLDummySurface = EGL_NO_SURFACE;
ContextType mCurrentContextType = ContextType::NONE;
// mEGLConfig is valid only if ext.egl.KHR_no_config_context is false
EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR;
Config mContextAttribs;
std::vector<EGLContext> mAdditionalContexts;
bool mMSAA4XSupport = false;
EGLDisplay getEglDisplay() const noexcept { return mEGLDisplay; }
EGLConfig getEglConfig() const noexcept { return mEGLConfig; }
EGLConfig getSuitableConfigForSwapChain(uint64_t flags, bool window, bool pbuffer) const;
// supported extensions detected at runtime
struct {
@@ -185,7 +174,11 @@ protected:
} egl;
} ext;
struct SwapChainEGL : public Platform::SwapChain {
struct SwapChainEGL : public SwapChain {
SwapChainEGL(PlatformEGL const& platform, void* nativeWindow, uint64_t flags);
SwapChainEGL(PlatformEGL const& platform, uint32_t width, uint32_t height, uint64_t flags);
void terminate(PlatformEGL& platform);
EGLSurface sur = EGL_NO_SURFACE;
Config attribs{};
EGLNativeWindowType nativeWindow{};
@@ -201,10 +194,25 @@ protected:
~ExternalImageEGL() override;
};
protected:
private:
// prevent derived classes' implementations to call through
[[nodiscard]] SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) override;
[[nodiscard]] SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
EGLConfig findSwapChainConfig(uint64_t flags, bool window, bool pbuffer) const;
private:
EGLDisplay mEGLDisplay = EGL_NO_DISPLAY;
EGLContext mEGLContext = EGL_NO_CONTEXT;
EGLContext mEGLContextProtected = EGL_NO_CONTEXT;
EGLSurface mEGLDummySurface = EGL_NO_SURFACE;
ContextType mCurrentContextType = ContextType::NONE;
// mEGLConfig is valid only if ext.egl.KHR_no_config_context is false
EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR;
Config mContextAttribs;
std::vector<EGLContext> mAdditionalContexts;
bool mMSAA4XSupport = false;
class EGL {
EGLDisplay& mEGLDisplay;
EGLSurface mCurrentDrawSurface = EGL_NO_SURFACE;

View File

@@ -17,7 +17,12 @@
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
#include "AndroidSwapChainHelper.h"
#include "AndroidFrameCallback.h"
#include "AndroidNdk.h"
#include <backend/AcquiredImage.h>
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/platforms/PlatformEGL.h>
@@ -25,6 +30,10 @@
#include <utils/android/PerformanceHintManager.h>
#include <utils/compiler.h>
#include <math/mat3.h>
#include "AndroidNativeWindow.h"
#include <chrono>
#include <stddef.h>
@@ -38,7 +47,7 @@ class ExternalStreamManagerAndroid;
* A concrete implementation of OpenGLPlatform and subclass of PlatformEGL that supports
* EGL on Android. It adds Android streaming functionality to PlatformEGL.
*/
class PlatformEGLAndroid : public PlatformEGL {
class PlatformEGLAndroid : public PlatformEGL, public AndroidNdk {
public:
PlatformEGLAndroid() noexcept;
@@ -67,9 +76,17 @@ public:
* @return `true` on success, `false` on failure. The default implementation
* returns `false`.
*/
bool convertSyncToFd(Platform::Sync* sync, int* fd) noexcept;
bool convertSyncToFd(Sync* sync, int* fd) noexcept;
protected:
struct {
struct {
bool ANDROID_presentation_time = false;
bool ANDROID_get_frame_timestamps = false;
bool ANDROID_native_fence_sync = false;
} egl;
} ext;
// --------------------------------------------------------------------------------------------
// Platform Interface
@@ -80,12 +97,22 @@ protected:
int getOSVersion() const noexcept override;
Driver* createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) override;
const DriverConfig& driverConfig) override;
bool isCompositorTimingSupported() const noexcept override;
bool queryCompositorTiming(SwapChain const* swapchain,
CompositorTiming* outCompositorTiming) const noexcept override;
bool setPresentFrameId(SwapChain const* swapchain, uint64_t frameId) noexcept override;
bool queryFrameTimestamps(SwapChain const* swapchain, uint64_t frameId,
FrameTimestamps* outFrameTimestamps) const noexcept override;
// --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface
struct SyncEGLAndroid : public Platform::Sync {
struct SyncEGLAndroid : public Sync {
EGLSyncKHR sync;
};
@@ -106,8 +133,8 @@ protected:
Stream* createStream(void* nativeStream) noexcept override;
void destroyStream(Stream* stream) noexcept override;
Platform::Sync* createSync() noexcept override;
void destroySync(Platform::Sync* sync) noexcept override;
Sync* createSync() noexcept override;
void destroySync(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;
@@ -120,7 +147,7 @@ protected:
*/
AcquiredImage transformAcquiredImage(AcquiredImage source) noexcept override;
OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override;
ExternalTexture* createExternalImageTexture() noexcept override;
void destroyExternalImageTexture(ExternalTexture* texture) noexcept override;
struct ExternalImageEGLAndroid : public ExternalImageEGL {
@@ -140,12 +167,32 @@ protected:
bool setImage(ExternalImageEGLAndroid const* eglExternalImage,
ExternalTexture* texture) noexcept;
protected:
bool makeCurrent(ContextType type,
SwapChain* drawSwapChain,
SwapChain* readSwapChain) override;
struct SwapChainEGLAndroid : public SwapChainEGL {
SwapChainEGLAndroid(PlatformEGLAndroid const& platform,
void* nativeWindow, uint64_t flags);
SwapChainEGLAndroid(PlatformEGLAndroid const& platform,
uint32_t width, uint32_t height, uint64_t flags);
void terminate(PlatformEGLAndroid& platform);
bool setPresentFrameId(uint64_t frameId) const noexcept;
uint64_t getFrameId(uint64_t frameId) const noexcept;
private:
AndroidSwapChainHelper mImpl{};
};
private:
// prevent derived classes' implementations to call through
[[nodiscard]] SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) override;
[[nodiscard]] SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool isProducerThrottlingControlSupported() const;
int32_t setProducerThrottlingEnabled(EGLNativeWindowType nativeWindow, bool enabled) const;
struct InitializeJvmForPerformanceManagerIfNeeded {
InitializeJvmForPerformanceManagerIfNeeded();
};
@@ -159,13 +206,14 @@ private:
InitializeJvmForPerformanceManagerIfNeeded const mInitializeJvmForPerformanceManagerIfNeeded;
utils::PerformanceHintManager mPerformanceHintManager;
utils::PerformanceHintManager::Session mPerformanceHintSession;
SwapChainEGLAndroid* mCurrentDrawSwapChain{};
using clock = std::chrono::high_resolution_clock;
clock::time_point mStartTimeOfActualWork;
void* mNativeWindowLib = nullptr;
int32_t (*ANativeWindow_getBuffersDefaultDataSpace)(ANativeWindow* window) = nullptr;
AndroidProducerThrottling mProducerThrottling;
bool mAssertNativeWindowIsValid = false;
AndroidFrameCallback mAndroidFrameCallback;
};
} // namespace filament::backend

View File

@@ -26,7 +26,10 @@
#include <backend/DriverEnums.h>
#include <shared_mutex>
#include <thread>
#include <vector>
#include <unordered_map>
namespace filament::backend {
@@ -48,6 +51,10 @@ protected:
void terminate() noexcept override;
bool isExtraContextSupported() const noexcept override;
void createContext(bool shared) override;
void releaseContext() 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;
@@ -60,6 +67,10 @@ private:
GLXFBConfig mGLXConfig{};
GLXPbuffer mDummySurface;
std::vector<GLXPbuffer> mPBuffers;
// Variables for shared contexts
std::unordered_map<std::thread::id, GLXContext> mAdditionalContexts;
std::shared_mutex mAdditionalContextsLock;
};
} // namespace filament::backend

View File

@@ -437,22 +437,19 @@ protected:
std::shared_ptr<VulkanCmdFence> fenceStatus;
};
virtual ExtensionSet getSwapchainInstanceExtensions() const;
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
virtual ExtensionSet getSwapchainInstanceExtensions() const = 0;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept;
uint64_t flags) const noexcept = 0;
virtual VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept;
/**
* Query if transient attachments are supported by the backend.
*/
bool isTransientAttachmentSupported() const noexcept;
private:
// Platform dependent helper methods
static ExtensionSet getSwapchainInstanceExtensionsImpl();
// Platform dependent helper methods
static SurfaceBundle createVkSurfaceKHRImpl(void* nativeWindow, VkInstance instance,
uint64_t flags) noexcept;
friend struct VulkanPlatformPrivate;
};

View File

@@ -17,6 +17,9 @@
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
#include "AndroidFrameCallback.h"
#include "AndroidNdk.h"
#include <backend/DriverEnums.h>
#include <backend/platforms/VulkanPlatform.h>
@@ -24,7 +27,7 @@
namespace filament::backend {
class VulkanPlatformAndroid : public VulkanPlatform {
class VulkanPlatformAndroid : public VulkanPlatform, public AndroidNdk {
public:
ExternalImageHandle UTILS_PUBLIC createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept;
@@ -36,13 +39,17 @@ public:
TextureUsage usage; // Texture usage flags
};
VulkanPlatformAndroid();
~VulkanPlatformAndroid() noexcept override;
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(
ExternalImageHandleRef externalImage) const noexcept;
virtual ExternalImageMetadata extractExternalImageMetadata(
ExternalImageMetadata extractExternalImageMetadata(
ExternalImageHandleRef image) const override;
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const override;
ImageData createVkImageFromExternal(ExternalImageHandleRef image) const override;
/**
* Converts a sync to an external file descriptor, if possible. Accepts an
@@ -53,25 +60,47 @@ public:
* @return `true` on success, `false` on failure. The default implementation
* returns `false`.
*/
bool convertSyncToFd(Platform::Sync* sync, int* fd) const noexcept;
bool convertSyncToFd(Sync* sync, int* fd) const noexcept;
int getOSVersion() const noexcept override;
void terminate() override;
Driver* createDriver(void* sharedContext,
DriverConfig const& driverConfig) override;
bool isCompositorTimingSupported() const noexcept override;
bool queryCompositorTiming(SwapChain const* swapchain,
CompositorTiming* outCompositorTiming) const noexcept override;
bool setPresentFrameId(SwapChain const* swapchain, uint64_t frameId) noexcept override;
bool queryFrameTimestamps(SwapChain const* swapchain, uint64_t frameId,
FrameTimestamps* outFrameTimestamps) const noexcept override;
protected:
virtual ExtensionSet getSwapchainInstanceExtensions() const override;
ExtensionSet getSwapchainInstanceExtensions() const override;
using SurfaceBundle = VulkanPlatform::SurfaceBundle;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
using SurfaceBundle = SurfaceBundle;
SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept override;
virtual VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept override;
VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept override;
private:
struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
struct ExternalImageVulkanAndroid : public ExternalImage {
AHardwareBuffer* aHardwareBuffer = nullptr;
bool sRGB = false;
protected:
~ExternalImageVulkanAndroid() override;
};
AndroidFrameCallback mAndroidFrameCallback;
int mOSVersion{};
};
}// namespace filament::backend

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMAPPLE_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMAPPLE_H
#include <backend/platforms/VulkanPlatform.h>
namespace filament::backend {
class VulkanPlatformApple : public VulkanPlatform {
protected:
ExtensionSet getSwapchainInstanceExtensions() const override;
SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept override;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMAPPLE_H

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMLINUX_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMLINUX_H
#include <backend/platforms/VulkanPlatform.h>
namespace filament::backend {
class VulkanPlatformLinux : public VulkanPlatform {
protected:
ExtensionSet getSwapchainInstanceExtensions() const override;
SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept override;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMLINUX_H

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMWINDOWS_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMWINDOWS_H
#include <backend/platforms/VulkanPlatform.h>
namespace filament::backend {
class VulkanPlatformWindows : public VulkanPlatform {
protected:
ExtensionSet getSwapchainInstanceExtensions() const override;
SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept override;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORMWINDOWS_H

View File

@@ -337,6 +337,7 @@ DECL_DRIVER_API_SYNCHRONOUS_N(int64_t, getStreamTimestamp, backend::StreamHandle
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(backend::FenceStatus, fenceWait, backend::FenceHandle, fh, uint64_t, timeout)
DECL_DRIVER_API_SYNCHRONOUS_N(void, fenceCancel, 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)
@@ -366,6 +367,10 @@ DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage, void*, image)
DECL_DRIVER_API_SYNCHRONOUS_N(backend::TimerQueryResult, getTimerQueryValue, backend::TimerQueryHandle, tqh, uint64_t*, elapsedTime)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isWorkaroundNeeded, backend::Workaround, workaround)
DECL_DRIVER_API_SYNCHRONOUS_0(backend::FeatureLevel, getFeatureLevel)
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getUniformBufferOffsetAlignment)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isCompositorTimingSupported)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, queryFrameTimestamps, backend::SwapChainHandle, swapChain, uint64_t, frameId, backend::FrameTimestamps*, outFrameTimestamps)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, queryCompositorTiming, backend::SwapChainHandle, swapChain, backend::CompositorTiming*, outCompositorTiming)
/*
* Updating driver objects

View File

@@ -169,10 +169,11 @@ public:
* Destroy the object D at Handle<B> and frees Handle<B>
* e.g.:
* Handle<HwTexture> h = ...;
* deallocate(h);
* deallocate<GLTexture>(h);
*/
template<typename D>
void deallocate(Handle<D>& handle) noexcept {
template<typename D, typename B,
typename = std::enable_if_t<std::is_base_of_v<B, D>, D>>
void deallocate(Handle<B>& handle) noexcept {
D const* d = handle_cast<const D*>(handle);
deallocate(handle, d);
}
@@ -208,7 +209,7 @@ public:
HandleBase::HandleId const index = (handle.getId() & HANDLE_INDEX_MASK);
// if we've already handed out this handle index before, it's definitely a
// use-after-free, otherwise it's probably just a corrupted handle
if (index < mId) {
if (index < mId.load(std::memory_order_relaxed)) {
FILAMENT_CHECK_POSTCONDITION(p != nullptr)
<< "use-after-free of heap Handle with id=" << handle.getId()
<< ", tag=" << getHandleTag(handle.getId()).c_str_safe();

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "AndroidFrameCallback.h"
#include <android/choreographer.h>
#include <android/looper.h>
#include <private/utils/Tracing.h>
#include <utils/Panic.h>
#include <utils/debug.h>
#include <utils/JobSystem.h>
#include <atomic>
#include <cstddef>
#include <cstdint>
#include <mutex>
namespace filament::backend {
using namespace utils;
AndroidFrameCallback::AndroidFrameCallback() = default;
AndroidFrameCallback::~AndroidFrameCallback() noexcept {
assert_invariant(!mLooperThread.joinable());
assert_invariant(mLooper == nullptr);
}
void AndroidFrameCallback::init() {
if (__builtin_available(android 33, *)) {
FILAMENT_CHECK_PRECONDITION(!mLooperThread.joinable()) << "init() already called";
// start the looper thread for our choreographer callbacks
mLooperThread = std::thread([this] {
// create the looper for this thread
mLooper = ALooper_prepare(0);
// acquire a reference, so we can use it from our main thread
ALooper_acquire(mLooper);
// set thread name
JobSystem::setThreadName("Filament Choreographer");
// set priority
JobSystem::setThreadPriority(JobSystem::Priority::DISPLAY);
// start the choreographer callbacks
if (__builtin_available(android 33, *)) {
mChoreographer = AChoreographer_getInstance();
// request our first callback for the next frame
AChoreographer_postVsyncCallback(mChoreographer, &vsyncCallback, this);
}
// signal we're ready to run and choreographer and looper are initialized
mInitBarrier.latch();
// our main loop just sits there to handle events
while (true) {
int const result = ALooper_pollOnce(-1, nullptr, nullptr, nullptr);
if (result == ALOOPER_POLL_ERROR || mExitRequested.
load(std::memory_order_relaxed)) {
return; // exit the loop
}
}
});
// wait for the thread and looper to be created and ready to run
mInitBarrier.await();
}
}
void AndroidFrameCallback::terminate() {
if (__builtin_available(android 33, *)) {
// the thread wouldn't be joinable if terminate() is called twice, or init() is not called.
if (mLooperThread.joinable()) {
// request exit
mExitRequested.store(true, std::memory_order_relaxed);
// wake the looper right away
ALooper_wake(mLooper);
// release our reference to the looper
ALooper_release(mLooper);
mLooper = nullptr;
// and make sure to wait for the thread to terminate
mLooperThread.join();
}
}
}
bool AndroidFrameCallback::isSupported() noexcept {
if (__builtin_available(android 33, *)) {
return true;
}
return false;
}
AndroidFrameCallback::Timeline AndroidFrameCallback::getPreferredTimeline() const noexcept {
std::lock_guard const l(mLock);
return mPreferredTimeline;
}
void AndroidFrameCallback::vsyncCallback(const AChoreographerFrameCallbackData* callbackData) {
if (__builtin_available(android 33, *)) {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
// request the next frame callback
AChoreographer_postVsyncCallback(mChoreographer, &vsyncCallback, this);
int64_t const frameTime = AChoreographerFrameCallbackData_getFrameTimeNanos(callbackData);
size_t const preferredIndex =
AChoreographerFrameCallbackData_getPreferredFrameTimelineIndex(callbackData);
int64_t const expectedPresentTime =
AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentationTimeNanos(
callbackData, preferredIndex);
int64_t const frameTimelineDeadline =
AChoreographerFrameCallbackData_getFrameTimelineDeadlineNanos(
callbackData, preferredIndex);
std::lock_guard const l(mLock);
mPreferredTimeline = {
.frameTime = frameTime,
.expectedPresentTime = expectedPresentTime,
.frameTimelineDeadline = frameTimelineDeadline
};
}
}
}

View File

@@ -0,0 +1,82 @@
/*
* 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_ANDROIDFRAMECALLBACK_H
#define TNT_FILAMENT_BACKEND_ANDROIDFRAMECALLBACK_H
#include <android/choreographer.h>
#include <android/looper.h>
#include <utils/CountDownLatch.h>
#include <atomic>
#include <cstdint>
#include <mutex>
#include <thread>
namespace filament::backend {
/*
* AndroidFrameCallback uses a dedicated thread running a Looper and uses
* Choreographer to receive the FrameCallbackData.
*
*/
class AndroidFrameCallback {
public:
struct Timeline {
using timepoint_ns = int64_t;
static constexpr timepoint_ns INVALID = -1;
timepoint_ns frameTime{ INVALID };
timepoint_ns expectedPresentTime{ INVALID };
timepoint_ns frameTimelineDeadline{ INVALID };
};
AndroidFrameCallback();
~AndroidFrameCallback() noexcept;
AndroidFrameCallback(AndroidFrameCallback const&) = delete;
AndroidFrameCallback& operator=(AndroidFrameCallback const&) = delete;
void init();
void terminate();
static bool isSupported() noexcept;
Timeline getPreferredTimeline() const noexcept;
private:
// looper
std::thread mLooperThread;
mutable utils::CountDownLatch mInitBarrier{ 1 };
ALooper* mLooper = nullptr;
std::atomic_bool mExitRequested{ false };
// choreographer
AChoreographer* mChoreographer{};
void vsyncCallback(const AChoreographerFrameCallbackData* callbackData);
static void vsyncCallback(const AChoreographerFrameCallbackData* callbackData, void* data) {
static_cast<AndroidFrameCallback*>(data)->vsyncCallback(callbackData);
}
mutable std::mutex mLock;
Timeline mPreferredTimeline{};
};
} // namespace filament::backend
#endif //TNT_FILAMENT_BACKEND_ANDROIDFRAMECALLBACK_H

View File

@@ -0,0 +1,131 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "AndroidNativeWindow.h"
#include <android/api-level.h>
#include <android/native_window.h>
#include <utils/compiler.h>
#include <utils/Logger.h>
#include <cstdint>
#include <cerrno>
#include <utility>
#include <dlfcn.h>
namespace filament::backend {
std::pair<int, bool> NativeWindow::isValid(ANativeWindow* const anw) noexcept {
#if __ANDROID_API__ >= 26
// libnativewindow.so is not available before API level 26, this means we can't call
// any method above 25 (even protected by __builtin_available()).
if (__builtin_available(android 28, *)) {
// this a proxy for is_valid()
auto const result = ANativeWindow_getBuffersDataSpace(anw);
return { result, result >= 0 };
}
#endif
// fallback on using private APIs
NativeWindow const* pWindow = reinterpret_cast<NativeWindow const*>(anw);
if (UTILS_LIKELY(pWindow->query)) {
int isValid = 0;
int const err = pWindow->query(anw, IS_VALID, &isValid);
return { err, bool(isValid) };
}
return { -EINVAL, false };
}
int NativeWindow::getNextFrameId(ANativeWindow* anw, uint64_t* frameId) {
NativeWindow const* pWindow = reinterpret_cast<NativeWindow const*>(anw);
return pWindow->perform(anw, GET_NEXT_FRAME_ID, frameId);
}
int NativeWindow::enableFrameTimestamps(ANativeWindow* anw, bool enable) {
NativeWindow const* pWindow = reinterpret_cast<NativeWindow const*>(anw);
return pWindow->perform(anw, ENABLE_FRAME_TIMESTAMPS, enable);
}
int NativeWindow::getCompositorTiming(ANativeWindow* anw,
int64_t* compositeDeadline, int64_t* compositeInterval,
int64_t* compositeToPresentLatency) {
NativeWindow const* pWindow = reinterpret_cast<NativeWindow const*>(anw);
return pWindow->perform(anw, GET_COMPOSITOR_TIMING,
compositeDeadline, compositeInterval, compositeToPresentLatency);
}
int NativeWindow::getFrameTimestamps(ANativeWindow* anw,
uint64_t frameId,
int64_t* outRequestedPresentTime, int64_t* outAcquireTime,
int64_t* outLatchTime, int64_t* outFirstRefreshStartTime,
int64_t* outLastRefreshStartTime, int64_t* outGpuCompositionDoneTime,
int64_t* outDisplayPresentTime, int64_t* outDequeueReadyTime,
int64_t* outReleaseTime) {
NativeWindow const* pWindow = reinterpret_cast<NativeWindow const*>(anw);
return pWindow->perform(anw, GET_FRAME_TIMESTAMPS,
frameId, outRequestedPresentTime, outAcquireTime, outLatchTime,
outFirstRefreshStartTime, outLastRefreshStartTime,
outGpuCompositionDoneTime, outDisplayPresentTime,
outDequeueReadyTime, outReleaseTime);
}
AndroidProducerThrottling::AndroidProducerThrottling() {
// note: we don't need to dlclose() mNativeWindowLib here, because the library will be cleaned
// when the process ends and dlopen() are ref-counted. dlclose() NDK documentation documents
// not to call dlclose().
// libnativewindow.so is not available before API level 26, this means we can't call
// any method above 25 (even protected by __builtin_available()).
void* nativeWindowLibHandle = dlopen("libnativewindow.so", RTLD_LOCAL | RTLD_NOW);
if (nativeWindowLibHandle) {
mSetProducerThrottlingEnabled =
(int32_t(*)(ANativeWindow*, bool)) dlsym(nativeWindowLibHandle,
"ANativeWindow_setProducerThrottlingEnabled");
mIsProducerThrottlingEnabled =
(int32_t(*)(ANativeWindow*, bool*)) dlsym(nativeWindowLibHandle,
"ANativeWindow_isProducerThrottlingEnabled");
if (mSetProducerThrottlingEnabled && mIsProducerThrottlingEnabled) {
LOG(INFO) << "Producer Throttling API available";
}
}
}
int32_t AndroidProducerThrottling::setProducerThrottlingEnabled(
ANativeWindow* window, bool enabled) const {
if (mSetProducerThrottlingEnabled) {
return mSetProducerThrottlingEnabled(window, enabled);
}
return -1;
}
int32_t AndroidProducerThrottling::isProducerThrottlingEnabled(
ANativeWindow* window, bool* outEnabled) const {
if (mIsProducerThrottlingEnabled) {
return mIsProducerThrottlingEnabled(window, outEnabled);
}
return -1;
}
bool AndroidProducerThrottling::isSupported() const noexcept {
return mSetProducerThrottlingEnabled && mIsProducerThrottlingEnabled;
}
} // namespace filament::backend

View File

@@ -0,0 +1,79 @@
/*
* 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 FILAMENT_BACKEND_ANDROIDNATIVEWINDOW_H
#define FILAMENT_BACKEND_ANDROIDNATIVEWINDOW_H
#include <android/native_window.h>
#include <cstdint>
#include <utility>
namespace filament::backend {
struct NativeWindow {
// this class can't be instantiated. It's used by casting ANativeWindow* to it.
NativeWindow() noexcept = delete;
~NativeWindow() noexcept = delete;
// is valid query enum value
enum {
IS_VALID = 17,
GET_NEXT_FRAME_ID = 24,
ENABLE_FRAME_TIMESTAMPS = 25,
GET_COMPOSITOR_TIMING = 26,
GET_FRAME_TIMESTAMPS = 27,
};
#if defined(__LP64__)
uint64_t pad[18];
#else
uint32_t pad[21];
#endif
int (*query)(ANativeWindow const*, int, int*);
int (*perform)(ANativeWindow*, int, ...);
// return whether the ANativeWindow is valid
static std::pair<int, bool> isValid(ANativeWindow* anw) noexcept;
static int getNextFrameId(ANativeWindow* anw, uint64_t* frameId);
static int enableFrameTimestamps(ANativeWindow* anw, bool enable);
static int getCompositorTiming(ANativeWindow* anw,
int64_t* compositeDeadline, int64_t* compositeInterval,
int64_t* compositeToPresentLatency);
static int getFrameTimestamps(ANativeWindow* anw,
uint64_t frameId,
int64_t* outRequestedPresentTime, int64_t* outAcquireTime,
int64_t* outLatchTime, int64_t* outFirstRefreshStartTime,
int64_t* outLastRefreshStartTime, int64_t* outGpuCompositionDoneTime,
int64_t* outDisplayPresentTime, int64_t* outDequeueReadyTime,
int64_t* outReleaseTime);
};
struct AndroidProducerThrottling {
AndroidProducerThrottling();
int32_t setProducerThrottlingEnabled(ANativeWindow* window, bool enabled) const;
int32_t isProducerThrottlingEnabled(ANativeWindow* window, bool* outEnabled) const;
bool isSupported() const noexcept;
private:
int32_t (*mSetProducerThrottlingEnabled)(ANativeWindow* window, bool enabled) = nullptr;
int32_t (*mIsProducerThrottlingEnabled)(ANativeWindow* window, bool* outEnabled) = nullptr;
};
} // namespace filament::backend
#endif //FILAMENT_BACKEND_ANDROIDNATIVEWINDOW_H

View File

@@ -0,0 +1,60 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "AndroidNdk.h"
#include <android/hardware_buffer.h>
#include <utils/compiler.h>
#include <mutex>
#include <dlfcn.h>
namespace filament::backend {
UTILS_UNUSED
static std::once_flag sInitOnce{};
template <typename T>
UTILS_UNUSED
static void loadSymbol(void* handle, const char* symbol, T& pfn) {
pfn = T(dlsym(handle, symbol));
}
#if FILAMENT_USE_DLSYM(26)
AndroidNdk::Ndk AndroidNdk::ndk{};
#endif
AndroidNdk::AndroidNdk() {
#if FILAMENT_USE_DLSYM(26)
std::call_once(sInitOnce, [] {
// note: we don't need to dlclose() mNativeWindowLib here, because the library will be cleaned
// when the process ends and dlopen() are ref-counted. dlclose() NDK documentation documents
// not to call dlclose().
if (__builtin_available(android 26, *)) {
void* h = dlopen("libnativewindow.so", RTLD_LOCAL | RTLD_NOW);
if (h) {
loadSymbol(h, "AHardwareBuffer_acquire", ndk.AHardwareBuffer_acquire);
loadSymbol(h, "AHardwareBuffer_release", ndk.AHardwareBuffer_release);
loadSymbol(h, "AHardwareBuffer_describe", ndk.AHardwareBuffer_describe);
}
}
});
#endif
}
} // filament::backend

View File

@@ -0,0 +1,79 @@
/*
* 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 FILAMENT_BACKEND_ANDROIDNDK_H
#define FILAMENT_BACKEND_ANDROIDNDK_H
#include <android/native_window.h>
#include <android/hardware_buffer.h>
#define FILAMENT_REQUIRES_API(x) __attribute__((__availability__(android,introduced=x)))
#define FILAMENT_USE_DLSYM(api) (__ANDROID_API__ < (api))
namespace filament::backend {
class AndroidNdk {
public:
AndroidNdk();
#if FILAMENT_USE_DLSYM(26)
static void AHardwareBuffer_acquire(
AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) {
ndk.AHardwareBuffer_acquire(buffer);
}
static void AHardwareBuffer_release(
AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) {
ndk.AHardwareBuffer_release(buffer);
}
static void AHardwareBuffer_describe(
AHardwareBuffer const* buffer, AHardwareBuffer_Desc* desc) FILAMENT_REQUIRES_API(26) {
ndk.AHardwareBuffer_describe(buffer, desc);
}
#else
static void AHardwareBuffer_acquire(
AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) {
::AHardwareBuffer_acquire(buffer);
}
static void AHardwareBuffer_release(
AHardwareBuffer* buffer) FILAMENT_REQUIRES_API(26) {
::AHardwareBuffer_release(buffer);
}
static void AHardwareBuffer_describe(
AHardwareBuffer const* buffer, AHardwareBuffer_Desc* desc) FILAMENT_REQUIRES_API(26) {
::AHardwareBuffer_describe(buffer, desc);
}
#endif
private:
#if FILAMENT_USE_DLSYM(26)
static struct Ndk {
void (*AHardwareBuffer_acquire)(AHardwareBuffer*);
void (*AHardwareBuffer_release)(AHardwareBuffer*);
void (*AHardwareBuffer_describe)(AHardwareBuffer const*, AHardwareBuffer_Desc*);
} ndk;
#endif
};
} // filament::backend
#endif //FILAMENT_BACKEND_ANDROIDNDK_H

View File

@@ -0,0 +1,59 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "AndroidSwapChainHelper.h"
#include "AndroidNativeWindow.h"
#include <android/native_window.h>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <mutex>
namespace filament::backend {
AndroidSwapChainHelper::AndroidSwapChainHelper() = default;
AndroidSwapChainHelper::~AndroidSwapChainHelper() noexcept = default;
bool AndroidSwapChainHelper::setPresentFrameId(
ANativeWindow* anw, uint64_t const frameId) const noexcept {
uint64_t sysFrameId{};
int const status = NativeWindow::getNextFrameId(anw, &sysFrameId);
if (status == 0) {
std::lock_guard const lock(mLock);
auto const pos = mFrameIdToSystemFrameId.find(frameId);
if (pos && *pos != sysFrameId) {
// we're trying to associate the same frame id to a different frame!
return false;
}
// oldest entry is removed
mFrameIdToSystemFrameId.insert(frameId, sysFrameId);
return true;
}
return false;
}
uint64_t AndroidSwapChainHelper::getFrameId(uint64_t const frameId) const noexcept {
std::lock_guard const lock(mLock);
if (auto const* const pos = mFrameIdToSystemFrameId.find(frameId)) {
return *pos;
}
return std::numeric_limits<uint64_t>::max();
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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_ANDROIDSWAPCHAINHELPER_H
#define TNT_FILAMENT_BACKEND_ANDROIDSWAPCHAINHELPER_H
#include <utils/Mutex.h>
#include <utils/MonotonicRingMap.h>
#include <android/native_window.h>
#include <map>
#include <cstddef>
#include <cstdint>
namespace filament::backend {
struct AndroidSwapChainHelper {
AndroidSwapChainHelper();
~AndroidSwapChainHelper() noexcept;
// methods below are thread-safe
bool setPresentFrameId(ANativeWindow* anw, uint64_t frameId) const noexcept;
uint64_t getFrameId(uint64_t frameId) const noexcept;
private:
static constexpr size_t MAX_HISTORY_SIZE = 32;
mutable utils::Mutex mLock; // very low-contention lock
mutable utils::MonotonicRingMap<MAX_HISTORY_SIZE, uint64_t, uint64_t> mFrameIdToSystemFrameId{};
};
}
#endif //TNT_FILAMENT_BACKEND_ANDROIDSWAPCHAINHELPER_H

View File

@@ -25,8 +25,11 @@ TextureFormat mapToFilamentFormat(unsigned int format, bool isSrgbTransfer) noex
switch (format) {
case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
return TextureFormat::SRGB8;
default:
case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
return TextureFormat::SRGB8_A8;
default:
break;
}
}
switch (format) {

View File

@@ -108,7 +108,7 @@ template <size_t P0, size_t P1, size_t P2>
UTILS_NOINLINE
void* HandleAllocator<P0, P1, P2>::handleToPointerSlow(HandleBase::HandleId id) const noexcept {
auto& overflowMap = mOverflowMap;
std::lock_guard lock(mLock);
std::lock_guard const lock(mLock);
auto pos = overflowMap.find(id);
if (pos != overflowMap.end()) {
return pos.value();
@@ -119,14 +119,15 @@ void* HandleAllocator<P0, P1, P2>::handleToPointerSlow(HandleBase::HandleId id)
template <size_t P0, size_t P1, size_t P2>
HandleBase::HandleId HandleAllocator<P0, P1, P2>::allocateHandleSlow(size_t size) {
void* p = ::malloc(size);
std::unique_lock lock(mLock);
HandleBase::HandleId id = (++mId) | HANDLE_HEAP_FLAG;
FILAMENT_CHECK_POSTCONDITION(mId < HANDLE_HEAP_FLAG) <<
auto const nextId = mId.fetch_add(1, std::memory_order_relaxed) + 1;
FILAMENT_CHECK_POSTCONDITION(nextId < HANDLE_HEAP_FLAG) <<
"No more Handle ids available! This can happen if HandleAllocator arena has been full"
" for a while. Please increase FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB";
HandleBase::HandleId id = nextId | HANDLE_HEAP_FLAG;
std::unique_lock lock(mLock);
mOverflowMap.emplace(id, p);
lock.unlock();

View File

@@ -122,6 +122,22 @@ bool Platform::pumpEvents() noexcept {
return false;
}
bool Platform::isCompositorTimingSupported() const noexcept {
return false;
}
bool Platform::queryCompositorTiming(SwapChain const*, CompositorTiming*) const noexcept {
return false;
}
bool Platform::setPresentFrameId(SwapChain const*, uint64_t) noexcept {
return false;
}
bool Platform::queryFrameTimestamps(SwapChain const*, uint64_t, FrameTimestamps*) const noexcept {
return false;
}
void Platform::setBlobFunc(InsertBlobFunc&& insertBlob, RetrieveBlobFunc&& retrieveBlob) noexcept {
mInsertBlob = std::move(insertBlob);
mRetrieveBlob = std::move(retrieveBlob);

View File

@@ -67,8 +67,12 @@
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
#if defined(__ANDROID__)
#include "backend/platforms/VulkanPlatformAndroid.h"
#else
#include "backend/platforms/VulkanPlatform.h"
#elif defined(__APPLE__)
#include "backend/platforms/VulkanPlatformApple.h"
#elif defined(__linux__)
#include "backend/platforms/VulkanPlatformLinux.h"
#elif defined(WIN32)
#include "backend/platforms/VulkanPlatformWindows.h"
#endif
#endif
@@ -117,8 +121,14 @@ Platform* PlatformFactory::create(Backend* backend) noexcept {
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
#if defined(__ANDROID__)
return new VulkanPlatformAndroid();
#elif defined(__APPLE__)
return new VulkanPlatformApple();
#elif defined(__linux__)
return new VulkanPlatformLinux();
#elif defined(WIN32)
return new VulkanPlatformWindows();
#else
return new VulkanPlatform();
return nullptr;
#endif
#else
return nullptr;

View File

@@ -19,6 +19,7 @@
#include "MetalContext.h"
#include "MetalFlags.h"
#include "MetalUtils.h"
#include <backend/DriverEnums.h>
#include <backend/platforms/PlatformMetal.h>
@@ -260,18 +261,8 @@ public:
// In practice, MetalRingBuffer is used for argument buffers, which are kept in the constant
// address space. Constant buffers have specific alignment requirements when specifying an
// offset.
#if defined(FILAMENT_IOS)
#if TARGET_OS_SIMULATOR
// The iOS simulator has differing alignment requirements.
static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 256;
#else
static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 4;
#endif // TARGET_OS_SIMULATOR
#else
static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 32;
#endif
static inline auto computeSlotSize(MTLSizeAndAlign layout) {
return align(align(layout.size, layout.align), METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT);
return align(align(layout.size, layout.align), getUniformBufferOffsetAlignment());
}
MetalRingBuffer(id<MTLDevice> device, MTLResourceOptions options, MTLSizeAndAlign layout,

View File

@@ -95,6 +95,38 @@ void initializeSupportedGpuFamilies(MetalContext* context) {
}
}
void logMTLCommandBufferError(MTLCommandBufferError error) {
#define MTL_COMMAND_ERROR_CASE(ERR) \
if (error == (ERR)) { \
LOG(ERROR) << "Filament Metal error: " #ERR "."; \
return; \
}
#if !defined(FILAMENT_IOS)
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorDeviceRemoved)
#endif
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorNone)
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorInternal)
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorTimeout)
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorPageFault)
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorAccessRevoked)
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorNotPermitted)
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorOutOfMemory)
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorInvalidResource)
if (@available(macOS 11.0, *)) {
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorMemoryless)
}
if (@available(iOS 15.0, macOS 12.0, *)) {
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorStackOverflow)
}
LOG(ERROR) << "Filament Metal unknown error.";
#undef MTL_COMMAND_ERROR_CASE
}
id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
if (context->pendingCommandBuffer) {
return context->pendingCommandBuffer;
@@ -120,8 +152,7 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
}
if (UTILS_UNLIKELY(errorCode != MTLCommandBufferErrorNone)) {
LOG(ERROR) << "Filament Metal command buffer errored with code: " << errorCode << " ("
<< stringifyMTLCommandBufferError(errorCode) << ").";
logMTLCommandBufferError(errorCode);
}
}];
FILAMENT_CHECK_POSTCONDITION(context->pendingCommandBuffer)

View File

@@ -29,6 +29,7 @@
#include "MetalHandles.h"
#include "MetalState.h"
#include "MetalTimerQuery.h"
#include "MetalUtils.h"
#include <backend/platforms/PlatformMetal.h>
#include <backend/platforms/PlatformMetal-ObjC.h>
@@ -620,7 +621,9 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
auto& sc = mContext->sampleCountLookup;
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
MetalRenderTarget::Attachment colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {{}};
using AttachmentInfo = MetalRenderTarget::AttachmentInfo;
AttachmentInfo colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = { {} };
for (size_t i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
if (none(targetBufferFlags & getTargetBufferFlagsAt(i))) {
continue;
@@ -634,7 +637,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
colorAttachments[i] = { colorTexture, color[i].level, color[i].layer };
}
MetalRenderTarget::Attachment depthAttachment = {};
AttachmentInfo depthAttachment = {};
if (any(targetBufferFlags & TargetBufferFlags::DEPTH)) {
FILAMENT_CHECK_PRECONDITION(depth.handle)
<< "The DEPTH flag was specified, but invalid depth handle provided.";
@@ -644,7 +647,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
depthAttachment = { depthTexture, depth.level, depth.layer };
}
MetalRenderTarget::Attachment stencilAttachment = {};
AttachmentInfo stencilAttachment = {};
if (any(targetBufferFlags & TargetBufferFlags::STENCIL)) {
FILAMENT_CHECK_PRECONDITION(stencil.handle)
<< "The STENCIL flag was specified, but invalid stencil handle provided.";
@@ -668,8 +671,6 @@ void MetalDriver::createFenceR(Handle<HwFence> fh, utils::ImmutableCString&& tag
void MetalDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags,
utils::ImmutableCString&& 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);
@@ -1038,19 +1039,27 @@ void MetalDriver::updateStreams(DriverApi* driver) {
void MetalDriver::destroyFence(Handle<HwFence> fh) {
if (fh) {
// note: it's invalid to call this during a fenceWait(fh) on another thread. For this
// reason there is no point signaling the waiters. There should be no waiters.
destruct_handle<MetalFence>(fh);
}
}
void MetalDriver::fenceCancel(FenceHandle const fh) {
// Even though this is a synchronous call, the fence handle must be (and stay) valid
assert_invariant(fh);
auto* fence = handle_cast<MetalFence>(fh);
fence->cancel();
}
FenceStatus MetalDriver::getFenceStatus(Handle<HwFence> fh) {
return fenceWait(fh, 0);
}
FenceStatus MetalDriver::fenceWait(FenceHandle fh, uint64_t const timeout) {
// Even though this is a synchronous call, the fence handle must be (and stay) valid
assert_invariant(fh);
auto* fence = handle_cast<MetalFence>(fh);
if (!fence) {
return FenceStatus::ERROR;
}
return fence->wait(timeout);
}
@@ -1149,8 +1158,7 @@ bool MetalDriver::isSRGBSwapChainSupported() {
}
bool MetalDriver::isMSAASwapChainSupported(uint32_t) {
// TODO: support MSAA swapchain
return false;
return true;
}
bool MetalDriver::isProtectedContentSupported() {
@@ -1252,6 +1260,10 @@ size_t MetalDriver::getMaxArrayTextureLayers() {
return 256;
}
size_t MetalDriver::getUniformBufferOffsetAlignment() {
return ::filament::backend::getUniformBufferOffsetAlignment();
}
void MetalDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& data,
uint32_t byteOffset) {
FILAMENT_CHECK_PRECONDITION(data.buffer)
@@ -1470,6 +1482,20 @@ void MetalDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, Primit
primitive->type = pt;
}
bool MetalDriver::isCompositorTimingSupported() {
return false;
}
bool MetalDriver::queryCompositorTiming(backend::SwapChainHandle swapChain,
CompositorTiming* outCompositorTiming) {
return false;
}
bool MetalDriver::queryFrameTimestamps(SwapChainHandle swapChain, uint64_t frameId,
FrameTimestamps* outFrameTimestamps) {
return false;
}
void MetalDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain> schRead) {
ASSERT_PRECONDITION_NON_FATAL(schDraw, "A draw SwapChain must be set.");
auto* drawSwapChain = handle_cast<MetalSwapChain>(schDraw);
@@ -1556,9 +1582,9 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
auto srcTarget = handle_cast<MetalRenderTarget>(src);
// We always readPixels from the COLOR0 attachment.
MetalRenderTarget::Attachment color = srcTarget->getDrawColorAttachment(0);
MetalAttachment color = srcTarget->getDrawColorAttachment(0);
id<MTLTexture> srcTexture = color.getTexture();
size_t miplevel = color.level;
size_t miplevel = color.getLevel();
// Clamp height and width to actual texture's height and width
MTLSize srcTextureSize = MTLSizeMake(srcTexture.width >> miplevel, srcTexture.height >> miplevel, 1);
@@ -1766,8 +1792,8 @@ void MetalDriver::blitDEPRECATED(TargetBufferFlags buffers,
};
// We always blit from/to the COLOR0 attachment.
MetalRenderTarget::Attachment const srcColorAttachment = srcTarget->getReadColorAttachment(0);
MetalRenderTarget::Attachment const dstColorAttachment = dstTarget->getDrawColorAttachment(0);
MetalAttachment const srcColorAttachment = srcTarget->getReadColorAttachment(0);
MetalAttachment const dstColorAttachment = dstTarget->getDrawColorAttachment(0);
if (srcColorAttachment && dstColorAttachment) {
FILAMENT_CHECK_PRECONDITION(
@@ -1779,13 +1805,13 @@ void MetalDriver::blitDEPRECATED(TargetBufferFlags buffers,
args.filter = filter;
args.source.region = srcTarget->getRegionFromClientRect(srcRect);
args.source.texture = srcColorAttachment.getTexture();
args.source.level = srcColorAttachment.level;
args.source.slice = srcColorAttachment.layer;
args.source.level = srcColorAttachment.getLevel();
args.source.slice = srcColorAttachment.getLayer();
args.destination.region = dstTarget->getRegionFromClientRect(dstRect);
args.destination.texture = dstColorAttachment.getTexture();
args.destination.level = dstColorAttachment.level;
args.destination.slice = dstColorAttachment.layer;
args.destination.level = dstColorAttachment.getLevel();
args.destination.slice = dstColorAttachment.getLayer();
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "blitDEPRECATED");
}
@@ -1821,24 +1847,28 @@ void MetalDriver::bindPipeline(PipelineState const& ps) {
auto [fragment, vertex] = functions.getRasterFunctions();
// Pipeline state
MetalRenderTarget* const rt = mContext->currentRenderTarget;
MTLPixelFormat colorPixelFormat[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = { MTLPixelFormatInvalid };
for (size_t i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
const auto& attachment = mContext->currentRenderTarget->getDrawColorAttachment(i);
const auto& attachment = rt->getDrawColorAttachment(i);
if (!attachment) {
continue;
}
colorPixelFormat[i] = attachment.getPixelFormat();
assert_invariant(attachment.getSampleCount() == rt->getSampleCount());
}
MTLPixelFormat depthPixelFormat = MTLPixelFormatInvalid;
const auto& depthAttachment = mContext->currentRenderTarget->getDepthAttachment();
const auto& depthAttachment = rt->getDepthAttachment();
if (depthAttachment) {
depthPixelFormat = depthAttachment.getPixelFormat();
assert_invariant(depthAttachment.getSampleCount() == rt->getSampleCount());
}
MTLPixelFormat stencilPixelFormat = MTLPixelFormatInvalid;
const auto& stencilAttachment = mContext->currentRenderTarget->getStencilAttachment();
const auto& stencilAttachment = rt->getStencilAttachment();
if (stencilAttachment) {
stencilPixelFormat = stencilAttachment.getPixelFormat();
assert_invariant(isMetalFormatStencil(stencilPixelFormat));
assert_invariant(stencilAttachment.getSampleCount() == rt->getSampleCount());
}
MetalPipelineState const pipelineState {
.vertexFunction = vertex,
@@ -1856,7 +1886,7 @@ void MetalDriver::bindPipeline(PipelineState const& ps) {
},
.depthAttachmentPixelFormat = depthPixelFormat,
.stencilAttachmentPixelFormat = stencilPixelFormat,
.sampleCount = mContext->currentRenderTarget->getSamples(),
.sampleCount = rt->getSampleCount(),
.blendState = BlendState {
.alphaBlendOperation = getMetalBlendOperation(rs.blendEquationAlpha),
.rgbBlendOperation = getMetalBlendOperation(rs.blendEquationRGB),

View File

@@ -451,38 +451,6 @@ inline MTLTextureSwizzleChannels getSwizzleChannels(TextureSwizzle r, TextureSwi
getSwizzle(a));
}
inline const char* stringifyMTLCommandBufferError(MTLCommandBufferError error) {
#if !defined(FILAMENT_IOS)
if (error == MTLCommandBufferErrorDeviceRemoved) {
return "MTLCommandBufferErrorDeviceRemoved";
}
#endif
switch (error) {
case MTLCommandBufferErrorNone:
return "MTLCommandBufferErrorNone";
case MTLCommandBufferErrorInternal:
return "MTLCommandBufferErrorInternal";
case MTLCommandBufferErrorTimeout:
return "MTLCommandBufferErrorTimeout";
case MTLCommandBufferErrorPageFault:
return "MTLCommandBufferErrorPageFault";
case MTLCommandBufferErrorAccessRevoked:
return "MTLCommandBufferErrorAccessRevoked";
case MTLCommandBufferErrorNotPermitted:
return "MTLCommandBufferErrorNotPermitted";
case MTLCommandBufferErrorOutOfMemory:
return "MTLCommandBufferErrorOutOfMemory";
case MTLCommandBufferErrorInvalidResource:
return "MTLCommandBufferErrorInvalidResource";
case MTLCommandBufferErrorMemoryless:
return "MTLCommandBufferErrorMemoryless";
case MTLCommandBufferErrorStackOverflow:
return "MTLCommandBufferErrorStackOverflow";
default:
return "Unknown";
}
}
} // namespace backend
} // namespace filament

View File

@@ -49,6 +49,61 @@
namespace filament {
namespace backend {
class MetalAttachment {
public:
MetalAttachment() = default;
MetalAttachment(id<MTLTexture> texture, uint8_t level = 0, uint16_t layer = 0)
: mLevel(level),
mLayer(layer),
mTexture(texture) {
assert_invariant(texture);
}
explicit operator bool() const { return mTexture != nil; }
id<MTLTexture> getTexture() const { return mTexture; }
id<MTLTexture> getMsaaTexture() const { return mMsaaTexture; }
MTLPixelFormat getPixelFormat() const {
return mTexture ? mTexture.pixelFormat : MTLPixelFormatInvalid;
}
NSUInteger getSampleCount() const {
if (mMsaaTexture) {
return mMsaaTexture.sampleCount;
}
if (mTexture) {
return mTexture.sampleCount;
}
return 1u;
}
uint8_t getLevel() const { return mLevel; }
uint16_t getLayer() const { return mLayer; }
MetalAttachment withMsaaTexture(id<MTLTexture> msaa) const {
assert_invariant(mTexture != nil);
assert_invariant(mTexture.sampleCount == 1u);
MetalAttachment result = *this;
result.mMsaaTexture = msaa;
return result;
}
static MetalAttachment invalidAttachment() { return MetalAttachment(); }
private:
uint8_t mLevel = 0;
uint16_t mLayer = 0;
// The main texture for this Attachment. May be single-sampled or MSAA.
id<MTLTexture> mTexture = nil;
// The MSAA "sidecar" texture that will resolve into mTexture.
// If this is non-nil, mTexture must be single-sampled.
id<MTLTexture> mMsaaTexture = nil;
};
class MetalSwapChain : public HwSwapChain {
public:
@@ -66,14 +121,14 @@ public:
~MetalSwapChain();
// Acquires a texture that can be used to render into this SwapChain.
// The texture source depends on the type of SwapChain:
// Acquires a texture that can be used to render into this SwapChain and returns a
// MetalAttachment. The texture source depends on the type of SwapChain:
// - CAMetalLayer-backed: acquires the CAMetalDrawable and returns its texture.
// - Headless: lazily creates and returns a headless texture.
id<MTLTexture> acquireDrawable();
id<MTLTexture> acquireDepthTexture();
id<MTLTexture> acquireStencilTexture();
// May return an invalid attachemnt if a drawable cannot be acquired.
MetalAttachment acquireDrawable();
MetalAttachment acquireDepthTexture();
MetalAttachment acquireStencilTexture();
void releaseDrawable();
@@ -88,6 +143,7 @@ public:
NSUInteger getSurfaceWidth() const;
NSUInteger getSurfaceHeight() const;
NSUInteger getSampleCount() const;
bool isPixelBuffer() const { return type == SwapChainType::CVPIXELBUFFERREF; }
@@ -106,13 +162,24 @@ private:
void scheduleFrameScheduledCallback();
void scheduleFrameCompletedCallback();
MetalAttachment acquireBaseDrawable();
id<MTLTexture> ensureDepthStencilTexture(uint32_t width, uint32_t height);
id<MTLTexture> ensureMsaaColorTexture(MTLPixelFormat format, uint32_t width, uint32_t height,
uint8_t samples);
id<MTLTexture> ensureMsaaDepthStencilTexture(MTLPixelFormat format, uint32_t width,
uint32_t height, uint8_t samples);
static MTLPixelFormat decideDepthStencilFormat(uint64_t flags);
void ensureDepthStencilTexture();
static id<MTLTexture> createMultisampledTexture(MetalContext const& context,
MTLPixelFormat format, uint32_t width, uint32_t height, uint8_t samples);
MetalContext& context;
PlatformMetal& platform;
id<CAMetalDrawable> drawable = nil;
id<MTLTexture> depthStencilTexture = nil;
id<MTLTexture> msaaColor = nil;
id<MTLTexture> msaaDepthStencil = nil;
id<MTLTexture> headlessDrawable = nil;
MTLPixelFormat depthStencilFormat = MTLPixelFormatInvalid;
NSUInteger headlessWidth = 0;
@@ -121,6 +188,7 @@ private:
std::shared_ptr<std::mutex> layerDrawableMutex;
MetalExternalImage externalImage;
SwapChainType type;
uint64_t flags;
int64_t abandonedUntilFrame = -1;
@@ -307,51 +375,15 @@ private:
class MetalRenderTarget : public HwRenderTarget {
public:
class Attachment {
public:
friend class MetalRenderTarget;
Attachment() = default;
Attachment(MetalTexture* metalTexture, uint8_t level = 0, uint16_t layer = 0) :
level(level), layer(layer),
texture(metalTexture->getMtlTextureForWrite()),
metalTexture(metalTexture) { }
id<MTLTexture> getTexture() const {
return texture;
}
NSUInteger getSampleCount() const {
return texture ? texture.sampleCount : 0u;
}
MTLPixelFormat getPixelFormat() const {
return texture ? texture.pixelFormat : MTLPixelFormatInvalid;
}
explicit operator bool() const {
return texture != nil;
}
struct AttachmentInfo {
MetalTexture* texture = nullptr;
uint8_t level = 0;
uint16_t layer = 0;
private:
id<MTLTexture> getMSAASidecarTexture() const {
// This should only be called from render targets associated with a MetalTexture.
assert_invariant(metalTexture);
return metalTexture->msaaSidecar;
}
id<MTLTexture> texture = nil;
MetalTexture* metalTexture = nullptr;
};
MetalRenderTarget(MetalContext* context, uint32_t width, uint32_t height, uint8_t samples,
Attachment colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
Attachment depthAttachment, Attachment stencilAttachment);
AttachmentInfo colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
AttachmentInfo depthAttachment, AttachmentInfo stencilAttachment);
explicit MetalRenderTarget(MetalContext* context)
: HwRenderTarget(0, 0), context(context), defaultRenderTarget(true) {}
@@ -387,12 +419,14 @@ public:
math::uint2 getAttachmentSize() noexcept;
uint8_t getSamples() const { return samples; }
MetalAttachment getDrawColorAttachment(size_t index);
MetalAttachment getReadColorAttachment(size_t index);
MetalAttachment getDepthAttachment();
MetalAttachment getStencilAttachment();
Attachment getDrawColorAttachment(size_t index);
Attachment getReadColorAttachment(size_t index);
Attachment getDepthAttachment();
Attachment getStencilAttachment();
// Returns the number of samples that should be used in the pipeline state that renders to this
// render target. Takes into account "automagic" resolve and MSAA SwapChains.
NSUInteger getSampleCount() const;
private:
@@ -402,12 +436,12 @@ private:
MetalContext* context;
bool defaultRenderTarget = false;
uint8_t samples = 1;
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
Attachment depth = {};
Attachment stencil = {};
MetalAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
MetalAttachment depth = {};
MetalAttachment stencil = {};
math::uint2 attachmentSize = {};
uint8_t samples = 1;
};
// MetalFence is used to implement both Fences and Syncs.
@@ -429,6 +463,8 @@ public:
API_AVAILABLE(ios(12.0))
void onSignal(MetalFenceSignalBlock block);
void cancel();
private:
MetalContext& context;

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