Compare commits

...
Author SHA1 Message Date
Sungun Park 23e1779f1e Fix a compile error
FMaterial accesses a private static method of the MaterialDefinition
class, which incurs a compile error for certain compilers.
2025-09-24 12:07:40 -07:00
Powei Feng 7663ca8dfe Release Filament 1.65.2 2025-09-24 11:53:16 -07:00
Powei Feng ee51c054b9 gl: disable fb fetch for llvmpipe (#9240) 2025-09-24 11:47:19 -07:00
Mathias Agopian 884b7590cb Update remote UI (#9247) 2025-09-24 09:53:49 -07:00
Powei Feng decf316802 Make check-headers test use c++20 2025-09-23 21:28:37 -07:00
Eliza 5516cd92e7 materials: introduce MaterialCache (#9205)
* materials: introduce MaterialCache

Presently, Filament Materials are instantiated by first parsing a bunch of
read-only data from a material file, then applying a bunch of options from its
Builder before settling on a final, immutable Material object. If two different
Material instances need to be parameterized differently, e.g. setting their spec
constants independently, each has to do all of these steps independently for
each variation.

This change introduces two new concepts: MaterialDefinition, representing the
deserialized, read-only state of a material file, and MaterialCache, a
reference-counted system responsible for managing the lifetimes of
MaterialDefinitions. Now, each Material asks the cache if a MaterialDefinition
exists for the particular UUID of the data it's trying to read; if not,
MaterialCache creates a new entry transparently. If a hundred different
Materials all try to load the same material data, only one
MaterialDefinition (and its associated GPU resources) will be created.

This first PR is the least possible invasive implementation of this feature.
There are a lot of room for improvements (and more planned). For example, each
Material still manages its own compiled shader program cache, but we can easily
move this to the MaterialCache in future PRs, further enabling the planned
mutable spec constants feature.

Additionally, there's room here to add a Material::toBuilder() method, which
could take an extant material and create a Builder object from it already
parameterized with all of the same options, a la the prototype design pattern.

* material cache: key on crc32

* material cache: make materialParser private

* move RefCountedMap to utils and add unit tests

* material cache: make create functions private

* material cache: fix broken tests on iOS/web

* material cache: address more comments
2025-09-24 00:35:59 +00:00
Mathias Agopian 77d6092890 Fix build. broken due to a bad merge (#9249) 2025-09-23 17:14:28 -07:00
Mathias Agopian 4ad619439a use RG32UI for picking instead of RG32F (#8917)
The reason for this is that according to the GLES 3.0 spec, RG32UI is 
guaranteed to be supported for writing, unlike RG32F.

We also use RGBA_INTEGER / UINT for readPixels() which is also a
guaranteed format.

Attempt to Fix #8762
2025-09-23 16:20:57 -07:00
Mathias Agopian 143d59a991 add support for memory mapped buffers in the backend (#9217)
* reorder backend methods

* add new "shared" buffer APIs to backends

We add the equivalent of GL's glMapBufferRange. The new APIs are:
- new "SHARED" flags when creating a BufferObject 
- a new HwMemoryMappedBuffer handle
- mapBuffer() maps a range of the buffer to memory
- unmapBuffer() unmaps the range
- copyToMemoryMappedBuffer() is basically a deferred memcpy, where the
  destination is a HwMemoryMappedBuffer and the source is a
  BufferDecriptor

The actual implementation doesn't have to actually map/unmap the range
on mapBuffer/unmapBuffer. e.g. on Vulkan these method are used mostly
to set offsets (for copyToMemoryMappedBuffer) and trigger the necessary
synchronizations.

Added some basic unit tests.

* add basic support for MemoryMappedBuffer in vk and metal

we implement copyToMemoryMappedBuffer() by calling updateBufferObject(),
which is enough to satisfy the API (And pass the tests).
2025-09-23 16:15:28 -07:00
Mathias Agopian 2a62518e5c imgui: new readme and update script 2025-09-23 16:13:37 -07:00
Powei Feng edb17200d0 android: fix hello-cam sample appID
Make the application id and namespace consistent.
2025-09-23 16:10:39 -07:00
Ben Doherty 2afd0bd7f7 Metal: add debug labels to Metal resources (#9187) 2025-09-23 10:57:15 -07:00
Ben Doherty bf2459540e Add Java bindings for setFrameScheduledCallback (#9182) 2025-09-22 18:40:42 -07:00
Powei Feng 09bb9828a2 Update .gitignore for renderdiff deps (#9242) 2025-09-22 21:40:20 +00:00
Sungun Park 8fda29657a Cache isMSAASwapChainSupported() result for EGL (#9235)
This change introduces a caching mechanism to store the results of MSAA
support queries.
2025-09-22 14:10:08 -07:00
Ben Doherty c53195fa9a Fix frame scheduled callbacks not being latched (#9243) 2025-09-22 14:08:20 -07:00
Powei Feng 1e59032396 vk: guard export fence creation 2025-09-22 12:36:44 -07:00
Ben Doherty 9e79be419d Metal: log command buffer errors (#9224) 2025-09-22 12:10:30 -07:00
Powei Feng a81d3217e7 github: make linux machines consistent (#9241) 2025-09-22 11:20:23 -07:00
Powei Feng aa67faefab vk: remove renderstandalone workaround (#9236)
The bug might have been addressed by PR #8952. This workaround
is no longer needed.
2025-09-22 05:38:06 +00:00
Mathias Agopian e982d8a3c6 pass backend tags by && reference
This saves a CString copy on the backend side and this forces the
frontend to make a copy explicitly.

In theory the CString should now ever be copied just once.
2025-09-19 15:25:31 -07:00
Mathias Agopian 9267563af2 fix clang-tidy warnings 2025-09-19 15:25:31 -07:00
Filament Bot 333d01a0a5 [automated] Updating /docs due to commit 24a6bd3
Full commit hash is 24a6bd30f9

DOCS_ALLOW_DIRECT_EDITS
2025-09-19 15:46:12 +00:00
Sungun Park 24a6bd30f9 Release Filament 1.65.1 2025-09-19 15:42:25 +00:00
Sungun Park fdc0bc472d Revert workaround default to ALL (#9233)
This makes artifacts on certain mobile devices. Revert them back to ALL.

BUGS=[445721121]
2025-09-18 23:45:08 -07:00
Mathias Agopian a791665a2c remove the limit to 48 material parameters
FIXES=[445698638]
2025-09-18 17:28:20 -07:00
Powei Feng 20fcce13a4 docs: fix broken renderdiff link 2025-09-18 14:52:17 -07:00
Powei Feng 8233ab1cda Revert "[automated] Updating /docs due to commit 60df8ac"
This reverts commit 656a4b6f32.
2025-09-18 14:40:07 -07:00
Filament Bot 656a4b6f32 [automated] Updating /docs due to commit 60df8ac
Full commit hash is 60df8ac6b8

DOCS_ALLOW_DIRECT_EDITS
2025-09-18 21:32:03 +00:00
Powei Feng 60df8ac6b8 renderdiff: enable webgpu and improvements (#9219)
- Allow for presets to override selected models when presented
   in order.
 - Add 'gltf' for model search path
 - Add a few simple gltf models to the 'base' preset
 - Improve UI so that missing tests do not generate any html
   bits.
 - Add documentation on using the viewer
 - Add renderdiff documentation to the project webpage.

RDIFF_BRANCH=pf/renderdiff-enable-webgpu
2025-09-18 21:29:00 +00:00
Ben Doherty 1fb3d48e90 Fix Metal LoadImageTest.UpdateImageMipLevel (#9229) 2025-09-18 13:57:36 -07:00
Powei Feng b47110c339 backend: fix read pixels test for vk (#9223)
- The vk readPixels relies heavily on DataReshaper to transform
   the output into the client's requested format. We make
   additions to the DataReshaper to address previously unavailable
   features:
     - A copy for half floats - Assuming input and output are
       both half floats.  We just do memcpy.
     - Reshaping with respect to PixelBufferDescriptor's top and
       left parameters.
 - On the test side, we do a bit of clean-up and add the ability
   to export image correctly when the PBD has dimensions different
   from the readRect and/or non-zero offsets.
 - Also re-enable two LoadImage tests.
2025-09-18 19:53:19 +00:00
Mathias Agopian 10f4632c88 small math::mat3f to GL mat3 optimizations 2025-09-18 12:15:56 -07:00
Mathias Agopian 6dbd702bb5 fix OpenGL backend tests on macOS
multi-sampled textures are only supported at feature level 2, but
on macOS OpenGL is only feature level 1.
2025-09-17 18:11:51 -07:00
Powei Feng 0dd1d3211b backend: fix scissor viewport region test (#9220)
Make sure that the rendertarget dimensions matches the texture
dimensions. Update the expected image for the test.

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

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

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

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

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

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

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

Support for other backends will follow.

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

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


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

Here we just call vkQueueWaitIdle to address that case.

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

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

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

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

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

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

* Create a frontend API for external fences

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

* Refactor Sync to rely on opaque handles

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

* Address PR comment - minor refactor

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

* Remove convertSyncToFd from non-android

Other platforms might have other handle types.

Also - remove unused header include

* Add logging for convert to fd

Also, remove duplicate includes

* Address PR - change fd type

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

* Address PR - remove vtable from sync

Also - rename derived sync classes

* Address comment - make SyncCallbackData private

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

* Add stub for metal backend for sync

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

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

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

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

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

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

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

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

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

```
matc -Wall
```

if you want to restore the previous behavior.

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

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

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

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

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

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

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

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

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

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

DOCS_ALLOW_DIRECT_EDITS
2025-09-02 18:33:11 +00:00
259 changed files with 7626 additions and 1975 deletions
+4 -1
View File
@@ -8,7 +8,7 @@ runs:
uses: actions/cache@v3
id: cache-vulkan-sdk
with:
path: ${{ runner.homedir }}/VulkanSDK
path: ~/VulkanSDK
key: vulkansdk-${{ env.GITHUB_VULKANSDK_VERSION }}-2-${{ runner.os }}
- name: Download Vulkan SDK
if: steps.cache-vulkan-sdk.outputs.cache-hit != 'true'
@@ -21,6 +21,9 @@ runs:
run: |
source ${{ github.workspace }}/build/common/get-vulkan-sdk.sh
unpack_vulkan_installer
shell: bash
- name: Run Vulkan SDK setup
run: |
pushd .
cd ~/VulkanSDK/${GITHUB_VULKANSDK_VERSION}
sudo ./install_vulkan.py
+1 -1
View File
@@ -14,7 +14,7 @@ runs:
id: brew-cache
uses: actions/cache@v4 # Use a specific version
with:
path: $HOME/Library/Caches/Homebrew
path: ~/Library/Caches/Homebrew
key: ${{ runner.os }}-brew-20250424
- name: Install Mac Prerequisites
shell: bash
+1 -1
View File
@@ -12,7 +12,7 @@ jobs:
name: build-android
# We intentially use a larger runner here to enable larger disk space
# (standard linux runner will fail on disk space and faster build time).
runs-on: 'ubuntu-24.04-32core'
runs-on: 'ubuntu-24.04-16core'
steps:
- uses: actions/checkout@v4.1.6
+2 -2
View File
@@ -26,7 +26,7 @@ jobs:
build-desktop-linux:
name: build-linux
runs-on: ubuntu-22.04-16core
runs-on: 'ubuntu-24.04-16core'
steps:
- uses: actions/checkout@v4.1.6
with:
@@ -171,7 +171,7 @@ jobs:
validate-wgsl-webgpu:
name: validate-wgsl-webgpu
runs-on: 'ubuntu-24.04-8core'
runs-on: 'ubuntu-24.04-16core'
steps:
- uses: actions/checkout@v4.1.6
with:
+1 -1
View File
@@ -31,7 +31,7 @@ on:
jobs:
build-linux:
name: build-linux
runs-on: ubuntu-22.04-32core
runs-on: 'ubuntu-24.04-16core'
if: github.event_name == 'release' || github.event.inputs.platform == 'desktop'
steps:
+6
View File
@@ -18,3 +18,9 @@ test*.json
results
/compile_commands.json
/.cache
# For /test/renderdiff dependencies
/mesa
/gltf
/filament-assets
/venv
+9
View File
@@ -532,6 +532,15 @@ if (FILAMENT_SUPPORTS_OPENGL)
add_definitions(-DFILAMENT_SUPPORTS_OPENGL)
endif()
if (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "MinSizeRel")
option(FILAMENT_ENABLE_PERFETTO "Enable perfetto SDK on Android" OFF)
else()
option(FILAMENT_ENABLE_PERFETTO "Enable perfetto SDK on Android" ON)
endif()
if (FILAMENT_ENABLE_PERFETTO)
add_definitions(-DFILAMENT_ENABLE_PERFETTO)
endif()
# By default, build with Vulkan support on desktop platforms, although clients must request to use
# it at run time.
if (WIN32 OR WEBGL OR IOS)
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.64.1'
implementation 'com.google.android.filament:filament-android:1.65.2'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.64.1'
pod 'Filament', '~> 1.65.2'
```
## Documentation
+14
View File
@@ -7,6 +7,20 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.65.3
## v1.65.2
## v1.65.1
- `setFrameScheduledCallback` now works on all backends (frame presentation scheduling is still only
available on Metal). Non-Metal backends can use the callback to be notified when Filament has
finished processing a frame on the CPU.
- materials: added `getEyeFromViewMatrix()` for vertex shader [⚠️ **Recompile Materials**]
- matc: make `--workarounds=none` the default [**Recompile Materials to take effect**]
## v1.65.0
lighting: the intermediate froxel record buffer is now dynamically sized [⚠️ **New Material Version**]
+2
View File
@@ -178,6 +178,8 @@ nexusPublishing {
repositories {
sonatype {
stagingProfileId = '9a75a224a4f17b'
nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/"))
snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/"))
}
}
}
+2
View File
@@ -1,6 +1,8 @@
cmake_minimum_required(VERSION 3.19)
project(filamat-android)
set(CMAKE_CXX_STANDARD 20)
option(FILAMENT_ENABLE_MATDBG "Enables Material debugger" OFF)
set(FILAMENT_DIR ${FILAMENT_DIST_DIR})
+19 -17
View File
@@ -7,6 +7,8 @@ option(FILAMENT_ENABLE_MATDBG "Enables Material debugger" OFF)
option(FILAMENT_DISABLE_MATOPT "Disables material optimizations" OFF)
option(FILAMENT_SUPPORTS_WEBGPU "Enables WebGPU on Android" OFF)
set(CMAKE_CXX_STANDARD 20)
set(FILAMENT_DIR ${FILAMENT_DIST_DIR})
add_library(filament STATIC IMPORTED)
@@ -130,6 +132,14 @@ add_library(filament-jni SHARED
../common/NioUtils.cpp
)
target_include_directories(filament-jni PRIVATE
..
${FILAMENT_DIR}/include
../../filament/backend/include
../../third_party/robin-map
../../third_party/perfetto
../../libs/utils/include)
# Ordering is significant in the following list. The PRIVATE qualifier prevents transitive deps.
target_link_libraries(filament-jni
PRIVATE filament-generatePrefilterMipmap
@@ -144,29 +154,21 @@ target_link_libraries(filament-jni
PRIVATE android
PRIVATE jnigraphics
PRIVATE utils
PRIVATE perfetto
PRIVATE abseil
PRIVATE perfetto # needed only when FILAMENT_ENABLE_PERFETTO is defined
PRIVATE abseil # needed only when FILAMENT_USE_ABSEIL_LOGGING is defined
PRIVATE zstd
# libgeometry is PUBLIC because gltfio uses it.
PUBLIC geometry
$<$<STREQUAL:${FILAMENT_ENABLE_FGVIEWER},ON>:fgviewer>
$<$<STREQUAL:${FILAMENT_ENABLE_MATDBG},ON>:matdbg>
$<$<STREQUAL:${FILAMENT_ENABLE_MATDBG},ON>:filamat>
$<$<STREQUAL:${FILAMENT_SUPPORTS_VULKAN},ON>:bluevk>
$<$<STREQUAL:${FILAMENT_SUPPORTS_VULKAN},ON>:vkshaders>
$<$<STREQUAL:${FILAMENT_SUPPORTS_WEBGPU},ON>:webgpu_dawn>
$<$<STREQUAL:${FILAMENT_SUPPORTS_VULKAN},ON>:smol-v>
PRIVATE $<$<STREQUAL:${FILAMENT_ENABLE_FGVIEWER},ON>:fgviewer>
PRIVATE $<$<STREQUAL:${FILAMENT_ENABLE_MATDBG},ON>:matdbg>
PRIVATE $<$<STREQUAL:${FILAMENT_ENABLE_MATDBG},ON>:filamat>
PRIVATE $<$<STREQUAL:${FILAMENT_SUPPORTS_VULKAN},ON>:bluevk>
PRIVATE $<$<STREQUAL:${FILAMENT_SUPPORTS_VULKAN},ON>:vkshaders>
PRIVATE $<$<STREQUAL:${FILAMENT_SUPPORTS_VULKAN},ON>:smol-v>
PRIVATE $<$<STREQUAL:${FILAMENT_SUPPORTS_WEBGPU},ON>:webgpu_dawn>
)
target_include_directories(filament-jni PRIVATE
..
${FILAMENT_DIR}/include
../../filament/backend/include
../../third_party/robin-map
../../third_party/perfetto
../../libs/utils/include)
# Force a relink when the version script is changed:
set_target_properties(filament-jni PROPERTIES LINK_DEPENDS ${VERSION_SCRIPT})
@@ -450,6 +450,13 @@ Java_com_google_android_filament_RenderableManager_nGetPrimitiveCount(JNIEnv*, j
return (jint) rm->getPrimitiveCount((RenderableManager::Instance) i);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_RenderableManager_nGetInstanceCount(JNIEnv*, jclass,
jlong nativeRenderableManager, jint i) {
RenderableManager *rm = (RenderableManager *) nativeRenderableManager;
return (jint) rm->getInstanceCount((RenderableManager::Instance) i);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_RenderableManager_nSetMaterialInstanceAt(JNIEnv*, jclass,
jlong nativeRenderableManager, jint i, jint primitiveIndex, jlong nativeMaterialInstance) {
@@ -28,9 +28,8 @@ Java_com_google_android_filament_SwapChain_nSetFrameCompletedCallback(JNIEnv* en
jlong nativeSwapChain, jobject handler, jobject runnable) {
SwapChain* swapChain = (SwapChain*) nativeSwapChain;
auto* callback = JniCallback::make(env, handler, runnable);
swapChain->setFrameCompletedCallback(nullptr, [callback](SwapChain* swapChain) {
JniCallback::postToJavaAndDestroy(callback);
});
swapChain->setFrameCompletedCallback(callback->getHandler(),
[callback](SwapChain* swapChain) { JniCallback::postToJavaAndDestroy(callback); });
}
extern "C" JNIEXPORT jboolean JNICALL
@@ -40,9 +39,35 @@ Java_com_google_android_filament_SwapChain_nIsSRGBSwapChainSupported(
return (jboolean)SwapChain::isSRGBSwapChainSupported(*engine);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_SwapChain_nIsMSAASwapChainSupported(
JNIEnv *, jclass, jlong nativeEngine, jint samples) {
Engine* engine = (Engine*) nativeEngine;
return (jboolean)SwapChain::isMSAASwapChainSupported(*engine, samples);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_SwapChain_nIsProtectedContentSupported(
JNIEnv *, jclass, jlong nativeEngine) {
Engine* engine = (Engine*) nativeEngine;
return (jboolean)SwapChain::isProtectedContentSupported(*engine);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_SwapChain_nSetFrameScheduledCallback(JNIEnv* env, jclass,
jlong nativeSwapChain, jobject handler, jobject runnable) {
SwapChain* swapChain = (SwapChain*) nativeSwapChain;
auto* callback = JniCallback::make(env, handler, runnable);
swapChain->setFrameScheduledCallback(callback->getHandler(),
[callback](backend::PresentCallable) {
// Ignore PresentCallable, which is only meaningful with the Metal backend.
JniCallback::postToJavaAndDestroy(callback);
});
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_SwapChain_nIsFrameScheduledCallbackSet(
JNIEnv *, jclass, jlong nativeSwapChain) {
SwapChain* swapChain = (SwapChain*) nativeSwapChain;
return (jboolean)swapChain->isFrameScheduledCallbackSet();
}
@@ -851,6 +851,14 @@ public class RenderableManager {
return nGetPrimitiveCount(mNativeObject, i);
}
/**
* Gets the immutable number of instances in the given renderable.
*/
@IntRange(from = 0)
public int getInstanceCount(@EntityInstance int i) {
return nGetInstanceCount(mNativeObject, i);
}
/**
* Changes the material instance binding for the given primitive.
*
@@ -1018,6 +1026,7 @@ public class RenderableManager {
private static native boolean nIsShadowReceiver(long nativeRenderableManager, int i);
private static native void nGetAxisAlignedBoundingBox(long nativeRenderableManager, int i, float[] center, float[] halfExtent);
private static native int nGetPrimitiveCount(long nativeRenderableManager, int i);
private static native int nGetInstanceCount(long nativeRenderableManager, int i);
private static native void nSetMaterialInstanceAt(long nativeRenderableManager, int i, int primitiveIndex, long nativeMaterialInstance);
private static native void nClearMaterialInstanceAt(long nativeRenderableManager, int i, int primitiveIndex);
private static native long nGetMaterialInstanceAt(long nativeRenderableManager, int i, int primitiveIndex);
@@ -97,6 +97,19 @@ public class SwapChain {
return nIsSRGBSwapChainSupported(engine.getNativeObject());
}
/**
* Return whether createSwapChain supports the CONFIG_MSAA_*_SAMPLES flag.
* The default implementation returns false.
*
* @param engine A reference to the filament Engine
* @param samples The number of samples
* @return true if CONFIG_MSAA_*_SAMPLES is supported, false otherwise.
* @see SwapChainFlags#CONFIG_MSAA_*_SAMPLES
*/
public static boolean isMSAASwapChainSupported(@NonNull Engine engine, int samples) {
return nIsMSAASwapChainSupported(engine.getNativeObject(), samples);
}
/**
* @return the native <code>Object</code> this <code>SwapChain</code> was created from or null
* for a headless SwapChain.
@@ -126,6 +139,78 @@ public class SwapChain {
nSetFrameCompletedCallback(getNativeObject(), handler, callback);
}
/**
* FrameScheduledCallback is a callback function that notifies an application about the status
* of a frame after Filament has finished its processing.
*
* <p>
* The exact timing and semantics of this callback differ depending on the graphics backend in
* use.
* </p>
*
* <h3>Metal Backend</h3>
* <p>
* With the Metal backend, this callback signifies that Filament has completed all CPU-side
* processing for a frame and the frame is ready to be scheduled for presentation.
* </p>
*
* <p>
* Typically, Filament is responsible for scheduling the frame's presentation to the SwapChain.
* If a FrameScheduledCallback is set, however, the application bears the responsibility of
* scheduling the frame for presentation by calling the PresentCallable passed to the callback
* function. In this mode, Filament will not automatically schedule the frame for presentation.
* </p>
*
* <p>
* When using the Metal backend, if your application delays the call to the PresentCallable
* (e.g., by invoking it on a separate thread), you must ensure all PresentCallables have been
* called before shutting down the Filament Engine. You can guarantee this by calling
* Engine.flushAndWait() before Engine.shutdown(). This is necessary to ensure the Engine has
* a chance to clean up all memory related to frame presentation.
* </p>
*
* <h3>Other Backends (OpenGL, Vulkan, WebGPU)</h3>
* <p>
* On other backends, this callback serves as a notification that Filament has completed all
* CPU-side processing for a frame. Filament proceeds with its normal presentation logic
* automatically, and the PresentCallable passed to the callback is a no-op that can be safely
* ignored.
* </p>
*
* <h3>General Behavior</h3>
* <p>
* A FrameScheduledCallback can be set on an individual SwapChain through
* setFrameScheduledCallback. Each SwapChain can have only one callback set per frame. If
* setFrameScheduledCallback is called multiple times on the same SwapChain before
* Renderer.endFrame(), the most recent call effectively overwrites any previously set callback.
* </p>
*
* <p>
* The callback set by setFrameScheduledCallback is "latched" when Renderer.endFrame() is
* executed. At this point, the callback is fixed for the frame that was just encoded.
* Subsequent calls to setFrameScheduledCallback after endFrame() will apply to the next frame.
* </p>
*
* <p>
* Use setFrameScheduledCallback() (with default arguments) to unset the callback.
* </p>
*
* @param handler A {@link java.util.concurrent.Executor Executor}.
* @param callback The Runnable callback to invoke when frame processing is complete.
*/
public void setFrameScheduledCallback(@NonNull Object handler, @NonNull Runnable callback) {
nSetFrameScheduledCallback(getNativeObject(), handler, callback);
}
/**
* Returns whether this SwapChain currently has a FrameScheduledCallback set.
*
* @return true, if the last call to setFrameScheduledCallback set a callback
*/
public boolean isFrameScheduledCallbackSet() {
return nIsFrameScheduledCallbackSet(getNativeObject());
}
public long getNativeObject() {
if (mNativeObject == 0) {
throw new IllegalStateException("Calling method on destroyed SwapChain");
@@ -138,6 +223,9 @@ public class SwapChain {
}
private static native void nSetFrameCompletedCallback(long nativeSwapChain, Object handler, Runnable callback);
private static native void nSetFrameScheduledCallback(long nativeSwapChain, Object handler, Runnable callback);
private static native boolean nIsFrameScheduledCallbackSet(long nativeSwapChain);
private static native boolean nIsSRGBSwapChainSupported(long nativeEngine);
private static native boolean nIsMSAASwapChainSupported(long nativeEngine, int samples);
private static native boolean nIsProtectedContentSupported(long nativeEngine);
}
@@ -92,6 +92,15 @@ public final class SwapChainFlags {
* The SwapChain contains protected content. Only supported when isProtectedContentSupported()
* is true.
*/
public static final long CONFIG_PROTECTED_CONTENT = 0x40;
}
public static final long CONFIG_PROTECTED_CONTENT = 0x40;
/**
* Indicates that the SwapChain is configured to use Multi-Sample Anti-Aliasing (MSAA) with the
* given sample points within each pixel. Only supported when isMSAASwapChainSupported(4) is
* true.
*
* This is only supported by EGL(Android). Other GL platforms (GLX, WGL, etc) don't support it
* because the swapchain MSAA settings must be configured before window creation.
*/
public static final long CONFIG_MSAA_4_SAMPLES = 0x80;
}
@@ -231,7 +231,7 @@ public class View {
*
* <p>
* The View does not take ownership of the Scene pointer. Before destroying a Camera, be sure
* to remove it from all assoicated Views.
* to remove it from all associated Views. If the camera isn't set, Renderer::render() will result in a no-op.
* </p>
*
* @see #getCamera
+31 -11
View File
@@ -1,6 +1,14 @@
cmake_minimum_required(VERSION 3.19)
project(filament-utils-android)
option(FILAMENT_SUPPORTS_VULKAN "Enables Vulkan on Android" OFF)
option(FILAMENT_ENABLE_FGVIEWER "Enables Frame Graph Viewer" OFF)
option(FILAMENT_ENABLE_MATDBG "Enables Material debugger" OFF)
option(FILAMENT_DISABLE_MATOPT "Disables material optimizations" OFF)
option(FILAMENT_SUPPORTS_WEBGPU "Enables WebGPU on Android" OFF)
set(CMAKE_CXX_STANDARD 20)
set(FILAMENT_DIR ${FILAMENT_DIST_DIR})
set(IMAGEIO_DIR ../../libs/imageio)
@@ -30,7 +38,16 @@ add_library(iblprefilter STATIC IMPORTED)
set_target_properties(iblprefilter PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilament-iblprefilter.a)
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/libfilament-utils-jni.map")
add_library(utils STATIC IMPORTED)
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
add_library(perfetto STATIC IMPORTED)
set_target_properties(perfetto PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libperfetto.a)
set(VERSION_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/libfilament-utils-jni.map")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${VERSION_SCRIPT}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,max-page-size=16384")
add_library(filament-utils-jni SHARED
@@ -53,22 +70,25 @@ add_library(filament-utils-jni SHARED
target_compile_definitions(filament-utils-jni PUBLIC IMAGEIO_LITE=1)
target_include_directories(filament-utils-jni PRIVATE
${FILAMENT_DIR}/include
..
${FILAMENT_DIR}/include
../../filament/backend/include
${IMAGEIO_DIR}/include
../../libs/utils/include)
set_target_properties(filament-utils-jni PROPERTIES LINK_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/libfilament-utils-jni.symbols)
set_target_properties(filament-utils-jni PROPERTIES LINK_DEPENDS ${VERSION_SCRIPT})
# The ordering in the following list is important because CMake does not have dependency information.
target_link_libraries(filament-utils-jni
gltfio-jni
civetweb
camutils
iblprefilter
image
ktxreader
viewer
PRIVATE gltfio-jni
PRIVATE civetweb
PRIVATE camutils
PRIVATE iblprefilter
PRIVATE image
PRIVATE filament-jni
PRIVATE ktxreader
PRIVATE viewer
PRIVATE log
PRIVATE utils
PRIVATE perfetto # needed only when FILAMENT_ENABLE_PERFETTO is defined
)
+37 -69
View File
@@ -1,20 +1,27 @@
cmake_minimum_required(VERSION 3.19)
project(gltfio-android)
option(FILAMENT_SUPPORTS_VULKAN "Enables Vulkan on Android" OFF)
option(FILAMENT_ENABLE_FGVIEWER "Enables Frame Graph Viewer" OFF)
option(FILAMENT_ENABLE_MATDBG "Enables Material debugger" OFF)
option(FILAMENT_DISABLE_MATOPT "Disables material optimizations" OFF)
option(FILAMENT_SUPPORTS_WEBGPU "Enables WebGPU on Android" OFF)
set(CMAKE_CXX_STANDARD 20)
set(FILAMENT_DIR ${FILAMENT_DIST_DIR})
set(GLTFIO_DIR ../../libs/gltfio)
set(DRACO_DIR ../../third_party/draco)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/../filament-android ${CMAKE_CURRENT_BINARY_DIR}/filament-android)
add_library(gltfio_core STATIC IMPORTED)
set_target_properties(gltfio_core PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libgltfio_core.a)
add_library(dracodec STATIC IMPORTED)
set_target_properties(dracodec PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libdracodec.a)
add_library(meshoptimizer STATIC IMPORTED)
set_target_properties(meshoptimizer PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libmeshoptimizer.a)
add_library(ktxreader STATIC IMPORTED)
set_target_properties(ktxreader PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libktxreader.a)
@@ -27,10 +34,6 @@ add_library(basis_transcoder STATIC IMPORTED)
set_target_properties(basis_transcoder PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libbasis_transcoder.a)
add_library(zstd STATIC IMPORTED)
set_target_properties(zstd PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libzstd.a)
add_library(utils STATIC IMPORTED)
set_target_properties(utils PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libutils.a)
@@ -47,60 +50,11 @@ add_library(uberarchive STATIC IMPORTED)
set_target_properties(uberarchive PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libuberarchive.a)
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.map")
set(VERSION_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.map")
set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} -Wl,--version-script=${VERSION_SCRIPT}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,max-page-size=16384")
set(GLTFIO_SRCS
${GLTFIO_DIR}/include/gltfio/Animator.h
${GLTFIO_DIR}/include/gltfio/AssetLoader.h
${GLTFIO_DIR}/include/gltfio/FilamentAsset.h
${GLTFIO_DIR}/include/gltfio/FilamentInstance.h
${GLTFIO_DIR}/include/gltfio/MaterialProvider.h
${GLTFIO_DIR}/include/gltfio/NodeManager.h
${GLTFIO_DIR}/include/gltfio/TrsTransformManager.h
${GLTFIO_DIR}/include/gltfio/ResourceLoader.h
${GLTFIO_DIR}/include/gltfio/TextureProvider.h
${GLTFIO_DIR}/include/gltfio/math.h
${GLTFIO_DIR}/src/ArchiveCache.cpp
${GLTFIO_DIR}/src/ArchiveCache.h
${GLTFIO_DIR}/src/Animator.cpp
${GLTFIO_DIR}/src/AssetLoader.cpp
${GLTFIO_DIR}/src/DependencyGraph.cpp
${GLTFIO_DIR}/src/DependencyGraph.h
${GLTFIO_DIR}/src/DracoCache.cpp
${GLTFIO_DIR}/src/DracoCache.h
${GLTFIO_DIR}/src/FFilamentAsset.h
${GLTFIO_DIR}/src/FFilamentInstance.h
${GLTFIO_DIR}/src/FilamentAsset.cpp
${GLTFIO_DIR}/src/FilamentInstance.cpp
${GLTFIO_DIR}/src/FNodeManager.h
${GLTFIO_DIR}/src/FTrsTransformManager.h
${GLTFIO_DIR}/src/GltfEnums.h
${GLTFIO_DIR}/src/Ktx2Provider.cpp
${GLTFIO_DIR}/src/MaterialProvider.cpp
${GLTFIO_DIR}/src/NodeManager.cpp
${GLTFIO_DIR}/src/TrsTransformManager.cpp
${GLTFIO_DIR}/src/ResourceLoader.cpp
${GLTFIO_DIR}/src/StbProvider.cpp
${GLTFIO_DIR}/src/TangentsJob.cpp
${GLTFIO_DIR}/src/TangentsJob.h
${GLTFIO_DIR}/src/UbershaderProvider.cpp
${GLTFIO_DIR}/src/Utility.cpp
${GLTFIO_DIR}/src/Utility.h
${GLTFIO_DIR}/src/Wireframe.cpp
${GLTFIO_DIR}/src/Wireframe.h
${GLTFIO_DIR}/src/downcast.h
${GLTFIO_DIR}/src/extended/AssetLoaderExtended.cpp
${GLTFIO_DIR}/src/extended/AssetLoaderExtended.h
${GLTFIO_DIR}/src/extended/ResourceLoaderExtended.cpp
${GLTFIO_DIR}/src/extended/ResourceLoaderExtended.h
${GLTFIO_DIR}/src/extended/TangentsJobExtended.cpp
${GLTFIO_DIR}/src/extended/TangentsJobExtended.h
${GLTFIO_DIR}/src/extended/TangentSpaceMeshWrapper.cpp
${GLTFIO_DIR}/src/extended/TangentSpaceMeshWrapper.h
src/main/cpp/Animator.cpp
src/main/cpp/AssetLoader.cpp
src/main/cpp/FilamentAsset.cpp
@@ -118,24 +72,38 @@ set(GLTFIO_SRCS
set(GLTFIO_INCLUDE_DIRS
..
${FILAMENT_DIR}/include
../../filament/backend/include
${FILAMENT_DIR}/backend/include
../../libs/gltfio/include
../../third_party/zstd/lib
../../third_party/cgltf
../../third_party/meshoptimizer/src
../../third_party/robin-map
../../third_party/stb
../../third_party/perfetto
../../libs/utils/include
../../libs/ktxreader/include
)
add_library(gltfio-jni SHARED ${GLTFIO_SRCS})
target_include_directories(gltfio-jni PRIVATE ${GLTFIO_INCLUDE_DIRS})
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.symbols)
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.map)
target_link_libraries(gltfio-jni filament-jni utils perfetto uberzlib log stb ktxreader basis_transcoder zstd uberarchive)
target_link_libraries(gltfio-jni dracodec meshoptimizer)
target_compile_definitions(gltfio-jni PUBLIC GLTFIO_DRACO_SUPPORTED=1)
target_include_directories(gltfio-jni PRIVATE ${GLTFIO_INCLUDE_DIRS})
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/src)
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/tnt)
# Force a relink when the version script is changed:
set_target_properties(gltfio-jni PROPERTIES LINK_DEPENDS
"${CMAKE_CURRENT_SOURCE_DIR}/libgltfio-jni.symbols;${VERSION_SCRIPT}")
# The ordering in the following list is important because CMake does not have dependency information.
target_link_libraries(gltfio-jni
PRIVATE gltfio_core
PRIVATE ktxreader
PUBLIC filament-jni
PRIVATE stb
PRIVATE basis_transcoder
PRIVATE uberarchive
PRIVATE dracodec
PRIVATE utils
PRIVATE uberzlib
PRIVATE perfetto # needed only when FILAMENT_ENABLE_PERFETTO is defined
PRIVATE log # needed only when perfetto above is used
)
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.64.1
VERSION_NAME=1.65.2
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
@@ -24,7 +24,7 @@ android {
compileSdkVersion versions.compileSdk
defaultConfig {
applicationId "com.google.android.filament.hellocamera"
applicationId "com.google.android.filament.hellocam"
minSdkVersion 23
targetSdkVersion versions.targetSdk
}
+8 -1
View File
@@ -75,6 +75,8 @@ function print_help {
echo " -S type"
echo " Enable stereoscopic rendering where type is one of [instanced|multiview]. This is only"
echo " meant for building the samples."
echo " -P"
echo " Enable perfetto traces on Android. Disabled by default on the Release build, enabled otherwise."
echo ""
echo "Build types:"
echo " release"
@@ -204,6 +206,7 @@ MATOPT_GRADLE_OPTION=""
ASAN_UBSAN_OPTION=""
COVERAGE_OPTION=""
ENABLE_PERFETTO=""
BACKEND_DEBUG_FLAG_OPTION=""
@@ -413,6 +416,7 @@ function build_android_target {
${WEBGPU_OPTION} \
${BACKEND_DEBUG_FLAG_OPTION} \
${STEREOSCOPIC_OPTION} \
${ENABLE_PERFETTO} \
../..
ln -sf "out/cmake-android-${lc_target}-${arch}/compile_commands.json" \
../../compile_commands.json
@@ -806,7 +810,7 @@ function check_debug_release_build {
pushd "$(dirname "$0")" > /dev/null
while getopts ":hacCfgimp:q:uvWslwedtk:bVx:S:X:" opt; do
while getopts ":hacCfgimp:q:uvWslwedtk:bVx:S:X:P" opt; do
case ${opt} in
h)
print_help
@@ -954,6 +958,9 @@ while getopts ":hacCfgimp:q:uvWslwedtk:bVx:S:X:" opt; do
V) COVERAGE_OPTION="-DFILAMENT_ENABLE_COVERAGE=ON"
echo "Enabled coverage"
;;
P) ENABLE_PERFETTO="-DFILAMENT_ENABLE_PERFETTO=ON"
echo "Enabled perfetto"
;;
x) BACKEND_DEBUG_FLAG_OPTION="-DFILAMENT_BACKEND_DEBUG_FLAG=${OPTARG}"
;;
S) case $(echo "${OPTARG}" | tr '[:upper:]' '[:lower:]') in
@@ -0,0 +1,349 @@
#!/usr/bin/env python3
# Copyright (C) 2025 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
This script automates the process of closing a Sonatype OSSRH staging repository.
To run the script:
python3 ./close-sonatype-staging-repository.py
To run the embedded unit tests:
python3 ./close-sonatype-staging-repository.py test
"""
import sys
import os
import base64
import json
import pathlib
import unittest
import io
from unittest import mock
import urllib.request
import urllib.error
PROPERTIES_PATH = pathlib.Path.home() / ".gradle" / "gradle.properties"
API_BASE_URL = "https://ossrh-staging-api.central.sonatype.com/manual"
def get_gradle_credentials(file_path: pathlib.Path) -> (str, str):
"""
Reads sonatypeUsername and sonatypePassword from the properties file.
"""
if not file_path.exists():
print(f"Error: Properties file not found at {file_path}", file=sys.stderr)
sys.exit(1)
username = None
password = None
try:
with open(file_path, 'r') as f:
for line in f:
line = line.strip()
# Ignore blank lines and comments
if not line or line.startswith("#"):
continue
if line.startswith("sonatypeUsername="):
username = line.split("=", 1)[1].strip()
elif line.startswith("sonatypePassword="):
password = line.split("=", 1)[1].strip()
if not username or not password:
print(f"Error: 'sonatypeUsername' or 'sonatypePassword' not found in {file_path}", file=sys.stderr)
sys.exit(1)
return username, password
except Exception as e:
print(f"Error reading properties file: {e}", file=sys.stderr)
sys.exit(1)
def generate_auth_token(username: str, password: str) -> str:
"""
Generates a Base64 encoded auth token from username and password.
"""
auth_string = f"{username}:{password}"
auth_bytes = auth_string.encode('utf-8')
token_bytes = base64.b64encode(auth_bytes)
return token_bytes.decode('utf-8')
def find_staging_repository(token: str) -> str:
"""
Finds the 'open' staging repository key from the Sonatype API.
"""
print("Searching for staging repository...")
url = f"{API_BASE_URL}/search/repositories"
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/json"
}
req = urllib.request.Request(url, headers=headers, method="GET")
try:
with urllib.request.urlopen(req) as response:
response_body = response.read().decode('utf-8')
data = json.loads(response_body)
repositories = data.get("repositories", [])
open_repositories = list(filter(lambda repo: repo.get("state") == "open", repositories))
if len(open_repositories) == 0:
print("Error: No 'open' staging repositories found.", file=sys.stderr)
sys.exit(1)
if len(open_repositories) > 1:
print(f"Error: Expected 1 'open' repository, but found {len(repositories)}.", file=sys.stderr)
sys.exit(1)
repo_key = open_repositories[0].get("key")
if not repo_key:
print("Error: Repository found, but it has no 'key'.", file=sys.stderr)
sys.exit(1)
return repo_key
except urllib.error.HTTPError as e:
# Handle HTTP errors (e.g., 401 Unauthorized, 404 Not Found, 500 Server Error)
print(f"Error during repository search: HTTP {e.code} {e.reason}", file=sys.stderr)
try:
# Try to read the error response body for more context
error_body = e.read().decode('utf-8')
print(f"Response body: {error_body}", file=sys.stderr)
except Exception:
pass # Ignore if we can't read the body
sys.exit(1)
except urllib.error.URLError as e:
# Handle network errors (e.g., connection refused)
print(f"Error during repository search: {e.reason}", file=sys.stderr)
sys.exit(1)
except json.JSONDecodeError:
print("Error: Failed to decode JSON response from server.", file=sys.stderr)
sys.exit(1)
def close_staging_repository(token: str, repo_key: str):
"""
Closes (promotes) the staging repository with the given key.
"""
print(f"Attempting to close staging repository: {repo_key}")
url = f"{API_BASE_URL}/upload/repository/{repo_key}"
headers = {"Authorization": f"Bearer {token}"}
req = urllib.request.Request(url, headers=headers, method="POST")
try:
with urllib.request.urlopen(req) as response:
print(f"Successfully submitted request. Server responded with: {response.status} {response.reason}")
print("It may take a few moments to process.")
except urllib.error.HTTPError as e:
print(f"Error closing repository: HTTP {e.code} {e.reason}", file=sys.stderr)
try:
error_body = e.read().decode('utf-8')
print(f"Response body: {error_body}", file=sys.stderr)
except Exception:
pass
sys.exit(1)
except urllib.error.URLError as e:
print(f"Error closing repository: {e.reason}", file=sys.stderr)
sys.exit(1)
def main():
username, password = get_gradle_credentials(PROPERTIES_PATH)
token = generate_auth_token(username, password)
print("Successfully generated auth token.")
repo_key = find_staging_repository(token)
print(f"Found staging repository: {repo_key}")
close_staging_repository(token, repo_key)
print("\nScript completed successfully.")
# --- Unit Tests ---
class TestSonatypeScript(unittest.TestCase):
@mock.patch('pathlib.Path.exists', return_value=True)
@mock.patch('builtins.open', new_callable=mock.mock_open,
read_data="# This is a comment\nsonatypeUsername=testuser\n\nsonatypePassword=testpass\nother=data")
def test_get_gradle_credentials_success(self, mock_file, mock_exists):
"""Tests successful credential parsing, ignoring comments."""
user, pw = get_gradle_credentials(pathlib.Path("/fake/path"))
self.assertEqual(user, "testuser")
self.assertEqual(pw, "testpass")
@mock.patch('pathlib.Path.exists', return_value=False)
def test_get_gradle_credentials_no_file(self, mock_exists):
"""Tests error exit when file is missing."""
with self.assertRaises(SystemExit) as cm:
get_gradle_credentials(pathlib.Path("/fake/path"))
@mock.patch('pathlib.Path.exists', return_value=True)
@mock.patch('builtins.open', new_callable=mock.mock_open, read_data="# Only comments\nsonatypeUsername=testuser")
def test_get_gradle_credentials_missing_key(self, mock_file, mock_exists):
"""Tests error exit when a key is missing."""
with self.assertRaises(SystemExit) as cm:
get_gradle_credentials(pathlib.Path("/fake/path"))
def test_generate_auth_token(self):
"""Tests the Base64 token encoding."""
token = generate_auth_token("foobar", "abc123")
# echo -n "foobar:abc123" | base64
self.assertEqual(token, "Zm9vYmFyOmFiYzEyMw==")
@mock.patch('urllib.request.urlopen')
def test_find_repository_success(self, mock_urlopen):
"""Tests finding exactly one open repository."""
mock_response = mock.Mock()
mock_response.status = 200
mock_response.reason = "OK"
mock_response.read.return_value = json.dumps({
"repositories": [{"key": "test-repo-key-123", "state": "open"}]
}).encode('utf-8')
mock_urlopen.return_value.__enter__.return_value = mock_response
key = find_staging_repository("fake-token")
self.assertEqual(key, "test-repo-key-123")
@mock.patch('urllib.request.urlopen')
def test_find_repository_success(self, mock_urlopen):
"""Tests finding exactly one open repository."""
mock_response = mock.Mock()
mock_response.status = 200
mock_response.reason = "OK"
mock_response.read.return_value = json.dumps({
"repositories": [{"key": "test-repo-key-123", "state": "open"}, {"key": "test-repo-key-456", "state": "closed"}]
}).encode('utf-8')
mock_urlopen.return_value.__enter__.return_value = mock_response
key = find_staging_repository("fake-token")
self.assertEqual(key, "test-repo-key-123")
@mock.patch('urllib.request.urlopen')
def test_find_repository_success(self, mock_urlopen):
"""Tests error exit when only a closed repository is found."""
mock_response = mock.Mock()
mock_response.status = 200
mock_response.reason = "OK"
mock_response.read.return_value = json.dumps({
"repositories": [{"key": "test-repo-key-123", "state": "closed"}]
}).encode('utf-8')
mock_urlopen.return_value.__enter__.return_value = mock_response
with self.assertRaises(SystemExit) as cm:
key = find_staging_repository("fake-token")
@mock.patch('urllib.request.urlopen')
def test_find_repository_not_found(self, mock_urlopen):
"""Tests error exit when zero repositories are found."""
mock_response = mock.Mock()
mock_response.status = 200
mock_response.read.return_value = json.dumps({"repositories": []}).encode('utf-8')
mock_urlopen.return_value.__enter__.return_value = mock_response
with self.assertRaises(SystemExit) as cm:
find_staging_repository("fake-token")
@mock.patch('urllib.request.urlopen')
def test_find_repository_too_many(self, mock_urlopen):
"""Tests error exit when multiple open repositories are found."""
mock_response = mock.Mock()
mock_response.status = 200
mock_response.read.return_value = json.dumps({
"repositories": [{"key": "key1", "state": "open"}, {"key": "key2", "state": "open"}]
}).encode('utf-8')
mock_urlopen.return_value.__enter__.return_value = mock_response
with self.assertRaises(SystemExit) as cm:
find_staging_repository("fake-token")
@mock.patch('urllib.request.urlopen')
def test_find_repository_http_error(self, mock_urlopen):
"""Tests error exit on a network HTTP error."""
# Mock an HTTPError
mock_urlopen.side_effect = urllib.error.HTTPError(
url='http://fake.com',
code=401,
msg="Unauthorized",
hdrs={},
fp=io.BytesIO(b'{"error":"bad token"}')
)
with self.assertRaises(SystemExit) as cm:
find_staging_repository("fake-token")
@mock.patch('urllib.request.urlopen')
def test_close_repository_success(self, mock_urlopen):
"""Tests successful repository close."""
mock_response = mock.Mock()
mock_response.status = 202 # 202 Accepted is a common response for POST
mock_response.reason = "Accepted"
mock_urlopen.return_value.__enter__.return_value = mock_response
close_staging_repository("fake-token", "test-repo-key")
# Check that the request object was created correctly
called_request = mock_urlopen.call_args[0][0]
self.assertEqual(called_request.full_url, f"{API_BASE_URL}/upload/repository/test-repo-key")
self.assertEqual(called_request.method, "POST")
self.assertEqual(called_request.headers["Authorization"], "Bearer fake-token")
@mock.patch('urllib.request.urlopen')
def test_close_repository_http_error(self, mock_urlopen):
"""Tests error exit on close network failure."""
mock_urlopen.side_effect = urllib.error.HTTPError(
url='http://fake.com',
code=500,
msg="Server Error",
hdrs={},
fp=io.BytesIO(b'{"error":"failed to close"}')
)
with self.assertRaises(SystemExit) as cm:
close_staging_repository("fake-token", "test-repo-key")
def run_unit_tests():
"""
Configures and runs the unit tests.
"""
print("Running unit tests...")
# We replace sys.argv to prevent unittest.main from
# trying to parse the 'test' argument.
original_argv = sys.argv
sys.argv = [original_argv[0]] # Keep only the script name
unittest.main()
sys.argv = original_argv # Restore original args
if __name__ == '__main__':
# Check if the user wants to run tests
if len(sys.argv) > 1 and sys.argv[1].lower() == 'test':
run_unit_tests()
else:
# Run the main script logic
main()
+25 -22
View File
@@ -212,7 +212,7 @@ when using textures.
This property can dramatically change the appearance of a surface. Non-metallic surfaces have
chromatic diffuse reflection and achromatic specular reflection (reflected light does not change
color). Metallic surfaces do not have any diffuse reflection and chromatic specular reflection
(reflected light takes on the color of the surfaced as defined by `baseColor`).
(reflected light takes on the color of the surfaced as defined by `baseColor`).
The effect of `metallic` is shown in figure [metallicProperty] (click on the image to see a
larger version).
@@ -248,7 +248,7 @@ The effect of `roughness` on metallic surfaces is shown in figure [roughnessCond
When refraction through an object is enabled (using a `refractonType` of `thin` or `solid`), the
`roughness` property will also affect the refractions, as shown in figure
[roughnessRefractionProperty] (click on the image to see a larger version).
![Figure [roughnessRefractionProperty]: Refractive sphere with `roughness` varying from 0.0
(left) to 1.0 (right)](images/materials/refraction_roughness.png)
@@ -307,7 +307,7 @@ The sheen color controls the color appearance and strength of an optional sheen
base layer described by the properties above. The sheen layer always sits below the clear coat layer
if such a layer is present.
The sheen layer can be used to represent cloth and fabric materials. Please refer to
The sheen layer can be used to represent cloth and fabric materials. Please refer to
section [Cloth model] for more information about cloth and fabric materials.
The effect of `sheenColor` is shown in figure [materialSheenColor]
@@ -520,14 +520,14 @@ light to bend further away from the initial path.
Table [commonMatIOR] describes acceptable refractive indices for various types of materials.
Material | IOR
Material | IOR
--------------------------:|:-----------------
Air | 1.0
Water | 1.33
Common liquids | 1.33 to 1.5
Common gemstones | 1.58 to 2.33
Plastics, glass | 1.5 to 1.58
Other dielectric materials | 1.33 to 1.58
Water | 1.33
Common liquids | 1.33 to 1.5
Common gemstones | 1.58 to 2.33
Plastics, glass | 1.5 to 1.58
Other dielectric materials | 1.33 to 1.58
[Table [commonMatIOR]: Index of refraction of common materials]
The appearance of a refractive material will greatly depend on the `refractionType` and
@@ -995,10 +995,10 @@ Value
name must be a valid GLSL identifier. Entries have an optional `precision`, which can be
one of `default` (best precision for the platform, typically `high` on desktop, `medium` on
mobile), `low`, `medium`, `high`. The type must be one of the types described in
table [materialParamsTypes]. For Android external textures, entries also have an optional
transformName parameter to specify the name of the material parameter that will be
table [materialParamsTypes]. For Android external textures, entries also have an optional
transformName parameter to specify the name of the material parameter that will be
used to expose the transform matrix associated with the external sampler. In iOS and Vulkan,
this will always be identity.
this will always be identity.
Type | Description
:----------------------|:---------------------------------
@@ -2476,14 +2476,17 @@ type aliases:
### Matrices
Name | Type | Description
:-----------------------------------|:--------:|:------------------------------------
**getViewFromWorldMatrix()** | float4x4 | Matrix that converts from world space to view/eye space
**getWorldFromViewMatrix()** | float4x4 | Matrix that converts from view/eye space to world space
**getClipFromViewMatrix()** | float4x4 | Matrix that converts from view/eye space to clip (NDC) space
**getViewFromClipMatrix()** | float4x4 | Matrix that converts from clip (NDC) space to view/eye space
**getClipFromWorldMatrix()** | float4x4 | Matrix that converts from world to clip (NDC) space
**getWorldFromClipMatrix()** | float4x4 | Matrix that converts from clip (NDC) space to world space
Name | Type | Description
:-----------------------------------------|:--------:|:------------------------------------
**getViewFromWorldMatrix()** | float4x4 | Matrix that converts from world space to view/eye space
**getWorldFromViewMatrix()** | float4x4 | Matrix that converts from view/eye space to world space
**getClipFromViewMatrix()** | float4x4 | Matrix that converts from view/eye space to clip (NDC) space
**getViewFromClipMatrix()** | float4x4 | Matrix that converts from clip (NDC) space to view/eye space
**getEyeFromViewMatrix()** | float4x4 | Matrix that converts from view space to eye space
**getEyeFromViewMatrix(int eyeIndex)** | float4x4 | Matrix that converts from view space to eye space for the eye referred to by eyeIndex
**getClipFromWorldMatrix()** | float4x4 | Matrix that converts from world to clip (NDC) space
**getClipFromWorldMatrix(int eyeIndex)** | float4x4 | Matrix that converts from world to clip (NDC) space for the eye referred to by eyeIndex
**getWorldFromClipMatrix()** | float4x4 | Matrix that converts from clip (NDC) space to world space
### Frame constants
@@ -2639,7 +2642,7 @@ standard skybox material. It produces a list of 2 parameters, named `showSun` an
respectively a boolean and a cubemap texture.
```text
$ matc --reflect parameters filament/src/materials/skybox.mat
$ matc --reflect parameters filament/src/materials/skybox.mat
{
"parameters": [
{
@@ -2655,7 +2658,7 @@ $ matc --reflect parameters filament/src/materials/skybox.mat
}
]
}
```
```
### --variant-filter
+267
View File
@@ -0,0 +1,267 @@
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Maven Release - Filament</title>
<!-- Custom HTML head -->
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="../highlight.css">
<link rel="stylesheet" href="../tomorrow-night.css">
<link rel="stylesheet" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
<!-- MathJax -->
<script async src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<!-- Provide site root to javascript -->
<script>
var path_to_root = "../";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "light";
</script>
<!-- Start loading toc.js asap -->
<script src="../toc.js"></script>
</head>
<body>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
var sidebar = null;
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
html.classList.remove('sidebar-visible');
html.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div style="display:flex;align-items:center;justify-content:center">
<img class="flogo" src="../images/filament_logo_small.png"></img>
</div>
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="../toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<!-- Filament: disable themes because the markdeep part does not look good for dark themes -->
<!--
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
-->
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Filament</h1>
<div class="right-buttons">
<a href="https://github.com/google/filament" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="maven-release"><a class="header" href="#maven-release">Maven Release</a></h1>
<h2 id="register-for-a-sonatype-account"><a class="header" href="#register-for-a-sonatype-account">Register for a Sonatype Account</a></h2>
<p>First, you'll need to register for a Sonatype account at the <a href="https://central.sonatype.org/register/central-portal/">Central
Portal</a>.</p>
<p>To publish under the <code>com.google.android</code> namespace, you'll also need to email
<code>central-support@sonatype.com</code> with your account information to request access.</p>
<p>Then, generate a user token through the Sonatype website. Navigate to
<a href="https://central.sonatype.com/account">https://central.sonatype.com/account</a> and select <strong>Generate
User Token</strong>.</p>
<p>Finally, add the generated token credentials to <code>~/.gradle/gradle.properties</code>. (Note: these are
different from the credentials used to log into your Sonatype account):</p>
<pre><code class="language-gradle"># Generated Sonatype token
sonatypeUsername=&lt;username&gt;
sonatypePassword=&lt;password&gt;
</code></pre>
<hr />
<h2 id="signing-key"><a class="header" href="#signing-key">Signing Key</a></h2>
<p>All release artifacts must be signed. You'll need to create an OpenPGP key pair. See Gradle's
documentation on <a href="https://docs.gradle.org/current/userguide/signing_plugin.html#sec:signatory_credentials">Signatory
credentials</a>
for instructions.</p>
<p>Update <code>~/.gradle/gradle.properties</code> with your new key's credentials:</p>
<pre><code class="language-gradle">signing.keyId=&lt;key id&gt;
signing.password=&lt;key password&gt;
signing.secretKeyRingFile=&lt;secret key ring file&gt;
</code></pre>
<hr />
<h2 id="build-the-android-release"><a class="header" href="#build-the-android-release">Build the Android Release</a></h2>
<p>Make sure <code>JAVA_HOME</code> is set correctly. For example:</p>
<pre><code class="language-bash">export JAVA_HOME=/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home
./build.sh -C -i -p android release
</code></pre>
<hr />
<h2 id="publish-to-sonatype"><a class="header" href="#publish-to-sonatype">Publish to Sonatype</a></h2>
<h3 id="a-note-on-the-legacy-staging-service"><a class="header" href="#a-note-on-the-legacy-staging-service">A Note on the Legacy Staging Service</a></h3>
<p>Previously, Filament was published to Maven via OSSRH (Open Source Software Repository Hosting). In
2025, this service was <a href="https://central.sonatype.org/pages/ossrh-eol/">sunsetted</a>. Now, we use
Sonatype's <a href="https://central.sonatype.org/">Central Publisher Portal</a>.</p>
<p>The new Central Publisher Portal does not officially support Gradle. However, Sonatype provides a
staging API compatibility service, which works with Filament's Gradle setup.</p>
<hr />
<h3 id="1-upload-to-the-staging-api-compatibility-service"><a class="header" href="#1-upload-to-the-staging-api-compatibility-service">1. Upload to the Staging API Compatibility Service</a></h3>
<pre><code class="language-bash">cd android
./gradlew publishToSonatype
</code></pre>
<h3 id="2-move-the-repository-to-the-central-publisher-portal"><a class="header" href="#2-move-the-repository-to-the-central-publisher-portal">2. Move the Repository to the Central Publisher Portal</a></h3>
<p>We have a script to automate this. It reads the <code>sonatypeUsername</code> and <code>sonatypePassword</code> from your
<code>~/.gradle/gradle.properties</code> file.</p>
<pre><code class="language-bash">python3 build/common/close-sonatype-staging-repository.py
</code></pre>
<h3 id="3-publish-the-release-on-sonatype"><a class="header" href="#3-publish-the-release-on-sonatype">3. Publish the Release on Sonatype</a></h3>
<p>Navigate to <a href="https://central.sonatype.com/publishing/deployments">Maven Central Repository Deployments</a>.</p>
<p>Here, you should see a new deployment with a <strong>Validated</strong> status and all your artifacts listed. Click
the <strong>Publish</strong> button to publish the artifacts. It typically takes around 5 minutes after clicking
<strong>Publish</strong> for the artifacts to go live.</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../build/windows_android.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../dup/contributing.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../build/windows_android.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../dup/contributing.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../elasticlunr.min.js"></script>
<script src="../mark.min.js"></script>
<script src="../searcher.js"></script>
<script src="../clipboard.min.js"></script>
<script src="../highlight.js"></script>
<script src="../book.js"></script>
<!-- Custom JS scripts -->
</div>
</body>
</html>
+2 -2
View File
@@ -273,7 +273,7 @@ copy filament-android\build\outputs\aar\filament-android-release.aar ..\..\out\
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../dup/contributing.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<a rel="next prefetch" href="../build/maven_release.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
@@ -287,7 +287,7 @@ copy filament-android\build\outputs\aar\filament-android-release.aar ..\..\out\
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../dup/contributing.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<a rel="next prefetch" href="../build/maven_release.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
+2 -2
View File
@@ -209,7 +209,7 @@ as possible. The current external dependencies of the runtime library include:</
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../build/windows_android.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<a rel="prev" href="../build/maven_release.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
@@ -223,7 +223,7 @@ as possible. The current external dependencies of the runtime library include:</
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../build/windows_android.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<a rel="prev" href="../build/maven_release.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
+2 -2
View File
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.64.0'
implementation 'com.google.android.filament:filament-android:1.65.1'
}
</code></pre>
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
@@ -196,7 +196,7 @@ dependencies {
</div>
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
<p>iOS projects can use CocoaPods to install the latest release:</p>
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.64.0'
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.65.1'
</code></pre>
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
<ul>
+334
View File
@@ -0,0 +1,334 @@
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>renderdiff - 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="rendering-difference-test"><a class="header" href="#rendering-difference-test">Rendering Difference Test</a></h1>
<p>We created a few scripts to run <code>gltf_viewer</code> and produce headless renderings.</p>
<p>This is mainly useful for continuous integration where GPUs are generally not available on cloud
machines. To perform software rasterization, these scripts are centered around <a href="https://docs.mesa3d.org">Mesa</a>'s software
rasterizers, but nothing bars us from using another rasterizer like <a href="https://github.com/google/swiftshader">SwiftShader</a>. Additionally,
we should be able to use GPUs where available (though this is more of a future work).</p>
<p>The script <code>render.py</code> contains the core logic for taking input parameters (such as the test
description file) and then running gltf_viewer to produce the renderings.</p>
<p>In the <code>test</code> directory is a list of test descriptions that are specified in json. Please see
<code>sample.json</code> to parse the structure.</p>
<h2 id="setting-up-python"><a class="header" href="#setting-up-python">Setting up python</a></h2>
<p>The <code>renderdiff</code> project uses <code>python</code> extensively. To install the dependencies for producing
renderings, do the following step</p>
<ul>
<li>Set up a virtual environment (from the root directory)
<pre><code> python3 -m venv venv
. ./venv/bin/activate
</code></pre>
</li>
<li>Install the rendering dependencies
<pre><code> pip install -r test/renderdiff/src/rendering_requirements.txt
</code></pre>
</li>
<li>Install the viewer depdencies
<pre><code> pip install -r test/renderdiff/src/viewer_requirements.txt
</code></pre>
</li>
<li>For the commands in the following section, do not exit the virtual environment. Once you've
completed all your work, you can exit with
<pre><code> deactivate
</code></pre>
</li>
</ul>
<h2 id="running-the-test-locally"><a class="header" href="#running-the-test-locally">Running the test locally</a></h2>
<ul>
<li>
<p>To run the same presbumit as <a href="presubmit-renderdiff"><code>test-renderdiff</code></a>, you can do</p>
<pre><code>bash test/renderdiff/local_test.sh
</code></pre>
</li>
<li>
<p>This script will generate the renderings based on the current state of your repo.
Additionally, it will also compare the generated images with corresponding images in the
golden repo.</p>
</li>
<li>
<p>To just render without running the test, you could use the following script</p>
<pre><code>bash test/renderdiff/generate.sh
</code></pre>
</li>
</ul>
<h2 id="update-the-golden-images"><a class="header" href="#update-the-golden-images">Update the golden images</a></h2>
<p>The golden images are stored in a github repository: https://github.com/google/filament-assets.
Filament team members should have access to write to the repository. A typical flow for updating
the goldens is to upload your changed images into <strong>branch</strong> of <code>filament-assets</code>. This branch is
paired with a PR or commit on the <code>filament</code> repo.</p>
<p>As an example, imagine I am working on a PR, and I've uploaded my change, which is in a branch
called <code>my-pr-branch</code>, to <code>filament</code>. This PR requires updating the golden. We would do it
in the following fashion</p>
<h3 id="using-a-script-to-update-the-golden-repo"><a class="header" href="#using-a-script-to-update-the-golden-repo">Using a script to update the golden repo</a></h3>
<ul>
<li>
<p>Run interactive mode in the <code>update_golden.py</code> script.</p>
<pre><code>python3 test/renderdiff/src/update_golden.py
</code></pre>
</li>
<li>
<p>This will guide you through a series of steps to push the changes to a remote branch
on <code>filament-assets</code>.</p>
</li>
</ul>
<h3 id="manually-updating-the-golden-repo"><a class="header" href="#manually-updating-the-golden-repo">Manually updating the golden repo</a></h3>
<ul>
<li>
<p>Check out the golden repo</p>
<pre><code>git clone git@github.com:google/filament-assets.git
</code></pre>
</li>
<li>
<p>Create a branch on the golden repo</p>
<pre><code>cd filament-assets
git switch -c my-pr-branch-golden
</code></pre>
</li>
<li>
<p>Copy the new images to their appropriate place in <code>filament-assets</code></p>
</li>
<li>
<p>Push the <code>filament-assets</code> working branch to remote</p>
<pre><code>git push origin my-pr-branch-golden
</code></pre>
</li>
<li>
<p>In the commit message of your working branch on <code>filament</code>, add the following line</p>
<pre><code>RDIFF_BBRANCH=my-pr-branch-golden
</code></pre>
</li>
</ul>
<p>Doing the above has multiple effects:</p>
<ul>
<li>The presubmit test <a href="https://github.com/google/filament/blob/e85dfe75c86106a05019e13ccdbef67e030af675/.github/workflows/presubmit.yml#L118"><code>test-renderdiff</code></a> will test against the provided
branch of the golden repo (i.e. <code>my-pr-branch-golden</code>).</li>
<li>If the PR is merged, then there is another workflow that will merge <code>my-pr-branch-golden</code> to
the <code>main</code> branch of the golden repo.</li>
</ul>
<h2 id="viewing-test-results"><a class="header" href="#viewing-test-results">Viewing test results</a></h2>
<p>We provide a viewer for looking at the result of a test run. The viewer is a webapp that can be used by
pointing your browser to a localhost port. If you input the viewer with a PR or a directory, it will
parse the test result and show the results and the rendered and/or golden images.</p>
<p><img src="../images/renderdiff_example.png" alt="Viewer" /></p>
<p>To run the viewer of a test output directory that has been generated locally, you would run the
following</p>
<pre><code>python3 test/renderdiff/src/viewer.py --diff=[test output]
</code></pre>
<p>where <code>[test output]</code> is a directory containing the <code>compare_results.json</code> of the test run.
For example, it could be <code>out/renderdiff/diffs/presubmit</code> for the standard path to the
<code>presubmit</code> test output.</p>
<p>To see the results of a Pull Request initiated test run, you would do the following</p>
<pre><code>python3 test/renderdiff/src/viewer.py --pr_number=[PR #] --github_token=[github token]
</code></pre>
<p>where <code>[PR #]</code> is the numeric ID of your pull request, and the <code>[github token]</code> is an acess token
that you (as a github user) needs to generate (<a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens">reference</a>).</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../notes/tests.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../notes/libs.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../notes/tests.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../notes/libs.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../elasticlunr.min.js"></script>
<script src="../mark.min.js"></script>
<script src="../searcher.js"></script>
<script src="../clipboard.min.js"></script>
<script src="../highlight.js"></script>
<script src="../book.js"></script>
<!-- Custom JS scripts -->
</div>
</body>
</html>
+1 -1
View File
@@ -162,7 +162,7 @@
<p>This tool makes it easy to analyze the composition of Android applications that use Filament.</p>
<ul>
<li>Filament materials are shown in the treemap if <code>resgen --json</code> was used in the build.</li>
<li>The input path can be a so file, folder, or zip archive (apk or aar).</li>
<li>The input path can be a <code>.so</code> or <code>.a</code> file, a folder, or a zip archive (apk or aar).</li>
<li>If the path is a zip or folder, interactively finds the file to analyze.</li>
<li>The generated web report is a self-contained HTML file.</li>
<li>Reports the gzipped size of all Filament materials.</li>
Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

+4 -4
View File
@@ -178,9 +178,9 @@ content:counter(h1) "."counter(h2) "."counter(h3) "."counter(h4) "."counter(h5)
counter-increment: h6;margin-right:10px}
</style><style>.hljs{display:block;overflow-x:auto;padding:0.5em;background:#fff;color:#000;-webkit-text-size-adjust:none}.hljs-comment{color:#006a00}.hljs-keyword{color:#02E}.hljs-literal,.nginx .hljs-title{color:#aa0d91}.method,.hljs-list .hljs-title,.hljs-tag .hljs-title,.setting .hljs-value,.hljs-winutils,.tex .hljs-command,.http .hljs-title,.hljs-request,.hljs-status,.hljs-name{color:#008}.hljs-envvar,.tex .hljs-special{color:#660}.hljs-string{color:#c41a16}.hljs-tag .hljs-value,.hljs-cdata,.hljs-filter .hljs-argument,.hljs-attr_selector,.apache .hljs-cbracket,.hljs-date,.hljs-regexp{color:#080}.hljs-sub .hljs-identifier,.hljs-pi,.hljs-tag,.hljs-tag .hljs-keyword,.hljs-decorator,.ini .hljs-title,.hljs-shebang,.hljs-prompt,.hljs-hexcolor,.hljs-rule .hljs-value,.hljs-symbol,.hljs-symbol .hljs-string,.hljs-number,.css .hljs-function,.hljs-function .hljs-title,.coffeescript .hljs-attribute{color:#A0C}.hljs-function .hljs-title{font-weight:bold;color:#000}.hljs-class .hljs-title,.smalltalk .hljs-class,.hljs-type,.hljs-typename,.hljs-tag .hljs-attribute,.hljs-doctype,.hljs-class .hljs-id,.hljs-built_in,.setting,.hljs-params,.clojure .hljs-attribute{color:#5c2699}.hljs-variable{color:#3f6e74}.css .hljs-tag,.hljs-rule .hljs-property,.hljs-pseudo,.hljs-subst{color:#000}.css .hljs-class,.css .hljs-id{color:#9b703f}.hljs-value .hljs-important{color:#ff7700;font-weight:bold}.hljs-rule .hljs-keyword{color:#c5af75}.hljs-annotation,.apache .hljs-sqbracket,.nginx .hljs-built_in{color:#9b859d}.hljs-preprocessor,.hljs-preprocessor *,.hljs-pragma{color:#643820}.tex .hljs-formula{background-color:#eee;font-style:italic}.diff .hljs-header,.hljs-chunk{color:#808080;font-weight:bold}.diff .hljs-change{background-color:#bccff9}.hljs-addition{background-color:#baeeba}.hljs-deletion{background-color:#ffc8bd}.hljs-comment .hljs-doctag{font-weight:bold}.method .hljs-id{color:#000}</style><style>div.title { padding-top: 40px; } div.afterTitles { height: 15px; }</style><meta charset="utf-8"></p>
<style>img { max-width: 100%; }</style>
<script type="text/x-mathjax-config">MathJax.Hub.Config({ TeX: { equationNumbers: {autoNumber: "AMS"} } });</script><span style="display:none">$$\newcommand{\n}{\hat{n}}\newcommand{\thetai}{\theta_\mathrm{i}}\newcommand{\thetao}{\theta_\mathrm{o}}\newcommand{\d}[1]{\mathrm{d}#1}\newcommand{\w}{\hat{\omega}}\newcommand{\wi}{\w_\mathrm{i}}\newcommand{\wo}{\w_\mathrm{o}}\newcommand{\wh}{\w_\mathrm{h}}\newcommand{\Li}{L_\mathrm{i}}\newcommand{\Lo}{L_\mathrm{o}}\newcommand{\Le}{L_\mathrm{e}}\newcommand{\Lr}{L_\mathrm{r}}\newcommand{\Lt}{L_\mathrm{t}}\newcommand{\O}{\mathrm{O}}\newcommand{\degrees}{{^{\large\circ}}}\newcommand{\T}{\mathsf{T}}\newcommand{\mathset}[1]{\mathbb{#1}}\newcommand{\Real}{\mathset{R}}\newcommand{\Integer}{\mathset{Z}}\newcommand{\Boolean}{\mathset{B}}\newcommand{\Complex}{\mathset{C}}\newcommand{\un}[1]{\,\mathrm{#1}}$$
<p><span style="display:none">$$\newcommand{\n}{\hat{n}}\newcommand{\thetai}{\theta_\mathrm{i}}\newcommand{\thetao}{\theta_\mathrm{o}}\newcommand{\d}[1]{\mathrm{d}#1}\newcommand{\w}{\hat{\omega}}\newcommand{\wi}{\w_\mathrm{i}}\newcommand{\wo}{\w_\mathrm{o}}\newcommand{\wh}{\w_\mathrm{h}}\newcommand{\Li}{L_\mathrm{i}}\newcommand{\Lo}{L_\mathrm{o}}\newcommand{\Le}{L_\mathrm{e}}\newcommand{\Lr}{L_\mathrm{r}}\newcommand{\Lt}{L_\mathrm{t}}\newcommand{\O}{\mathrm{O}}\newcommand{\degrees}{{^{\large\circ}}}\newcommand{\T}{\mathsf{T}}\newcommand{\mathset}[1]{\mathbb{#1}}\newcommand{\Real}{\mathset{R}}\newcommand{\Integer}{\mathset{Z}}\newcommand{\Boolean}{\mathset{B}}\newcommand{\Complex}{\mathset{C}}\newcommand{\un}[1]{,\mathrm{#1}}$$
</span>
<span class="md"><p><title>Physically Based Rendering in Filament</title><div class="title"> Physically Based Rendering in Filament </div>
<span class="md"><p><title>Physically Based Rendering in Filament</title><div class="title"> Physically Based Rendering in Filament </div></p>
<div class="afterTitles"></div>
</p><p>
</p>
@@ -2060,7 +2060,7 @@ In practice only 4 or 9 coefficients (i.e.: 2 or 3 bands) are enough for \(\cosT
In practice we pre-convolve \(\Lt\) with \(\cosTheta\) and pre-scale these coefficients by the basis scaling factors \(K_l^m\) so that the reconstruction code is as simple as possible in the shader:
</p><a class="target" name="listing_irradiancesh">&nbsp;</a><pre class="listing tilde"><code><span class="line"></span>vec3 irradianceSH(vec3 n) {
<span class="line"></span> // uniform vec3 sphericalHarmonics<span class="hljs-selector-attr">[9]</span>
<span class="line"></span> // We can use only the first <span class="hljs-number">2</span> bands for better performance
<span class="line"></span> // We can <span class="hljs-selector-tag">use</span> only the first <span class="hljs-number">2</span> bands for better performance
<span class="line"></span> return
<span class="line"></span> sphericalHarmonics<span class="hljs-selector-attr">[0]</span>
<span class="line"></span> + sphericalHarmonics<span class="hljs-selector-attr">[1]</span> * (n<span class="hljs-selector-class">.y</span>)
@@ -4395,7 +4395,7 @@ For Fast Realistic Image Display. <em class="asterisk">SIGGRAPH '00 Proceedings
</div><div class="bib">[<a class="target" name="citation-schlick94">&nbsp;</a><b>Schlick94</b>] Christophe Schlick. 1994. An Inexpensive BRDF Model for Physically-Based Rendering. <em class="asterisk">Computer Graphics Forum</em>, 13 (3), 233–246.
</div><div class="bib">[<a class="target" name="citation-walter07">&nbsp;</a><b>Walter07</b>] Bruce Walter et al. 2007. Microfacet Models for Refraction through Rough Surfaces. <em class="asterisk">Proceedings of the Eurographics Symposium on Rendering</em>.
</div>
</sample></float></sample></float></float></p></span><div id="mdContextMenu" style="visibility:hidden"></div><div class="markdeepFooter"><i>formatted by <a href="https://casual-effects.com/markdeep" style="color:#999">Markdeep&nbsp;1.18&nbsp;&nbsp;</a></i><div style="display:inline-block;font-size:13px;font-family:'Times New Roman',serif;vertical-align:middle;transform:translate(-3px,-1px)rotate(135deg);">&#x2712;</div></div><script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.6/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</sample></float></sample></float></float></p></span><div id="mdContextMenu" style="visibility:hidden"></div><div class="markdeepFooter"><i>formatted by <a href="https://casual-effects.com/markdeep" style="color:#999">Markdeep&nbsp;1.18&nbsp;&nbsp;</a></i><div style="display:inline-block;font-size:13px;font-family:'Times New Roman',serif;vertical-align:middle;transform:translate(-3px,-1px)rotate(135deg);">&#x2712;</div></div><script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
+12 -9
View File
@@ -178,9 +178,9 @@ content:counter(h1) "."counter(h2) "."counter(h3) "."counter(h4) "."counter(h5)
counter-increment: h6;margin-right:10px}
</style><style>.hljs{display:block;overflow-x:auto;padding:0.5em;background:#fff;color:#000;-webkit-text-size-adjust:none}.hljs-comment{color:#006a00}.hljs-keyword{color:#02E}.hljs-literal,.nginx .hljs-title{color:#aa0d91}.method,.hljs-list .hljs-title,.hljs-tag .hljs-title,.setting .hljs-value,.hljs-winutils,.tex .hljs-command,.http .hljs-title,.hljs-request,.hljs-status,.hljs-name{color:#008}.hljs-envvar,.tex .hljs-special{color:#660}.hljs-string{color:#c41a16}.hljs-tag .hljs-value,.hljs-cdata,.hljs-filter .hljs-argument,.hljs-attr_selector,.apache .hljs-cbracket,.hljs-date,.hljs-regexp{color:#080}.hljs-sub .hljs-identifier,.hljs-pi,.hljs-tag,.hljs-tag .hljs-keyword,.hljs-decorator,.ini .hljs-title,.hljs-shebang,.hljs-prompt,.hljs-hexcolor,.hljs-rule .hljs-value,.hljs-symbol,.hljs-symbol .hljs-string,.hljs-number,.css .hljs-function,.hljs-function .hljs-title,.coffeescript .hljs-attribute{color:#A0C}.hljs-function .hljs-title{font-weight:bold;color:#000}.hljs-class .hljs-title,.smalltalk .hljs-class,.hljs-type,.hljs-typename,.hljs-tag .hljs-attribute,.hljs-doctype,.hljs-class .hljs-id,.hljs-built_in,.setting,.hljs-params,.clojure .hljs-attribute{color:#5c2699}.hljs-variable{color:#3f6e74}.css .hljs-tag,.hljs-rule .hljs-property,.hljs-pseudo,.hljs-subst{color:#000}.css .hljs-class,.css .hljs-id{color:#9b703f}.hljs-value .hljs-important{color:#ff7700;font-weight:bold}.hljs-rule .hljs-keyword{color:#c5af75}.hljs-annotation,.apache .hljs-sqbracket,.nginx .hljs-built_in{color:#9b859d}.hljs-preprocessor,.hljs-preprocessor *,.hljs-pragma{color:#643820}.tex .hljs-formula{background-color:#eee;font-style:italic}.diff .hljs-header,.hljs-chunk{color:#808080;font-weight:bold}.diff .hljs-change{background-color:#bccff9}.hljs-addition{background-color:#baeeba}.hljs-deletion{background-color:#ffc8bd}.hljs-comment .hljs-doctag{font-weight:bold}.method .hljs-id{color:#000}</style><style>div.title { padding-top: 40px; } div.afterTitles { height: 15px; }</style><meta charset="utf-8"></p>
<style>img { max-width: 100%; }</style>
<script type="text/x-mathjax-config">MathJax.Hub.Config({ TeX: { equationNumbers: {autoNumber: "AMS"} } });</script><span style="display:none">$$\newcommand{\n}{\hat{n}}\newcommand{\thetai}{\theta_\mathrm{i}}\newcommand{\thetao}{\theta_\mathrm{o}}\newcommand{\d}[1]{\mathrm{d}#1}\newcommand{\w}{\hat{\omega}}\newcommand{\wi}{\w_\mathrm{i}}\newcommand{\wo}{\w_\mathrm{o}}\newcommand{\wh}{\w_\mathrm{h}}\newcommand{\Li}{L_\mathrm{i}}\newcommand{\Lo}{L_\mathrm{o}}\newcommand{\Le}{L_\mathrm{e}}\newcommand{\Lr}{L_\mathrm{r}}\newcommand{\Lt}{L_\mathrm{t}}\newcommand{\O}{\mathrm{O}}\newcommand{\degrees}{{^{\large\circ}}}\newcommand{\T}{\mathsf{T}}\newcommand{\mathset}[1]{\mathbb{#1}}\newcommand{\Real}{\mathset{R}}\newcommand{\Integer}{\mathset{Z}}\newcommand{\Boolean}{\mathset{B}}\newcommand{\Complex}{\mathset{C}}\newcommand{\un}[1]{\,\mathrm{#1}}$$
<p><span style="display:none">$$\newcommand{\n}{\hat{n}}\newcommand{\thetai}{\theta_\mathrm{i}}\newcommand{\thetao}{\theta_\mathrm{o}}\newcommand{\d}[1]{\mathrm{d}#1}\newcommand{\w}{\hat{\omega}}\newcommand{\wi}{\w_\mathrm{i}}\newcommand{\wo}{\w_\mathrm{o}}\newcommand{\wh}{\w_\mathrm{h}}\newcommand{\Li}{L_\mathrm{i}}\newcommand{\Lo}{L_\mathrm{o}}\newcommand{\Le}{L_\mathrm{e}}\newcommand{\Lr}{L_\mathrm{r}}\newcommand{\Lt}{L_\mathrm{t}}\newcommand{\O}{\mathrm{O}}\newcommand{\degrees}{{^{\large\circ}}}\newcommand{\T}{\mathsf{T}}\newcommand{\mathset}[1]{\mathbb{#1}}\newcommand{\Real}{\mathset{R}}\newcommand{\Integer}{\mathset{Z}}\newcommand{\Boolean}{\mathset{B}}\newcommand{\Complex}{\mathset{C}}\newcommand{\un}[1]{,\mathrm{#1}}$$
</span>
<span class="md"><p><title>Filament Materials Guide</title><div class="title"> Filament Materials Guide </div>
<span class="md"><p><title>Filament Materials Guide</title><div class="title"> Filament Materials Guide </div></p>
<div class="afterTitles"></div>
</p><p>
</p>
@@ -504,7 +504,7 @@ when using textures.</p>
This property can dramatically change the appearance of a surface. Non-metallic surfaces have
chromatic diffuse reflection and achromatic specular reflection (reflected light does not change
color). Metallic surfaces do not have any diffuse reflection and chromatic specular reflection
(reflected light takes on the color of the surfaced as defined by <code>baseColor</code>).
(reflected light takes on the color of the surfaced as defined by <code>baseColor</code>).
</p><p>
The effect of <code>metallic</code> is shown in <a href="#figure_metallicproperty" target="_self">figure&nbsp;1</a> (click on the image to see a
larger version).
@@ -597,7 +597,7 @@ indices.
base layer described by the properties above. The sheen layer always sits below the clear coat layer
if such a layer is present.</p>
</p><p>
The sheen layer can be used to represent cloth and fabric materials. Please refer to
The sheen layer can be used to represent cloth and fabric materials. Please refer to
section <a href="#toc3.3" target="_self">3.3</a> for more information about cloth and fabric materials.
</p><p>
The effect of <code>sheenColor</code> is shown in <a href="#figure_materialsheencolor" target="_self">figure&nbsp;7</a>
@@ -1220,10 +1220,10 @@ non-shader data.</p>
name must be a valid GLSL identifier. Entries have an optional <code>precision</code>, which can be
one of <code>default</code> (best precision for the platform, typically <code>high</code> on desktop, <code>medium</code> on
mobile), <code>low</code>, <code>medium</code>, <code>high</code>. The type must be one of the types described in
<a href="#table_materialparamstypes" target="_self">table&nbsp;14</a>. For Android external textures, entries also have an optional
transformName parameter to specify the name of the material parameter that will be
<a href="#table_materialparamstypes" target="_self">table&nbsp;14</a>. For Android external textures, entries also have an optional
transformName parameter to specify the name of the material parameter that will be
used to expose the transform matrix associated with the external sampler. In iOS and Vulkan,
this will always be identity.
this will always be identity.
</p></dd></dl><div class='table'>
<a class="target" name="table_materialparamstypes">&nbsp;</a><table class="table longtable"><tr><th style="text-align:left"> Type </th><th style="text-align:left"> Description </th></tr>
<tr><td style="text-align:left"> bool </td><td style="text-align:left"> Single boolean </td></tr>
@@ -2361,7 +2361,10 @@ type aliases:</p>
<tr><td style="text-align:left"> <strong class="asterisk">getWorldFromViewMatrix()</strong> </td><td style="text-align:center"> float4&times;4 </td><td style="text-align:left"> Matrix that converts from view/eye space to world space </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">getClipFromViewMatrix()</strong> </td><td style="text-align:center"> float4&times;4 </td><td style="text-align:left"> Matrix that converts from view/eye space to clip (NDC) space </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">getViewFromClipMatrix()</strong> </td><td style="text-align:center"> float4&times;4 </td><td style="text-align:left"> Matrix that converts from clip (NDC) space to view/eye space </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">getEyeFromViewMatrix()</strong> </td><td style="text-align:center"> float4&times;4 </td><td style="text-align:left"> Matrix that converts from view space to eye space </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">getEyeFromViewMatrix(int eyeIndex)</strong> </td><td style="text-align:center"> float4&times;4 </td><td style="text-align:left"> Matrix that converts from view space to eye space for the eye referred to by eyeIndex </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">getClipFromWorldMatrix()</strong> </td><td style="text-align:center"> float4&times;4 </td><td style="text-align:left"> Matrix that converts from world to clip (NDC) space </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">getClipFromWorldMatrix(int eyeIndex)</strong> </td><td style="text-align:center"> float4&times;4 </td><td style="text-align:left"> Matrix that converts from world to clip (NDC) space for the eye referred to by eyeIndex </td></tr>
<tr><td style="text-align:left"> <strong class="asterisk">getWorldFromClipMatrix()</strong> </td><td style="text-align:center"> float4&times;4 </td><td style="text-align:left"> Matrix that converts from clip (NDC) space to world space </td></tr>
</table></div>
</p>
@@ -2501,7 +2504,7 @@ a good compromise between runtime performance and size.</p>
metadata in JSON format. The example below prints out the list of parameters defined in Filament's
standard skybox material. It produces a list of 2 parameters, named <code>showSun</code> and <code>skybox</code>,
respectively a boolean and a cubemap texture.</p>
</p><pre class="listing backtick"><code><span class="line"></span>$ matc --reflect parameters filament/src/materials/skybox.mat
</p><pre class="listing backtick"><code><span class="line"></span>$ matc --reflect parameters filament/src/materials/skybox.mat
<span class="line"></span>{
<span class="line"></span> &quot;parameters&quot;: [
<span class="line"></span> {
@@ -2600,7 +2603,7 @@ Finally if <code>variantFilter</code> contains the <code>fog</code> filter, an e
</p><p>
Be aware that <code>external</code> samplers account for 2 regular samplers.</div>
</p><p>
</p></span><div id="mdContextMenu" style="visibility:hidden"></div><div class="markdeepFooter"><i>formatted by <a href="https://casual-effects.com/markdeep" style="color:#999">Markdeep&nbsp;1.18&nbsp;&nbsp;</a></i><div style="display:inline-block;font-size:13px;font-family:'Times New Roman',serif;vertical-align:middle;transform:translate(-3px,-1px)rotate(135deg);">&#x2712;</div></div><script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.6/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
</p></span><div id="mdContextMenu" style="visibility:hidden"></div><div class="markdeepFooter"><i>formatted by <a href="https://casual-effects.com/markdeep" style="color:#999">Markdeep&nbsp;1.18&nbsp;&nbsp;</a></i><div style="display:inline-block;font-size:13px;font-family:'Times New Roman',serif;vertical-align:middle;transform:translate(-3px,-1px)rotate(135deg);">&#x2712;</div></div><script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js"></script>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
+2 -2
View File
@@ -335,7 +335,7 @@ nodes</li>
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../notes/libs.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<a rel="next prefetch" href="../notes/tests.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
@@ -349,7 +349,7 @@ nodes</li>
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../notes/libs.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<a rel="next prefetch" href="../notes/tests.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
+2 -2
View File
@@ -165,7 +165,7 @@
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../notes/framegraph.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<a rel="prev" href="../dup/renderdiff.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
@@ -179,7 +179,7 @@
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../notes/framegraph.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<a rel="prev" href="../dup/renderdiff.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
+215
View File
@@ -0,0 +1,215 @@
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Tests - 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="tests"><a class="header" href="#tests">Tests</a></h1>
<p>Filament has a collection of tests that can be run locally. Many of them are used
for in our Continuation Integration flow <a href="https://https://github.com/google/filament/tree/main/.github/workflows">on github</a>.</p>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../notes/framegraph.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/renderdiff.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../notes/framegraph.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/renderdiff.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>
File diff suppressed because one or more lines are too long
Binary file not shown.
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+6
View File
@@ -78,5 +78,11 @@
},
"docs_src/README.md": {
"dest": "dup/docs.md"
},
"test/renderdiff/README.md": {
"dest": "dup/renderdiff.md",
"link_transforms": {
"docs/images/": "../images/"
}
}
}
+25 -22
View File
@@ -212,7 +212,7 @@ when using textures.
This property can dramatically change the appearance of a surface. Non-metallic surfaces have
chromatic diffuse reflection and achromatic specular reflection (reflected light does not change
color). Metallic surfaces do not have any diffuse reflection and chromatic specular reflection
(reflected light takes on the color of the surfaced as defined by `baseColor`).
(reflected light takes on the color of the surfaced as defined by `baseColor`).
The effect of `metallic` is shown in figure [metallicProperty] (click on the image to see a
larger version).
@@ -248,7 +248,7 @@ The effect of `roughness` on metallic surfaces is shown in figure [roughnessCond
When refraction through an object is enabled (using a `refractonType` of `thin` or `solid`), the
`roughness` property will also affect the refractions, as shown in figure
[roughnessRefractionProperty] (click on the image to see a larger version).
![Figure [roughnessRefractionProperty]: Refractive sphere with `roughness` varying from 0.0
(left) to 1.0 (right)](images/materials/refraction_roughness.png)
@@ -307,7 +307,7 @@ The sheen color controls the color appearance and strength of an optional sheen
base layer described by the properties above. The sheen layer always sits below the clear coat layer
if such a layer is present.
The sheen layer can be used to represent cloth and fabric materials. Please refer to
The sheen layer can be used to represent cloth and fabric materials. Please refer to
section [Cloth model] for more information about cloth and fabric materials.
The effect of `sheenColor` is shown in figure [materialSheenColor]
@@ -520,14 +520,14 @@ light to bend further away from the initial path.
Table [commonMatIOR] describes acceptable refractive indices for various types of materials.
Material | IOR
Material | IOR
--------------------------:|:-----------------
Air | 1.0
Water | 1.33
Common liquids | 1.33 to 1.5
Common gemstones | 1.58 to 2.33
Plastics, glass | 1.5 to 1.58
Other dielectric materials | 1.33 to 1.58
Water | 1.33
Common liquids | 1.33 to 1.5
Common gemstones | 1.58 to 2.33
Plastics, glass | 1.5 to 1.58
Other dielectric materials | 1.33 to 1.58
[Table [commonMatIOR]: Index of refraction of common materials]
The appearance of a refractive material will greatly depend on the `refractionType` and
@@ -995,10 +995,10 @@ Value
name must be a valid GLSL identifier. Entries have an optional `precision`, which can be
one of `default` (best precision for the platform, typically `high` on desktop, `medium` on
mobile), `low`, `medium`, `high`. The type must be one of the types described in
table [materialParamsTypes]. For Android external textures, entries also have an optional
transformName parameter to specify the name of the material parameter that will be
table [materialParamsTypes]. For Android external textures, entries also have an optional
transformName parameter to specify the name of the material parameter that will be
used to expose the transform matrix associated with the external sampler. In iOS and Vulkan,
this will always be identity.
this will always be identity.
Type | Description
:----------------------|:---------------------------------
@@ -2476,14 +2476,17 @@ type aliases:
### Matrices
Name | Type | Description
:-----------------------------------|:--------:|:------------------------------------
**getViewFromWorldMatrix()** | float4x4 | Matrix that converts from world space to view/eye space
**getWorldFromViewMatrix()** | float4x4 | Matrix that converts from view/eye space to world space
**getClipFromViewMatrix()** | float4x4 | Matrix that converts from view/eye space to clip (NDC) space
**getViewFromClipMatrix()** | float4x4 | Matrix that converts from clip (NDC) space to view/eye space
**getClipFromWorldMatrix()** | float4x4 | Matrix that converts from world to clip (NDC) space
**getWorldFromClipMatrix()** | float4x4 | Matrix that converts from clip (NDC) space to world space
Name | Type | Description
:-----------------------------------------|:--------:|:------------------------------------
**getViewFromWorldMatrix()** | float4x4 | Matrix that converts from world space to view/eye space
**getWorldFromViewMatrix()** | float4x4 | Matrix that converts from view/eye space to world space
**getClipFromViewMatrix()** | float4x4 | Matrix that converts from view/eye space to clip (NDC) space
**getViewFromClipMatrix()** | float4x4 | Matrix that converts from clip (NDC) space to view/eye space
**getEyeFromViewMatrix()** | float4x4 | Matrix that converts from view space to eye space
**getEyeFromViewMatrix(int eyeIndex)** | float4x4 | Matrix that converts from view space to eye space for the eye referred to by eyeIndex
**getClipFromWorldMatrix()** | float4x4 | Matrix that converts from world to clip (NDC) space
**getClipFromWorldMatrix(int eyeIndex)** | float4x4 | Matrix that converts from world to clip (NDC) space for the eye referred to by eyeIndex
**getWorldFromClipMatrix()** | float4x4 | Matrix that converts from clip (NDC) space to world space
### Frame constants
@@ -2639,7 +2642,7 @@ standard skybox material. It produces a list of 2 parameters, named `showSun` an
respectively a boolean and a cubemap texture.
```text
$ matc --reflect parameters filament/src/materials/skybox.mat
$ matc --reflect parameters filament/src/materials/skybox.mat
{
"parameters": [
{
@@ -2655,7 +2658,7 @@ $ matc --reflect parameters filament/src/materials/skybox.mat
}
]
}
```
```
### --variant-filter
+3
View File
@@ -3,6 +3,7 @@
- [Introduction](./dup/intro.md)
- [Build](./dup/building.md)
- [Build for Android on Windows](./build/windows_android.md)
- [Maven Release](./build/maven_release.md)
- [Contribute](./dup/contributing.md)
- [Coding Style](./dup/code_style.md)
- [Core Concepts](./main/README.md)
@@ -26,6 +27,8 @@
- [Code coverage analysis](./notes/coverage.md)
- [Performance analysis](./notes/performance_analysis.md)
- [Framegraph](./notes/framegraph.md)
- [Tests](./notes/tests.md)
- [renderdiff](./dup/renderdiff.md)
- [Libraries](./notes/libs.md)
- [bluegl](./dup/bluegl.md)
- [bluevk](./dup/bluevk.md)
@@ -0,0 +1,89 @@
# Maven Release
## Register for a Sonatype Account
First, you'll need to register for a Sonatype account at the [Central
Portal](https://central.sonatype.org/register/central-portal/).
To publish under the `com.google.android` namespace, you'll also need to email
`central-support@sonatype.com` with your account information to request access.
Then, generate a user token through the Sonatype website. Navigate to
[https://central.sonatype.com/account](https://central.sonatype.com/account) and select **Generate
User Token**.
Finally, add the generated token credentials to `~/.gradle/gradle.properties`. (Note: these are
different from the credentials used to log into your Sonatype account):
```gradle
# Generated Sonatype token
sonatypeUsername=<username>
sonatypePassword=<password>
```
-----
## Signing Key
All release artifacts must be signed. You'll need to create an OpenPGP key pair. See Gradle's
documentation on [Signatory
credentials](https://docs.gradle.org/current/userguide/signing_plugin.html#sec:signatory_credentials)
for instructions.
Update `~/.gradle/gradle.properties` with your new key's credentials:
```gradle
signing.keyId=<key id>
signing.password=<key password>
signing.secretKeyRingFile=<secret key ring file>
```
-----
## Build the Android Release
Make sure `JAVA_HOME` is set correctly. For example:
```bash
export JAVA_HOME=/Library/Java/JavaVirtualMachines/zulu-17.jdk/Contents/Home
./build.sh -C -i -p android release
```
-----
## Publish to Sonatype
### A Note on the Legacy Staging Service
Previously, Filament was published to Maven via OSSRH (Open Source Software Repository Hosting). In
2025, this service was [sunsetted](https://central.sonatype.org/pages/ossrh-eol/). Now, we use
Sonatype's [Central Publisher Portal](https://central.sonatype.org/).
The new Central Publisher Portal does not officially support Gradle. However, Sonatype provides a
staging API compatibility service, which works with Filament's Gradle setup.
-----
### 1\. Upload to the Staging API Compatibility Service
```bash
cd android
./gradlew publishToSonatype
```
### 2\. Move the Repository to the Central Publisher Portal
We have a script to automate this. It reads the `sonatypeUsername` and `sonatypePassword` from your
`~/.gradle/gradle.properties` file.
```bash
python3 build/common/close-sonatype-staging-repository.py
```
### 3\. Publish the Release on Sonatype
Navigate to [Maven Central Repository Deployments](https://central.sonatype.com/publishing/deployments).
Here, you should see a new deployment with a **Validated** status and all your artifacts listed. Click
the **Publish** button to publish the artifacts. It typically takes around 5 minutes after clicking
**Publish** for the artifacts to go live.
Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

+4
View File
@@ -0,0 +1,4 @@
# Tests
Filament has a collection of tests that can be run locally. Many of them are used
for in our Continuation Integration flow [on github](https://https://github.com/google/filament/tree/main/.github/workflows).
File diff suppressed because one or more lines are too long
Binary file not shown.
+7
View File
@@ -40,6 +40,7 @@ set(PUBLIC_HDRS
include/filament/Skybox.h
include/filament/Stream.h
include/filament/SwapChain.h
include/filament/Sync.h
include/filament/Texture.h
include/filament/TextureSampler.h
include/filament/ToneMapper.h
@@ -83,6 +84,8 @@ set(SRCS
src/InstanceBuffer.cpp
src/LightManager.cpp
src/Material.cpp
src/MaterialCache.cpp
src/MaterialDefinition.cpp
src/MaterialInstance.cpp
src/MaterialInstanceManager.cpp
src/MaterialParser.cpp
@@ -103,6 +106,7 @@ set(SRCS
src/Skybox.cpp
src/Stream.cpp
src/SwapChain.cpp
src/Sync.cpp
src/Texture.cpp
src/ToneMapper.cpp
src/TransformManager.cpp
@@ -132,6 +136,7 @@ set(SRCS
src/details/Skybox.cpp
src/details/Stream.cpp
src/details/SwapChain.cpp
src/details/Sync.cpp
src/details/Texture.cpp
src/details/VertexBuffer.cpp
src/details/View.cpp
@@ -172,6 +177,8 @@ set(PRIVATE_HDRS
src/HwRenderPrimitiveFactory.h
src/HwVertexBufferInfoFactory.h
src/Intersections.h
src/MaterialCache.h
src/MaterialDefinition.h
src/MaterialParser.h
src/MaterialInstanceManager.h
src/PIDController.h
+4 -3
View File
@@ -78,6 +78,8 @@ if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3)
src/opengl/GLTexture.h
src/opengl/GLUtils.cpp
src/opengl/GLUtils.h
src/opengl/GLMemoryMappedBuffer.cpp
src/opengl/GLMemoryMappedBuffer.h
src/opengl/OpenGLBlobCache.cpp
src/opengl/OpenGLBlobCache.h
src/opengl/OpenGLContext.cpp
@@ -295,6 +297,7 @@ if (FILAMENT_SUPPORTS_WEBGPU)
src/webgpu/WebGPUStrings.h
src/webgpu/WebGPUSwapChain.cpp
src/webgpu/WebGPUSwapChain.h
src/webgpu/WebGPUSync.h
src/webgpu/WebGPUTexture.cpp
src/webgpu/WebGPUTexture.h
src/webgpu/WebGPUTextureHelpers.h
@@ -494,9 +497,6 @@ else()
-Wover-aligned -Wreorder-init-list
-Werror
)
if (CMAKE_CXX_STANDARD EQUAL 20)
list(APPEND FILAMENT_WARNINGS -Wno-deprecated-this-capture)
endif()
endif()
if (APPLE)
@@ -568,6 +568,7 @@ if (APPLE OR LINUX)
test/test_ReadPixels.cpp
test/test_BufferUpdates.cpp
test/test_Callbacks.cpp
test/test_MemoryMappedBuffer.cpp
test/test_MRT.cpp
test/test_PushConstants.cpp
test/test_LoadImage.cpp
+37 -4
View File
@@ -101,6 +101,16 @@ static constexpr uint64_t SWAP_CHAIN_HAS_STENCIL_BUFFER = SWAP_CHAIN_CON
*/
static constexpr uint64_t SWAP_CHAIN_CONFIG_PROTECTED_CONTENT = 0x40;
/**
* Indicates that the SwapChain is configured to use Multi-Sample Anti-Aliasing (MSAA) with the
* given sample points within each pixel. Only supported when isMSAASwapChainSupported(4) is
* true.
*
* This is only supported by EGL(Android). Other GL platforms (GLX, WGL, etc) don't support it
* because the swapchain MSAA settings must be configured before window creation.
*/
static constexpr uint64_t SWAP_CHAIN_CONFIG_MSAA_4_SAMPLES = 0x80;
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_SAMPLER_COUNT = 62; // Maximum needed at feature level 3.
static constexpr size_t MAX_VERTEX_BUFFER_COUNT = 16; // Max number of bound buffer objects.
@@ -546,12 +556,21 @@ constexpr TargetBufferFlags getTargetBufferFlagsAt(size_t index) noexcept {
}
/**
* Frequency at which a buffer is expected to be modified and used. This is used as an hint
* for the driver to make better decisions about managing memory internally.
* How the buffer will be used.
*/
enum class BufferUsage : uint8_t {
STATIC, //!< content modified once, used many times
DYNAMIC, //!< content modified frequently, used many times
STATIC = 0, //!< (legacy) content modified once, used many times
DYNAMIC = 1, //!< (legacy) content modified frequently, used many times
DYNAMIC_BIT = 0x1, //!< buffer can be modified frequently, used many times
SHARED_WRITE_BIT = 0x04, //!< buffer can be memory mapped for write operations
};
/**
* How the buffer will be mapped.
*/
enum class MapBufferAccessFlags : uint8_t {
WRITE_BIT = 0x2, //!< buffer is mapped from writing
INVALIDATE_RANGE_BIT = 0x4, //!< the mapped range content is lost
};
/**
@@ -617,6 +636,15 @@ enum class ShaderModel : uint8_t {
};
static constexpr size_t SHADER_MODEL_COUNT = 2;
constexpr std::string_view to_string(ShaderModel model) noexcept {
switch (model) {
case ShaderModel::MOBILE:
return "mobile";
case ShaderModel::DESKTOP:
return "desktop";
}
}
/**
* Primitive types
*/
@@ -1682,6 +1710,11 @@ template<> struct utils::EnableBitMaskOperators<filament::backend::TextureUsage>
: public std::true_type {};
template<> struct utils::EnableBitMaskOperators<filament::backend::StencilFace>
: public std::true_type {};
template<> struct utils::EnableBitMaskOperators<filament::backend::BufferUsage>
: public std::true_type {};
template<> struct utils::EnableBitMaskOperators<filament::backend::MapBufferAccessFlags>
: public std::true_type {};
template<> struct utils::EnableIntegerOperators<filament::backend::TextureCubemapFace>
: public std::true_type {};
template<> struct utils::EnableIntegerOperators<filament::backend::FeatureLevel>
@@ -38,12 +38,14 @@ struct HwRenderPrimitive;
struct HwRenderTarget;
struct HwStream;
struct HwSwapChain;
struct HwSync;
struct HwTexture;
struct HwTimerQuery;
struct HwVertexBufferInfo;
struct HwVertexBuffer;
struct HwDescriptorSetLayout;
struct HwDescriptorSet;
struct HwMemoryMappedBuffer;
/*
* A handle to a backend resource. HandleBase is for internal use only.
@@ -151,12 +153,14 @@ using RenderPrimitiveHandle = Handle<HwRenderPrimitive>;
using RenderTargetHandle = Handle<HwRenderTarget>;
using StreamHandle = Handle<HwStream>;
using SwapChainHandle = Handle<HwSwapChain>;
using SyncHandle = Handle<HwSync>;
using TextureHandle = Handle<HwTexture>;
using TimerQueryHandle = Handle<HwTimerQuery>;
using VertexBufferHandle = Handle<HwVertexBuffer>;
using VertexBufferInfoHandle = Handle<HwVertexBufferInfo>;
using DescriptorSetLayoutHandle = Handle<HwDescriptorSetLayout>;
using DescriptorSetHandle = Handle<HwDescriptorSet>;
using MemoryMappedBufferHandle = Handle<HwMemoryMappedBuffer>;
} // namespace filament::backend
@@ -29,6 +29,7 @@
namespace filament::backend {
class CallbackHandler;
class Driver;
/**
@@ -42,6 +43,9 @@ public:
struct SwapChain {};
struct Fence {};
struct Stream {};
struct Sync {};
using SyncCallback = void(*)(Sync* UTILS_NONNULL sync, void* UTILS_NULLABLE userData);
class ExternalImageHandle;
@@ -27,12 +27,12 @@ namespace filament::backend {
* A PresentCallable is a callable object that, when called, schedules a frame for presentation on
* a SwapChain.
*
* Typically, Filament's backend is responsible scheduling a frame's presentation. However, there
* are certain cases where the application might want to control when a frame is scheduled for
* Typically, Filament's backend is responsible for scheduling a frame's presentation. However,
* there are certain cases where the application might want to control when a frame is scheduled for
* presentation.
*
* For example, on iOS, UIKit elements can be synchronized to 3D content by scheduling a present
* within a CATransation:
* within a CATransaction:
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* void myFrameScheduledCallback(PresentCallable presentCallable, void* user) {
@@ -55,12 +55,13 @@ namespace filament::backend {
* }
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* @remark Only Filament's Metal backend supports PresentCallables and frame callbacks. Other
* backends ignore the callback (which will never be called) and proceed normally.
* @remark The PresentCallable mechanism for user-controlled presentation is only supported by
* Filament's Metal backend. On other backends, the FrameScheduledCallback is still invoked, but the
* PresentCallable passed to it is a no-op and calling it has no effect.
*
* Applications *must* call each PresentCallable they receive. Each PresentCallable represents a
* frame that is waiting to be presented. If an application fails to call a PresentCallable, a
* memory leak could occur. To "cancel" the presentation of a frame, pass false to the
* When using the Metal backend, applications *must* call each PresentCallable they receive. Each
* PresentCallable represents a frame that is waiting to be presented, and failing to call it
* will result in a memory leak. To "cancel" the presentation of a frame, pass false to the
* PresentCallable, which will cancel the presentation of the frame and release associated memory.
*
* @see Renderer, SwapChain::setFrameScheduledCallback
@@ -69,6 +70,7 @@ class UTILS_PUBLIC PresentCallable {
public:
using PresentFn = void(*)(bool presentFrame, void* user);
static void noopPresent(bool, void*) {}
PresentCallable(PresentFn fn, void* user) noexcept;
~PresentCallable() noexcept = default;
@@ -85,6 +85,15 @@ public:
*/
virtual bool isSRGBSwapChainSupported() const noexcept;
/**
* Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_MSAA_*_SAMPLES flag.
* The default implementation returns false.
*
* @param samples The number of samples
* @return true if SWAP_CHAIN_CONFIG_MSAA_*_SAMPLES is supported, false otherwise.
*/
virtual bool isMSAASwapChainSupported(uint32_t samples) const noexcept;
/**
* Return whether protected contexts are supported by this backend.
* If protected context are supported, the SWAP_CHAIN_CONFIG_PROTECTED_CONTENT flag can be
@@ -276,6 +285,26 @@ public:
*/
virtual backend::FenceStatus waitFence(Fence* UTILS_NONNULL fence, uint64_t timeout) noexcept;
// --------------------------------------------------------------------------------------------
// Sync support
/**
* Creates a Sync. These can be used for frame synchronization externally
* (certain platform implementations can be exported to handles that can
* be used in other processes).
*
* @return A Sync object.
*/
virtual Platform::Sync* UTILS_NONNULL createSync() noexcept;
/**
* Destroys a sync. If called with a sync not created by this platform
* object, this will lead to undefined behavior.
*
* @param sync The sync to destroy, that was created by this platform
* instance.
*/
virtual void destroySync(Platform::Sync* UTILS_NONNULL sync) noexcept;
// --------------------------------------------------------------------------------------------
// Streaming support
@@ -98,8 +98,9 @@ protected:
void terminate() noexcept override;
bool isProtectedContextSupported() const noexcept override;
bool isSRGBSwapChainSupported() const noexcept override;
bool isMSAASwapChainSupported(uint32_t samples) const noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
@@ -167,6 +168,7 @@ protected:
EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR;
Config mContextAttribs;
std::vector<EGLContext> mAdditionalContexts;
bool mMSAA4XSupport = false;
// supported extensions detected at runtime
struct {
@@ -217,6 +219,8 @@ private:
return makeCurrent(mCurrentContext, drawSurface, readSurface);
}
} egl{ mEGLDisplay };
bool checkIfMSAASwapChainSupported(uint32_t samples) const noexcept;
};
} // namespace filament::backend
@@ -58,6 +58,17 @@ public:
ExternalImageDescAndroid UTILS_PUBLIC getExternalImageDesc(ExternalImageHandle externalImage) noexcept;
/**
* Converts a sync to an external file descriptor, if possible. Accepts an
* opaque handle to a sync, as well as a pointer to where the fd should be
* stored.
* @param sync The sync to be converted to a file descriptor.
* @param fd A pointer to where the file descriptor should be stored.
* @return `true` on success, `false` on failure. The default implementation
* returns `false`.
*/
bool convertSyncToFd(Platform::Sync* sync, int* fd) noexcept;
protected:
// --------------------------------------------------------------------------------------------
// Platform Interface
@@ -74,6 +85,10 @@ protected:
// --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface
struct SyncEGLAndroid : public Platform::Sync {
EGLSyncKHR sync;
};
void terminate() noexcept override;
void beginFrame(
@@ -89,9 +104,10 @@ protected:
*/
void setPresentationTime(int64_t presentationTimeInNanosecond) noexcept override;
Stream* createStream(void* nativeStream) noexcept override;
void destroyStream(Stream* stream) noexcept override;
Platform::Sync* createSync() noexcept override;
void destroySync(Platform::Sync* sync) noexcept override;
void attach(Stream* stream, intptr_t tname) noexcept override;
void detach(Stream* stream) noexcept override;
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept override;
@@ -17,8 +17,9 @@
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H
#include <backend/Platform.h>
#include <backend/CallbackHandler.h>
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include <bluevk/BlueVK.h>
@@ -29,9 +30,9 @@
#include <cstddef>
#include <functional>
#include <string>
#include <tuple>
#include <unordered_set>
#include <string>
#include <stddef.h>
#include <stdint.h>
@@ -45,6 +46,10 @@ using SwapChain = Platform::SwapChain;
*/
struct VulkanPlatformPrivate;
// Forward declare the fence status that will be maintained by the command
// buffer manager.
struct VulkanCmdFence;
/**
* A Platform interface that creates a Vulkan backend.
*/
@@ -229,6 +234,25 @@ public:
virtual SwapChainPtr createSwapChain(void* nativeWindow, uint64_t flags = 0,
VkExtent2D extent = {0, 0});
/**
* Creates a Platform::Sync object, which tracks a fence and its status,
* and allows conversion to an external sync.
* @param fence The underlying VkFence to use for synchronization.
* @param fenceStatus An object tracking the fence's state
* @return A Platform::Sync object tracking the provided fence.
*/
virtual Platform::Sync* createSync(VkFence fence,
std::shared_ptr<VulkanCmdFence> fenceStatus) noexcept;
/**
* Destroys a sync. If called with a sync not created by this platform
* object, this will lead to undefined behavior.
*
* @param sync The sync to destroy, which was created by this platform
* instance.
*/
virtual void destroySync(Platform::Sync* sync) noexcept;
/**
* Allows implementers to provide instance extensions that they'd like to include in the
* instance creation.
@@ -408,12 +432,19 @@ public:
}
protected:
struct VulkanSync : public Platform::Sync {
VkFence fence;
std::shared_ptr<VulkanCmdFence> fenceStatus;
};
virtual ExtensionSet getSwapchainInstanceExtensions() const;
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept;
virtual VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept;
private:
// Platform dependent helper methods
static ExtensionSet getSwapchainInstanceExtensionsImpl();
@@ -44,6 +44,17 @@ public:
virtual ImageData createVkImageFromExternal(ExternalImageHandleRef image) const override;
/**
* Converts a sync to an external file descriptor, if possible. Accepts an
* opaque handle to a sync, as well as a pointer to where the fd should be
* stored.
* @param sync The sync to be converted to a file descriptor.
* @param fd A pointer to where the file descriptor should be stored.
* @return `true` on success, `false` on failure. The default implementation
* returns `false`.
*/
bool convertSyncToFd(Platform::Sync* sync, int* fd) const noexcept;
protected:
virtual ExtensionSet getSwapchainInstanceExtensions() const override;
@@ -51,6 +62,8 @@ protected:
virtual SurfaceBundle createVkSurfaceKHR(void* nativeWindow, VkInstance instance,
uint64_t flags) const noexcept override;
virtual VkExternalFenceHandleTypeFlagBits getFenceExportFlags() const noexcept override;
private:
struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
AHardwareBuffer* aHardwareBuffer = nullptr;
@@ -107,9 +107,17 @@
#define DECL_DRIVER_API_R_N(R, N, ...) \
DECL_DRIVER_API_RETURN(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__), PAIR_ARGS_N(PARAM, ##__VA_ARGS__))
#define DECL_DRIVER_API_TAGGED_R_N(R, N, ...) \
DECL_DRIVER_API_RETURN(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__, utils::CString&&, tag = {}), \
PAIR_ARGS_N(PARAM, ##__VA_ARGS__, utils::CString&&, tag))
#define DECL_DRIVER_API_SYNCHRONOUS_N(R, N, ...) \
DECL_DRIVER_API_SYNCHRONOUS(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__), PAIR_ARGS_N(PARAM, ##__VA_ARGS__))
#define DECL_DRIVER_API_SYNCHRONOUS_TAGGED_N(R, N, ...) \
DECL_DRIVER_API_SYNCHRONOUS(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__, utils::CString, tag = {}), \
PAIR_ARGS_N(PARAM, ##__VA_ARGS__, utils::CString, tag))
// on some compilers the ##__VA_ARGS__ hack is not supported, so we can't handle 0-parameter APIs
// with DECL_DRIVER_API_SYNCHRONOUS_N
#define DECL_DRIVER_API_SYNCHRONOUS_0(R, N) \
@@ -165,35 +173,31 @@ DECL_DRIVER_API_0(finish)
// reset state tracking, if the driver does any state tracking (e.g. GL)
DECL_DRIVER_API_0(resetState)
DECL_DRIVER_API_N(setDebugTag,
backend::HandleBase::HandleId, handleId,
utils::CString, tag)
/*
* Creating driver objects
* -----------------------
*/
DECL_DRIVER_API_R_N(backend::VertexBufferInfoHandle, createVertexBufferInfo,
DECL_DRIVER_API_TAGGED_R_N(backend::VertexBufferInfoHandle, createVertexBufferInfo,
uint8_t, bufferCount,
uint8_t, attributeCount,
backend::AttributeArray, attributes)
DECL_DRIVER_API_R_N(backend::VertexBufferHandle, createVertexBuffer,
DECL_DRIVER_API_TAGGED_R_N(backend::VertexBufferHandle, createVertexBuffer,
uint32_t, vertexCount,
backend::VertexBufferInfoHandle, vbih)
DECL_DRIVER_API_R_N(backend::IndexBufferHandle, createIndexBuffer,
DECL_DRIVER_API_TAGGED_R_N(backend::IndexBufferHandle, createIndexBuffer,
backend::ElementType, elementType,
uint32_t, indexCount,
backend::BufferUsage, usage)
DECL_DRIVER_API_R_N(backend::BufferObjectHandle, createBufferObject,
DECL_DRIVER_API_TAGGED_R_N(backend::BufferObjectHandle, createBufferObject,
uint32_t, byteCount,
backend::BufferObjectBinding, bindingType,
backend::BufferUsage, usage)
DECL_DRIVER_API_R_N(backend::TextureHandle, createTexture,
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTexture,
backend::SamplerType, target,
uint8_t, levels,
backend::TextureFormat, format,
@@ -203,19 +207,19 @@ DECL_DRIVER_API_R_N(backend::TextureHandle, createTexture,
uint32_t, depth,
backend::TextureUsage, usage)
DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureView,
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTextureView,
backend::TextureHandle, texture,
uint8_t, baseLevel,
uint8_t, levelCount)
DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureViewSwizzle,
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTextureViewSwizzle,
backend::TextureHandle, texture,
backend::TextureSwizzle, r,
backend::TextureSwizzle, g,
backend::TextureSwizzle, b,
backend::TextureSwizzle, a)
DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureExternalImage2,
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTextureExternalImage2,
backend::SamplerType, target,
backend::TextureFormat, format,
uint32_t, width,
@@ -223,7 +227,7 @@ DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureExternalImage2,
backend::TextureUsage, usage,
backend::Platform::ExternalImageHandleRef, image)
DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureExternalImage,
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTextureExternalImage,
backend::SamplerType, target,
backend::TextureFormat, format,
uint32_t, width,
@@ -231,7 +235,7 @@ DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureExternalImage,
backend::TextureUsage, usage,
void*, image)
DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureExternalImagePlane,
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, createTextureExternalImagePlane,
backend::TextureFormat, format,
uint32_t, width,
uint32_t, height,
@@ -239,7 +243,7 @@ DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureExternalImagePlane,
void*, image,
uint32_t, plane)
DECL_DRIVER_API_R_N(backend::TextureHandle, importTexture,
DECL_DRIVER_API_TAGGED_R_N(backend::TextureHandle, importTexture,
intptr_t, id,
backend::SamplerType, target,
uint8_t, levels,
@@ -250,17 +254,17 @@ DECL_DRIVER_API_R_N(backend::TextureHandle, importTexture,
uint32_t, depth,
backend::TextureUsage, usage)
DECL_DRIVER_API_R_N(backend::RenderPrimitiveHandle, createRenderPrimitive,
DECL_DRIVER_API_TAGGED_R_N(backend::RenderPrimitiveHandle, createRenderPrimitive,
backend::VertexBufferHandle, vbh,
backend::IndexBufferHandle, ibh,
backend::PrimitiveType, pt)
DECL_DRIVER_API_R_N(backend::ProgramHandle, createProgram,
DECL_DRIVER_API_TAGGED_R_N(backend::ProgramHandle, createProgram,
backend::Program&&, program)
DECL_DRIVER_API_R_0(backend::RenderTargetHandle, createDefaultRenderTarget)
DECL_DRIVER_API_TAGGED_R_N(backend::RenderTargetHandle, createDefaultRenderTarget)
DECL_DRIVER_API_R_N(backend::RenderTargetHandle, createRenderTarget,
DECL_DRIVER_API_TAGGED_R_N(backend::RenderTargetHandle, createRenderTarget,
backend::TargetBufferFlags, targetBufferFlags,
uint32_t, width,
uint32_t, height,
@@ -270,46 +274,32 @@ DECL_DRIVER_API_R_N(backend::RenderTargetHandle, createRenderTarget,
backend::TargetBufferInfo, depth,
backend::TargetBufferInfo, stencil)
DECL_DRIVER_API_R_0(backend::FenceHandle, createFence)
DECL_DRIVER_API_TAGGED_R_N(backend::FenceHandle, createFence)
DECL_DRIVER_API_R_N(backend::SwapChainHandle, createSwapChain,
DECL_DRIVER_API_TAGGED_R_N(backend::SyncHandle, createSync)
DECL_DRIVER_API_TAGGED_R_N(backend::SwapChainHandle, createSwapChain,
void*, nativeWindow,
uint64_t, flags)
DECL_DRIVER_API_R_N(backend::SwapChainHandle, createSwapChainHeadless,
DECL_DRIVER_API_TAGGED_R_N(backend::SwapChainHandle, createSwapChainHeadless,
uint32_t, width,
uint32_t, height,
uint64_t, flags)
DECL_DRIVER_API_R_0(backend::TimerQueryHandle, createTimerQuery)
DECL_DRIVER_API_TAGGED_R_N(backend::TimerQueryHandle, createTimerQuery)
DECL_DRIVER_API_R_N(backend::DescriptorSetLayoutHandle, createDescriptorSetLayout,
DECL_DRIVER_API_TAGGED_R_N(backend::DescriptorSetLayoutHandle, createDescriptorSetLayout,
backend::DescriptorSetLayout&&, info)
DECL_DRIVER_API_R_N(backend::DescriptorSetHandle, createDescriptorSet,
DECL_DRIVER_API_TAGGED_R_N(backend::DescriptorSetHandle, createDescriptorSet,
backend::DescriptorSetLayoutHandle, dslh)
DECL_DRIVER_API_N(updateDescriptorSetBuffer,
backend::DescriptorSetHandle, dsh,
backend::descriptor_binding_t, binding,
DECL_DRIVER_API_TAGGED_R_N(backend::MemoryMappedBufferHandle, mapBuffer,
backend::BufferObjectHandle, boh,
uint32_t, offset,
uint32_t, size
)
DECL_DRIVER_API_N(updateDescriptorSetTexture,
backend::DescriptorSetHandle, dsh,
backend::descriptor_binding_t, binding,
backend::TextureHandle, th,
SamplerParams, params
)
DECL_DRIVER_API_N(bindDescriptorSet,
backend::DescriptorSetHandle, dsh,
backend::descriptor_set_t, set,
backend::DescriptorSetOffsetArray&&, offsets
)
size_t, offset,
size_t, size,
backend::MapBufferAccessFlags, access)
/*
* Destroying driver objects
@@ -319,7 +309,7 @@ DECL_DRIVER_API_N(bindDescriptorSet,
DECL_DRIVER_API_N(destroyVertexBuffer, backend::VertexBufferHandle, vbh)
DECL_DRIVER_API_N(destroyVertexBufferInfo, backend::VertexBufferInfoHandle, vbih)
DECL_DRIVER_API_N(destroyIndexBuffer, backend::IndexBufferHandle, ibh)
DECL_DRIVER_API_N(destroyBufferObject, backend::BufferObjectHandle, ibh)
DECL_DRIVER_API_N(destroyBufferObject, backend::BufferObjectHandle, boh)
DECL_DRIVER_API_N(destroyRenderPrimitive, backend::RenderPrimitiveHandle, rph)
DECL_DRIVER_API_N(destroyProgram, backend::ProgramHandle, ph)
DECL_DRIVER_API_N(destroyTexture, backend::TextureHandle, th)
@@ -328,8 +318,10 @@ DECL_DRIVER_API_N(destroySwapChain, backend::SwapChainHandle, sch)
DECL_DRIVER_API_N(destroyStream, backend::StreamHandle, sh)
DECL_DRIVER_API_N(destroyTimerQuery, backend::TimerQueryHandle, sh)
DECL_DRIVER_API_N(destroyFence, backend::FenceHandle, fh)
DECL_DRIVER_API_N(destroySync, backend::SyncHandle, sh)
DECL_DRIVER_API_N(destroyDescriptorSetLayout, backend::DescriptorSetLayoutHandle, dslh)
DECL_DRIVER_API_N(destroyDescriptorSet, backend::DescriptorSetHandle, dsh)
DECL_DRIVER_API_N(unmapBuffer, backend::MemoryMappedBufferHandle, mmbh)
/*
* Synchronous APIs
@@ -337,13 +329,15 @@ DECL_DRIVER_API_N(destroyDescriptorSet, backend::DescriptorSetHandle, ds
*/
DECL_DRIVER_API_SYNCHRONOUS_0(void, terminate)
DECL_DRIVER_API_SYNCHRONOUS_N(backend::StreamHandle, createStreamNative, void*, stream)
DECL_DRIVER_API_SYNCHRONOUS_0(backend::StreamHandle, createStreamAcquired)
DECL_DRIVER_API_SYNCHRONOUS_TAGGED_N(backend::StreamHandle, createStreamNative, void*, stream)
DECL_DRIVER_API_SYNCHRONOUS_TAGGED_N(backend::StreamHandle, createStreamAcquired)
DECL_DRIVER_API_SYNCHRONOUS_N(void, setAcquiredImage, backend::StreamHandle, stream, void*, image, const math::mat3f&, transform, backend::CallbackHandler*, handler, backend::StreamCallback, cb, void*, userData)
DECL_DRIVER_API_SYNCHRONOUS_N(void, setStreamDimensions, backend::StreamHandle, stream, uint32_t, width, uint32_t, height)
DECL_DRIVER_API_SYNCHRONOUS_N(int64_t, getStreamTimestamp, backend::StreamHandle, stream)
DECL_DRIVER_API_SYNCHRONOUS_N(void, updateStreams, backend::DriverApi*, driver)
DECL_DRIVER_API_SYNCHRONOUS_N(backend::FenceStatus, getFenceStatus, backend::FenceHandle, fh)
DECL_DRIVER_API_SYNCHRONOUS_N(void, getPlatformSync, backend::SyncHandle, sh,
backend::CallbackHandler*, handler, backend::Platform::SyncCallback, cb, void*, userData)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isTextureFormatSupported, backend::TextureFormat, format)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isTextureSwizzleSupported)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isTextureFormatMipmappable, backend::TextureFormat, format)
@@ -353,6 +347,7 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameBufferFetchMultiSampleSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameTimeSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isAutoDepthResolveSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isSRGBSwapChainSupported)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isMSAASwapChainSupported, uint32_t, samples)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedContentSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isStereoSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported)
@@ -367,7 +362,7 @@ DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxArrayTextureLayers)
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage2, backend::Platform::ExternalImageHandleRef, image)
DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage, void*, image)
DECL_DRIVER_API_SYNCHRONOUS_N(backend::TimerQueryResult, getTimerQueryValue, backend::TimerQueryHandle, query, uint64_t*, elapsedTime)
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)
@@ -392,16 +387,16 @@ DECL_DRIVER_API_N(updateBufferObject,
uint32_t, byteOffset)
DECL_DRIVER_API_N(registerBufferObjectStreams,
backend::BufferObjectHandle, ibh,
backend::BufferObjectHandle, boh,
backend::BufferObjectStreamDescriptor&&, streams)
DECL_DRIVER_API_N(updateBufferObjectUnsynchronized,
backend::BufferObjectHandle, ibh,
backend::BufferObjectHandle, boh,
backend::BufferDescriptor&&, data,
uint32_t, byteOffset)
DECL_DRIVER_API_N(resetBufferObject,
backend::BufferObjectHandle, ibh)
backend::BufferObjectHandle, boh)
DECL_DRIVER_API_N(update3DImage,
backend::TextureHandle, th,
@@ -430,10 +425,10 @@ DECL_DRIVER_API_0(endRenderPass)
DECL_DRIVER_API_0(nextSubpass)
DECL_DRIVER_API_N(beginTimerQuery,
backend::TimerQueryHandle, query)
backend::TimerQueryHandle, tqh)
DECL_DRIVER_API_N(endTimerQuery,
backend::TimerQueryHandle, query)
backend::TimerQueryHandle, tqh)
DECL_DRIVER_API_N(compilePrograms,
backend::CompilerPriorityQueue, priority,
@@ -441,6 +436,26 @@ DECL_DRIVER_API_N(compilePrograms,
backend::CallbackHandler::Callback, callback,
void*, user)
DECL_DRIVER_API_N(updateDescriptorSetBuffer,
backend::DescriptorSetHandle, dsh,
backend::descriptor_binding_t, binding,
backend::BufferObjectHandle, boh,
uint32_t, offset,
uint32_t, size
)
DECL_DRIVER_API_N(updateDescriptorSetTexture,
backend::DescriptorSetHandle, dsh,
backend::descriptor_binding_t, binding,
backend::TextureHandle, th,
SamplerParams, params
)
DECL_DRIVER_API_N(copyToMemoryMappedBuffer,
backend::MemoryMappedBufferHandle, mmbh,
size_t, offset,
backend::BufferDescriptor&&, data)
/*
* Swap chain
*/
@@ -529,6 +544,12 @@ DECL_DRIVER_API_N(bindPipeline,
DECL_DRIVER_API_N(bindRenderPrimitive,
backend::RenderPrimitiveHandle, rph)
DECL_DRIVER_API_N(bindDescriptorSet,
backend::DescriptorSetHandle, dsh,
backend::descriptor_set_t, set,
backend::DescriptorSetOffsetArray&&, offsets
)
DECL_DRIVER_API_N(draw2,
uint32_t, indexOffset,
uint32_t, indexCount,
@@ -249,8 +249,9 @@ public:
}
void associateTagToHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
// TODO: for now, only pool handles check for use-after-free, so we only keep tags for
// those
if (tag.empty()) {
return;
}
uint32_t key = id;
if (UTILS_LIKELY(isPoolHandle(id))) {
// Truncate the age to get the debug tag
+10 -4
View File
@@ -18,10 +18,16 @@
#include "DataReshaper.h"
#include <backend/PixelBufferDescriptor.h>
#include <backend/DriverEnums.h>
#include <utils/BitmaskEnum.h>
#include <utils/compiler.h>
#include <utils/CString.h>
#include <cstddef>
#include <cstdint>
#include <utility>
#include <string_view>
namespace filament::backend {
@@ -636,7 +642,7 @@ bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped)
switch (data.type) {
case PixelDataType::BYTE:
case PixelDataType::UBYTE: {
uint8_t* bytes = (uint8_t*) malloc(reshapedSize);
uint8_t* bytes = static_cast<uint8_t*>(malloc(reshapedSize));
DataReshaper::reshape<uint8_t, 3, 4>(bytes, data.buffer, data.size);
PixelBufferDescriptor pbd(bytes, reshapedSize, reshapedFormat, data.type,
data.alignment, data.left, data.top, data.stride, freeFunc);
@@ -646,7 +652,7 @@ bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped)
case PixelDataType::SHORT:
case PixelDataType::USHORT:
case PixelDataType::HALF: {
uint8_t* bytes = (uint8_t*) malloc(reshapedSize);
uint8_t* bytes = static_cast<uint8_t*>(malloc(reshapedSize));
DataReshaper::reshape<uint16_t, 3, 4>(bytes, data.buffer, data.size);
PixelBufferDescriptor pbd(bytes, reshapedSize, reshapedFormat, data.type,
data.alignment, data.left, data.top, data.stride, freeFunc);
@@ -655,7 +661,7 @@ bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped)
}
case PixelDataType::INT:
case PixelDataType::UINT: {
uint8_t* bytes = (uint8_t*) malloc(reshapedSize);
uint8_t* bytes = static_cast<uint8_t*>(malloc(reshapedSize));
DataReshaper::reshape<uint32_t, 3, 4>(bytes, data.buffer, data.size);
PixelBufferDescriptor pbd(bytes, reshapedSize, reshapedFormat, data.type,
data.alignment, data.left, data.top, data.stride, freeFunc);
@@ -663,7 +669,7 @@ bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped)
return true;
}
case PixelDataType::FLOAT: {
uint8_t* bytes = (uint8_t*) malloc(reshapedSize);
uint8_t* bytes = static_cast<uint8_t*>(malloc(reshapedSize));
DataReshaper::reshape<float, 3, 4>(bytes, data.buffer, data.size);
PixelBufferDescriptor pbd(bytes, reshapedSize, reshapedFormat, data.type,
data.alignment, data.left, data.top, data.stride, freeFunc);
+24
View File
@@ -38,6 +38,11 @@
#include <stdlib.h>
#include <string.h>
#if defined(WIN32)
# include <windows.h> // for VirtualAlloc, VirtualProtect, VirtualFree
# include <utils/unwindows.h>
#endif
using namespace utils;
namespace filament::backend {
@@ -134,6 +139,21 @@ void* CircularBuffer::alloc(size_t size) {
mprotect(guard, BLOCK_SIZE, PROT_NONE);
}
return data;
#elif defined(WIN32)
size_t const BLOCK_SIZE = getBlockSize();
// On Windows, use VirtualAlloc instead of malloc to allocate virtual memory.
// This allows us to set page protection by VirtualProtect.
void* data = VirtualAlloc(nullptr, size * 2 + BLOCK_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
FILAMENT_CHECK_POSTCONDITION(data != nullptr)
<< "couldn't allocate " << (size * 2 / 1024)
<< " KiB of virtual address space for the command buffer";
// guard page at the end
void* guard = (void*)(uintptr_t(data) + size * 2);
DWORD oldProtect = 0;
BOOL ok = VirtualProtect(guard, BLOCK_SIZE, PAGE_NOACCESS, &oldProtect);
FILAMENT_CHECK_POSTCONDITION(ok) << "VirtualProtect failed to set guard page";
return data;
#else
return ::malloc(2 * size);
#endif
@@ -150,6 +170,10 @@ void CircularBuffer::dealloc() noexcept {
mAshmemFd = -1;
}
}
#elif defined(WIN32)
if (mData) {
VirtualFree(mData, 0, MEM_RELEASE);
}
#else
::free(mData);
#endif
+5 -5
View File
@@ -57,7 +57,7 @@ static void printParameterPack(io::ostream& out, const FIRST& first, const REMAI
printParameterPack(out, rest...);
}
static UTILS_NOINLINE UTILS_UNUSED std::string extractMethodName(std::string& command) noexcept {
static UTILS_NOINLINE UTILS_UNUSED std::string_view extractMethodName(std::string_view command) noexcept {
constexpr const char startPattern[] = "::Command<&filament::backend::Driver::";
auto pos = command.rfind(startPattern);
auto end = command.rfind('(');
@@ -133,9 +133,9 @@ template<std::size_t... I>
void CommandType<void (Driver::*)(ARGS...)>::Command<METHOD>::log(std::index_sequence<I...>) noexcept {
#if DEBUG_COMMAND_STREAM
static_assert(UTILS_HAS_RTTI, "DEBUG_COMMAND_STREAM can only be used with RTTI");
std::string command = utils::CallStack::demangleTypeName(typeid(Command).name()).c_str();
DLOG(INFO) << extractMethodName(command) << " : size=" << sizeof(Command);
utils::io::sstream parameterPack;
CString command = CallStack::demangleTypeName(typeid(Command).name());
DLOG(INFO) << extractMethodName({command.data(), command.size()}) << " : size=" << sizeof(Command);
io::sstream parameterPack;
printParameterPack(parameterPack, std::get<I>(mArgs)...);
DLOG(INFO) << "\t" << parameterPack.c_str();
#endif
@@ -164,7 +164,7 @@ void CommandType<void (Driver::*)(ARGS...)>::Command<METHOD>::log() noexcept {
// ------------------------------------------------------------------------------------------------
void CustomCommand::execute(Driver&, CommandBase* base, intptr_t* next) {
*next = CustomCommand::align(sizeof(CustomCommand));
*next = align(sizeof(CustomCommand));
static_cast<CustomCommand*>(base)->mCommand();
static_cast<CustomCommand*>(base)->~CustomCommand();
}
+25 -8
View File
@@ -63,6 +63,7 @@ public:
static void copyImage(uint8_t* UTILS_RESTRICT dest,
const uint8_t* UTILS_RESTRICT src,
size_t srcBytesPerRow, size_t /*srcChannelCount*/,
size_t /*dstRowOffset */, size_t /*dstColumnOffset */,
size_t dstBytesPerRow, size_t /*dstChannelCount*/,
size_t /*width*/, size_t height, bool /*swizzle*/) {
if (srcBytesPerRow == dstBytesPerRow) {
@@ -79,17 +80,20 @@ public:
template<typename dstComponentType, typename srcComponentType>
static void reshapeImage(uint8_t* UTILS_RESTRICT dest, const uint8_t* UTILS_RESTRICT src,
size_t srcBytesPerRow,
size_t srcChannelCount, size_t dstBytesPerRow, size_t dstChannelCount,
size_t srcChannelCount,
size_t dstRowOffset, size_t dstColumnOffset,
size_t dstBytesPerRow, size_t dstChannelCount,
size_t width, size_t height, bool swizzle) {
const dstComponentType dstMaxValue = getMaxValue<dstComponentType>();
const srcComponentType srcMaxValue = getMaxValue<srcComponentType>();
const size_t minChannelCount = math::min(srcChannelCount, dstChannelCount);
assert_invariant(minChannelCount <= 4);
UTILS_ASSUME(minChannelCount <= 4);
dest += (dstRowOffset * dstBytesPerRow);
const int inds[4] = { swizzle ? 2 : 0, 1, swizzle ? 0 : 2, 3 };
for (size_t row = 0; row < height; ++row) {
const srcComponentType* in = (const srcComponentType*)src;
dstComponentType* out = (dstComponentType*)dest;
const srcComponentType* in = (const srcComponentType*) src;
dstComponentType* out = (dstComponentType*)dest + (dstColumnOffset * dstChannelCount);
for (size_t column = 0; column < width; ++column) {
for (size_t channel = 0; channel < minChannelCount; ++channel) {
if constexpr (std::is_same_v<dstComponentType, srcComponentType>) {
@@ -128,17 +132,23 @@ public:
default: return false;
}
void (*reshaper)(uint8_t* dest, const uint8_t* src, size_t srcBytesPerRow,
size_t srcChannelCount, size_t dstBytesPerRow, size_t dstChannelCount,
size_t srcChannelCount,
size_t srcRowOffset, size_t srcColumnOffset,
size_t dstBytesPerRow, size_t dstChannelCount,
size_t width, size_t height, bool swizzle) = nullptr;
constexpr auto UBYTE = PixelDataType::UBYTE, FLOAT = PixelDataType::FLOAT,
UINT = PixelDataType::UINT, INT = PixelDataType::INT;
constexpr auto UBYTE = PixelDataType::UBYTE;
constexpr auto FLOAT = PixelDataType::FLOAT;
constexpr auto UINT = PixelDataType::UINT;
constexpr auto INT = PixelDataType::INT;
constexpr auto HALF = PixelDataType::HALF;
switch (dst->type) {
case UBYTE:
switch (srcType) {
case UBYTE:
reshaper = reshapeImage<uint8_t, uint8_t>;
if (dst->format == PixelDataFormat::RGBA &&
dstChannelCount == srcChannelCount && !swizzle) {
dstChannelCount == srcChannelCount && !swizzle && dst->top == 0 &&
dst->left == 0) {
reshaper = copyImage;
}
break;
@@ -175,13 +185,20 @@ public:
default: return false;
}
break;
case HALF:
switch (srcType) {
case HALF: reshaper = copyImage; break;
default: return false;
}
break;
default:
return false;
}
uint8_t* dstBytes = (uint8_t*) dst->buffer;
const int dstBytesPerRow = PixelBufferDescriptor::computeDataSize(dst->format, dst->type,
dst->stride ? dst->stride : width, 1, dst->alignment);
reshaper(dstBytes, srcBytes, srcBytesPerRow, srcChannelCount, dstBytesPerRow,
reshaper(dstBytes, srcBytes, srcBytesPerRow, srcChannelCount,
dst->top, dst->left, dstBytesPerRow,
dstChannelCount, width, height, swizzle);
return true;
}
+8 -1
View File
@@ -78,6 +78,9 @@ struct HwBufferObject : public HwBase {
explicit HwBufferObject(uint32_t byteCount) noexcept : byteCount(byteCount) {}
};
struct HwMemoryMappedBuffer : public HwBase {
};
struct HwIndexBuffer : public HwBase {
uint32_t count : 27;
uint32_t elementSize : 5;
@@ -140,6 +143,10 @@ struct HwFence : public HwBase {
Platform::Fence* fence = nullptr;
};
struct HwSync : public HwBase {
Platform::Sync* sync = nullptr;
};
struct HwSwapChain : public HwBase {
Platform::SwapChain* swapChain = nullptr;
};
@@ -207,7 +214,7 @@ public:
protected:
class CallbackDataDetails;
inline void scheduleDestroy(BufferDescriptor&& buffer) noexcept {
void scheduleDestroy(BufferDescriptor&& buffer) noexcept {
if (buffer.hasCallback()) {
scheduleDestroySlow(std::move(buffer));
}
+7 -6
View File
@@ -21,7 +21,8 @@
#include <utils/ostream.h>
#ifndef NDEBUG
# include <string>
# include <utils/CString.h>
# include <string_view>
#endif
#include <stddef.h>
@@ -36,11 +37,11 @@ static char const * const kOurNamespace = "filament::backend::";
// removes all occurrences of "what" from "str"
UTILS_NOINLINE
static std::string& removeAll(std::string& str, const std::string& what) noexcept {
static CString& removeAll(CString& str, const std::string_view what) noexcept {
if (!what.empty()) {
const std::string empty;
const CString empty;
size_t pos = 0;
while ((pos = str.find(what, pos)) != std::string::npos) {
while ((pos = std::string_view{ str.data(), str.size() }.find(what, pos)) != std::string_view::npos) {
str.replace(pos, what.length(), empty);
}
}
@@ -49,13 +50,13 @@ static std::string& removeAll(std::string& str, const std::string& what) noexcep
template <typename T>
UTILS_NOINLINE
static io::ostream& logHandle(io::ostream& out, std::string& typeName, T id) noexcept {
static io::ostream& logHandle(io::ostream& out, CString& typeName, T id) noexcept {
return out << removeAll(typeName, kOurNamespace) << " @ " << id;
}
template <typename T>
io::ostream& operator<<(io::ostream& out, const Handle<T>& h) noexcept {
std::string s(CallStack::typeName<Handle<T>>().c_str());
CString s{ CallStack::typeName<Handle<T>>() };
return logHandle(out, s, h.getId());
}
+10
View File
@@ -18,6 +18,7 @@
#define TNT_FILAMENT_DRIVER_METALBUFFER_H
#include "MetalContext.h"
#include "MetalFlags.h"
#include <backend/DriverEnums.h>
#include <backend/platforms/PlatformMetal.h>
@@ -191,6 +192,15 @@ public:
void* getCpuBuffer() const noexcept { return mCpuBuffer; }
void setLabel(const utils::CString& label) {
#if FILAMENT_METAL_DEBUG_LABELS
if (label.empty()) {
return;
}
mBuffer.get().label = @(label.c_str_safe());
#endif
}
enum Stage : uint8_t {
VERTEX = 1u << 0u,
FRAGMENT = 1u << 1u,
+6 -1
View File
@@ -102,7 +102,7 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
context->pendingCommandBuffer = [context->commandQueue commandBuffer];
// It's safe for this block to capture the context variable. MetalDriver::terminate will ensure
// all frames and their completion handlers finish before context is deallocated.
uint64_t thisCommandBufferId = context->pendingCommandBufferId;
const uint64_t thisCommandBufferId = context->pendingCommandBufferId;
[context->pendingCommandBuffer addCompletedHandler:^(id <MTLCommandBuffer> buffer) {
context->resourceTracker.clearResources((__bridge void*) buffer);
@@ -118,6 +118,11 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
context->memorylessLimitsReached = true;
}
}
if (UTILS_UNLIKELY(errorCode != MTLCommandBufferErrorNone)) {
LOG(ERROR) << "Filament Metal command buffer errored with code: " << errorCode << " ("
<< stringifyMTLCommandBufferError(errorCode) << ").";
}
}];
FILAMENT_CHECK_POSTCONDITION(context->pendingCommandBuffer)
<< "Could not obtain command buffer.";
+145 -62
View File
@@ -418,49 +418,42 @@ void MetalDriver::finish(int) {
}
void MetalDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes) {
uint8_t attributeCount, AttributeArray attributes, utils::CString&& tag) {
construct_handle<MetalVertexBufferInfo>(vbih, *mContext,
bufferCount, attributeCount, attributes);
mHandleAllocator.associateTagToHandle(vbih.getId(), std::move(tag));
}
void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih) {
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih, utils::CString&& tag) {
MetalVertexBufferInfo const* const vbi = handle_cast<const MetalVertexBufferInfo>(vbih);
construct_handle<MetalVertexBuffer>(vbh, *mContext, vertexCount, vbi->bufferCount, vbih);
mHandleAllocator.associateTagToHandle(vbh.getId(), std::move(tag));
// No actual GPU memory is allocated here, so no need to check for allocation success.
}
void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) {
uint32_t indexCount, BufferUsage usage, utils::CString&& tag) {
auto elementSize = (uint8_t)getElementTypeSize(elementType);
auto* indexBuffer =
construct_handle<MetalIndexBuffer>(ibh, *mContext, usage, elementSize, indexCount);
auto& buffer = indexBuffer->buffer;
// If the allocation was not successful, postpone the error message until the next tick, to give
// Filament a chance to call setDebugTag on the handle; this way we get a nicer error message.
if (UTILS_UNLIKELY(!buffer.wasAllocationSuccessful())) {
const size_t byteCount = buffer.getSize();
runAtNextTick([byteCount, this, ibh]() {
FILAMENT_CHECK_POSTCONDITION(false)
<< "Could not allocate Metal index buffer of size " << byteCount
<< ", tag=" << mHandleAllocator.getHandleTag(ibh.getId()).c_str_safe();
});
}
FILAMENT_CHECK_POSTCONDITION(buffer.wasAllocationSuccessful())
<< "Could not allocate Metal index buffer of size " << buffer.getSize()
<< ", tag=" << tag.c_str_safe();
buffer.setLabel(tag);
mHandleAllocator.associateTagToHandle(ibh.getId(), std::move(tag));
}
void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {
BufferObjectBinding bindingType, BufferUsage usage, utils::CString&& tag) {
auto* bufferObject =
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
// If the allocation was not successful, postpone the error message until the next tick, to give
// Filament a chance to call setDebugTag on the handle; this way we get a nicer error message.
if (UTILS_UNLIKELY(!bufferObject->getBuffer()->wasAllocationSuccessful())) {
runAtNextTick([byteCount, this, boh]() {
FILAMENT_CHECK_POSTCONDITION(false)
<< "Could not allocate Metal buffer of size " << byteCount
<< ", tag=" << mHandleAllocator.getHandleTag(boh.getId()).c_str_safe();
});
}
FILAMENT_CHECK_POSTCONDITION(bufferObject->getBuffer()->wasAllocationSuccessful())
<< "Could not allocate Metal buffer of size " << byteCount
<< ", tag=" << tag.c_str_safe();
bufferObject->getBuffer()->setLabel(tag);
mHandleAllocator.associateTagToHandle(boh.getId(), std::move(tag));
}
// fixme: TextureUsage is a bitfield
@@ -495,67 +488,83 @@ inline const char* stringify(SamplerType samplerType) {
void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height,
uint32_t depth, TextureUsage usage) {
uint32_t depth, TextureUsage usage, utils::CString&& tag) {
// Clamp sample count to what the device supports.
auto& sc = mContext->sampleCountLookup;
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
mContext->textures.insert(construct_handle<MetalTexture>(
th, *mContext, target, levels, format, samples, width, height, depth, usage));
MetalTexture* texture = construct_handle<MetalTexture>(th, *mContext, target, levels, format,
samples, width, height, depth, usage);
mContext->textures.insert(texture);
texture->setLabel(tag);
DEBUG_LOG(
"createTextureR(th = %d, target = %s, levels = %d, format = ?, samples = %d, width = "
"%d, height = %d, depth = %d, usage = %s)\n",
th.getId(), stringify(target), levels, samples, width, height, depth, stringify(usage));
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
}
void MetalDriver::createTextureViewR(
Handle<HwTexture> th, Handle<HwTexture> srch, uint8_t baseLevel, uint8_t levelCount) {
void MetalDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
uint8_t baseLevel, uint8_t levelCount, utils::CString&& tag) {
MetalTexture const* src = handle_cast<MetalTexture>(srch);
mContext->textures.insert(
construct_handle<MetalTexture>(th, *mContext, src, baseLevel, levelCount));
MetalTexture* texture =
construct_handle<MetalTexture>(th, *mContext, src, baseLevel, levelCount);
mContext->textures.insert(texture);
texture->setLabel(tag);
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
}
void MetalDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
backend::TextureSwizzle a) {
backend::TextureSwizzle a, utils::CString&& tag) {
MetalTexture const* src = handle_cast<MetalTexture>(srch);
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext, src, r, g, b, a));
MetalTexture* texture = construct_handle<MetalTexture>(th, *mContext, src, r, g, b, a);
mContext->textures.insert(texture);
texture->setLabel(tag);
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
}
void MetalDriver::createTextureExternalImage2R(Handle<HwTexture> th,
backend::SamplerType target,
backend::TextureFormat format,
uint32_t width, uint32_t height, backend::TextureUsage usage,
Platform::ExternalImageHandleRef image) {
Platform::ExternalImageHandleRef image, utils::CString&& tag) {
// FIXME: implement createTextureExternalImage2R
}
void MetalDriver::createTextureExternalImageR(Handle<HwTexture> th,
backend::SamplerType target,
backend::TextureFormat format,
uint32_t width, uint32_t height, backend::TextureUsage usage, void* image) {
mContext->textures.insert(construct_handle<MetalTexture>(
th, *mContext, format, width, height, usage, (CVPixelBufferRef)image));
void MetalDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, utils::CString&& tag) {
MetalTexture* texture = construct_handle<MetalTexture>(th, *mContext, format, width, height,
usage, (CVPixelBufferRef) image);
mContext->textures.insert(texture);
texture->setLabel(tag);
// This release matches the retain call in setupExternalImage. The MetalTexture will have
// retained the buffer by now.
CVPixelBufferRelease((CVPixelBufferRef)image);
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
}
void MetalDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, uint32_t plane) {
mContext->textures.insert(construct_handle<MetalTexture>(
th, *mContext, format, width, height, usage, (CVPixelBufferRef)image, plane));
void* image, uint32_t plane, utils::CString&& tag) {
MetalTexture* texture = construct_handle<MetalTexture>(th, *mContext, format, width, height,
usage, (CVPixelBufferRef) image, plane);
mContext->textures.insert(texture);
texture->setLabel(tag);
// This release matches the retain call in setupExternalImage. The MetalTexture will have
// retained the buffer by now.
CVPixelBufferRelease((CVPixelBufferRef)image);
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
}
void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height,
uint32_t depth, TextureUsage usage) {
uint32_t depth, TextureUsage usage, utils::CString&& tag) {
id<MTLTexture> metalTexture = (id<MTLTexture>) CFBridgingRelease((void*) i);
FILAMENT_CHECK_PRECONDITION(metalTexture.width == width)
<< "Imported id<MTLTexture> width (" << metalTexture.width
@@ -570,34 +579,40 @@ void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
FILAMENT_CHECK_PRECONDITION(metalTexture.textureType == filamentMetalType)
<< "Imported id<MTLTexture> type (" << metalTexture.textureType
<< ") != Filament texture type (" << filamentMetalType << ")";
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext,
target, levels, format, samples, width, height, depth, usage, metalTexture));
MetalTexture* texture = construct_handle<MetalTexture>(th, *mContext, target, levels, format,
samples, width, height, depth, usage, metalTexture);
mContext->textures.insert(texture);
texture->setLabel(tag);
mHandleAllocator.associateTagToHandle(th.getId(), std::move(tag));
}
void MetalDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
PrimitiveType pt) {
PrimitiveType pt, utils::CString&& tag) {
construct_handle<MetalRenderPrimitive>(rph);
MetalDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh);
mHandleAllocator.associateTagToHandle(rph.getId(), std::move(tag));
}
void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program) {
void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program, utils::CString&& tag) {
#if FILAMENT_METAL_DEBUG_LOG
auto handleId = rph.getId();
DEBUG_LOG("createProgramR(rph = %d, program = ", handleId);
DLOG(INFO) << program;
#endif
construct_handle<MetalProgram>(rph, *mContext, std::move(program));
mHandleAllocator.associateTagToHandle(rph.getId(), std::move(tag));
}
void MetalDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int dummy) {
void MetalDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils::CString&& tag) {
construct_handle<MetalRenderTarget>(rth, mContext);
mHandleAllocator.associateTagToHandle(rth.getId(), std::move(tag));
}
void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
uint8_t samples, uint8_t layerCount, MRT color,
TargetBufferInfo depth, TargetBufferInfo stencil) {
TargetBufferInfo depth, TargetBufferInfo stencil, utils::CString&& tag) {
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
<< "createRenderTarget must be called outside of a render pass.";
// Clamp sample count to what the device supports.
@@ -640,14 +655,20 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
construct_handle<MetalRenderTarget>(rth, mContext, width, height, samples,
colorAttachments, depthAttachment, stencilAttachment);
mHandleAllocator.associateTagToHandle(rth.getId(), std::move(tag));
}
void MetalDriver::createFenceR(Handle<HwFence> fh, int dummy) {
void MetalDriver::createFenceR(Handle<HwFence> fh, utils::CString&& tag) {
auto* fence = handle_cast<MetalFence>(fh);
fence->encode();
mHandleAllocator.associateTagToHandle(fh.getId(), std::move(tag));
}
void MetalDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
void MetalDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags,
utils::CString&& tag) {
// TODO: support MSAA swapchain
if (UTILS_UNLIKELY(flags & SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER)) {
CVPixelBufferRef pixelBuffer = (CVPixelBufferRef) nativeWindow;
construct_handle<MetalSwapChain>(sch, *mContext, mPlatform, pixelBuffer, flags);
@@ -658,15 +679,24 @@ void MetalDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
auto* metalLayer = (__bridge CAMetalLayer*) nativeWindow;
construct_handle<MetalSwapChain>(sch, *mContext, mPlatform, metalLayer, flags);
}
mHandleAllocator.associateTagToHandle(sch.getId(), std::move(tag));
}
void MetalDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
uint32_t width, uint32_t height, uint64_t flags) {
uint32_t width, uint32_t height, uint64_t flags, utils::CString&& tag) {
construct_handle<MetalSwapChain>(sch, *mContext, mPlatform, width, height, flags);
mHandleAllocator.associateTagToHandle(sch.getId(), std::move(tag));
}
void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
void MetalDriver::createSyncR(Handle<HwSync> sh, utils::CString&& tag) {
// TODO: Ensure sync is active, and then invoke and clear all pending
// callbacks.
mHandleAllocator.associateTagToHandle(sh.getId(), std::move(tag));
}
void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::CString&& tag) {
// nothing to do, timer query was constructed in createTimerQueryS
mHandleAllocator.associateTagToHandle(tqh.getId(), std::move(tag));
}
UTILS_UNUSED
@@ -703,7 +733,7 @@ const char* toString(DescriptorFlags flags) {
}
void MetalDriver::createDescriptorSetLayoutR(
Handle<HwDescriptorSetLayout> dslh, DescriptorSetLayout&& info) {
Handle<HwDescriptorSetLayout> dslh, DescriptorSetLayout&& info, utils::CString&& tag) {
#if FILAMENT_METAL_DEBUG_LOG == 1
const char* labelStr = "";
std::visit([&labelStr](auto&& arg) {
@@ -724,13 +754,16 @@ void MetalDriver::createDescriptorSetLayoutR(
DEBUG_LOG("})\n");
#endif
construct_handle<MetalDescriptorSetLayout>(dslh, std::move(info));
mHandleAllocator.associateTagToHandle(dslh.getId(), std::move(tag));
}
void MetalDriver::createDescriptorSetR(
Handle<HwDescriptorSet> dsh, Handle<HwDescriptorSetLayout> dslh) {
Handle<HwDescriptorSet> dsh, Handle<HwDescriptorSetLayout> dslh, utils::CString&& tag) {
DEBUG_LOG("createDescriptorSetR(dsh = %d, dslh = %d)\n", dsh.getId(), dslh.getId());
MetalDescriptorSetLayout* layout = handle_cast<MetalDescriptorSetLayout>(dslh);
construct_handle<MetalDescriptorSet>(dsh, layout);
MetalDescriptorSet* ds = construct_handle<MetalDescriptorSet>(dsh, layout);
ds->setLabel(tag);
mHandleAllocator.associateTagToHandle(dsh.getId(), std::move(tag));
}
Handle<HwVertexBufferInfo> MetalDriver::createVertexBufferInfoS() noexcept {
@@ -807,6 +840,10 @@ Handle<HwSwapChain> MetalDriver::createSwapChainHeadlessS() noexcept {
return alloc_handle<MetalSwapChain>();
}
Handle<HwSync> MetalDriver::createSyncS() noexcept {
return alloc_handle<MetalSync>();
}
Handle<HwTimerQuery> MetalDriver::createTimerQueryS() noexcept {
// The handle must be constructed here, as a synchronous call to getTimerQueryValue might happen
// before createTimerQueryR is executed.
@@ -969,11 +1006,11 @@ ShaderLanguage MetalDriver::getShaderLanguage() const noexcept {
return ShaderLanguage::MSL;
}
Handle<HwStream> MetalDriver::createStreamNative(void* stream) {
Handle<HwStream> MetalDriver::createStreamNative(void* stream, utils::CString tag) {
return {};
}
Handle<HwStream> MetalDriver::createStreamAcquired() {
Handle<HwStream> MetalDriver::createStreamAcquired(utils::CString tag) {
return {};
}
@@ -1008,6 +1045,19 @@ FenceStatus MetalDriver::getFenceStatus(Handle<HwFence> fh) {
return fence->wait(0);
}
void MetalDriver::destroySync(Handle<HwSync> sh) {
if (sh) {
destruct_handle<MetalSync>(sh);
}
}
void MetalDriver::getPlatformSync(Handle<HwSync> sh, CallbackHandler* handler,
Platform::SyncCallback cb, void* userData) {
// TODO: If the sync has been inserted into the command stream, execute
// the callback. Otherwise, enqueue it.
}
bool MetalDriver::isTextureFormatSupported(TextureFormat format) {
return MetalTexture::decidePixelFormat(mContext, format) != MTLPixelFormatInvalid;
}
@@ -1089,6 +1139,11 @@ bool MetalDriver::isSRGBSwapChainSupported() {
return false;
}
bool MetalDriver::isMSAASwapChainSupported(uint32_t) {
// TODO: support MSAA swapchain
return false;
}
bool MetalDriver::isProtectedContentSupported() {
// the SWAP_CHAIN_CONFIG_PROTECTED_CONTENT flag is not supported
return false;
@@ -2135,10 +2190,6 @@ void MetalDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
void MetalDriver::resetState(int) {
}
void MetalDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
mHandleAllocator.associateTagToHandle(handleId, std::move(tag));
}
void MetalDriver::runAtNextTick(const std::function<void()>& fn) noexcept {
mTickOps.push_back(fn);
}
@@ -2166,6 +2217,38 @@ void MetalDriver::executeDeferredOps() noexcept {
}
}
MemoryMappedBufferHandle MetalDriver::mapBufferS() noexcept {
return alloc_handle<MetalMemoryMappedBuffer>();
}
void MetalDriver::mapBufferR(MemoryMappedBufferHandle mmbh,
BufferObjectHandle boh, size_t offset,
size_t size, MapBufferAccessFlags access, utils::CString&& tag) {
construct_handle<MetalMemoryMappedBuffer>(mmbh, boh, offset, size, access);
mHandleAllocator.associateTagToHandle(mmbh.getId(), std::move(tag));
}
void MetalDriver::unmapBuffer(MemoryMappedBufferHandle mmbh) {
if (UTILS_UNLIKELY(!mmbh)) {
return;
}
destruct_handle<MetalMemoryMappedBuffer>(mmbh);
}
void MetalDriver::copyToMemoryMappedBuffer(MemoryMappedBufferHandle mmbh, size_t offset,
BufferDescriptor&& data) {
auto mmb = handle_cast<MetalMemoryMappedBuffer>(mmbh);
assert_invariant(any(mmb->access & MapBufferAccessFlags::WRITE_BIT));
assert_invariant(offset + data.size <= mmb->size);
// TODO: this isa zero-effort implementation of copyToMemoryMappedBuffer(), where we just
// call updateBufferObject(). This could be a fallback implementation for when
// shared memory is not available.
// On UMA systems, this should just be a memcpy into the memory-mapped buffer.
updateBufferObject(mmb->boh, std::move(data), mmb->offset + offset);
}
// explicit instantiation of the Dispatcher
template class ConcreteDispatcher<MetalDriver>;
+32
View File
@@ -451,6 +451,38 @@ 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
+28
View File
@@ -0,0 +1,28 @@
/*
* 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_DRIVER_METALFLAGS_H
#define TNT_FILAMENT_DRIVER_METALFLAGS_H
#ifndef FILAMENT_METAL_DEBUG_LABELS
#ifndef NDEBUG
#define FILAMENT_METAL_DEBUG_LABELS 1
#else
#define FILAMENT_METAL_DEBUG_LABELS 0
#endif
#endif
#endif
+40
View File
@@ -27,6 +27,7 @@
#include "MetalContext.h"
#include "MetalEnums.h"
#include "MetalExternalImage.h"
#include "MetalFlags.h"
#include "MetalState.h" // for MetalState::VertexDescription
#include <backend/DriverEnums.h>
@@ -266,6 +267,16 @@ public:
static MTLPixelFormat decidePixelFormat(MetalContext* context, TextureFormat format);
void setLabel(const utils::CString& label) {
#if FILAMENT_METAL_DEBUG_LABELS
if (label.empty()) {
return;
}
texture.label = @(label.c_str_safe());
swizzledTextureView.label = @(label.c_str_safe());
#endif
}
MetalContext& context;
// A "sidecar" texture used to implement automatic MSAA resolve.
@@ -437,6 +448,10 @@ private:
uint64_t value;
};
// TODO: Provide implementation for MetalSync.
class MetalSync : public HwSync {
};
struct MetalTimerQuery : public HwTimerQuery {
MetalTimerQuery() : status(std::make_shared<Status>()) {}
@@ -479,6 +494,15 @@ struct MetalDescriptorSet : public HwDescriptorSet {
void finalize(MetalDriver* driver);
void setLabel(const utils::CString& l) {
#if FILAMENT_METAL_DEBUG_LABELS
if (l.empty()) {
return;
}
label = l;
#endif
}
id<MTLBuffer> finalizeAndGetBuffer(MetalDriver* driver, ShaderStage stage);
MetalDescriptorSetLayout* layout;
@@ -501,6 +525,22 @@ struct MetalDescriptorSet : public HwDescriptorSet {
std::vector<std::shared_ptr<MetalExternalImage>> externalImages;
std::array<TrackedMetalBuffer, Program::SHADER_TYPE_COUNT> cachedBuffer = { nil };
#if FILAMENT_METAL_DEBUG_LABELS
utils::CString label;
#endif
};
struct MetalMemoryMappedBuffer : public HwMemoryMappedBuffer {
MetalMemoryMappedBuffer(BufferObjectHandle boh, size_t const offset,
size_t const size, MapBufferAccessFlags const access)
: boh(boh), access(access), size(size), offset(offset) {
}
BufferObjectHandle boh{};
MapBufferAccessFlags access{};
uint32_t size = 0;
uint32_t offset = 0;
};
} // namespace backend
@@ -295,6 +295,12 @@ void MetalSwapChain::ensureDepthStencilTexture() {
void MetalSwapChain::setFrameScheduledCallback(
CallbackHandler* handler, FrameScheduledCallback&& callback, uint64_t flags) {
if (!callback) {
frameScheduled.handler = nullptr;
frameScheduled.callback.reset();
frameScheduled.flags = 0;
return;
}
frameScheduled.handler = handler;
frameScheduled.callback = std::make_shared<FrameScheduledCallback>(std::move(callback));
frameScheduled.flags = flags;
@@ -1486,6 +1492,11 @@ id<MTLBuffer> MetalDescriptorSet::finalizeAndGetBuffer(MetalDriver* driver, Shad
buffer = { [context.device newBufferWithLength:encoder.encodedLength
options:MTLResourceStorageModeShared],
TrackedMetalBuffer::Type::DESCRIPTOR_SET };
#if FILAMENT_METAL_DEBUG_LABELS
if (!label.empty()) {
buffer.get().label = @(label.c_str_safe());
}
#endif
}
[encoder setArgumentBuffer:buffer.get() offset:0];
+32 -18
View File
@@ -16,12 +16,11 @@
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/Platform.h>
#include "noop/NoopDriver.h"
#include "CommandStreamDispatcher.h"
#include <backend/Handle.h>
#include <stdint.h>
namespace filament::backend {
@@ -116,6 +115,9 @@ void NoopDriver::destroySwapChain(Handle<HwSwapChain> sch) {
void NoopDriver::destroyStream(Handle<HwStream> sh) {
}
void NoopDriver::destroySync(Handle<HwSync> sh) {
}
void NoopDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
}
@@ -125,11 +127,11 @@ void NoopDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh) {
void NoopDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) {
}
Handle<HwStream> NoopDriver::createStreamNative(void* nativeStream) {
Handle<HwStream> NoopDriver::createStreamNative(void* nativeStream, utils::CString tag) {
return {};
}
Handle<HwStream> NoopDriver::createStreamAcquired() {
Handle<HwStream> NoopDriver::createStreamAcquired(utils::CString tag) {
return {};
}
@@ -147,6 +149,10 @@ int64_t NoopDriver::getStreamTimestamp(Handle<HwStream> sh) {
void NoopDriver::updateStreams(CommandStream* driver) {
}
void NoopDriver::getPlatformSync(Handle<HwSync> sh, CallbackHandler* handler,
Platform::SyncCallback cb, void* userData) {
}
void NoopDriver::destroyFence(Handle<HwFence> fh) {
}
@@ -192,6 +198,10 @@ bool NoopDriver::isSRGBSwapChainSupported() {
return false;
}
bool NoopDriver::isMSAASwapChainSupported(uint32_t) {
return false;
}
bool NoopDriver::isProtectedContentSupported() {
return false;
}
@@ -318,8 +328,8 @@ void NoopDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> re
void NoopDriver::commit(Handle<HwSwapChain> sch) {
}
void NoopDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
void NoopDriver::setPushConstant(ShaderStage stage, uint8_t index,
PushConstantVariant value) {
}
void NoopDriver::insertEventMarker(char const* string) {
@@ -343,8 +353,8 @@ void NoopDriver::readPixels(Handle<HwRenderTarget> src,
scheduleDestroy(std::move(p));
}
void NoopDriver::readBufferSubData(backend::BufferObjectHandle boh,
uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) {
void NoopDriver::readBufferSubData(BufferObjectHandle boh,
uint32_t offset, uint32_t size, BufferDescriptor&& p) {
scheduleDestroy(std::move(p));
}
@@ -395,27 +405,31 @@ void NoopDriver::resetState(int) {
}
void NoopDriver::updateDescriptorSetBuffer(
backend::DescriptorSetHandle dsh,
backend::descriptor_binding_t binding,
backend::BufferObjectHandle boh,
DescriptorSetHandle dsh,
descriptor_binding_t binding,
BufferObjectHandle boh,
uint32_t offset,
uint32_t size) {
}
void NoopDriver::updateDescriptorSetTexture(
backend::DescriptorSetHandle dsh,
backend::descriptor_binding_t binding,
backend::TextureHandle th,
DescriptorSetHandle dsh,
descriptor_binding_t binding,
TextureHandle th,
SamplerParams params) {
}
void NoopDriver::bindDescriptorSet(
backend::DescriptorSetHandle dsh,
backend::descriptor_set_t set,
backend::DescriptorSetOffsetArray&& offsets) {
DescriptorSetHandle dsh,
descriptor_set_t set,
DescriptorSetOffsetArray&& offsets) {
}
void NoopDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
void NoopDriver::unmapBuffer(MemoryMappedBufferHandle mmbh) {
}
void NoopDriver::copyToMemoryMappedBuffer(MemoryMappedBufferHandle mmbh, size_t offset,
BufferDescriptor&& data) {
}
} // namespace filament
+5 -3
View File
@@ -40,10 +40,12 @@ struct GLBufferObject : public HwBufferObject {
GLenum binding;
void* buffer;
};
} gl;
BufferUsage usage;
BufferObjectBinding bindingType;
} gl{};
BufferUsage usage : 4;
BufferObjectBinding bindingType : 4;
uint8_t mappingCount = 0;
uint16_t age = 0;
};
} // namespace filament::backend
@@ -0,0 +1,154 @@
/*
* 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 "GLMemoryMappedBuffer.h"
#include "GLBufferObject.h"
#include "GLUtils.h"
#include "OpenGLContext.h"
#include "OpenGLDriver.h"
#include "gl_headers.h"
#include <private/backend/HandleAllocator.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/BitmaskEnum.h>
#include <limits>
#include <utility>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
namespace filament::backend {
GLMemoryMappedBuffer::GLMemoryMappedBuffer() = default;
GLMemoryMappedBuffer::~GLMemoryMappedBuffer() = default;
GLMemoryMappedBuffer::GLMemoryMappedBuffer(OpenGLContext& glc, HandleAllocatorGL& handleAllocator,
BufferObjectHandle boh,
size_t const offset, size_t const size, MapBufferAccessFlags const access)
: boh(boh), access(access) {
GLBufferObject* const bo = handleAllocator.handle_cast<GLBufferObject*>(boh);
assert_invariant(bo);
assert_invariant(bo->mappingCount < std::numeric_limits<uint8_t>::max());
assert_invariant(offset + size <= bo->byteCount);
if (any(access & MapBufferAccessFlags::WRITE_BIT)) {
assert_invariant(any(bo->usage & BufferUsage::SHARED_WRITE_BIT));
}
if (UTILS_UNLIKELY(glc.isES2())) {
gl.vaddr = static_cast<char*>(bo->gl.buffer) + offset;
gl.size = size;
gl.offset = offset;
gl.binding = 0; // in ES2 mode, bo->gl.binding is not available
gl.id = bo->gl.id;
bo->age++; // technically we could do this only in copy()
bo->mappingCount++;
return;
}
#if !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
void* addr = nullptr;
# if !defined(__EMSCRIPTEN__)
// unsynchronized is implied (but is incompatible with reading)
GLbitfield gl_access = GL_MAP_UNSYNCHRONIZED_BIT;
if (any(access & MapBufferAccessFlags::WRITE_BIT)) {
gl_access |= GL_MAP_WRITE_BIT;
}
if (any(access & MapBufferAccessFlags::INVALIDATE_RANGE_BIT)) {
// note: GL_MAP_INVALIDATE_RANGE_BIT is incompatible with GL_MAP_READ_BIT
gl_access |= GL_MAP_INVALIDATE_RANGE_BIT;
}
glc.bindBuffer(bo->gl.binding, bo->gl.id);
addr = glMapBufferRange(bo->gl.binding, GLsizeiptr(offset), GLsizeiptr(size), gl_access);
CHECK_GL_ERROR();
# endif
// if we failed here, addr would be nullptr
gl.vaddr = addr;
gl.size = size;
gl.offset = offset;
gl.binding = bo->gl.binding;
gl.id = bo->gl.id;
bo->mappingCount++;
#endif
}
void GLMemoryMappedBuffer::unmap(OpenGLContext& glc, HandleAllocatorGL& handleAllocator) const {
GLBufferObject* const bo = handleAllocator.handle_cast<GLBufferObject*>(boh);
assert_invariant(bo);
assert_invariant(bo->mappingCount > 0);
bo->mappingCount--;
if (UTILS_UNLIKELY(glc.isES2())) {
// nothing to do
return;
}
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
glc.bindBuffer(gl.binding, gl.id);
# if !defined(__EMSCRIPTEN__)
// don't unmap if we don't have a mapping or it didn't work
if (UTILS_LIKELY(gl.vaddr)) {
if (UTILS_UNLIKELY(glUnmapBuffer(gl.binding) == GL_FALSE)) {
// TODO: According to the spec, UnmapBuffer can return FALSE in rare conditions (e.g.
// during a screen mode change). Note that this is not a GL error, but the whole
// mapping is lost. This is very problematic for us here.
}
CHECK_GL_ERROR();
}
# endif
#endif
}
void GLMemoryMappedBuffer::copy(OpenGLContext& glc, OpenGLDriver& gld,
size_t const offset, BufferDescriptor&& data) const {
assert_invariant(any(access & MapBufferAccessFlags::WRITE_BIT));
assert_invariant(offset + data.size <= gl.size);
if (UTILS_LIKELY(gl.vaddr)) {
memcpy(static_cast<char *>(gl.vaddr) + offset, data.buffer, data.size);
} else {
assert_invariant(!glc.isES2());
// we couldn't map (WebGL or error), revert to glBufferSubData. In the future we can
// improve this by keeping around the BufferDescriptor and coalescing the calls
// to glBufferSubData.
glc.bindBuffer(gl.binding, gl.id);
glBufferSubData(gl.binding, GLintptr(gl.offset + offset), GLsizeiptr(data.size), data.buffer);
CHECK_GL_ERROR();
}
gld.scheduleDestroy(std::move(data));
}
} // namespace filament::backend
@@ -0,0 +1,66 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_OPENGL_GLMEMORYMAPPEDBUFFER_H
#define TNT_FILAMENT_BACKEND_OPENGL_GLMEMORYMAPPEDBUFFER_H
#include "DriverBase.h"
#include <private/backend/HandleAllocator.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include "gl_headers.h"
#include <stddef.h>
#include <stdint.h>
namespace filament::backend {
class OpenGLContext;
class OpenGLDriver;
class BufferDescriptor;
struct GLBufferObject;
struct GLMemoryMappedBuffer : public HwMemoryMappedBuffer {
BufferObjectHandle boh;
MapBufferAccessFlags access;
struct {
void* vaddr = nullptr;
uint32_t size = 0;
uint32_t offset = 0;
GLenum binding = 0;
GLuint id = 0;
} gl;
GLMemoryMappedBuffer();
GLMemoryMappedBuffer(OpenGLContext& glc, HandleAllocatorGL& handleAllocator,
BufferObjectHandle boh, size_t offset, size_t size, MapBufferAccessFlags access);
~GLMemoryMappedBuffer();
void unmap(OpenGLContext& gl, HandleAllocatorGL& handleAllocator) const;
void copy(OpenGLContext& glc, OpenGLDriver& gld,
size_t offset, BufferDescriptor&& data) const;
};
} // namespace filament::backend
#endif //TNT_FILAMENT_BACKEND_OPENGL_GLMEMORYMAPPEDBUFFER_H
+26 -7
View File
@@ -21,12 +21,14 @@
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include <utils/Logger.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <algorithm>
#include <functional>
#include <string_view>
#include <utility>
@@ -101,6 +103,8 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
initBugs(&bugs, ext, state.major, state.minor,
state.vendor, state.renderer, state.version, state.shader);
initWorkarounds(bugs, &ext);
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &gets.max_renderbuffer_size);
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &gets.max_texture_image_units);
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gets.max_combined_texture_image_units);
@@ -559,13 +563,22 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
}
if (strstr(vendor, "Mesa")) {
// Seen on
// [Mesa],
// [llvmpipe (LLVM 17.0.6, 256 bits)],
// [4.5 (Core Profile) Mesa 24.0.6-1],
// [4.50]
// not known which version are affected
bugs->rebind_buffer_after_deletion = true;
if (strstr(renderer, "llvmpipe")) {
// Seen on
// [Mesa],
// [llvmpipe (LLVM 17.0.6, 256 bits)],
// [4.5 (Core Profile) Mesa 24.0.6-1],
// [4.50]
// not known which version are affected
bugs->rebind_buffer_after_deletion = true;
// Seen on
// [Mesa]
// [llvmpipe (LLVM 17.0.6, 256 bits)]
// [4.5 (Core Profile) Mesa 24.2.1 (git-c222f7299c)]
// [4.50]
bugs->disable_framebuffer_fetch_extension = true;
}
}
} else {
// When running under ANGLE, it's a different set of workaround that we need.
@@ -611,6 +624,12 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
#endif
}
void OpenGLContext::initWorkarounds(Bugs const& bugs, Extensions* ext) {
if (bugs.disable_framebuffer_fetch_extension) {
ext->EXT_shader_framebuffer_fetch = false;
}
}
FeatureLevel OpenGLContext::resolveFeatureLevel(GLint major, GLint minor,
Extensions const& exts,
Gets const& gets,
@@ -334,6 +334,12 @@ public:
// those particular browsers.
bool disable_depth_precache_for_default_material;
// On llvmpipe (mesa), enabling framebuffer fetch causes a crash in draw2
// 'OpenGL error 0x502 (GL_INVALID_OPERATION) in "draw2" at line 4389'
// This coincides with the use of framebuffer fetch (ColorGradingAsSubpass). We disable
// framebuffer fetch in the case of llvmpipe.
bool disable_framebuffer_fetch_extension;
} bugs = {};
// state getters -- as needed.
@@ -577,6 +583,9 @@ private:
{ bugs.disable_depth_precache_for_default_material,
"disable_depth_precache_for_default_material",
""},
{ bugs.disable_framebuffer_fetch_extension,
"disable_framebuffer_fetch_extension",
""},
}};
// this is chosen to minimize code size
@@ -607,6 +616,8 @@ private:
static void initProcs(Procs* procs,
Extensions const& exts, GLint major, GLint minor) noexcept;
static void initWorkarounds(Bugs const& bugs, Extensions* ext);
static FeatureLevel resolveFeatureLevel(GLint major, GLint minor,
Extensions const& exts,
Gets const& gets,
File diff suppressed because it is too large Load Diff
@@ -24,10 +24,12 @@
#include "GLBufferObject.h"
#include "GLDescriptorSet.h"
#include "GLDescriptorSetLayout.h"
#include "GLMemoryMappedBuffer.h"
#include "GLTexture.h"
#include "ShaderCompilerService.h"
#include <backend/AcquiredImage.h>
#include <backend/CallbackHandler.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/PipelineState.h>
@@ -99,6 +101,10 @@ public:
struct GLSwapChain : public HwSwapChain {
using HwSwapChain::HwSwapChain;
bool rec709 = false;
struct {
CallbackHandler* handler = nullptr;
std::shared_ptr<FrameScheduledCallback> callback = nullptr;
} frameScheduled;
};
struct GLVertexBufferInfo : public HwVertexBufferInfo {
@@ -146,6 +152,8 @@ public:
using GLDescriptorSet = filament::backend::GLDescriptorSet;
using GLMemoryMappedBuffer = filament::backend::GLMemoryMappedBuffer;
struct GLStream : public HwStream {
using HwStream::HwStream;
struct Info {
@@ -193,9 +201,24 @@ public:
std::shared_ptr<State> state{ std::make_shared<State>() };
};
// Note: named "GLSyncFence" to avoid confusion with the GL handle,
// "GLsync" (lowercase S)
struct GLSyncFence : public HwSync {
struct CallbackData {
CallbackHandler* handler;
Platform::SyncCallback cb;
Platform::Sync* sync;
void* userData;
};
std::mutex lock;
std::vector<std::unique_ptr<CallbackData>> conversionCallbacks;
};
OpenGLDriver(OpenGLDriver const&) = delete;
OpenGLDriver& operator=(OpenGLDriver const&) = delete;
using DriverBase::scheduleDestroy;
private:
OpenGLPlatform& mPlatform;
OpenGLContext mContext;
@@ -76,6 +76,10 @@ bool OpenGLPlatform::isSRGBSwapChainSupported() const noexcept {
return false;
}
bool OpenGLPlatform::isMSAASwapChainSupported(uint32_t) const noexcept {
return false;
}
uint32_t OpenGLPlatform::getDefaultFramebufferObject() noexcept {
return 0;
}
@@ -117,6 +121,15 @@ FenceStatus OpenGLPlatform::waitFence(
return FenceStatus::ERROR;
}
Platform::Sync* OpenGLPlatform::createSync() noexcept {
return new Platform::Sync();
}
void OpenGLPlatform::destroySync(Platform::Sync* sync) noexcept {
// sync must be a Platform::Sync, since it was created by this platform
// object.
delete sync;
}
Platform::Stream* OpenGLPlatform::createStream(
UTILS_UNUSED void* nativeStream) noexcept {
@@ -46,7 +46,6 @@
#include <cctype>
#include <mutex>
#include <memory>
#include <string>
#include <string_view>
#include <thread>
#include <utility>
@@ -67,9 +66,9 @@ constexpr uint32_t MAX_NUM_SYNCHRONOUS_PROGRAMS_PER_FRAME = 1;
// ------------------------------------------------------------------------------------------------
static std::string to_string(bool const b) { return b ? "true" : "false"; }
static std::string to_string(int const i) { return std::to_string(i); }
static std::string to_string(float const f) { return "float(" + std::to_string(f) + ")"; }
static CString to_string(bool const b) { return CString{ b ? "true" : "false" }; }
static CString to_string(int const i) { return utils::to_string(i); }
static CString to_string(float const f) { return "float(" + utils::to_string(f) + ")"; }
static void logCompilationError(ShaderStage shaderType, const char* name, GLuint shaderId,
Program::ShaderBlob const& sourceCode) noexcept;
@@ -696,14 +695,14 @@ void ShaderCompilerService::cancelPendingSynchronousProgram(program_token_t cons
bool multiview, program_token_t const& token) noexcept {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
auto const appendSpecConstantString = +[](std::string& s, Program::SpecializationConstant const& sc) {
s += "#define SPIRV_CROSS_CONSTANT_ID_" + std::to_string(sc.id) + ' ';
auto const appendSpecConstantString = +[](CString& s, Program::SpecializationConstant const& sc) {
s += "#define SPIRV_CROSS_CONSTANT_ID_" + utils::to_string(sc.id) + ' ';
s += std::visit([](auto&& arg) { return to_string(arg); }, sc.value);
s += '\n';
return s;
};
std::string specializationConstantString;
CString specializationConstantString;
int32_t numViews = 2;
for (auto const& sc: specializationConstants) {
appendSpecConstantString(specializationConstantString, sc);
@@ -766,8 +765,10 @@ void ShaderCompilerService::cancelPendingSynchronousProgram(program_token_t cons
}
std::array<std::string_view, 5> sources = {
version, prolog, specializationConstantString, packingFunctions,
{ body.data(), body.size() - 1 }// null-terminated
version, prolog,
{ specializationConstantString.data(), specializationConstantString.size() },
packingFunctions,
{ body.data(), body.size() - 1 } // null-terminated
};
// Some of the sources may be zero-length. Remove them as to avoid passing lengths of
@@ -966,16 +967,18 @@ UTILS_NOINLINE
sourceCode.size() };
size_t lc = 1;
size_t start = 0;
std::string line;
CString line;
while (true) {
size_t const end = shader.find('\n', start);
if (end == std::string::npos) {
line = shader.substr(start);
if (end == std::string_view::npos) {
auto sv = shader.substr(start);
line = { sv.data(), sv.size() };
} else {
line = shader.substr(start, end - start);
auto sv = shader.substr(start, end - start);
line = { sv.data(), sv.size() };
}
LOG(ERROR) << lc++ << ": " << line.c_str();
if (end == std::string::npos) {
if (end == std::string_view::npos) {
break;
}
start = end + 1;
@@ -322,6 +322,7 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const DriverConfig& drive
mCurrentContextType = ContextType::UNPROTECTED;
mContextAttribs = std::move(contextAttribs);
mMSAA4XSupport = checkIfMSAASwapChainSupported(4);
initializeGlExtensions();
@@ -448,6 +449,11 @@ EGLConfig PlatformEGL::findSwapChainConfig(uint64_t flags, bool window, bool pbu
configAttribs[EGL_RECORDABLE_ANDROID] = EGL_TRUE;
}
if (flags & SWAP_CHAIN_CONFIG_MSAA_4_SAMPLES) {
configAttribs[EGL_SAMPLE_BUFFERS] = 1;
configAttribs[EGL_SAMPLES] = 4;
}
if (UTILS_UNLIKELY(
!eglChooseConfig(mEGLDisplay, configAttribs.data(), &config, 1, &configsCount))) {
logEglError("eglChooseConfig");
@@ -481,11 +487,25 @@ bool PlatformEGL::isSRGBSwapChainSupported() const noexcept {
return ext.egl.KHR_gl_colorspace;
}
bool PlatformEGL::isMSAASwapChainSupported(uint32_t samples) const noexcept {
if (samples <= 1) {
return true;
}
if (samples == 4) {
return mMSAA4XSupport;
}
return false;
}
Platform::SwapChain* PlatformEGL::createSwapChain(
void* nativeWindow, uint64_t flags) noexcept {
Config attribs;
if (ext.egl.KHR_gl_colorspace) {
// Remove flags for unsupported features.
if (isSRGBSwapChainSupported()) {
if (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) {
attribs[EGL_GL_COLORSPACE_KHR] = EGL_GL_COLORSPACE_SRGB_KHR;
}
@@ -493,7 +513,7 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
flags &= ~SWAP_CHAIN_CONFIG_SRGB_COLORSPACE;
}
if (ext.egl.EXT_protected_content) {
if (isProtectedContextSupported()) {
if (flags & SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) {
attribs[EGL_PROTECTED_CONTENT_EXT] = EGL_TRUE;
}
@@ -501,6 +521,13 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
flags &= ~SWAP_CHAIN_CONFIG_PROTECTED_CONTENT;
}
if (flags & SWAP_CHAIN_CONFIG_MSAA_4_SAMPLES) {
if (!isMSAASwapChainSupported(4)) {
flags &= ~SWAP_CHAIN_CONFIG_MSAA_4_SAMPLES;
}
}
// Retrieve a config for the given flags.
EGLConfig config = EGL_NO_CONFIG_KHR;
if (UTILS_LIKELY(ext.egl.KHR_no_config_context)) {
config = findSwapChainConfig(flags, true, false);
@@ -789,6 +816,38 @@ EGLContext PlatformEGL::getContextForType(ContextType type) const noexcept {
}
}
bool PlatformEGL::checkIfMSAASwapChainSupported(uint32_t samples) const noexcept {
// Retrieve the config to see if the given number of samples is supported. The result is cached.
Config configAttribs = {
{ EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT },
{ EGL_RED_SIZE, 8 },
{ EGL_GREEN_SIZE, 8 },
{ EGL_BLUE_SIZE, 8 },
{ EGL_DEPTH_SIZE, 24 },
{ EGL_SAMPLE_BUFFERS, 1 },
{ EGL_SAMPLES, (EGLint)samples },
};
if (!ext.egl.KHR_no_config_context) {
if (isOpenGL()) {
configAttribs[EGL_RENDERABLE_TYPE] = EGL_OPENGL_BIT;
} else {
configAttribs[EGL_RENDERABLE_TYPE] = EGL_OPENGL_ES2_BIT;
if (ext.egl.KHR_create_context) {
configAttribs[EGL_RENDERABLE_TYPE] |= EGL_OPENGL_ES3_BIT_KHR;
}
}
}
EGLConfig config = EGL_NO_CONFIG_KHR;
EGLint configsCount;
if (!eglChooseConfig(mEGLDisplay, configAttribs.data(), &config, 1, &configsCount)) {
return false;
}
return configsCount > 0;
}
// ---------------------------------------------------------------------------------------------
PlatformEGL::Config::Config() = default;
@@ -83,6 +83,7 @@ UTILS_PRIVATE PFNEGLGETCOMPOSITORTIMINGANDROIDPROC eglGetCompositorTimingANDROID
UTILS_PRIVATE PFNEGLGETNEXTFRAMEIDANDROIDPROC eglGetNextFrameIdANDROID = {};
UTILS_PRIVATE PFNEGLGETFRAMETIMESTAMPSUPPORTEDANDROIDPROC eglGetFrameTimestampSupportedANDROID = {};
UTILS_PRIVATE PFNEGLGETFRAMETIMESTAMPSANDROIDPROC eglGetFrameTimestampsANDROID = {};
UTILS_PRIVATE PFNEGLDUPNATIVEFENCEFDANDROIDPROC eglDupNativeFenceFDANDROID = {};
}
using namespace glext;
@@ -226,6 +227,8 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
"eglGetFrameTimestampSupportedANDROID");
eglGetFrameTimestampsANDROID = (PFNEGLGETFRAMETIMESTAMPSANDROIDPROC)eglGetProcAddress(
"eglGetFrameTimestampsANDROID");
eglDupNativeFenceFDANDROID =
(PFNEGLDUPNATIVEFENCEFDANDROIDPROC) eglGetProcAddress("eglDupNativeFenceFDANDROID");
}
mAssertNativeWindowIsValid = driverConfig.assertNativeWindowIsValid;
@@ -395,6 +398,31 @@ void PlatformEGLAndroid::destroyStream(Platform::Stream* stream) noexcept {
mExternalStreamManager.release(stream);
}
Platform::Sync* PlatformEGLAndroid::createSync() noexcept {
auto sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, nullptr);
return new SyncEGLAndroid{.sync = sync};
}
bool PlatformEGLAndroid::convertSyncToFd(Platform::Sync* sync, int* fd) noexcept {
assert_invariant(sync && fd);
SyncEGLAndroid& eglSync = static_cast<SyncEGLAndroid&>(*sync);
*fd = eglDupNativeFenceFDANDROID(mEGLDisplay, eglSync.sync);
// In the case where there was no native FD, -1 is returned. Return false
// to indicate there was an error in this case.
if (*fd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
LOG(ERROR) << "Failed to convert sync to fd: " << eglGetError();
return false;
}
return true;
}
void PlatformEGLAndroid::destroySync(Platform::Sync* sync) noexcept {
assert_invariant(sync);
SyncEGLAndroid& eglSync = static_cast<SyncEGLAndroid&>(*sync);
eglDestroySyncKHR(mEGLDisplay, eglSync.sync);
delete sync;
}
void PlatformEGLAndroid::attach(Stream* stream, intptr_t tname) noexcept {
mExternalStreamManager.attach(stream, tname);
}
+1 -5
View File
@@ -95,11 +95,7 @@ io::ostream& operator<<(io::ostream& out, ElementType type) {
}
io::ostream& operator<<(io::ostream& out, BufferUsage usage) {
switch (usage) {
CASE(BufferUsage, STATIC)
CASE(BufferUsage, DYNAMIC)
}
return out;
return out << io::hex << uint32_t(usage) << io::dec;
}
io::ostream& operator<<(io::ostream& out, CullingMode mode) {
@@ -20,6 +20,7 @@
#include <bluevk/BlueVK.h>
#include "DriverBase.h"
#include "backend/Platform.h"
#include "vulkan/memory/Resource.h"
@@ -52,6 +53,19 @@ struct VulkanFence : public HwFence, fvkmemory::ThreadSafeResource {
std::shared_ptr<VulkanCmdFence> fence;
};
struct VulkanSync : fvkmemory::ThreadSafeResource, public HwSync {
struct CallbackData {
CallbackHandler* handler;
Platform::SyncCallback cb;
Platform::Sync* sync;
void* userData;
};
VulkanSync() {}
std::mutex lock;
std::vector<std::unique_ptr<CallbackData>> conversionCallbacks;
};
struct VulkanTimerQuery : public HwTimerQuery, fvkmemory::ThreadSafeResource {
VulkanTimerQuery(uint32_t startingIndex, uint32_t stoppingIndex)
: mStartingQueryIndex(startingIndex),
@@ -96,7 +96,6 @@ inline void resolveFast(VulkanCommandBuffer* commands, VkImageAspectFlags aspect
VulkanLayout oldSrcLayout = src.getLayout();
VulkanLayout oldDstLayout = dst.getLayout();
src.texture->transitionLayout(commands, srcRange, VulkanLayout::TRANSFER_SRC);
dst.texture->transitionLayout(commands, dstRange, VulkanLayout::TRANSFER_DST);
assert_invariant(
@@ -109,10 +108,9 @@ inline void resolveFast(VulkanCommandBuffer* commands, VkImageAspectFlags aspect
.extent = { src.getExtent2D().width, src.getExtent2D().height, 1 },
}};
vkCmdResolveImage(cmdbuffer,
src.getImage(), fvkutils::getVkLayout(VulkanLayout::TRANSFER_SRC),
src.getImage(), fvkutils::getVkLayout(src.getLayout()),
dst.getImage(), fvkutils::getVkLayout(VulkanLayout::TRANSFER_DST),
1, resolveRegions);
if (oldSrcLayout == VulkanLayout::UNDEFINED) {
oldSrcLayout = src.texture->getDefaultLayout();
}
+25 -8
View File
@@ -24,6 +24,7 @@
#include "VulkanConstants.h"
#include "VulkanContext.h"
#include <utils/CString.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/debug.h>
@@ -57,26 +58,26 @@ VkCommandBuffer createCommandBuffer(VkDevice device, VkCommandPool pool) {
} // anonymous namespace
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
void VulkanGroupMarkers::push(std::string const& marker, Timestamp start) noexcept {
void VulkanGroupMarkers::push(CString const& marker, Timestamp start) noexcept {
mMarkers.push_back({marker,
start.time_since_epoch().count() > 0.0
? start
: std::chrono::high_resolution_clock::now()});
}
std::pair<std::string, Timestamp> VulkanGroupMarkers::pop() noexcept {
std::pair<CString, Timestamp> VulkanGroupMarkers::pop() noexcept {
auto ret = mMarkers.back();
mMarkers.pop_back();
return ret;
}
std::pair<std::string, Timestamp> VulkanGroupMarkers::pop_bottom() noexcept {
std::pair<CString, Timestamp> VulkanGroupMarkers::pop_bottom() noexcept {
auto ret = mMarkers.front();
mMarkers.pop_front();
return ret;
}
std::pair<std::string, Timestamp> const& VulkanGroupMarkers::top() const {
std::pair<CString, Timestamp> const& VulkanGroupMarkers::top() const {
assert_invariant(!empty());
return mMarkers.back();
}
@@ -102,6 +103,15 @@ VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext const& context, VkDevice
vkCreateSemaphore(mDevice, &sci, VKALLOC, &mSubmission);
VkFenceCreateInfo fenceCreateInfo{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
VkExportFenceCreateInfo exportFenceCreateInfo{
.sType = VK_STRUCTURE_TYPE_EXPORT_FENCE_CREATE_INFO,
.handleTypes = context.getFenceExportFlags()
};
// Necessary to guard this. Otherwise, swiftshader would throw an error.
if (context.getFenceExportFlags()) {
fenceCreateInfo.pNext = &exportFenceCreateInfo;
}
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &mFence);
}
@@ -353,7 +363,7 @@ void CommandBufferPool::waitFor(VkSemaphore previousAction, VkPipelineStageFlags
}
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
std::string CommandBufferPool::topMarker() const {
CString CommandBufferPool::topMarker() const {
if (!mGroupMarkers || mGroupMarkers->empty()) {
return "";
}
@@ -364,11 +374,11 @@ void CommandBufferPool::pushMarker(char const* marker, VulkanGroupMarkers::Times
if (!mGroupMarkers) {
mGroupMarkers = std::make_unique<VulkanGroupMarkers>();
}
mGroupMarkers->push(marker, timestamp);
mGroupMarkers->push(CString{ marker }, timestamp);
getRecording().pushMarker(marker);
}
std::pair<std::string, VulkanGroupMarkers::Timestamp> CommandBufferPool::popMarker() {
std::pair<CString, VulkanGroupMarkers::Timestamp> CommandBufferPool::popMarker() {
assert_invariant(mGroupMarkers && !mGroupMarkers->empty());
auto ret = mGroupMarkers->pop();
@@ -424,6 +434,9 @@ bool VulkanCommands::flush() {
VkSemaphore lastSubmit = mLastSubmit;
bool hasFlushed = false;
VkFence flushedFence = VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> flushedFenceStatus;
// Note that we've ordered it so that the non-protected commands are followed by the protected
// commands. This assumes that the protected commands will be that one doing the rendering into
// the protected memory (i.e. protected render target).
@@ -445,6 +458,8 @@ bool VulkanCommands::flush() {
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_TRANSFER_BIT);
lastSubmit = VK_NULL_HANDLE;
}
flushedFence = pool->getRecording().getVkFence();
flushedFenceStatus = pool->getRecording().getFenceStatus();
dependency = pool->flush();
hasFlushed = true;
}
@@ -452,6 +467,8 @@ bool VulkanCommands::flush() {
if (hasFlushed) {
mInjectedDependency = VK_NULL_HANDLE;
mLastSubmit = dependency;
mLastFence = flushedFence;
mLastFenceStatus = flushedFenceStatus;
}
return true;
@@ -528,7 +545,7 @@ void VulkanCommands::insertEventMarker(char const* str, uint32_t len) {
}
}
std::string VulkanCommands::getTopGroupMarker() const {
CString VulkanCommands::getTopGroupMarker() const {
if (mProtectedPool) {
return mProtectedPool->topMarker();
}
+21 -10
View File
@@ -28,6 +28,7 @@
#include "vulkan/utils/StaticVector.h"
#include <utils/Condition.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Mutex.h>
@@ -35,7 +36,6 @@
#include <chrono>
#include <list>
#include <string>
#include <utility>
namespace filament::backend {
@@ -47,14 +47,14 @@ class VulkanGroupMarkers {
public:
using Timestamp = std::chrono::time_point<std::chrono::high_resolution_clock>;
void push(std::string const& marker, Timestamp start = {}) noexcept;
std::pair<std::string, Timestamp> pop() noexcept;
std::pair<std::string, Timestamp> pop_bottom() noexcept;
std::pair<std::string, Timestamp> const& top() const;
void push(utils::CString const& marker, Timestamp start = {}) noexcept;
std::pair<utils::CString, Timestamp> pop() noexcept;
std::pair<utils::CString, Timestamp> pop_bottom() noexcept;
std::pair<utils::CString, Timestamp> const& top() const;
bool empty() const noexcept;
private:
std::list<std::pair<std::string, Timestamp>> mMarkers;
std::list<std::pair<utils::CString, Timestamp>> mMarkers;
};
#endif // FVK_DEBUG_GROUP_MARKERS
@@ -148,9 +148,9 @@ struct CommandBufferPool {
void waitFor(VkSemaphore previousAction, VkPipelineStageFlags waitStage);
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
std::string topMarker() const;
utils::CString topMarker() const;
void pushMarker(char const* marker, VulkanGroupMarkers::Timestamp timestamp);
std::pair<std::string, VulkanGroupMarkers::Timestamp> popMarker();
std::pair<utils::CString, VulkanGroupMarkers::Timestamp> popMarker();
void insertEvent(char const* marker);
#endif
@@ -223,11 +223,19 @@ public:
// it from the existing dependency chain. This is especially useful for setting up
// vkQueuePresentKHR.
VkSemaphore acquireFinishedSignal() {
VkSemaphore ret= mLastSubmit;
VkSemaphore ret = mLastSubmit;
mLastSubmit = VK_NULL_HANDLE;
return ret;
}
VkFence getMostRecentFence() {
return mLastFence;
}
std::shared_ptr<VulkanCmdFence> getMostRecentFenceStatus() {
return mLastFenceStatus;
}
// Takes a semaphore that signals when the next flush can occur. Only one injected
// semaphore is allowed per flush. Useful after calling vkAcquireNextImageKHR.
// waitStage
@@ -249,7 +257,7 @@ public:
void pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp = {});
void popGroupMarker();
void insertEventMarker(char const* string, uint32_t len);
std::string getTopGroupMarker() const;
utils::CString getTopGroupMarker() const;
#endif
private:
@@ -265,6 +273,9 @@ private:
VkSemaphore mInjectedDependency = VK_NULL_HANDLE;
VkSemaphore mLastSubmit = VK_NULL_HANDLE;
VkFence mLastFence = VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> mLastFenceStatus;
VkPipelineStageFlags mInjectedDependencyWaitStage = 0;
};
@@ -106,6 +106,10 @@ public:
return mPhysicalDeviceProperties.properties.vendorID;
}
inline VkExternalFenceHandleTypeFlags getFenceExportFlags() const noexcept {
return mFenceExportFlags;
}
inline bool isImageCubeArraySupported() const noexcept {
return mPhysicalDeviceFeatures.features.imageCubeArray == VK_TRUE;
}
@@ -168,6 +172,9 @@ private:
// non-conformant vulkan implementation).
.imageView2DOn3DImage = VK_TRUE,
};
VkExternalFenceHandleTypeFlags mFenceExportFlags = {};
bool mDebugMarkersSupported = false;
bool mDebugUtilsSupported = false;
bool mLazilyAllocatedMemorySupported = false;
+204 -42
View File
@@ -35,6 +35,7 @@
#include <backend/DriverEnums.h>
#include <backend/platforms/VulkanPlatform.h>
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/Panic.h>
@@ -136,6 +137,13 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFla
}
#endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
static CallbackHandler::Callback syncCallbackWrapper = [](void* userData) {
std::unique_ptr<VulkanSync::CallbackData> cbData(
static_cast<VulkanSync::CallbackData*>(userData));
// This assumes the sync has not yet been destroyed. If it has, this will be
// undefined behavior.
cbData->cb(cbData->sync, cbData->userData);
};
}// anonymous namespace
@@ -232,6 +240,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context,
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache,
&mDescriptorSetLayoutCache),
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
mIsMSAASwapChainSupported(false), // TODO: support MSAA swapchain
mStereoscopicType(driverConfig.stereoscopicType) {
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
@@ -416,6 +425,8 @@ void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
FrameScheduledCallback&& callback, uint64_t flags) {
auto swapChain = resource_ptr<VulkanSwapChain>::cast(&mResourceManager, sch);
swapChain->setFrameScheduledCallback(handler, std::move(callback));
}
void VulkanDriver::setFrameCompletedCallback(Handle<HwSwapChain> sch,
@@ -465,6 +476,21 @@ void VulkanDriver::updateDescriptorSetTexture(
}
}
void VulkanDriver::copyToMemoryMappedBuffer(MemoryMappedBufferHandle mmbh, size_t offset,
BufferDescriptor&& data) {
FVK_SYSTRACE_SCOPE();
auto mmb = resource_ptr<VulkanMemoryMappedBuffer>::cast(&mResourceManager, mmbh);
assert_invariant(any(mmb->access & MapBufferAccessFlags::WRITE_BIT));
assert_invariant(offset + data.size <= mmb->size);
// TODO: this isa zero-effort implementation of copyToMemoryMappedBuffer(), where we just
// call updateBufferObject(). This could be a fallback implementation for when
// shared memory is not available.
// On UMA systems, this should just be a memcpy into the memory-mapped buffer.
updateBufferObject(mmb->boh, std::move(data), mmb->offset + offset);
}
void VulkanDriver::flush(int) {
FVK_SYSTRACE_SCOPE();
endCommandRecording();
@@ -474,6 +500,16 @@ void VulkanDriver::finish(int dummy) {
FVK_SYSTRACE_SCOPE();
endCommandRecording();
// It's not enough to wait on the fences of the buffers submitted. Present calls are
// submitted to this same queue, so the more correct option is to call vkQueueWaitIdle.
vkQueueWaitIdle(mPlatform->getGraphicsQueue());
if (auto protectedQueue = mPlatform->getProtectedGraphicsQueue();
UTILS_UNLIKELY(protectedQueue != VK_NULL_HANDLE)) {
vkQueueWaitIdle(protectedQueue);
}
// This wait() is still necessary to update the fence status.
mCommands.wait();
mReadPixels.runUntilComplete();
@@ -481,12 +517,13 @@ void VulkanDriver::finish(int dummy) {
void VulkanDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
PrimitiveType pt) {
PrimitiveType pt, utils::CString&& tag) {
FVK_SYSTRACE_SCOPE();
auto vb = resource_ptr<VulkanVertexBuffer>::cast(&mResourceManager, vbh);
auto ib = resource_ptr<VulkanIndexBuffer>::cast(&mResourceManager, ibh);
auto ptr = resource_ptr<VulkanRenderPrimitive>::make(&mResourceManager, rph, pt, vb, ib);
ptr.inc();
mResourceManager.associateHandle(rph.getId(), std::move(tag));
}
void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
@@ -499,11 +536,12 @@ void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
}
void VulkanDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes) {
uint8_t attributeCount, AttributeArray attributes, utils::CString&& tag) {
FVK_SYSTRACE_SCOPE();
auto vbi = resource_ptr<VulkanVertexBufferInfo>::make(&mResourceManager, vbih, bufferCount,
attributeCount, attributes);
vbi.inc();
mResourceManager.associateHandle(vbih.getId(), std::move(tag));
}
void VulkanDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
@@ -516,12 +554,13 @@ void VulkanDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
}
void VulkanDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint32_t vertexCount,
Handle<HwVertexBufferInfo> vbih) {
Handle<HwVertexBufferInfo> vbih, utils::CString&& tag) {
FVK_SYSTRACE_SCOPE();
auto vbi = resource_ptr<VulkanVertexBufferInfo>::cast(&mResourceManager, vbih);
auto vb = resource_ptr<VulkanVertexBuffer>::make(&mResourceManager, vbh, mContext, mStagePool,
vertexCount, vbi);
vb.inc();
mResourceManager.associateHandle(vbh.getId(), std::move(tag));
}
void VulkanDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
@@ -534,12 +573,13 @@ void VulkanDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
}
void VulkanDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) {
uint32_t indexCount, BufferUsage usage, utils::CString&& tag) {
FVK_SYSTRACE_SCOPE();
auto elementSize = (uint8_t) getElementTypeSize(elementType);
auto ib = resource_ptr<VulkanIndexBuffer>::make(&mResourceManager, ibh, mContext, mAllocator,
mStagePool, mBufferCache, elementSize, indexCount);
ib.inc();
mResourceManager.associateHandle(ibh.getId(), std::move(tag));
}
void VulkanDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
@@ -552,11 +592,12 @@ void VulkanDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
}
void VulkanDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {
BufferObjectBinding bindingType, BufferUsage usage, utils::CString&& tag) {
FVK_SYSTRACE_SCOPE();
auto bo = resource_ptr<VulkanBufferObject>::make(&mResourceManager, boh, mContext, mAllocator,
mStagePool, mBufferCache, byteCount, bindingType);
bo.inc();
mResourceManager.associateHandle(boh.getId(), std::move(tag));
}
void VulkanDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
@@ -570,7 +611,7 @@ void VulkanDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
void VulkanDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage usage) {
TextureUsage usage, utils::CString&& tag) {
FVK_SYSTRACE_SCOPE();
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mResourceManager, &mCommands,
@@ -583,31 +624,34 @@ void VulkanDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
texture->transitionLayout(&commandsBuf, primaryViewRange, defaultLayout);
texture.inc();
mResourceManager.associateHandle(th.getId(), std::move(tag));
}
void VulkanDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
uint8_t baseLevel, uint8_t levelCount) {
uint8_t baseLevel, uint8_t levelCount, utils::CString&& tag) {
auto src = resource_ptr<VulkanTexture>::cast(&mResourceManager, srch);
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, src, baseLevel,
levelCount);
texture.inc();
mResourceManager.associateHandle(th.getId(), std::move(tag));
}
void VulkanDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
backend::TextureSwizzle a) {
TextureSwizzle const swizzleArray[] = {r, g, b, a};
VkComponentMapping const swizzle = fvkutils::getSwizzleMap(swizzleArray);
auto src = resource_ptr<VulkanTexture>::cast(&mResourceManager, srch);
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, src, swizzle);
texture.inc();
backend::TextureSwizzle a, utils::CString&& tag) {
TextureSwizzle const swizzleArray[] = { r, g, b, a };
VkComponentMapping const swizzle = fvkutils::getSwizzleMap(swizzleArray);
auto src = resource_ptr<VulkanTexture>::cast(&mResourceManager, srch);
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, src, swizzle);
texture.inc();
mResourceManager.associateHandle(th.getId(), std::move(tag));
}
void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
Platform::ExternalImageHandleRef externalImage) {
Platform::ExternalImageHandleRef externalImage, utils::CString&& tag) {
FVK_SYSTRACE_SCOPE();
auto metadata = mPlatform->extractExternalImageMetadata(externalImage);
@@ -653,11 +697,12 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
}
texture.inc();
mResourceManager.associateHandle(th.getId(), std::move(tag));
}
void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* externalImage) {
void* externalImage, utils::CString&& tag) {
FVK_SYSTRACE_SCOPE();
assert_invariant(false && "Not supported in Vulkan backend");
// not supported in this backend
@@ -665,16 +710,17 @@ void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::Sa
void VulkanDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, uint32_t plane) {
void* image, uint32_t plane, utils::CString&& tag) {
assert_invariant(false && "Not supported in Vulkan backend");
}
void VulkanDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage usage) {
TextureUsage usage, utils::CString&& tag) {
// not supported in this backend
assert_invariant(false && "Not supported in Vulkan backend");
mResourceManager.associateHandle(th.getId(), std::move(tag));
}
void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
@@ -687,11 +733,12 @@ void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
mExternalImageManager.removeExternallySampledTexture(texture);
}
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program, utils::CString&& tag) {
FVK_SYSTRACE_SCOPE();
auto vprogram = resource_ptr<VulkanProgram>::make(&mResourceManager, ph, mPlatform->getDevice(),
program);
vprogram.inc();
mResourceManager.associateHandle(ph.getId(), std::move(tag));
}
void VulkanDriver::destroyProgram(Handle<HwProgram> ph) {
@@ -702,18 +749,20 @@ void VulkanDriver::destroyProgram(Handle<HwProgram> ph) {
vprogram.dec();
}
void VulkanDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
void VulkanDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils::CString&& tag) {
assert_invariant(mDefaultRenderTarget); // Default render target should already exist.
auto renderTarget = resource_ptr<VulkanRenderTarget>::make(&mResourceManager, rth,
std::move(std::move(*mDefaultRenderTarget.get())));
mDefaultRenderTarget = renderTarget;
mResourceManager.associateHandle(rth.getId(), std::move(tag));
}
void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
TargetBufferFlags targets, uint32_t width, uint32_t height, uint8_t samples,
uint8_t layerCount, MRT color, TargetBufferInfo depth, TargetBufferInfo stencil) {
uint8_t layerCount, MRT color, TargetBufferInfo depth, TargetBufferInfo stencil,
utils::CString&& tag) {
FVK_SYSTRACE_SCOPE();
@@ -774,6 +823,8 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
mPlatform->getPhysicalDevice(), mContext, &mResourceManager, mAllocator, &mCommands,
width, height, samples, colorTargets, depthStencil, mStagePool, layerCount);
rt.inc();
mResourceManager.associateHandle(rth.getId(), std::move(tag));
}
void VulkanDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
@@ -782,15 +833,13 @@ void VulkanDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
}
auto rt = resource_ptr<VulkanRenderTarget>::cast(&mResourceManager, rth);
if (UTILS_UNLIKELY(rt == mDefaultRenderTarget)) {
// Note that this should only happen on driver shutdown.
mDefaultRenderTarget = {};
} else {
// Note that the default render target will be destroyed on shutdown
if (UTILS_LIKELY(rt != mDefaultRenderTarget)) {
rt.dec();
}
}
void VulkanDriver::createFenceR(Handle<HwFence> fh, int) {
void VulkanDriver::createFenceR(Handle<HwFence> fh, utils::CString&& tag) {
VulkanCommandBuffer* cmdbuf;
if (mCurrentRenderPass.commandBuffer) {
cmdbuf = mCurrentRenderPass.commandBuffer;
@@ -802,19 +851,57 @@ void VulkanDriver::createFenceR(Handle<HwFence> fh, int) {
// buffer.
auto fence = resource_ptr<VulkanFence>::cast(&mResourceManager, fh);
fence->fence = cmdbuf->getFenceStatus();
mResourceManager.associateHandle(fh.getId(), std::move(tag));
}
void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
void VulkanDriver::createSyncR(Handle<HwSync> sh, utils::CString&& tag) {
auto sync = resource_ptr<VulkanSync>::cast(&mResourceManager, sh);
VkFence fence = VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> fenceStatus;
if (mCurrentRenderPass.commandBuffer) {
VulkanCommandBuffer* cmdBuff = mCurrentRenderPass.commandBuffer;
fence = cmdBuff->getVkFence();
fenceStatus = cmdBuff->getFenceStatus();
// If we're currently recording, flush so that the fence only applies
// to commands already issued.
mCommands.flush();
} else {
fence = mCommands.getMostRecentFence();
fenceStatus = mCommands.getMostRecentFenceStatus();
}
{
std::lock_guard<std::mutex> guard(sync->lock);
sync->sync = mPlatform->createSync(fence, fenceStatus);
}
for (auto& cbData : sync->conversionCallbacks) {
cbData->sync = sync->sync;
scheduleCallback(cbData->handler, cbData.release(), syncCallbackWrapper);
}
sync->conversionCallbacks.clear();
mResourceManager.associateHandle(sh.getId(), std::move(tag));
}
void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags,
utils::CString&& tag) {
FVK_SYSTRACE_SCOPE();
// Running gc() to guard against an edge case where the old swapchains need to have been
// destroyed before the new swapchain can be created. Otherwise, we would fail
// vkCreateSwapchainKHR with VK_ERROR_NATIVE_WINDOW_IN_USE_KHR.
mResourceManager.gc();
// TODO: support MSAA swapchain
if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 && !isSRGBSwapChainSupported()) {
FVK_LOGW << "sRGB swapchain requested, but Platform does not support it";
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
}
if ((flags & backend::SWAP_CHAIN_CONFIG_MSAA_4_SAMPLES) != 0 && !isMSAASwapChainSupported(4)) {
FVK_LOGW << "MSAAx4 swapchain requested, but Platform does not support it";
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_MSAA_4_SAMPLES);
}
if (flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) {
if (!isProtectedContentSupported()) {
FVK_LOGW << "protected swapchain requested, but Platform does not support it";
@@ -823,10 +910,11 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
auto swapChain = resource_ptr<VulkanSwapChain>::make(&mResourceManager, sch, mPlatform,
mContext, &mResourceManager, mAllocator, &mCommands, mStagePool, nativeWindow, flags);
swapChain.inc();
mResourceManager.associateHandle(sch.getId(), std::move(tag));
}
void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
uint32_t height, uint64_t flags) {
uint32_t height, uint64_t flags, utils::CString&& tag) {
if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 && !isSRGBSwapChainSupported()) {
FVK_LOGW << "sRGB swapchain requested, but Platform does not support it";
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
@@ -836,21 +924,24 @@ void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t wi
mContext, &mResourceManager, mAllocator, &mCommands, mStagePool, nullptr, flags,
VkExtent2D{width, height});
swapChain.inc();
mResourceManager.associateHandle(sch.getId(), std::move(tag));
}
void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::CString&& tag) {
// nothing to do, timer query was constructed in createTimerQueryS
mResourceManager.associateHandle(tqh.getId(), std::move(tag));
}
void VulkanDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
backend::DescriptorSetLayout&& info) {
backend::DescriptorSetLayout&& info, utils::CString&& tag) {
FVK_SYSTRACE_SCOPE();
auto layout = mDescriptorSetLayoutCache.createLayout(dslh, std::move(info));
layout.inc();
mResourceManager.associateHandle(dslh.getId(), std::move(tag));
}
void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
Handle<HwDescriptorSetLayout> dslh) {
Handle<HwDescriptorSetLayout> dslh, utils::CString&& tag) {
FVK_SYSTRACE_SCOPE();
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout =
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
@@ -860,6 +951,18 @@ void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
if (layout->hasExternalSamplers()) {
mAppState.hasExternalSamplerLayouts = true;
}
mResourceManager.associateHandle(dsh.getId(), std::move(tag));
}
void VulkanDriver::mapBufferR(MemoryMappedBufferHandle mmbh,
BufferObjectHandle boh, size_t offset,
size_t size, MapBufferAccessFlags access, utils::CString&& tag) {
FVK_SYSTRACE_SCOPE();
auto mmb = resource_ptr<VulkanMemoryMappedBuffer>::make(&mResourceManager, mmbh,
boh, offset, size, access);
mmb.inc();
mResourceManager.associateHandle(mmbh.getId(), std::move(tag));
}
Handle<HwVertexBufferInfo> VulkanDriver::createVertexBufferInfoS() noexcept {
@@ -929,6 +1032,13 @@ Handle<HwFence> VulkanDriver::createFenceS() noexcept {
return handle;
}
Handle<HwSync> VulkanDriver::createSyncS() noexcept {
auto handle = mResourceManager.allocHandle<VulkanSync>();
auto sync = resource_ptr<VulkanSync>::make(&mResourceManager, handle);
sync.inc();
return handle;
}
Handle<HwSwapChain> VulkanDriver::createSwapChainS() noexcept {
return mResourceManager.allocHandle<VulkanSwapChain>();
}
@@ -953,6 +1063,19 @@ Handle<HwDescriptorSet> VulkanDriver::createDescriptorSetS() noexcept {
return mResourceManager.allocHandle<VulkanDescriptorSet>();
}
MemoryMappedBufferHandle VulkanDriver::mapBufferS() noexcept {
return mResourceManager.allocHandle<VulkanMemoryMappedBuffer>();
}
void VulkanDriver::unmapBuffer(MemoryMappedBufferHandle mmbh) {
if (!mmbh) {
return;
}
auto mmb = resource_ptr<VulkanMemoryMappedBuffer>::cast(&mResourceManager, mmbh);
mmb.dec();
}
void VulkanDriver::destroySwapChain(Handle<HwSwapChain> sch) {
if (!sch) {
return;
@@ -967,6 +1090,17 @@ void VulkanDriver::destroySwapChain(Handle<HwSwapChain> sch) {
void VulkanDriver::destroyStream(Handle<HwStream> sh) {
}
void VulkanDriver::destroySync(Handle<HwSync> sh) {
if (!sh) {
return;
}
FVK_SYSTRACE_SCOPE();
auto sync = resource_ptr<VulkanSync>::cast(&mResourceManager, sh);
mPlatform->destroySync(sync->sync);
sync.dec();
}
void VulkanDriver::destroyTimerQuery(Handle<HwTimerQuery> tqh) {
if (!tqh) {
return;
@@ -985,16 +1119,16 @@ void VulkanDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
auto set = resource_ptr<VulkanDescriptorSet>::cast(&mResourceManager, dsh);
set.dec();
if (mAppState.hasExternalSamplers() && set->getExternalSamplerVkSet() != VK_NULL_HANDLE) {
if (mAppState.hasExternalSamplers()) {
mExternalImageManager.removeDescriptorSet(set);
}
}
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream) {
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream, utils::CString tag) {
return {};
}
Handle<HwStream> VulkanDriver::createStreamAcquired() {
Handle<HwStream> VulkanDriver::createStreamAcquired(utils::CString tag) {
return {};
}
@@ -1042,6 +1176,29 @@ FenceStatus VulkanDriver::getFenceStatus(Handle<HwFence> fh) {
return FenceStatus::TIMEOUT_EXPIRED;
}
void VulkanDriver::getPlatformSync(Handle<HwSync> sh, CallbackHandler* handler,
Platform::SyncCallback cb, void* userData) {
auto sync = resource_ptr<VulkanSync>::cast(&mResourceManager, sh);
auto cbData = std::make_unique<VulkanSync::CallbackData>();
cbData->handler = handler;
cbData->cb = cb;
cbData->userData = userData;
// If we haven't already set the handle, toss the conversion callback in the
// back of the list.
{
std::lock_guard guard(sync->lock);
if (sync->sync == nullptr) {
sync->conversionCallbacks.push_back(std::move(cbData));
return;
}
}
// Otherwise, go ahead and schedule the callback now.
cbData->sync = sync->sync;
scheduleCallback(cbData->handler, cbData.release(), syncCallbackWrapper);
}
// We create all textures using VK_IMAGE_TILING_OPTIMAL, so our definition of "supported" is that
// the GPU supports the given texture format with non-zero optimal tiling features.
bool VulkanDriver::isTextureFormatSupported(TextureFormat format) {
@@ -1104,6 +1261,10 @@ bool VulkanDriver::isSRGBSwapChainSupported() {
return mIsSRGBSwapChainSupported;
}
bool VulkanDriver::isMSAASwapChainSupported(uint32_t) {
return mIsMSAASwapChainSupported;
}
bool VulkanDriver::isProtectedContentSupported() {
return mContext.isProtectedMemorySupported();
}
@@ -1607,7 +1768,7 @@ void VulkanDriver::commit(Handle<HwSwapChain> sch) {
auto swapChain = resource_ptr<VulkanSwapChain>::cast(&mResourceManager, sch);
// Present the backbuffer after the most recent command buffer submission has finished.
swapChain->present();
swapChain->present(*this);
}
void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
@@ -1788,9 +1949,14 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
VkOffset3D const srcOffsets[2] = { { srcLeft, srcTop, 0 }, { srcRight, srcBottom, 1 }};
VkOffset3D const dstOffsets[2] = { { dstLeft, dstTop, 0 }, { dstRight, dstBottom, 1 }};
mBlitter.blit(vkfilter,
dstTarget->getColor0(), dstOffsets,
srcTarget->getColor0(), srcOffsets);
auto const& dstAttachment = dstTarget->getColor0();
auto const& srcAttachment = srcTarget->getColor0();
if (srcAttachment.texture->samples > 1) {
mBlitter.resolve(dstAttachment, srcAttachment);
} else {
mBlitter.blit(vkfilter, dstAttachment, dstOffsets, srcAttachment, srcOffsets);
}
}
void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
@@ -2079,10 +2245,6 @@ void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, cons
void VulkanDriver::resetState(int) {
}
void VulkanDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
mResourceManager.associateHandle(handleId, std::move(tag));
}
void VulkanDriver::endCommandRecording() {
mCommands.flush();
mPipelineCache.resetBoundPipeline();
@@ -187,6 +187,7 @@ private:
} mAppState;
bool const mIsSRGBSwapChainSupported;
bool const mIsMSAASwapChainSupported;
backend::StereoscopicType const mStereoscopicType;
};
@@ -32,6 +32,7 @@
#include <utils/compiler.h> // UTILS_FALLTHROUGH
#include <utils/Panic.h> // ASSERT_POSTCONDITION
#include <utils/CString.h>
using namespace bluevk;
@@ -285,7 +286,7 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
<< " error=" << static_cast<int32_t>(result);
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
std::string name{ builder.getName().c_str(), builder.getName().size() };
utils::CString name{ builder.getName().c_str(), builder.getName().size() };
switch (static_cast<ShaderStage>(i)) {
case ShaderStage::VERTEX:
name += "_vs";
@@ -225,6 +225,17 @@ private:
fvkutils::UniformBufferBitmask mUboMask;
};
struct VulkanMemoryMappedBuffer : public HwMemoryMappedBuffer, Resource {
VulkanMemoryMappedBuffer(BufferObjectHandle boh, size_t const offset,
size_t const size, MapBufferAccessFlags const access)
: boh(boh), access(access), size(size), offset(offset) {
}
BufferObjectHandle boh{};
MapBufferAccessFlags access{};
uint32_t size = 0;
uint32_t offset = 0;
};
using PushConstantNameArray = utils::FixedCapacityVector<char const*>;
using PushConstantNameByStage = std::array<PushConstantNameArray, Program::SHADER_TYPE_COUNT>;
@@ -93,7 +93,7 @@ void VulkanSwapChain::update() {
mLayerCount = bundle.layerCount;
}
void VulkanSwapChain::present() {
void VulkanSwapChain::present(DriverBase& driver) {
if (!mHeadless && mTransitionSwapChainImageLayoutForPresent) {
VulkanCommandBuffer& commands = mCommands->get();
VkImageSubresourceRange const subresources{
@@ -120,6 +120,14 @@ void VulkanSwapChain::present() {
// We presented the last acquired buffer.
mAcquired = false;
mIsFirstRenderPass = true;
if (frameScheduled.callback) {
driver.scheduleCallback(frameScheduled.handler,
[callback = frameScheduled.callback]() {
PresentCallable noop = PresentCallable(PresentCallable::noopPresent, nullptr);
callback->operator()(noop);
});
}
}
void VulkanSwapChain::acquire(bool& resized) {

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