Compare commits

...
164 Commits
Author SHA1 Message Date
raviola 8e72e67bce Add fgdbg::Session.h, Pass.h, Resource.h dtos/models 2022-11-04 15:43:42 +11:00
raviola a0c486f608 pr feedback 2022-10-21 14:28:59 +11:00
raviola eb7447fd17 fgdbg client project structure + simple server connection 2022-10-20 13:48:48 +11:00
Fernando Raviola a870d01475 Add DebugServer for fgdbg with support for Websockets (#6169)
* Add dummy DebugServer for fgdbg with support for Websockets

* PR feedback
2022-10-20 11:00:13 +11:00
raviola 83e747b732 rename debug.server to debug.matdbg 2022-10-20 11:00:13 +11:00
raviola ca5cc32dc1 Use smart pointer 2022-10-20 11:00:13 +11:00
raviola 5c03e290c2 PR suggestions 2022-10-20 11:00:13 +11:00
Fernando RaviolaandMathias Agopian e4953ada3f Concat namespaces
Co-authored-by: Mathias Agopian <mathias@google.com>
2022-10-20 11:00:13 +11:00
Fernando Raviola 10022f983b Add FrameGraph debugger lib: package structure + CmakeLists.txt 2022-10-20 11:00:13 +11:00
Benjamin Doherty 977d369e40 Fix unused variable warnings 2022-10-19 12:44:07 -04:00
Josh Faust 5cf5b25175 Add a function to reset internal GL backend state (#6105)
This allows bug-free integration with external renderers in WebGL

See https://github.com/google/filament/issues/6091
2022-10-19 09:31:11 -07:00
Ben Doherty e75c3020c8 Initial support for Metal compute shaders / SSBOs (#6189) 2022-10-18 16:21:57 -04:00
k1rnt aeb0a4008d fix(ci): replace set-output to GITHUB_OUTPUT (#6195) 2022-10-18 13:41:25 -04:00
Benjamin Doherty 2d43ad59ec Revert Android API_LEVEL change
glDispatchCompute requires Android API level 21, however bumping our
required minimum from 19 to 21 caused some clients' builds to fail.
Commenting-out that line for now to proceed with the 1.28.0 upgrade.
2022-10-18 13:34:50 -04:00
daemyung jang f86ec07c45 Fix ios hello-gltf runtime errors 2022-10-18 10:19:22 -07:00
Ben Doherty fc012d6d65 Metal: fix use-after-free ASAN error (#6203) 2022-10-17 19:13:36 -04:00
Philip Rideout 6b71232e38 matc / matinfo / filamesh: add vulnerability message 2022-10-17 15:49:35 -07:00
Philip Rideout e157023fc1 Use mField style for fields in FFilamentInstance 2022-10-17 15:49:19 -07:00
Philip Rideout 6e38db882f Fix installed IBL location for desktop.
Fixes #6193
2022-10-17 12:30:11 -07:00
daemyung jang 09600699cb Update README.md 2022-10-17 09:57:58 -07:00
daemyung jang 6a5e74f031 Update README for meshoptimizer 2022-10-17 09:57:58 -07:00
daemyung jang 795bd1e995 Fix build errors in ios samples 2022-10-17 09:57:58 -07:00
daemyung jang 784416f658 Support glTF EXT_meshopt_compression 2022-10-17 09:57:58 -07:00
daemyung jang b151afe567 Update meshoptimizer to version 0.18 2022-10-17 09:57:58 -07:00
Mathias Agopian 117ae1d648 RenderPass doesn't store an FEngine anymore 2022-10-14 15:59:03 -07:00
Mathias Agopian 7165d8aa54 fix a directional light shadowing issue
the shadow frustum could be be smaller than expected.
2022-10-14 13:16:14 -07:00
Ben Doherty 6b461b2d89 Metal: fix simulator regression due to arg buffers (#6181) 2022-10-13 14:34:33 -04:00
Benjamin Doherty affc5be840 Release Filament 1.28.0 2022-10-13 12:35:45 -04:00
Ben Doherty e3ff5616ef G3 fixes for 1.28.0 (#6174) 2022-10-11 17:02:46 -07:00
Mathias Agopian d9a401d57e Reduce size of ShadowMap object
- remove mEngine from ShadowMap
- remove storage of ShadowMapInfo in ShadowMap
- ShadowMap::render should be on ShadowMapManager
- remove more attributes from ShadowMap

ShadowMap is now reduced to 32 bytes.
2022-10-10 13:03:36 -07:00
Mathias Agopian acd5823953 Add support for point light shadows
[experimental]
2022-10-10 13:03:36 -07:00
Mathias Agopian 9b13565750 spotlight shadow map skipped if the scene is empty. 2022-10-10 13:03:36 -07:00
Mathias Agopian 734d8ff85c rename upcast() to downcast()
it was down casting all along!
2022-10-07 17:50:56 -07:00
Mathias Agopian a02ef1a6c7 Remove the limit to 14 spot shadow maps, now 64.
Spotlight shadow maps are now limited only by h/w limits (UBO size and
2d array maximum layers), which works out to about 146 shadows.

In practice we hardcode the limite to 64, but this can be changed at
compile time. A higher maximum increases memory usage.

There used to be a bitfield per renderable with a bit for each shadow
map indicating if the renderable was "visible" from this light. The
limit to 14 came from that bitfield. All shadow maps used to be culled
first, setting the bitfield appropriately.

Instead, we are now processing spotlight shadows one at a time,
performing culling as we go. For this reason we don't need a bit per
shadow map.
2022-10-07 14:07:51 -07:00
Mathias Agopian f13bf11d0f simplify command generation
No need to generate a sentinel command in the main loop when a 
command is canceled, this is now done automatically at a higher level.
The main benefit is that we can reject renderables that are not visible
a bit faster.
2022-10-07 14:07:51 -07:00
Ben Doherty 7d0b5fed8f Fix Metal argument buffer encoding (#6150) 2022-10-07 13:53:26 -07:00
Philip Rideout e1f51f04c1 Vulkan: fix black screen regression
The VulkanProgram constructor was bailing out early and emitting a
warning because it saw that one of the stages wasn't fulfilled.
However it's okay for a pipeline to be missing a compute program.

Fixes regression that started with fabba73b1.
2022-10-04 10:47:16 -07:00
Philip Rideout f6d74be123 Fix windows build. 2022-10-03 12:35:35 -07:00
Philip Rideout 50a1935b68 Vulkan performance improvements for readPixels. 2022-10-03 09:54:35 -07:00
Mathias Agopian 694ecce150 fix shadowing when the scene is outside the "shadow" view frustum
this case was causing uninitialized variables to be used creating a
false positive "shadows on" in the first cascade.

The main change here is to bail out as soon as we know there is no shadowing.
2022-09-30 16:12:36 -07:00
Mathias Agopian 3e3bd2722f fix lispsm when camera and light direction are aligned
in that case we have to revert to the ortho projection
2022-09-30 16:12:15 -07:00
Mathias Agopian 44fdf83e4c fix multiview mode in gltf_viewer
The "shadow" camera now works again.
2022-09-30 16:12:15 -07:00
Benjamin Doherty 99c4ce67e4 Print warning when Metal reaches memoryless geo limit 2022-09-30 13:59:49 -07:00
Balazs Vegh 71aceec4cf Memoryless storage mode with fallback (Metal) (#6124) 2022-09-30 13:54:54 -07:00
Philip Rideout 98e4cdbd4c gltfio ubershader mode: fix re-loading.
Fixes #6128
2022-09-30 13:54:36 -07:00
Mathias Agopian 86ef0109f4 use the correct gaussian blur shader
we attempted to select a shader which processed the required number of
component, but were always selecting the 4-components version.

this should improve blur performance for VSM.
2022-09-30 13:30:15 -07:00
Mathias Agopian b4142261c2 trivial typo and ide warnings fixes 2022-09-30 13:30:15 -07:00
Mathias Agopian 359624e8eb Fix gaussian blur kernel computation
This improves significantly VSM shadows with blur,
 but will also improve SSR and the flare.
2022-09-30 13:30:15 -07:00
Mathias Agopian 5bcc11d9e8 fix typo that broke the VSM blur radius setting 2022-09-30 13:30:15 -07:00
Mathias Agopian 5aa414dd24 support for ELVSM and 32 bits xVSM
This basically adds two settings:
- highPrecision at the View level, which controls the bit depth
  of the vsm shadow texture used. This affects all shadowmaps.
- elvsm per shadowmap, which enables Exponential Layered VSM.

the main change in the shaders is that we now always output the 
"negative" EVSM, even when it's not enabled. If no shadowmap uses
ELVSM, then the texture is stil an RG texture and the calculation is
lost (with some luck culled by the shader compiler), either way it's
not a lot of math and it's done only once per shadow texel.

During the color pass, on the other hand, we compute the "negative"
EVSM only if enabled.
2022-09-30 12:04:27 -07:00
Mathias Agopian 1e51b03971 fix VSM 2nd moment calculation
the linear approximation only works with a linear depth, it fails
with our warped depth.
2022-09-30 12:04:27 -07:00
Mathias Agopian fc484bd319 fix "stable" shadows
We were always adjusting the near/far of the view volume based on the
scene content for shadowing, which defeated the "stable" shadows.

Also changed the default cascade splits from a linear split to a
log2 split, because due to the perspective projection, the log2 split
actually looks linear. Also intuitively, it makes more sense to give
more resolution to the shadows close to the camera.
2022-09-30 12:00:02 -07:00
Mathias Agopian 9c4783142e matc: minor cleanup 2022-09-29 19:34:07 -07:00
Mathias Agopian 4dc03318e8 break circular dependency between Package and Flattener 2022-09-29 19:34:07 -07:00
Philip Rideout 15ba54c4c5 Update README wrt Android Studio Version. 2022-09-29 16:28:50 -07:00
Philip Rideout 342138aecb gltfio: Update Java API. 2022-09-29 16:28:50 -07:00
Philip Rideout 9f53c62e06 gltfio: Update Web API.
Java API changes will come next.
2022-09-29 16:28:50 -07:00
Philip Rideout 2d996033e3 gltfio API change part 2: remove asset-level animator 2022-09-29 16:28:50 -07:00
Philip Rideout 9fa6dc4131 gltfio API change part 1: move all MaterialInstance methods to FilamentInstance 2022-09-29 16:28:50 -07:00
daemyung jangandPhilip Rideout 4930227743 Fix a crash by setting not exist material parameters on ubershader (#6122)
* Fix a crash by setting not exist material parameters on ubershader

* gltfio: add getFeatureMap, remove logic for dummy textures

Co-authored-by: Philip Rideout <philiprideout@gmail.com>
2022-09-29 15:51:35 -07:00
Benjamin Doherty fbfcce563d Release Filament 1.27.2 2022-09-29 13:11:43 -07:00
Ben Doherty 7f5ad3743b Fixes for 1.27.2 release (#6125) 2022-09-29 13:08:00 -07:00
daemyung jang 981020e72b Get the morph target buffer to the given primitive 2022-09-29 13:01:42 -07:00
Mathias Agopian 7c1a705a07 reduce setSarameter(texture) calls 2022-09-28 22:10:27 -07:00
Mathias Agopian 3d53ae583f switch our min sdk from 19 to 21
this is so that we can support OpenGL ES3.1
2022-09-28 22:08:27 -07:00
Mathias Agopian dc9f03cbd0 don't attempt to use more texture units than present
to emulate the bindless API in the gl backend we always used the highest
texture unit available. However at feature level 3, we support up to 62
textures, so the that max was bumped to 62 -- however, where we're not
on a feature level 2 device, that texture unit doesn't exist.

Instead we now always use binding 31, which is guaranteed to exist by 
EGL's minspec.
2022-09-28 22:05:05 -07:00
Philip Rideout de7bca7ef8 gltfio: fix occlusion strength
Fixes #6106
2022-09-28 17:42:29 -07:00
Ben Doherty f151f56a2e Metal: use argument buffers for SamplerGroups (#6093) 2022-09-28 16:50:11 -07:00
Ben Doherty fe3d1713a4 Fix generic/Mutex.h not installed on Linux (#6117) 2022-09-28 16:36:38 -07:00
Joel Winarske 2cad3be7a3 Wayland FilamentApp
- move Wayland specific code into getNativeWindow
- make wayland struct static

Signed-off-by: Joel Winarske <joel.winarske@gmail.com>
2022-09-28 16:14:01 -07:00
Thomas Gorisse 5262512eea Fix JNI for TransformManager.getChildren() (#6116) 2022-09-28 11:27:35 -07:00
Mathias Agopian fabba73b1e Initial support for compute
* minimal backend support for compute
- added api to dispatch a compute shader
- added api to create and bind a ssbo
- added api to read back a buffer
Only implemented in the gl backend

* Add a backend compute test suite
* basic support for compute shaders in matc
this is still very much work-in-progress.
We're not supporting images nor ssbo for now.

* rename UniformInterfaceBlock to BufferInterfaceBlock
* augment BufferInterfaceBlock to support ssbo features
- add support for std430
- add support for ssbo
- add support for variable-size array
- add support for memory qualifiers

* reformat MaterialBuilder
* material format: move subpasses outside of parameters
subpasses now are their own json property instead of being a
"parameter".

* refactor parameter() methods to match Buffer/SamplerInterfaceBlock
We're just shuffling the arguments.

* add support for buffers in .mat files
* filamat now generates buffer blocks (ssbo)
* take feature level into consideration when optimizing shaders
* don't store the 'uniform binding' chunk for level 2 materials
this includes some refactoring/cleanups of MaterialParser

* matinfo: fixes for compute
- separate subpasses from parameters
- don't attempt to print material properties
2022-09-27 15:52:40 -07:00
daemyung jang d291ec739b Update cgltf to version 1.13 (#6099) 2022-09-23 11:52:57 -07:00
Mathias Agopian 0dc0bbd4f3 LiSPSM is now a user settable option 2022-09-22 09:35:45 -07:00
Mathias Agopian 984a029ce4 fix (again) the box-frustum intersection
it is not true that the whole box is inside the frustum if we have
8 vertices and some frustum vertices are in the box -- that's even
non-sensical.

We now test for this case by checking that we have all (8) vertices of
the box inside the frustum.

This caused some shadows to be missing.
2022-09-22 09:35:28 -07:00
Mathias Agopian 1cbc1f30a6 filamesh: handle empty meshes
empty meshes would cause a OOB read.

b/246719043
2022-09-20 16:45:45 -07:00
Mathias Agopian 08fb39a1e4 Fix buffer overflow in matc
b/247441389
2022-09-20 16:45:45 -07:00
Mathias Agopian d8a71ce195 minor cleanup and IDE warning suppression 2022-09-20 16:45:45 -07:00
Mathias Agopian 13986c12bc fix out of bounds access in matc
b/247442731
2022-09-20 16:45:45 -07:00
Mathias Agopian cc64704a82 fix buffer overflow filamat
b/247447117
2022-09-20 16:45:45 -07:00
Benjamin Doherty df2ad99260 Release Filament 1.27.1 2022-09-20 11:04:07 -07:00
Mathias Agopian fb1f277b9a attempt to fix android build again 2022-09-20 09:11:42 -07:00
Mathias Agopian 0388161464 fix android debug build 2022-09-19 23:03:12 -07:00
Ben Doherty e989d5603f Add MetalRingBuffer for future argument buffer management (#6087) 2022-09-19 16:18:54 -07:00
Mathias Agopian defaa0d008 use ES3.1 headers
We now compile with ES3.1 headers but we make sure we can still compile
with ES3.0 headers (still needed for iOS).
2022-09-18 20:37:43 -07:00
Mathias Agopian e161ba80f2 cleanup: move string_view over string uses 2022-09-16 18:31:48 -07:00
Philip Rideout 034aaff56c gltfio: FilamentInstance owner is now const.
In the future, we will allow instances to be constructed from
immutable assets (this is a Google feature request) so this PR
prepares for that.
2022-09-16 16:32:49 -07:00
Mathias Agopian b0fb52c5f4 matinfo: fix feature level print output 2022-09-16 16:22:25 -07:00
Philip Rideout a05f9c172f gltfio: change instancing behavior for punctual lights 2022-09-16 15:32:18 -07:00
Ben Doherty adde936d18 Update glslang to c0cf8ad87 (master) (#6076) 2022-09-16 12:29:56 -07:00
Philip Rideout feda999eb3 matinfo and filamesh: misc ASAN fixes 2022-09-15 17:23:54 -07:00
Romain Guy 8dae181c2f Upgrade AGP and dependencies including plugins (#6078)
This moves to a new, maintained Maven publishing plugin.
2022-09-15 16:05:18 -07:00
Philip Rideout 8cdac431a6 Vk getFeatureLevel() should use maxPerStageDescriptorSamplers 2022-09-15 13:51:10 -07:00
Romain Guy 0ecf5118ba Update docs 2022-09-14 20:18:27 -07:00
Philip Rideout f21638a1e2 gltfio: fix material-free assets 2022-09-14 16:54:27 -07:00
Mathias Agopian 8dc16494a7 introduce "feature level 3"
This is because most android devices only support 16 texture in the
shaders (94.4%), so we can't realistically have feature level 2 demand
that.

Instead feature level 2 enables compute/ES3.1 features.
2022-09-14 16:53:43 -07:00
Mathias Agopian a8765b1dc0 fix some trivial ide warnings 2022-09-14 16:53:43 -07:00
Philip Rideout d346b94b8a gltfio: fix regression with recomputeBoundingBoxes() 2022-09-14 16:53:20 -07:00
Philip Rideout c4abe4f46a gltfio: allow zero-instance assets.
This is a feature request from Google. It allows users to "preload" an
asset, ie you can now create all VertexBuffer objects, Texture objects,
etc, without actually creating any entities or renderable components.

In the past we used TransformManager to help out with computing the big
asset-level bounding box, but now we use `gltf_node_transform_world()`
because entities might not yet exist.

One minor side effect is that `FilamentAsset::getBoundingBox()` now
returns the AABB that was determined at load time, and does not account
for instances. As a result, our `gltf_instances` sample app looks
slightly different but this is expected.
2022-09-14 16:40:47 -07:00
Philip Rideout ad6a5eb4e1 Vulkan samplers: fit incorrect bitset logic.
In VulkanPipelineCache, `getShaderStageFlags()` and `getUsageFlags()`
are used to track which samplers are used in the VERTEX stage and which
are used in the FRAGMENT stage. The recent change I made was incorrect
because it did not account for samplers that are used in BOTH stages.

This fixes the recent regression where we see validation errors when
running `gltf_viewer` in ubershader mode.
2022-09-14 15:21:26 -07:00
Mathias Agopian c84834d666 bump max sampler count to 62
This is needed for feature level 2.
2022-09-13 10:49:21 -07:00
Benjamin Doherty 334fe70d6c Release Filament 1.27.0 2022-09-13 10:14:02 -07:00
Philip Rideout 3b4fbaccc9 gltfio: fix regression when textures are "shared"
Some (poorly authored) glTF assets have several `image` elements
that all refer to the same URL or buffer view. When this occurs,
we create only 1 Filament Texture.

These assets regressed after PR 6051, which consolidated the texture
related fields in `FilamentAsset`, but did not include an ownership flag
in the new `TextureInfo` struct.
2022-09-12 16:00:24 -07:00
Mathias Agopian 58401e0b6e Fix FilamentApp multiview mode in gltf_viewer
- fix View::setVisibleLayers to match code. By default only layer 0
  is active, despite the documentation stating otherwise.

- add a helper to enable/disable layers more easily

- don't use layer 0,1,2 for the overdraw function as they were used by
  FilamentApp in "multi view" mode.
2022-09-12 15:55:16 -07:00
Mathias Agopian 3496f3a8ba rename ShaderType to ShaderStage 2022-09-12 15:54:07 -07:00
Mathias Agopian 441708ea12 Make ShaderStage an enum class 2022-09-12 15:54:07 -07:00
Mathias Agopian ba8abb9031 scissor improvements
- improve scissor documentation, specify coordinate system and
  clipping behavior
- scissor now takes the guard bands into account
- on the backend side the scissor is no longer clipped to the viewport
- clipping to the viewport is now done on the filament side
- don't set scissor when material instance doesn't specify one
  (common case)
2022-09-12 12:45:36 -07:00
Ben Doherty 2718a22f64 Add return case to fix G3 compiler error (#6062) 2022-09-12 12:34:23 -07:00
Philip Rideout 85fb4be37e gltfio: use BitmaskFlags for sRGB
This PR also contains a small change in ResourceLoader::createTextures
that will permit zero-instance assets (an upcoming gltfio feature).
2022-09-12 12:27:01 -07:00
Mathias Agopian f772c92eed fix typo in spotlight attenuation doc 2022-09-12 12:04:35 -07:00
Philip Rideout cd58d39eb9 Vk cleanup: incorporate code review feedback. 2022-09-09 13:00:49 -07:00
Philip Rideout 2d730a4618 Vk cleanup: remove dummy samplers. 2022-09-09 13:00:49 -07:00
Philip Rideout da6d17531c Vk: do not provide descriptors for unused samplers. 2022-09-09 13:00:49 -07:00
Philip Rideout 37b9b182b1 gltfio: prep step for allowing zero-instance assets
AssetLoader now loads assets in two passes:
- Pass 1 creates VertexBuffer objects, MorphTargetBuffer objects, etc.
- Pass 2 creates entities, renderables, etc (only if numInstances > 0)

This feature will help us integrate gltfio with an internal library at
Google.
2022-09-09 09:21:33 -07:00
Romain Guy eaa86060e2 Fix memory leak (#6056)
This change fixes #6055
2022-09-08 20:57:46 -07:00
Philip Rideout 5e128214fa Vk: When the layout requirement change, the pipeline is "dirty".
Also, there's no such thing as a "bound layout".
2022-09-08 16:40:49 -07:00
Philip Rideout 9a7b967889 Rename mResult to mAsset. 2022-09-07 16:28:30 -07:00
Philip Rideout 5e498e7442 gltfio: big simplification for textures + add sRGB related assert.
The asset previously had three confusing fields related to textures:
mTextures, mTextureSlots, and mTextureBindings.

These are now consolidated into a single field, which simply has one top
level array item per `cgltf_texture`.

The memory footprint is smaller because we no longer bother to store
`TextureSampler` objects, instead we simply defer their construction
until calling setParameter.

Last but not least, we now assert in debug builds if the usages of a
particular texture have inconsistent sRGB flags. In the past,
inconsistent sRGB would be silently ignored.

In other words, we will now assert if you try to use the same texture
for `baseColor` and `normalMap`, whereas in the past we would simply
exhibit non-deterministic behavior in this situation (with respect to
sRGB semantics).
2022-09-07 16:28:30 -07:00
Mathias Agopian 25990ecd37 FilamentApp sets the feature level to highest allowed
There is also a new option to override the default with a lower
feature level. This is used in gltf_viewer only at the moment.
2022-09-07 15:32:58 -07:00
Mathias Agopian ead5a97871 make AutomationEngine feature level aware
this basically just adds an Engine* parameter too all applySettings
methods
2022-09-07 15:32:58 -07:00
Mathias Agopian a66495b2e0 fix setting spec constants in gl backend
a failure could happen if a comment contained the word `#extension`
2022-09-07 15:32:58 -07:00
Philip Rideout f83172f9f9 gltfio MaterialProvider: add new method for getting Material
gltfio can now ask the material provider plugin which `Material` would
be used for a given set of requirements.  Prior to this change, gltfio
could only create a new `MaterialInstance`.

This method is necessary to support an upcoming gltfio feature.
2022-09-07 12:08:55 -07:00
Philip Rideout 80a957c1e3 gltfio: do not clear out the texture slots too early.
This broke asyncGetLoadProgress() and caused WebGL to crash reliably
because ResourceLoader got destroyed too soon.

Bug was introduced with de7dfc2ea6.

I intend to cherry pick this to rc/1.27.0, which is where it was
introduced, so there's no need to update the release notes.
2022-09-07 08:29:07 -07:00
Philip Rideout e983169b35 gltfio: fix crash when material is unspecified 2022-09-07 08:28:36 -07:00
Mathias Agopian b21d633c0c check that user materials don't exceed their allowed features (#6030)
* check that user materials don't exceed their allowed features

* backend sampler limits now take feature level into account


- in the backend, the constants are now in an array indexed by the 
  feature level

- samplerBindingMap now asserts only what it can.

- matc (filamat) now logs the user samplers when an error is detected.
2022-09-02 16:37:19 -07:00
Ben Doherty f538d028d5 In debug builds, verify destroyed textures aren't in sampler groups (#6029) 2022-09-02 14:32:53 -04:00
Ben Doherty 4e15b7abc2 Add overdraw visualization to gltf_viewer (#6018) 2022-09-02 12:33:26 -04:00
e80ea5eae2 Add JNI for TransformManager.getChildCount(), TransformManager.getChildren() and Scene.hasEntity() (#6007)
* Add JNI for TransformManager.getChildCount(), TransformManager.getChildren() and scene.hasEntity()

* Update RELEASE_NOTES.md

* Fixes

* Change getChildren to take a nullable array

* Remove no params TransformManager.getChildren()

* Update RELEASE_NOTES.md

Co-authored-by: Ben Doherty <benjdoherty15@gmail.com>
Co-authored-by: Ben Doherty <bendoherty@google.com>
2022-09-01 11:22:44 -07:00
Mathias Agopian 6215050086 Fix a "dangling" texture in a SamplerGroup
One of the bloom texture ended-up dangling in the material's
SamplerGroup. That sampler group was never used to draw with, but was
made active which caused problem with the backends.


Also make sure the public API can unset a texture in a MaterialInstance
by passing nullptr for the texture.
2022-09-01 11:21:41 -07:00
Mathias Agopian 1fdb44ff42 graphvizify should only be implemented on debug builds 2022-08-31 17:17:38 -07:00
Ben Doherty 1768a2f8b7 Fix uberarchive not included in PodSpec (#6024) 2022-08-31 18:27:15 -04:00
Ben Doherty 2f26ef0873 Fix OpenGL stencil buffer writes (#6023) 2022-08-31 17:17:16 -04:00
Mathias Agopian ac61fd7ba4 make a small optimization more explicit, fix typos 2022-08-31 09:11:25 -07:00
Benjamin Doherty a886166489 Release Filament 1.26.0 2022-08-31 12:03:43 -04:00
Mathias Agopian 2073a9f3e5 cleanup Froxelizer comments
also fix typos, formatting.
2022-08-30 20:31:47 -07:00
Mathias Agopian be12049a87 The default arena sizes in the makefile didn't match the code
Also added some ascii art to explain better how the various arenas are
used.
2022-08-30 16:48:30 -07:00
Philip Rideout de7dfc2ea6 gltfio: Better behavior for releaseSourceData() 2022-08-30 16:00:11 -07:00
Philip Rideout 7337a467b9 gltfio cleanup: NodeMap is now a FixedCapacityVector, etc. 2022-08-30 15:57:30 -07:00
Philip Rideout 8118b4a774 gltfio: reset MaterialInstance cache for each instance.
For each "cgltf_material", we now create one "MaterialInstance", even
if a given asset has multiple instances. In the future, we might make
this behavior configurable to make better use of Filament's
auto-instancing feature.

This change is a feature request from Google, but also this behavior is
more consistent with the code comments.

Also some related cleanup:

(1) Use FixedCapacityVector instead of robin_map.

(2) Move the cache to move out of the asset and into the loader,
    because it is only used at load time.
2022-08-30 12:03:52 -07:00
Mathias Agopian b295743330 Fix filamat internal state tracking
in a few places filamat loses the value of targetApi and targetLanguage
and attempt to guess it from other values, this lead to inconsistencies.

we now keep both targetApi and targetLanguage and use them appropriately.

in particular we don't use the optimization level to "guess" what 
targetLangage we're on.


In the end this resolve a prior unit test failure and allows us to not
have to use "spirv" rules when generating only GLSL.
2022-08-29 22:11:17 -07:00
Mathias Agopian 4f8d7092e0 fix typos and reformat code 2022-08-29 22:11:17 -07:00
Mathias Agopian 2701e57779 fix filamat unit tests
We use TargetLanguage::SPIRV instead of GLSL which affects how the
source GLSL is generated, in particular which version is used.

This change is not a problem for these unit tests, but is probably
exposing another problem -- we will address that separately.
2022-08-29 17:31:51 -07:00
Philip Rideout f9375d5c33 gltfio: fix spotlight regression.
Spotlights were accidentally disabled in PR #5932.
2022-08-29 15:29:35 -07:00
Mathias Agopian d04f049fa3 fix (again) frustum-box intersection
We were early-exiting too aggressively in the some case.
2022-08-29 14:55:49 -07:00
Mathias Agopian 8b8e724761 fix a use after free when setting a Program's uniform infos
We were keeping the list if uniform names in Material and passing
pointers to the backend. Unfortunately, HwProgram can outlive
Material because it is legal to destroy a Material once it's not
needed on the client side. e.g. just after rendering something.

This was happening with the IBLPrefilter code, but probably elsewhere
too.

We now just store CStrings in HwProgram.
2022-08-29 13:49:34 -07:00
Philip Rideout 45e8c57f77 gltfio: Innocuous comment fixes / renamings. 2022-08-29 13:06:13 -07:00
Philip Rideout d01b3301d0 Vulkan: fix a warning, fix use-before-init. 2022-08-29 10:53:48 -07:00
Mathias Agopian a7a55c7617 Specification constants support (#6001)
* Add support for specialization constants

* remove WebGL instancing count hack

use a specification constant instead

* update release notes
2022-08-29 09:38:23 -07:00
Philip Rideout 0695c12420 gltfio: Refactor skinning, fix duplicated data.
The immutable inverse bind matrices can be shared among instances, so
they are now stored in Asset, not in Instance.

Also, there are now two "load" phases for skinning data:

(1) storing the inverse bind matrices
(2) building the Entity mappings (for animation efficiency)

Phase 1 is done in `ResourceLoader` because inverse bind matrices can
live in an external bin file.

Phase 2 is done during Instance creation, because that's when entities
are created.
2022-08-29 09:18:17 -07:00
Mathias Agopian 4af533c010 material instanced property must be false by default.
this fix auto-instancing.
2022-08-26 14:43:52 -07:00
Balazs Vegh 6a28d2b81c Implement setThreadName() on Windows (#5999) 2022-08-26 13:29:42 -04:00
Philip Rideout 54769a2ad3 gltfio: Move the API for recomputeBoundingBoxes.
Prior to this change, `recomputeBoundingBoxes` was an opt-in config
parameter in ResourceLoader. It is now a method on FilamentInstance.

The old API did not work for dynamically created instances. Since this
is a relatively obscure feature, we considered removing it completely,
especially since the computation requires the presence of CPU-side
vertex data combined with the transform hierarchy.

Instead of removing the feature, we decided to move it to a better
place. This paves the way for some upcoming improvements, which include
reducing the memory footprint for assets. It also improves overall code
organization and separation of concerns.
2022-08-26 09:48:04 -07:00
Mathias AgopianandBenjamin Doherty 87cece8379 use separate enums for uniform and sampler binding points (#5984)
Co-authored-by: Benjamin Doherty <bendoherty@google.com>
2022-08-24 16:36:13 -07:00
Philip Rideout fe3790cb9c WASM: Set CONFIG_MAX_INSTANCES to a small value + hack the GLSL.
Temporary fix until spec constants arrive.

Second attempt, fixes #5859.
2022-08-24 16:24:25 -07:00
gaborvalasekandLevente Koncz 39cb238065 Fix MSVC warnings (#5926)
* Fix warning C4146: unary minus operator applied to unsigned type, result still unsigned

* Fix warning C4068: unknown pragma 'nounroll'

* Fix warning C4068: unknown pragma 'unroll'

* Fix warning C4068: unknown pragma 'clang'

* Fix warning C4305: 'initializing': truncation from 'double' to 'float'

* Fix warning C4267: 'argument': conversion from 'size_t' to 'utils::FixedCapacityVector<filament::uberz::WritableArchive::Material,std::allocator<T>,true>::size_type', possible loss of data

* Fix warning C4267: 'argument': conversion from 'size_t' to 'uint32_t', possible loss of data

* Fix warning C4244: 'initializing': conversion from 'A' to 'T', possible loss of data

* Fix warning C4334: '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?)

* Fix warning C4293: '>>': shift count negative or too big, undefined behavior

* Fix diagnostic warning C4189: 'channels': local variable is initialized but not referenced

* Use [[maybe_unused]] where possible and revert aa79bd6fa8.

* Revert unary minus for non-MSVC compilers

* Add macro for enabling warnings temporarily

* Get rid of UTILS_HAS_CXX17

* Revisit warning related macros

Co-authored-by: Levente Koncz <levente.koncz@shapr3d.com>
2022-08-24 11:41:10 -07:00
Mathias Agopian 2182149b7a fix shadows when there is no receivers in the frustum
this case could cause an assert in debug builds. There was a few bugs:

- when computing the intersection between a box and a frustum, a special
  case test had a typo which would cause extra work in some case. Also,
  we were not detecting correctly when there was no intersection at all.
  This could result in some intersection point being found.

- Finally, we reject the intersection volume when it has only 3 vertices
  because that's not a volume.

Fixes #5933
2022-08-24 11:07:32 -07:00
Ben Doherty 006e0548c1 Adjust Metal buffer bindings in preparation for argument buffers (#5986) 2022-08-24 13:50:45 -04:00
Mathias Agopian 8257fbdbf3 Fix a merge that went bad 2022-08-23 17:16:56 -07:00
Mathias Agopian f57224952a record sampler informations in the material file
The main goal of this change is to have "ugly" code only in filamat.
In particular, the sampler binding information is now recorded
into the material file and used on the filament side to inform the
backend.

This means that on the filament side we only have generic code with
all the "uglyness" in filamat. In particular SamplerBindingMap and
SibGenerator now only exist in filamat.

Files of greater interest:
Material.cpp, MaterialBuilder.cpp
2022-08-23 17:03:30 -07:00
Philip Rideout 3c982cae64 Revert "WASM: Set CONFIG_MAX_INSTANCES to a small value."
This reverts commit 7aa5d4a375.
2022-08-23 16:42:16 -07:00
Mathias Agopian 3ffe63c02c don't use std::is_pod which is soon to be deprecated
fixes #5980
2022-08-23 15:49:35 -07:00
Philip Rideout 7aa5d4a375 WASM: Set CONFIG_MAX_INSTANCES to a small value.
This is a workaround for the following Chrome issues:

https://crbug.com/1348017 Compiling GLSL is very slow with struct arrays
https://crbug.com/1348363 Lighting looks wrong with D3D11 but not OpenGL

This requires a rebuild of materials for WebGL users.

Fixes #5859.
2022-08-23 11:57:26 -07:00
Philip Rideout 1d06dd630a Add CONFIG for MaxInstanceCount. 2022-08-23 09:32:01 -07:00
Mathias Agopian f630fb6732 'strict' field is no longer needed in Program::Sampler 2022-08-23 09:08:13 -07:00
595 changed files with 64322 additions and 15945 deletions
+10 -10
View File
@@ -39,8 +39,8 @@ jobs:
run: |
REF=${RELEASE_TAG:-${GITHUB_REF}}
TAG=${REF##*/}
echo ::set-output name=ref::${REF}
echo ::set-output name=tag::${TAG}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v2.0.0
with:
ref: ${{ steps.git_ref.outputs.ref }}
@@ -69,8 +69,8 @@ jobs:
run: |
REF=${RELEASE_TAG:-${GITHUB_REF}}
TAG=${REF##*/}
echo ::set-output name=ref::${REF}
echo ::set-output name=tag::${TAG}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v2.0.0
with:
ref: ${{ steps.git_ref.outputs.ref }}
@@ -97,8 +97,8 @@ jobs:
run: |
REF=${RELEASE_TAG:-${GITHUB_REF}}
TAG=${REF##*/}
echo ::set-output name=ref::${REF}
echo ::set-output name=tag::${TAG}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v2.0.0
with:
ref: ${{ steps.git_ref.outputs.ref }}
@@ -143,8 +143,8 @@ jobs:
run: |
REF=${RELEASE_TAG:-${GITHUB_REF}}
TAG=${REF##*/}
echo ::set-output name=ref::${REF}
echo ::set-output name=tag::${TAG}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
- uses: actions/checkout@v2.0.0
with:
ref: ${{ steps.git_ref.outputs.ref }}
@@ -171,8 +171,8 @@ jobs:
run: |
REF=${RELEASE_TAG:-${GITHUB_REF}}
TAG=${REF##*/}
echo ::set-output name=ref::${REF}
echo ::set-output name=tag::${TAG}
echo "ref=${REF}" >> $GITHUB_OUTPUT
echo "tag=${TAG}" >> $GITHUB_OUTPUT
shell: bash
- uses: actions/checkout@v2.0.0
with:
+1 -1
View File
@@ -15,7 +15,7 @@ To build Filament for Android you must also install the following:
- Android Studio Arctic Fox or more recent
- Android SDK
- Android NDK "side-by-side" 23.1 or higher
- Android NDK 25.1 or higher
### Environment variables
+18 -7
View File
@@ -35,16 +35,16 @@ set(FILAMENT_NDK_VERSION "" CACHE STRING
"Android NDK version or version prefix to be used when building for Android."
)
set(FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB "2" CACHE STRING
"Per render pass arena size. Must be roughly 1 MB larger than FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB, default 2."
set(FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB "3" CACHE STRING
"Per render pass arena size. Must be roughly 1 MB larger than FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB, default 3."
)
set(FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB "1" CACHE STRING
"Size of the high-level draw commands buffer. Rule of thumb, 1 MB less than FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB, default 1."
set(FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB "2" CACHE STRING
"Size of the high-level draw commands buffer. Rule of thumb, 1 MB less than FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB, default 2."
)
set(FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB "1" CACHE STRING
"Size of the command-stream buffer. As a rule of thumb use the same value as FILAMENT_PER_FRRAME_COMMANDS_SIZE_IN_MB, default 1."
set(FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB "2" CACHE STRING
"Size of the command-stream buffer. As a rule of thumb use the same value as FILAMENT_PER_FRRAME_COMMANDS_SIZE_IN_MB, default 2."
)
set(FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB "4" CACHE STRING
@@ -459,6 +459,13 @@ else()
option(FILAMENT_ENABLE_MATDBG "Enable the material debugger" OFF)
endif()
# By default, link in fgdbg for Desktop + Debug
if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND IS_HOST_PLATFORM)
option(FILAMENT_ENABLE_FGDBG "Enable the framegraph debugger" ON)
else()
option(FILAMENT_ENABLE_FGDBG "Enable the framegraph debugger" OFF)
endif()
# Only optimize materials in Release mode (so error message lines match the source code)
if (CMAKE_BUILD_TYPE MATCHES Release)
option(FILAMENT_DISABLE_MATOPT "Disable material optimizations" OFF)
@@ -666,7 +673,7 @@ add_subdirectory(${EXTERNAL}/imgui/tnt)
add_subdirectory(${EXTERNAL}/robin-map/tnt)
add_subdirectory(${EXTERNAL}/smol-v/tnt)
add_subdirectory(${EXTERNAL}/benchmark/tnt)
add_subdirectory(${EXTERNAL}/meshoptimizer)
add_subdirectory(${EXTERNAL}/meshoptimizer/tnt)
add_subdirectory(${EXTERNAL}/cgltf/tnt)
add_subdirectory(${EXTERNAL}/draco/tnt)
add_subdirectory(${EXTERNAL}/jsmn/tnt)
@@ -685,6 +692,10 @@ if (FILAMENT_BUILD_FILAMAT OR IS_HOST_PLATFORM)
if (FILAMENT_ENABLE_MATDBG OR IS_HOST_PLATFORM)
add_subdirectory(${LIBRARIES}/matdbg)
endif()
if (FILAMENT_ENABLE_FGDBG OR IS_HOST_PLATFORM)
add_subdirectory(${LIBRARIES}/fgdbg)
endif()
endif()
if (FILAMENT_SUPPORTS_VULKAN)
+4 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.25.6'
implementation 'com.google.android.filament:filament-android:1.28.0'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```
pod 'Filament', '~> 1.25.6'
pod 'Filament', '~> 1.28.0'
```
### Snapshots
@@ -179,6 +179,7 @@ steps:
- [x] KHR_mesh_quantization
- [x] KHR_texture_basisu
- [x] KHR_texture_transform
- [x] EXT_meshopt_compression
## Rendering with Filament
@@ -312,6 +313,7 @@ and tools.
- `bluegl`: OpenGL bindings for macOS, Linux and Windows
- `bluevk`: Vulkan bindings for macOS, Linux, Windows and Android
- `camutils`: Camera manipulation utilities
- `fgdbg`: Frame Graph inspector and debugger (debug builds only)
- `filabridge`: Library shared by the Filament engine and host tools
- `filaflat`: Serialization/deserialization library used for materials
- `filagui`: Helper library for [Dear ImGui](https://github.com/ocornut/imgui)
+54
View File
@@ -5,6 +5,60 @@ A new header is inserted each time a *tag* is created.
## main branch
- gltfio: support EXT_meshopt_compression
- release packaging: fixed location of default IBL file
## v1.28.1
- gltfio: fix reloading crash in ubershader mode
- Vulkan: improve performance in the readPixels path
- engine: raise the spot shadows limit to 64, from 14.
- engine: add experimental support for point light shadows.
- gltfio: fix ubershader issues with assignment of dummy textures
- gltfio: material instances and variants are now accessed via `FilamentInstance` [⚠️ **API Change**]
- gltfio: the animator can now only be accessed via `FilamentInstance` [⚠️ **API Change**]
- engine: fix "stable" shadows and make the default cascade splits logarithmic.
- engine: Add new quality options to EVSM shadows + rendering fixes
## v1.28.0
- engine: LiSPSM is now a user settable option
- engine: get the morph target buffer to the given primitive
- Java: Fix TransformManager.getChildren()
- Metal: newer devices are no longer limited to 16 samplers per Material.
- gltfio: fix interpretation of occlusion strength
- engine: minsdk is now 21 instead of 19. This allows the use of OpenGL ES 3.1
- Vulkan: fix black screen regression
## v1.27.2
- gltfio: punctual lights are now duplicated when adding new asset instances
- gltfio: FilamentInstance getAsset method now returns an immutable asset
- gltfio: allow zero-instance assets
- gltfio: fix regression with meshes that have no material
- gltfio: fix regression with recomputeBoundingBoxes()
- filamesh / matinfo: fix minor ASAN issues
- engine: Added `Engine::resetBackendState()`, available only in WebGL builds
## v1.27.1
- Java: add methods for TransformManager.getChildCount(), TransformManager.getChildren() and Scene.hasEntity()
- engine: Fix stencil buffer writes with OpenGL backend.
- gltfio: add new virtual method to MaterialProvider that all plugins must implement
- gltfio: add an assert for inconsistent sRGB flags among usages of a particular texture
- engine: improve scissor documentation
- backend: scissor is no longer clipped to the viewport (done on filament side)
- samples: add debug overdraw visualization to gltf_viewer
## v1.27.0
- WebGL: reduce max instance count to work around Chrome issues [⚠️ **Recompile Materials**]
- engine: rework material/shader sampler binding code [⚠️ **Recompile Materials**]
- gltfio: move the API for `recomputeBoundingBoxes` [⚠️ **API Change**]
- engine: add support for specialization constants [⚠️ **Recompile Materials**]
- gltfio: fix spotlight regression
- gltfio: clear the MaterialInstance cache when creating new instances
## v1.26.0
- engine: new feature level APIs, see `Engine::getSupportedFeatureLevel()`
+21 -21
View File
@@ -29,15 +29,12 @@
// Publishing to Maven Central:
// - Build and upload artifacts with ./gradlew publish
// - Close and release staging repo on Nexus with ./gradlew closeAndReleaseRepository
// - Close and release staging repo on Nexus with ./gradlew closeAndReleaseStagingRepository
//
// The following is needed in ~/gradle/gradle.properties:
//
// SONATYPE_NEXUS_USERNAME=nexus_user
// SONATYPE_NEXUS_PASSWORD=nexus_password
//
// nexusUsername=nexus_user
// nexusPassword=nexus_password
// sonatypeUsername=nexus_user
// sonatypePassword=nexus_password
//
// signing.keyId=pgp_key_id
// signing.password=pgp_key_password
@@ -47,7 +44,7 @@
buildscript {
def path = providers
.gradleProperty("com.google.android.filament.dist-dir")
.forUseAtConfigurationTime().get()
.get()
def directory = objects.fileProperty().fileValue(new File(path)).getAsFile().get()
def filamentPath = directory.absolutePath
@@ -59,18 +56,15 @@ buildscript {
// Warning: changing this property does not work well with incremental builds.
def excludeVulkan = providers
.gradleProperty("com.google.android.filament.exclude-vulkan")
.forUseAtConfigurationTime()
.isPresent()
def matdbg = providers
.gradleProperty("com.google.android.filament.matdbg")
.forUseAtConfigurationTime()
.isPresent()
def abis = ["arm64-v8a", "armeabi-v7a", "x86_64", "x86"]
def newAbis = providers
.gradleProperty("com.google.android.filament.abis")
.forUseAtConfigurationTime()
.get()
.split(',')
if (!newAbis.contains("all")) {
@@ -79,18 +73,20 @@ buildscript {
ext.versions = [
'minSdk': 19,
'targetSdk': 31,
'compileSdk': 31,
'targetSdk': 33,
'compileSdk': 33,
'kotlin': '1.7.10',
'kotlin_coroutines': '1.6.1',
'buildTools': '33.0.0',
'ndk': '25.0.8775105'
'ndk': '25.1.8937393',
'androidx_core': '1.9.0',
'androidx_annotations': '1.3.0'
]
ext.deps = [
'androidx': [
'annotations': "androidx.annotation:annotation:1.3.0",
'core': "androidx.core:core:1.7.0",
'annotations': "androidx.annotation:annotation:${versions.androidx_annotations}",
'core': "androidx.core:core:${versions.androidx_core}",
],
'kotlin': "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}",
'coroutines': [
@@ -101,7 +97,7 @@ buildscript {
dependencies {
// NOTE: See TODO in gradle.properties once we move to Gradle 7.4
classpath 'com.android.tools.build:gradle:7.2.1'
classpath 'com.android.tools.build:gradle:7.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
}
@@ -148,14 +144,18 @@ buildscript {
}
plugins {
id 'io.codearte.nexus-staging' version '0.30.0'
id "io.github.gradle-nexus.publish-plugin" version "1.1.0"
}
// Nexus Staging configuration
// See https://github.com/Codearte/gradle-nexus-staging-plugin/
nexusStaging {
// See https://github.com/gradle-nexus/publish-plugin
// Publish to https://oss.sonatype.org/ (not s01)
nexusPublishing {
packageGroup = 'com.google.android'
stagingProfileId = '9a75a224a4f17b'
repositories {
sonatype {
stagingProfileId = '9a75a224a4f17b'
}
}
}
subprojects {
@@ -126,8 +126,8 @@ Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderUniform
JNIEnv* env, jclass, jlong nativeBuilder, jint uniformType, jint precision, jstring name_) {
auto builder = (MaterialBuilder*) nativeBuilder;
const char* name = env->GetStringUTFChars(name_, nullptr);
builder->parameter((MaterialBuilder::UniformType) uniformType,
(MaterialBuilder::ParameterPrecision) precision, name);
builder->parameter(name, (MaterialBuilder::UniformType) uniformType,
(MaterialBuilder::ParameterPrecision) precision);
env->ReleaseStringUTFChars(name_, name);
}
@@ -137,8 +137,8 @@ Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderUniform
jstring name_) {
auto builder = (MaterialBuilder*) nativeBuilder;
const char* name = env->GetStringUTFChars(name_, nullptr);
builder->parameter((MaterialBuilder::UniformType) uniformType, (size_t) size,
(MaterialBuilder::ParameterPrecision) precision, name);
builder->parameter(name, (size_t) size, (MaterialBuilder::UniformType) uniformType,
(MaterialBuilder::ParameterPrecision) precision);
env->ReleaseStringUTFChars(name_, name);
}
@@ -148,9 +148,8 @@ Java_com_google_android_filament_filamat_MaterialBuilder_nMaterialBuilderSampler
jint precision, jstring name_) {
auto builder = (MaterialBuilder*) nativeBuilder;
const char* name = env->GetStringUTFChars(name_, nullptr);
builder->parameter((MaterialBuilder::SamplerType) samplerType,
(MaterialBuilder::SamplerFormat) format, (MaterialBuilder::ParameterPrecision) precision,
name);
builder->parameter(name, (MaterialBuilder::SamplerType) samplerType,
(MaterialBuilder::SamplerFormat) format, (MaterialBuilder::ParameterPrecision) precision);
env->ReleaseStringUTFChars(name_, name);
}
@@ -76,10 +76,10 @@ extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env, jclass,
jlong nativeBuilder, jint mapSize, jint cascades, jfloatArray splitPositions,
jfloat constantBias, jfloat normalBias, jfloat shadowFar, jfloat shadowNearHint,
jfloat shadowFarHint, jboolean stable,
jfloat shadowFarHint, jboolean stable, jboolean lispsm,
jfloat polygonOffsetConstant, jfloat polygonOffsetSlope,
jboolean screenSpaceContactShadows, jint stepCount,
jfloat maxShadowDistance, jint vsmMsaaSamples, jfloat blurWidth, jfloat shadowBulbRadius) {
jfloat maxShadowDistance, jint vsmMsaaSamples, jboolean elvsm, jfloat blurWidth, jfloat shadowBulbRadius) {
LightManager::Builder *builder = (LightManager::Builder *) nativeBuilder;
LightManager::ShadowOptions shadowOptions {
.mapSize = (uint32_t)mapSize,
@@ -90,6 +90,7 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
.shadowNearHint = shadowNearHint,
.shadowFarHint = shadowFarHint,
.stable = (bool)stable,
.lispsm = (bool)lispsm,
.polygonOffsetConstant = polygonOffsetConstant,
.polygonOffsetSlope = polygonOffsetConstant,
.screenSpaceContactShadows = (bool)screenSpaceContactShadows,
@@ -97,6 +98,7 @@ Java_com_google_android_filament_LightManager_nBuilderShadowOptions(JNIEnv* env,
.maxShadowDistance = maxShadowDistance,
.vsm = {
.msaaSamples = (uint8_t) vsmMsaaSamples,
.elvsm = (bool)elvsm,
.blurWidth = blurWidth
},
.shadowBulbRadius = shadowBulbRadius
@@ -83,3 +83,11 @@ Java_com_google_android_filament_Scene_nGetLightCount(JNIEnv *env, jclass type,
Scene* scene = (Scene*) nativeScene;
return (jint) scene->getLightCount();
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Scene_nHasEntity(JNIEnv *env, jclass type, jlong nativeScene,
jint entityId) {
Scene* scene = (Scene*) nativeScene;
Entity entity = Entity::import(entityId);
return (jboolean) scene->hasEntity(entity);
}
@@ -110,6 +110,26 @@ Java_com_google_android_filament_TransformManager_nGetParent(JNIEnv*, jclass,
return tm->getParent((TransformManager::Instance) i).getId();
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_TransformManager_nGetChildCount(JNIEnv*, jclass,
jlong nativeTransformManager, jint i) {
TransformManager* tm = (TransformManager*) nativeTransformManager;
return tm->getChildCount((TransformManager::Instance) i);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_TransformManager_nGetChildren(JNIEnv* env,
jclass, jlong nativeTransformManager, jint i,
jintArray outEntities_, jint count) {
TransformManager* tm = (TransformManager*) nativeTransformManager;
jint* entities = env->GetIntArrayElements(outEntities_, nullptr);
// This is very very gross, we just pretend Entity is just like an jint
// (which it is), but still.
tm->getChildren((TransformManager::Instance) i,
reinterpret_cast<Entity *>(entities), (size_t) count);
env->ReleaseIntArrayElements(outEntities_, entities, 0);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_TransformManager_nSetTransform(JNIEnv* env,
jclass, jlong nativeTransformManager, jint i,
@@ -149,12 +149,13 @@ Java_com_google_android_filament_View_nSetShadowType(JNIEnv*, jclass, jlong nati
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nSetVsmShadowOptions(JNIEnv*, jclass, jlong nativeView,
jint anisotropy, jboolean mipmapping, jfloat minVarianceScale,
jint anisotropy, jboolean mipmapping, jboolean highPrecision, jfloat minVarianceScale,
jfloat lightBleedReduction) {
View* view = (View*) nativeView;
View::VsmShadowOptions options;
options.anisotropy = (uint8_t)anisotropy;
options.mipmapping = (bool)mipmapping;
options.highPrecision = (bool)highPrecision;
options.minVarianceScale = minVarianceScale;
options.lightBleedReduction = lightBleedReduction;
view->setVsmShadowOptions(options);
@@ -240,7 +240,7 @@ public class LightManager {
*/
@NonNull
@Size(min = 3)
public float[] cascadeSplitPositions = { 0.25f, 0.50f, 0.75f };
public float[] cascadeSplitPositions = { 0.125f, 0.25f, 0.50f };
/** Constant bias in world units (e.g. meters) by which shadows are moved away from the
* light. 1mm by default.
@@ -280,9 +280,25 @@ public class LightManager {
* Controls whether the shadow map should be optimized for resolution or stability.
* When set to true, all resolution enhancing features that can affect stability are
* disabling, resulting in significantly lower resolution shadows, albeit stable ones.
*
* Setting this flag to true always disables LiSPSM (see below).
*/
public boolean stable = false;
/**
* LiSPSM, or light-space perspective shadow-mapping is a technique allowing to better
* optimize the use of the shadow-map texture. When enabled the effective resolution of
* shadows is greatly improved and yields result similar to using cascades without the
* extra cost. LiSPSM comes with some drawbacks however, in particular it is incompatible
* with blurring because it effectively affects the blur kernel size.
*
* Blurring is only an issue when using ShadowType.VSM with a large blur or with
* ShadowType.PCSS however.
*
* If these blurring artifacts become problematic, this flag can be used to disable LiSPSM.
*/
public boolean lispsm = false;
/**
* Constant bias in depth-resolution units by which shadows are moved away from the
* light. The default value of 0.5 is used to round depth values up.
@@ -346,6 +362,15 @@ public class LightManager {
@IntRange(from = 1)
public int vsmMsaaSamples = 1;
/**
* When elvsm is set to true, "Exponential Layered VSM without Layers" are used. It is
* an improvement to the default EVSM which suffers important light leaks. Enabling
* ELVSM for a single shadowmap doubles the memory usage of all shadow maps.
* ELVSM is mostly useful when large blurs are used.
*/
public boolean elvsm = false;
/**
* Blur width for the VSM blur. Zero do disable.
* The maximum value is 125.
@@ -470,11 +495,6 @@ public class LightManager {
/**
* Whether this Light casts shadows (disabled by default)
*
* <p>
* <b>warning:</b>
* {@link Type#POINT} lights cannot cast shadows.
* </p>
*
* @param enable Enables or disables casting shadows from this Light.
*
* @return This Builder, for chaining calls.
@@ -496,11 +516,11 @@ public class LightManager {
nBuilderShadowOptions(mNativeBuilder,
options.mapSize, options.shadowCascades, options.cascadeSplitPositions,
options.constantBias, options.normalBias, options.shadowFar, options.shadowNearHint,
options.shadowFarHint, options.stable,
options.shadowFarHint, options.stable, options.lispsm,
options.polygonOffsetConstant, options.polygonOffsetSlope,
options.screenSpaceContactShadows,
options.stepCount, options.maxShadowDistance, options.vsmMsaaSamples,
options.blurWidth, options.shadowBulbRadius);
options.elvsm, options.blurWidth, options.shadowBulbRadius);
return this;
}
@@ -1158,7 +1178,14 @@ public class LightManager {
private static native void nDestroyBuilder(long nativeBuilder);
private static native boolean nBuilderBuild(long nativeBuilder, long nativeEngine, int entity);
private static native void nBuilderCastShadows(long nativeBuilder, boolean enable);
private static native void nBuilderShadowOptions(long nativeBuilder, int mapSize, int cascades, float[] splitPositions, float constantBias, float normalBias, float shadowFar, float shadowNearHint, float shadowFarhint, boolean stable, float polygonOffsetConstant, float polygonOffsetSlope, boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance, int vsmMsaaSamples, float blurWidth, float shadowBulbRadius);
private static native void nBuilderShadowOptions(long nativeBuilder, int mapSize,
int cascades, float[] splitPositions,
float constantBias, float normalBias,
float shadowFar, float shadowNearHint, float shadowFarhint,
boolean stable, boolean lispsm,
float polygonOffsetConstant, float polygonOffsetSlope,
boolean screenSpaceContactShadows, int stepCount, float maxShadowDistance,
int vsmMsaaSamples, boolean elvsm, float blurWidth, float shadowBulbRadius);
private static native void nBuilderCastLight(long nativeBuilder, boolean enabled);
private static native void nBuilderPosition(long nativeBuilder, float x, float y, float z);
private static native void nBuilderDirection(long nativeBuilder, float x, float y, float z);
@@ -402,19 +402,40 @@ public class MaterialInstance {
}
/**
* Set up a custom scissor rectangle; by default this encompasses the View.
* Set-up a custom scissor rectangle; by default it is disabled.
*
* @param left left coordinate of the scissor box
* @param bottom bottom coordinate of the scissor box
* <p>
* The scissor rectangle gets clipped by the View's viewport, in other words, the scissor
* cannot affect fragments outside of the View's Viewport.
* </p>
*
* <p>
* Currently the scissor is not compatible with dynamic resolution and should always be
* disabled when dynamic resolution is used.
* </p>
*
* @param left left coordinate of the scissor box relative to the viewport
* @param bottom bottom coordinate of the scissor box relative to the viewport
* @param width width of the scissor box
* @param height height of the scissor box
*
* @see #unsetScissor
* @see View#setViewport
* @see View#setDynamicResolutionOptions
*/
public void setScissor(@IntRange(from = 0) int left, @IntRange(from = 0) int bottom,
@IntRange(from = 0) int width, @IntRange(from = 0) int height) {
nSetScissor(getNativeObject(), left, bottom, width, height);
}
/** Returns the scissor rectangle to its default setting, which encompasses the View. */
/**
* Returns the scissor rectangle to its default disabled setting.
* <p>
* Currently the scissor is not compatible with dynamic resolution and should always be
* disabled when dynamic resolution is used.
* </p>
* @see View#setDynamicResolutionOptions
*/
public void unsetScissor() {
nUnsetScissor(getNativeObject());
}
@@ -163,6 +163,15 @@ public class Scene {
return nGetLightCount(getNativeObject());
}
/**
* Returns true if the given entity is in the Scene.
*
* @return Whether the given entity is in the Scene.
*/
public boolean hasEntity(@Entity int entity) {
return nHasEntity(getNativeObject(), entity);
}
public long getNativeObject() {
if (mNativeObject == 0) {
throw new IllegalStateException("Calling method on destroyed Scene");
@@ -182,4 +191,5 @@ public class Scene {
private static native void nRemoveEntities(long nativeScene, int[] entities);
private static native int nGetRenderableCount(long nativeScene);
private static native int nGetLightCount(long nativeScene);
private static native boolean nHasEntity(long nativeScene, int entity);
}
@@ -16,6 +16,7 @@
package com.google.android.filament;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.Size;
@@ -199,6 +200,36 @@ import androidx.annotation.Size;
return nGetParent(mNativeObject, i);
}
/**
* Returns the number of children of an {@link EntityInstance}.
*
* @param i the {@link EntityInstance} of the transform component to query.
* @return The number of children of the queried component.
*/
public int getChildCount(@EntityInstance int i) {
return nGetChildCount(mNativeObject, i);
}
/**
* Gets a list of children for a transform component.
*
* @param i the {@link EntityInstance} of the transform component to get the children
* from.
* @param outEntities array to receive the result sized to the maximum number of children to
* retrieve. If <code>null</code> is given, a new suitable array sized to
* {@link #getChildCount(int)} is allocated.
* @return Array of retrieved children {@link Entity}.
*/
public @Entity @NonNull int[] getChildren(@EntityInstance int i, @Nullable int[] outEntities) {
if (outEntities == null) {
outEntities = new int[getChildCount(i)];
}
if (outEntities.length > 0) {
nGetChildren(mNativeObject, i, outEntities, outEntities.length);
}
return outEntities;
}
/**
* Sets a local transform of a transform component.
* <p>This operation can be slow if the hierarchy of transform is too deep, and this
@@ -360,6 +391,8 @@ import androidx.annotation.Size;
private static native void nDestroy(long nativeTransformManager, int entity);
private static native void nSetParent(long nativeTransformManager, int i, int newParent);
private static native int nGetParent(long nativeTransformManager, int i);
private static native int nGetChildCount(long nativeTransformManager, int i);
private static native void nGetChildren(long nativeEntityManager, int i, int[] outEntities, int count);
private static native void nSetTransform(long nativeTransformManager, int i, float[] localTransform);
private static native void nSetTransformFp64(long nativeTransformManager, int i, double[] localTransform);
private static native void nGetTransform(long nativeTransformManager, int i, float[] outLocalTransform);
@@ -793,7 +793,7 @@ public class View {
public void setVsmShadowOptions(@NonNull VsmShadowOptions options) {
mVsmShadowOptions = options;
nSetVsmShadowOptions(getNativeObject(), options.anisotropy, options.mipmapping,
options.minVarianceScale, options.lightBleedReduction);
options.highPrecision, options.minVarianceScale, options.lightBleedReduction);
}
/**
@@ -1144,7 +1144,7 @@ public class View {
private static native void nSetRenderQuality(long nativeView, int hdrColorBufferQuality);
private static native void nSetDynamicLightingOptions(long nativeView, float zLightNear, float zLightFar);
private static native void nSetShadowType(long nativeView, int type);
private static native void nSetVsmShadowOptions(long nativeView, int anisotropy, boolean mipmapping, float minVarianceScale, float lightBleedReduction);
private static native void nSetVsmShadowOptions(long nativeView, int anisotropy, boolean mipmapping, boolean highPrecision, float minVarianceScale, float lightBleedReduction);
private static native void nSetSoftShadowOptions(long nativeView, float penumbraScale, float penumbraRatioScale);
private static native void nSetColorGrading(long nativeView, long nativeColorGrading);
private static native void nSetPostProcessingEnabled(long nativeView, boolean enabled);
@@ -1817,6 +1817,13 @@ public class View {
* Whether to generate mipmaps for all VSM shadow maps.
*/
public boolean mipmapping = false;
/**
* Whether to use a 32-bits or 16-bits texture format for VSM shadow maps. 32-bits
* precision is rarely needed, but it does reduces light leaks as well as "fading"
* of the shadows in some situations. Setting highPrecision to true for a single
* shadow map will double the memory usage of all shadow maps.
*/
public boolean highPrecision = false;
/**
* VSM minimum variance scale, must be positive.
*/
@@ -70,7 +70,8 @@ Java_com_google_android_filament_utils_AutomationEngine_nStartBatchMode(JNIEnv*
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_utils_AutomationEngine_nTick(JNIEnv* env, jclass klass,
jlong nativeAutomation, jlong view, jlongArray materials, jlong renderer, jfloat deltaTime) {
jlong nativeAutomation, jlong nativeEngine,
jlong view, jlongArray materials, jlong renderer, jfloat deltaTime) {
using MaterialPointer = MaterialInstance*;
jsize materialCount = 0;
jlong* longMaterials = nullptr;
@@ -90,7 +91,8 @@ Java_com_google_android_filament_utils_AutomationEngine_nTick(JNIEnv* env, jclas
.materials = ptrMaterials,
.materialCount = (size_t) materialCount,
};
automation->tick(content, deltaTime);
Engine* engine = (Engine*)nativeEngine;
automation->tick(engine, content, deltaTime);
if (longMaterials) {
env->ReleaseLongArrayElements(materials, longMaterials, 0);
delete[] ptrMaterials;
@@ -99,7 +101,8 @@ Java_com_google_android_filament_utils_AutomationEngine_nTick(JNIEnv* env, jclas
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_utils_AutomationEngine_nApplySettings(JNIEnv* env, jclass klass,
jlong nativeAutomation, jstring json, jlong view, jlongArray materials, jlong nativeIbl,
jlong nativeAutomation, jlong nativeEngine,
jstring json, jlong view, jlongArray materials, jlong nativeIbl,
jint sunlightEntity, jintArray assetLights, jlong nativeLm, jlong scene, jlong renderer) {
using MaterialPointer = MaterialInstance*;
@@ -140,8 +143,8 @@ Java_com_google_android_filament_utils_AutomationEngine_nApplySettings(JNIEnv* e
.assetLights = (Entity*) intLights,
.assetLightCount = (size_t) lightCount,
};
automation->applySettings(nativeJson, jsonLength, content);
Engine* engine = (Engine*)nativeEngine;
automation->applySettings(engine, nativeJson, jsonLength, content);
env->ReleaseStringUTFChars(json, nativeJson);
if (longMaterials) {
env->ReleaseLongArrayElements(materials, longMaterials, 0);
@@ -156,10 +156,11 @@ public class AutomationEngine {
* This is when settings get applied, screenshots are (optionally) exported, and the internal
* test counter is potentially incremented.
*
* @param engine The filament Engine of interest.
* @param content Contains the Filament View, Materials, and Renderer that get modified.
* @param deltaTime The amount of time that has passed since the previous tick in seconds.
*/
public void tick(@NonNull ViewerContent content, float deltaTime) {
public void tick(@NonNull Engine engine, @NonNull ViewerContent content, float deltaTime) {
if (content.view == null || content.renderer == null) {
throw new IllegalStateException("Must provide a View and Renderer");
}
@@ -172,7 +173,7 @@ public class AutomationEngine {
}
long nativeView = content.view.getNativeObject();
long nativeRenderer = content.renderer.getNativeObject();
nTick(mNativeObject, nativeView, nativeMaterialInstances, nativeRenderer, deltaTime);
nTick(mNativeObject, engine.getNativeObject(), nativeView, nativeMaterialInstances, nativeRenderer, deltaTime);
}
/**
@@ -184,10 +185,12 @@ public class AutomationEngine {
* This updates the stashed Settings object, then pushes those settings to the given
* Filament objects. Clients can optionally call getColorGrading() after calling this method.
*
* @param engine Filament Engine to use.
* @param settingsJson Contains the JSON string with a set of changes that need to be pushed.
* @param content Contains a set of Filament objects that you want to mutate.
*/
public void applySettings(@NonNull String settingsJson, @NonNull ViewerContent content) {
public void applySettings(@NonNull Engine engine, @NonNull String settingsJson,
@NonNull ViewerContent content) {
if (content.view == null || content.renderer == null) {
throw new IllegalStateException("Must provide a View and Renderer");
}
@@ -206,7 +209,8 @@ public class AutomationEngine {
long nativeLm = content.lightManager.getNativeObject();
long nativeScene = content.scene.getNativeObject();
long nativeRenderer = content.renderer.getNativeObject();
nApplySettings(mNativeObject, settingsJson, nativeView, nativeMaterialInstances,
nApplySettings(mNativeObject, engine.getNativeObject(),
settingsJson, nativeView, nativeMaterialInstances,
nativeIbl, content.sunlight, content.assetLights, nativeLm, nativeScene,
nativeRenderer);
}
@@ -267,9 +271,10 @@ public class AutomationEngine {
int minFrameCount, boolean verbose);
private static native void nStartRunning(long nativeObject);
private static native void nStartBatchMode(long nativeObject);
private static native void nTick(long nativeObject, long view, long[] materials, long renderer,
float deltaTime);
private static native void nApplySettings(long nativeObject, String jsonSettings, long view,
private static native void nTick(long nativeObject, long nativeEngine,
long view, long[] materials, long renderer, float deltaTime);
private static native void nApplySettings(long nativeObject, long nativeEngine,
String jsonSettings, long view,
long[] materials, long ibl, int sunlight, int[] assetLights, long lightManager,
long scene, long renderer);
private static native void nGetViewerOptions(long nativeObject, Object result);
@@ -73,8 +73,6 @@ class ModelViewer(
get() = resourceLoader.asyncGetLoadProgress()
var normalizeSkinningWeights = true
var recomputeBoundingBoxes = false
var ignoreBindTransform = false
var cameraFocalLength = 28f
set(value) {
@@ -116,7 +114,7 @@ class ModelViewer(
materialProvider = UbershaderProvider(engine)
assetLoader = AssetLoader(engine, materialProvider, EntityManager.get())
resourceLoader = ResourceLoader(engine, normalizeSkinningWeights, recomputeBoundingBoxes, ignoreBindTransform)
resourceLoader = ResourceLoader(engine, normalizeSkinningWeights)
// Always add a direct light source since it is required for shadowing.
// We highly recommend adding an indirect light as well.
@@ -181,7 +179,7 @@ class ModelViewer(
asset = assetLoader.createAsset(buffer)
asset?.let { asset ->
resourceLoader.asyncBeginLoad(asset)
animator = asset.animator
animator = asset.getInstance().animator
asset.releaseSourceData()
}
}
@@ -204,7 +202,7 @@ class ModelViewer(
resourceLoader.addResourceData(uri, resourceBuffer)
}
resourceLoader.asyncBeginLoad(asset)
animator = asset.animator
animator = asset.getInstance().animator
asset.releaseSourceData()
}
}
@@ -312,8 +310,8 @@ class ModelViewer(
private fun addDetachListener(view: android.view.View) {
view.addOnAttachStateChangeListener(object : android.view.View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: android.view.View?) {}
override fun onViewDetachedFromWindow(v: android.view.View?) {
override fun onViewAttachedToWindow(v: android.view.View) {}
override fun onViewDetachedFromWindow(v: android.view.View) {
uiHelper.detach()
destroyModel()
@@ -361,7 +359,7 @@ class ModelViewer(
resourceLoader.addResourceData(uri, buffer)
}
resourceLoader.asyncBeginLoad(asset)
animator = asset.animator
animator = asset.getInstance().animator
asset.releaseSourceData()
}
}
+7 -2
View File
@@ -11,6 +11,10 @@ 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)
@@ -76,7 +80,7 @@ set(GLTFIO_SRCS
${GLTFIO_DIR}/src/UbershaderProvider.cpp
${GLTFIO_DIR}/src/Wireframe.cpp
${GLTFIO_DIR}/src/Wireframe.h
${GLTFIO_DIR}/src/upcast.h
${GLTFIO_DIR}/src/downcast.h
src/main/cpp/Animator.cpp
src/main/cpp/AssetLoader.cpp
@@ -99,6 +103,7 @@ set(GLTFIO_INCLUDE_DIRS
../../libs/gltfio/include
../../third_party/basisu/zstd
../../third_party/cgltf
../../third_party/meshoptimizer/src
../../third_party/robin-map
../../third_party/hat-trie
../../third_party/stb
@@ -111,7 +116,7 @@ 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 uberzlib log stb ktxreader basis_transcoder zstd uberarchive)
target_link_libraries(gltfio-jni dracodec)
target_link_libraries(gltfio-jni dracodec meshoptimizer)
target_compile_definitions(gltfio-jni PUBLIC GLTFIO_DRACO_SUPPORTED=1)
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/src)
target_include_directories(gltfio-jni PRIVATE ${DRACO_DIR}/tnt)
@@ -14,5 +14,4 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.filament.gltfio" />
<manifest xmlns:android="http://schemas.android.com/apk/res/android" />
@@ -42,6 +42,7 @@ class JavaMaterialProvider : public MaterialProvider {
jmethodID mMaterialKeyConstructor;
jmethodID mCreateMaterialInstance;
jmethodID mGetMaterial;
jmethodID mGetMaterials;
jmethodID mNeedsDummyData;
jmethodID mDestroyMaterials;
@@ -72,6 +73,10 @@ public:
"(L" JAVA_MATERIAL_KEY ";[ILjava/lang/String;Ljava/lang/String;)Lcom/google/android/filament/MaterialInstance;");
assert_invariant(mCreateMaterialInstance);
mGetMaterial = env->GetMethodID(providerClass, "getMaterial",
"(L" JAVA_MATERIAL_KEY ";[ILjava/lang/String;)Lcom/google/android/filament/Material;");
assert_invariant(mGetMaterial);
mGetMaterials = env->GetMethodID(providerClass, "getMaterials",
"()[Lcom/google/android/filament/Material;");
assert_invariant(mGetMaterials);
@@ -103,7 +108,7 @@ public:
jstring stringExtras = extras ? mEnv->NewStringUTF(extras) : nullptr;
// Allocate space for the output argument.
jintArray uvMapArray = mEnv->NewIntArray(8);
jintArray uvMapArray = mEnv->NewIntArray(uvmap->size());
// Call the Java-based material provider.
jobject materialInstance = mEnv->CallObjectMethod(mJavaProvider, mCreateMaterialInstance,
@@ -139,6 +144,49 @@ public:
return (MaterialInstance*) mEnv->CallLongMethod(materialInstance, mMaterialInstanceGetNativeObject);
}
Material* getMaterial(MaterialKey* config, UvMap* uvmap, const char* label) override {
// Create a Java object for the material key and copy the native fields into it.
jobject javaKey = mEnv->NewObject(mMaterialKeyClass, mMaterialKeyConstructor);
auto& helper = MaterialKeyHelper::get();
helper.copy(mEnv, javaKey, *config);
// Convert the optional label into a Java string.
jstring stringLabel = label ? mEnv->NewStringUTF(label) : nullptr;
// Allocate space for the output argument.
jintArray uvMapArray = mEnv->NewIntArray(uvmap->size());
// Call the Java-based material provider.
jobject material = mEnv->CallObjectMethod(mJavaProvider, mGetMaterial,
javaKey, uvMapArray, stringLabel);
// Copy the UvMap results from the JVM array into the native array.
if (uvmap) {
jint* elements = mEnv->GetIntArrayElements(uvMapArray, nullptr);
for (size_t i = 0; i < uvmap->size(); i++) {
(*uvmap)[i] = (UvSet) elements[i];
}
mEnv->ReleaseIntArrayElements(uvMapArray, elements, JNI_ABORT);
}
// The config parameter is an in-out parameter so we need to copy the results from Java.
helper.copy(mEnv, *config, javaKey);
mEnv->DeleteLocalRef(javaKey);
mEnv->DeleteLocalRef(uvMapArray);
if (stringLabel) {
mEnv->DeleteLocalRef(stringLabel);
}
if (material == nullptr) {
return nullptr;
}
return (Material*) mEnv->CallLongMethod(material, mMaterialGetNativeObject);
}
const Material* const* getMaterials() const noexcept override {
jobjectArray javaMaterials = (jobjectArray) mEnv->CallObjectMethod(mJavaProvider, mGetMaterials);
@@ -172,27 +172,6 @@ Java_com_google_android_filament_gltfio_FilamentAsset_nGetCameraEntityCount(JNIE
return asset->getCameraEntityCount();
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_gltfio_FilamentAsset_nGetMaterialInstanceCount(JNIEnv*, jclass,
jlong nativeAsset) {
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
return asset->getMaterialInstanceCount();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_FilamentAsset_nGetMaterialInstances(JNIEnv* env, jclass,
jlong nativeAsset, jlongArray result) {
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
jsize available = env->GetArrayLength(result);
jsize count = std::min(available, (jsize) asset->getMaterialInstanceCount());
jlong* dst = env->GetLongArrayElements(result, nullptr);
const MaterialInstance * const* src = asset->getMaterialInstances();
for (jsize i = 0; i < count; i++) {
dst[i] = (jlong) src[i];
}
env->ReleaseLongArrayElements(result, dst, 0);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_FilamentAsset_nGetBoundingBox(JNIEnv* env, jclass,
jlong nativeAsset, jfloatArray result) {
@@ -229,10 +208,10 @@ Java_com_google_android_filament_gltfio_FilamentAsset_nGetExtras(JNIEnv* env, jc
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_gltfio_FilamentAsset_nGetAnimator(JNIEnv* , jclass,
Java_com_google_android_filament_gltfio_FilamentAsset_nGetInstance(JNIEnv* , jclass,
jlong nativeAsset) {
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
return (jlong) asset->getAnimator();
return (jlong) asset->getInstance();
}
extern "C" JNIEXPORT jint JNICALL
@@ -272,30 +251,6 @@ Java_com_google_android_filament_gltfio_FilamentAsset_nGetMorphTargetNames(JNIEn
}
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_gltfio_FilamentAsset_nGetMaterialVariantCount(JNIEnv*, jclass,
jlong nativeAsset) {
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
return (jint) asset->getMaterialVariantCount();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_FilamentAsset_nGetMaterialVariantNames(JNIEnv* env, jclass,
jlong nativeAsset, jobjectArray result) {
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
for (int i = 0; i < asset->getMaterialVariantCount(); ++i) {
const char* name = asset->getMaterialVariantName(i);
env->SetObjectArrayElement(result, (jsize) i, env->NewStringUTF(name));
}
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_FilamentAsset_nApplyMaterialVariant(JNIEnv* env, jclass,
jlong nativeAsset, jint variantIndex) {
FilamentAsset* asset = (FilamentAsset*) nativeAsset;
asset->applyMaterialVariant(variantIndex);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_FilamentAsset_nReleaseSourceData(JNIEnv* env, jclass,
jlong nativeAsset) {
@@ -20,6 +20,7 @@
#include <algorithm>
using namespace filament;
using namespace filament::gltfio;
using namespace utils;
@@ -62,6 +63,44 @@ Java_com_google_android_filament_gltfio_FilamentInstance_nApplyMaterialVariant(J
instance->applyMaterialVariant(variantIndex);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_gltfio_FilamentInstance_nGetMaterialVariantCount(JNIEnv*, jclass,
jlong nativeInstance) {
FilamentInstance* instance = (FilamentInstance*) nativeInstance;
return (jint) instance->getMaterialVariantCount();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_FilamentInstance_nGetMaterialVariantNames(JNIEnv* env, jclass,
jlong nativeInstance, jobjectArray result) {
FilamentInstance* instance = (FilamentInstance*) nativeInstance;
for (int i = 0; i < instance->getMaterialVariantCount(); ++i) {
const char* name = instance->getMaterialVariantName(i);
env->SetObjectArrayElement(result, (jsize) i, env->NewStringUTF(name));
}
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_gltfio_FilamentInstance_nGetMaterialInstanceCount(JNIEnv*, jclass,
jlong nativeInstance) {
FilamentInstance* instance = (FilamentInstance*) nativeInstance;
return instance->getMaterialInstanceCount();
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_FilamentInstance_nGetMaterialInstances(JNIEnv* env, jclass,
jlong nativeInstance, jlongArray result) {
FilamentInstance* instance = (FilamentInstance*) nativeInstance;
jsize available = env->GetArrayLength(result);
jsize count = std::min(available, (jsize) instance->getMaterialInstanceCount());
jlong* dst = env->GetLongArrayElements(result, nullptr);
const MaterialInstance * const* src = instance->getMaterialInstances();
for (jsize i = 0; i < count; i++) {
dst[i] = (jlong) src[i];
}
env->ReleaseLongArrayElements(result, dst, 0);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_gltfio_FilamentInstance_nAttachSkin(JNIEnv* env, jclass,
jlong nativeInstance, jint skinIndex, jint targetEntity) {
@@ -36,11 +36,9 @@ static void destroy(void*, size_t, void *userData) {
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_gltfio_ResourceLoader_nCreateResourceLoader(JNIEnv*, jclass,
jlong nativeEngine, jboolean normalizeSkinningWeights, jboolean recomputeBoundingBoxes,
jboolean ignoreBindTransform) {
jlong nativeEngine, jboolean normalizeSkinningWeights) {
Engine* engine = (Engine*) nativeEngine;
return (jlong) new ResourceLoader({ engine, {}, (bool) normalizeSkinningWeights,
(bool) recomputeBoundingBoxes, (bool) ignoreBindTransform});
return (jlong) new ResourceLoader({ engine, {}, (bool) normalizeSkinningWeights});
}
extern "C" JNIEXPORT void JNICALL
@@ -49,16 +49,19 @@ Java_com_google_android_filament_gltfio_UbershaderProvider_nDestroyMaterials(JNI
extern "C" JNIEXPORT long JNICALL
Java_com_google_android_filament_gltfio_UbershaderProvider_nCreateMaterialInstance(JNIEnv* env, jclass,
jlong nativeProvider, jobject materialKey, jintArray uvmap, jstring label) {
jlong nativeProvider, jobject materialKey, jintArray uvmap, jstring label, jstring extras) {
MaterialKey nativeKey = {};
auto& helper = MaterialKeyHelper::get();
helper.copy(env, nativeKey, materialKey);
const char* nativeLabel = label ? env->GetStringUTFChars(label, nullptr) : nullptr;
const char* nativeExtras = extras ? env->GetStringUTFChars(extras, nullptr) : nullptr;
UvMap nativeUvMap = {};
auto provider = (MaterialProvider*) nativeProvider;
MaterialInstance* instance = provider->createMaterialInstance(&nativeKey, &nativeUvMap, nativeLabel);
MaterialInstance* instance = provider->createMaterialInstance(&nativeKey, &nativeUvMap,
nativeLabel, nativeExtras);
// Copy the UvMap results from the native array into the JVM array.
jint* elements = env->GetIntArrayElements(uvmap, nullptr);
@@ -77,9 +80,47 @@ Java_com_google_android_filament_gltfio_UbershaderProvider_nCreateMaterialInstan
env->ReleaseStringUTFChars(label, nativeLabel);
}
if (extras) {
env->ReleaseStringUTFChars(extras, nativeExtras);
}
return (long) instance;
}
extern "C" JNIEXPORT long JNICALL
Java_com_google_android_filament_gltfio_UbershaderProvider_nGetMaterial(JNIEnv* env, jclass,
jlong nativeProvider, jobject materialKey, jintArray uvmap, jstring label) {
MaterialKey nativeKey = {};
auto& helper = MaterialKeyHelper::get();
helper.copy(env, nativeKey, materialKey);
const char* nativeLabel = label ? env->GetStringUTFChars(label, nullptr) : nullptr;
UvMap nativeUvMap = {};
auto provider = (MaterialProvider*) nativeProvider;
Material* material = provider->getMaterial(&nativeKey, &nativeUvMap, nativeLabel);
// Copy the UvMap results from the native array into the JVM array.
jint* elements = env->GetIntArrayElements(uvmap, nullptr);
if (elements) {
const size_t javaSize = env->GetArrayLength(uvmap);
for (int i = 0, n = std::min(javaSize, nativeUvMap.size()); i < n; ++i) {
elements[i] = nativeUvMap[i];
}
env->ReleaseIntArrayElements(uvmap, elements, 0);
}
// The config parameter is an in-out parameter so we need to copy the results back to Java.
helper.copy(env, materialKey, nativeKey);
if (label) {
env->ReleaseStringUTFChars(label, nativeLabel);
}
return (long) material;
}
extern "C" JNIEXPORT int JNICALL
Java_com_google_android_filament_gltfio_UbershaderProvider_nGetMaterialCount(JNIEnv*, jclass,
jlong nativeProvider) {
@@ -160,7 +160,6 @@ public class AssetLoader {
* create/destroy churn, as noted above.
*
* This cannot be called after FilamentAsset#releaseSourceData().
* Animation is not supported in new instances.
* See also AssetLoader#createInstancedAsset().
*/
@Nullable
@@ -23,7 +23,6 @@ import androidx.annotation.Nullable;
import com.google.android.filament.Box;
import com.google.android.filament.Engine;
import com.google.android.filament.Entity;
import com.google.android.filament.MaterialInstance;
/**
* Owns a bundle of Filament objects that have been created by <code>AssetLoader</code>.
@@ -35,25 +34,32 @@ import com.google.android.filament.MaterialInstance;
* <code>NameComponentManager</code>, <code>RenderableManager</code>, and others.</p>
*
* <p>In addition to the aforementioned entities, an asset has strong ownership over a list of
* <code>VertexBuffer</code>, <code>IndexBuffer</code>, <code>MaterialInstance</code>, and
* <code>Texture</code>.</p>
* <code>VertexBuffer</code>, <code>IndexBuffer</code>, and <code>Texture</code>.</p>
*
* <p>Clients can use {@link ResourceLoader} to create textures, compute tangent quaternions, and
* upload data into vertex buffers and index buffers.</p>
*
* @see ResourceLoader
* @see Animator
* @see FilamentInstance
* @see AssetLoader
*/
public class FilamentAsset {
private long mNativeObject;
private Animator mAnimator;
private FilamentInstance mPrimaryInstance;
private Engine mEngine;
FilamentAsset(Engine engine, long nativeObject) {
mEngine = engine;
mNativeObject = nativeObject;
mAnimator = null;
}
public FilamentInstance getInstance() {
if (mPrimaryInstance != null) {
return mPrimaryInstance;
}
long nativeInstance = nGetInstance(getNativeObject());
mPrimaryInstance = new FilamentInstance(this, nativeInstance);
return mPrimaryInstance;
}
long getNativeObject() {
@@ -169,19 +175,11 @@ public class FilamentAsset {
return result;
}
public @NonNull MaterialInstance[] getMaterialInstances() {
final int count = nGetMaterialInstanceCount(mNativeObject);
MaterialInstance[] result = new MaterialInstance[count];
long[] natives = new long[count];
nGetMaterialInstances(mNativeObject, natives);
for (int i = 0; i < count; i++) {
result[i] = new MaterialInstance(mEngine, natives[i]);
}
return result;
}
/**
* Gets the bounding box computed from the supplied min / max values in glTF accessors.
*
* This does not return a bounding box over all FilamentInstance, it's just a straightforward
* AAAB that can be determined at load time from the asset data.
*/
public @NonNull Box getBoundingBox() {
float[] box = new float[6];
@@ -206,25 +204,6 @@ public class FilamentAsset {
return nGetExtras(mNativeObject, entity);
}
/**
* Retrieves the <code>Animator</code> interface for this asset.
*
* <p>When calling this for the first time, this must be called after
* {@link ResourceLoader#loadResources} or {@link ResourceLoader#asyncBeginLoad}. When the asset
* is destroyed, its animator becomes invalid.</p>
*/
public @NonNull Animator getAnimator() {
if (mAnimator != null) {
return mAnimator;
}
long nativeAnimator = nGetAnimator(getNativeObject());
if (nativeAnimator == 0) {
throw new IllegalStateException("Unable to create animator");
}
mAnimator = new Animator(nativeAnimator);
return mAnimator;
}
/**
* Gets the names of all morph targets in the given entity.
*/
@@ -243,31 +222,6 @@ public class FilamentAsset {
return uris;
}
/**
* Returns the names of all material variants.
*/
public @NonNull String[] getMaterialVariantNames() {
String[] names = new String[nGetMaterialVariantCount(mNativeObject)];
nGetMaterialVariantNames(mNativeObject, names);
return names;
}
/**
* Applies the given material variant to all primitives that it affects.
*
* This is efficient because it merely swaps around persistent MaterialInstances. If you change
* a material parameter while a certain variant is active, the updated value will be remembered
* after you re-apply that variant.
*
* If the asset is instanced, this affects all instances in the same way.
* To set the variant on an individual instance, use FilamentInstance#applyMaterialVariant.
*
* Ignored if variantIndex is out of bounds.
*/
public void applyMaterialVariant(@IntRange(from = 0) int variantIndex) {
nApplyMaterialVariant(getNativeObject(), variantIndex);
}
/**
* Reclaims CPU-side memory for URI strings, binding lists, and raw animation data.
*
@@ -279,8 +233,10 @@ public class FilamentAsset {
nReleaseSourceData(mNativeObject);
}
public Engine getEngine() { return mEngine; }
void clearNativeObject() {
if (mAnimator != null) mAnimator.clearNativeObject();
mPrimaryInstance = null;
mNativeObject = 0;
}
@@ -304,21 +260,17 @@ public class FilamentAsset {
private static native int nGetCameraEntityCount(long nativeAsset);
private static native void nGetCameraEntities(long nativeAsset, int[] result);
private static native int nGetMaterialInstanceCount(long nativeAsset);
private static native void nGetMaterialInstances(long nativeAsset, long[] nativeResults);
private static native int nGetMaterialVariantCount(long nativeAsset);
private static native void nGetMaterialVariantNames(long nativeAsset, String[] result);
private static native int nGetMorphTargetCount(long nativeAsset, int entity);
private static native void nGetMorphTargetNames(long nativeAsset, int entity, String[] result);
private static native void nGetBoundingBox(long nativeAsset, float[] box);
private static native String nGetName(long nativeAsset, int entity);
private static native String nGetExtras(long nativeAsset, int entity);
private static native long nGetAnimator(long nativeAsset);
private static native void nApplyMaterialVariant(long nativeAsset, int variantIndex);
private static native long nGetInstance(long nativeAsset);
private static native int nGetResourceUriCount(long nativeAsset);
private static native void nGetResourceUris(long nativeAsset, String[] result);
private static native void nReleaseSourceData(long nativeAsset);
}
@@ -19,7 +19,9 @@ package com.google.android.filament.gltfio;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import com.google.android.filament.Engine;
import com.google.android.filament.Entity;
import com.google.android.filament.MaterialInstance;
/**
* Provides access to a hierarchy of entities that have been instanced from a glTF asset.
@@ -146,11 +148,39 @@ public class FilamentInstance {
nApplyMaterialVariant(mNativeObject, variantIndex);
}
public @NonNull MaterialInstance[] getMaterialInstances() {
final int count = nGetMaterialInstanceCount(mNativeObject);
MaterialInstance[] result = new MaterialInstance[count];
long[] natives = new long[count];
nGetMaterialInstances(mNativeObject, natives);
Engine engine = mAsset.getEngine();
for (int i = 0; i < count; i++) {
result[i] = new MaterialInstance(engine, natives[i]);
}
return result;
}
/**
* Returns the names of all material variants.
*/
public @NonNull String[] getMaterialVariantNames() {
String[] names = new String[nGetMaterialVariantCount(mNativeObject)];
nGetMaterialVariantNames(mNativeObject, names);
return names;
}
private static native int nGetRoot(long nativeInstance);
private static native int nGetEntityCount(long nativeInstance);
private static native void nGetEntities(long nativeInstance, int[] result);
private static native long nGetAnimator(long nativeInstance);
private static native int nGetMaterialInstanceCount(long nativeAsset);
private static native void nGetMaterialInstances(long nativeAsset, long[] nativeResults);
private static native void nApplyMaterialVariant(long nativeInstance, int variantIndex);
private static native int nGetMaterialVariantCount(long nativeAsset);
private static native void nGetMaterialVariantNames(long nativeAsset, String[] result);
private static native void nGetJointsAt(long nativeInstance, int skinIndex, int[] result);
private static native int nGetSkinCount(long nativeInstance);
private static native void nGetSkinNames(long nativeInstance, String[] result);
@@ -108,6 +108,12 @@ public interface MaterialProvider {
public @Nullable MaterialInstance createMaterialInstance(MaterialKey config,
@NonNull @Size(min = 8) int[] uvmap, @Nullable String label, @Nullable String extras);
/**
* Creates or fetches a compiled Filament material corresponding to the given config.
*/
public @Nullable Material getMaterial(MaterialKey config, @NonNull @Size(min = 8) int[] uvmap,
@Nullable String label);
/**
* Creates and returns an array containing all cached materials.
*/
@@ -47,7 +47,7 @@ public class ResourceLoader {
*/
public ResourceLoader(@NonNull Engine engine) {
long nativeEngine = engine.getNativeObject();
mNativeObject = nCreateResourceLoader(nativeEngine, false, false, false);
mNativeObject = nCreateResourceLoader(nativeEngine, false);
mNativeStbProvider = nCreateStbProvider(nativeEngine);
mNativeKtx2Provider = nCreateKtx2Provider(nativeEngine);
nAddTextureProvider(mNativeObject, "image/jpeg", mNativeStbProvider);
@@ -60,16 +60,12 @@ public class ResourceLoader {
*
* @param engine the engine that gets passed to all builder methods
* @param normalizeSkinningWeights scale non-conformant skinning weights so they sum to 1
* @param recomputeBoundingBoxes use computed bounding boxes rather than the ones in the asset
* @param ignoreBindTransform ignore skinned primitives bind transform when compute bounding boxes
* @throws IllegalAccessException
* @throws InvocationTargetException
*/
public ResourceLoader(@NonNull Engine engine, boolean normalizeSkinningWeights,
boolean recomputeBoundingBoxes, boolean ignoreBindTransform) {
public ResourceLoader(@NonNull Engine engine, boolean normalizeSkinningWeights) {
long nativeEngine = engine.getNativeObject();
mNativeObject = nCreateResourceLoader(nativeEngine, normalizeSkinningWeights,
recomputeBoundingBoxes, ignoreBindTransform);
mNativeObject = nCreateResourceLoader(nativeEngine, normalizeSkinningWeights);
mNativeStbProvider = nCreateStbProvider(nativeEngine);
mNativeKtx2Provider = nCreateKtx2Provider(nativeEngine);
nAddTextureProvider(mNativeObject, "image/jpeg", mNativeStbProvider);
@@ -181,7 +177,7 @@ public class ResourceLoader {
}
private static native long nCreateResourceLoader(long nativeEngine,
boolean normalizeSkinningWeights, boolean recomputeBoundingBoxes, boolean ignoreBindTransform);
boolean normalizeSkinningWeights);
private static native void nDestroyResourceLoader(long nativeLoader);
private static native void nAddResourceData(long nativeLoader, String url, Buffer buffer,
int remaining);
@@ -57,10 +57,16 @@ public class UbershaderProvider implements MaterialProvider {
public @Nullable MaterialInstance createMaterialInstance(MaterialKey config,
@NonNull @Size(min = 8) int[] uvmap, @Nullable String label, @Nullable String extras) {
long nativeMaterialInstance = nCreateMaterialInstance(mNativeObject, config, uvmap, label);
long nativeMaterialInstance = nCreateMaterialInstance(mNativeObject, config, uvmap, label, extras);
return nativeMaterialInstance == 0 ? null : new MaterialInstance(null, nativeMaterialInstance);
}
public @Nullable Material getMaterial(MaterialKey config, @NonNull @Size(min = 8) int[] uvmap,
@Nullable String label) {
long nativeMaterial = nGetMaterial(mNativeObject, config, uvmap, label);
return nativeMaterial == 0 ? null : new Material(nativeMaterial);
}
public @NonNull Material[] getMaterials() {
final int count = nGetMaterialCount(mNativeObject);
Material[] result = new Material[count];
@@ -96,6 +102,8 @@ public class UbershaderProvider implements MaterialProvider {
private static native void nDestroyUbershaderProvider(long nativeProvider);
private static native void nDestroyMaterials(long nativeProvider);
private static native long nCreateMaterialInstance(long nativeProvider,
MaterialKey config, int[] uvmap, String label, String extras);
private static native long nGetMaterial(long nativeProvider,
MaterialKey config, int[] uvmap, String label);
private static native int nGetMaterialCount(long nativeProvider);
private static native void nGetMaterials(long nativeProvider, long[] result);
+1 -5
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.25.6
VERSION_NAME=1.28.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
@@ -19,10 +19,6 @@ org.gradle.jvmargs=-Xmx1536m
android.useAndroidX=true
org.gradle.unsafe.configuration-cache=true
# TODO: Remove this when we switch to Gradle 7.4
org.gradle.unsafe.configuration-cache.max-problems=3
com.google.android.filament.tools-dir=../../../out/release/filament
com.google.android.filament.dist-dir=../out/android-release/filament
com.google.android.filament.abis=all
+1 -1
View File
@@ -1,6 +1,6 @@
#Wed Nov 17 10:40:18 PST 2021
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+3 -3
View File
@@ -112,9 +112,9 @@ the Android SDK.
## Android Studio
You must use Android Studio 3.6 RC 1 or higher. To open the project, point Studio to the `android`
folder. After opening the project and syncing to gradle, select the sample of your choice using the
drop-down widget in the toolbar.
You must use the latest stable release of Android Studio. To open the project, point Studio to the
`android` folder. After opening the project and syncing to gradle, select the sample of your choice
using the drop-down widget in the toolbar.
## Compiling
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.filament.gltf">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
@@ -353,7 +353,7 @@ class MainActivity : Activity() {
fun loadSettings(message: RemoteServer.ReceivedMessage) {
val json = StandardCharsets.UTF_8.decode(message.buffer).toString()
viewerContent.assetLights = modelViewer.asset?.lightEntities
automation.applySettings(json, viewerContent)
automation.applySettings(modelViewer.engine, json, viewerContent)
modelViewer.view.colorGrading = automation.getColorGrading(modelViewer.engine)
modelViewer.cameraFocalLength = automation.viewerOptions.cameraFocalLength
updateRootTransform()
@@ -417,7 +417,7 @@ class MainActivity : Activity() {
// Just for testing purposes, this releases the current model and reloads the default model.
inner class DoubleTapListener : GestureDetector.SimpleOnGestureListener() {
override fun onDoubleTap(e: MotionEvent?): Boolean {
override fun onDoubleTap(e: MotionEvent): Boolean {
modelViewer.destroyModel()
createDefaultRenderables()
return super.onDoubleTap(e)
@@ -16,8 +16,7 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.filament.hellocam">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.CAMERA" />
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.filament.hellotriangle">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.filament.ibl">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.filament.litcube">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.filament.livewallpaper">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature android:name="android.software.live_wallpaper" />
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.filament.material_builder">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.filament.multiview">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.filament.pagecurl">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
@@ -16,8 +16,7 @@
limitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.filament.streamtest">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.filament.textureview">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.filament.textured">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.google.android.filament.transparentrendering">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
+6
View File
@@ -162,6 +162,8 @@ EGL_ON_LINUX_OPTION="-DFILAMENT_SUPPORTS_EGL_ON_LINUX=OFF"
MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=OFF"
MATDBG_GRADLE_OPTION=""
FGDBG_OPTION="-DFILAMENT_ENABLE_FGDBG=OFF"
IOS_BUILD_SIMULATOR=false
BUILD_UNIVERSAL_LIBRARIES=false
@@ -221,6 +223,7 @@ function build_desktop_target {
${SWIFTSHADER_OPTION} \
${EGL_ON_LINUX_OPTION} \
${MATDBG_OPTION} \
${FGDBG_OPTION} \
${deployment_target} \
${architectures} \
../..
@@ -347,6 +350,7 @@ function build_android_target {
-DCMAKE_INSTALL_PREFIX="../android-${lc_target}/filament" \
-DCMAKE_TOOLCHAIN_FILE="../../build/toolchain-${arch}-linux-android.cmake" \
${MATDBG_OPTION} \
${FGDBG_OPTION} \
${VULKAN_ANDROID_OPTION} \
../..
fi
@@ -576,6 +580,7 @@ function build_ios_target {
-DIOS=1 \
-DCMAKE_TOOLCHAIN_FILE=../../third_party/clang/iOS.cmake \
${MATDBG_OPTION} \
${FGDBG_OPTION} \
../..
fi
@@ -760,6 +765,7 @@ while getopts ":hacCfijmp:q:uvslwtedk:" opt; do
PRINT_MATDBG_HELP=true
MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=ON, -DFILAMENT_DISABLE_MATOPT=ON, -DFILAMENT_BUILD_FILAMAT=ON"
MATDBG_GRADLE_OPTION="-Pcom.google.android.filament.matdbg"
FGDBG_OPTION="-DFILAMENT_ENABLE_FGDBG=ON"
;;
f)
ISSUE_CMAKE_ALWAYS=true
+1 -1
View File
@@ -1 +1 @@
25.0.8775105
25.1.8937393
+7 -11
View File
@@ -1850,7 +1850,7 @@ Subsurface scattering is implemented using the wrapped diffuse lighting techniqu
</p><p>
$$\begin{equation}
f_{d}(v,h) = \frac{c_{diff}}{\pi}(1 - F(v,h)) \left&lt; \NoL + \frac{w}{(1 + w)^2} \right&gt; \left&lt; c_{subsurface} + \NoL \right&gt;
f_{d}(v,h) = \frac{c_{diff}}{\pi}(1 - F(v,h)) \left&lt; \frac{\NoL + w}{(1 + w)^2} \right&gt; \left&lt; c_{subsurface} + \NoL \right&gt;
\end{equation}$$
</p><p>
@@ -2301,7 +2301,7 @@ The term \( \lambda(l) \) in equations \( \ref{spotAbsorber} \) and \( \ref{spot
</p><p>
$$\begin{equation}\label{spotAngleAtt}
\lambda(l) = \frac{l \times spotDirection - cos\theta_{outer}}{cos\theta_{inner} - cos\theta_{outer}}
\lambda(l) = \frac{l \cdot spotDirection - cos\theta_{outer}}{cos\theta_{inner} - cos\theta_{outer}}
\end{equation}$$
</p>
@@ -5605,7 +5605,7 @@ However we only need the real bases:
$$\begin{align*}
y^{m &gt; 0}_l &amp;= \sqrt{2} K^m_l cos(m \phi) P^m_l(cos \theta) \\
y^{m &lt; 0}_l &amp;= \sqrt{2} K^m_l sin(m \phi) P^{|m|}_l(cos \theta) \\
y^{m &lt; 0}_l &amp;= \sqrt{2} K^m_l sin(|m| \phi) P^{|m|}_l(cos \theta) \\
y^0_l &amp;= K^0_l P^0_l(cos \theta)
\end{align*}$$
@@ -5652,8 +5652,8 @@ It’s also fairly easy to compute the trigonometric terms recursively:
</p><p>
$$\begin{align*}
C_m &amp;\equiv cos(m \phi) \\
S_m &amp;\equiv sin(m \phi) \\
C_m &amp;\equiv cos(m \phi)sin(\theta)^m \\
S_m &amp;\equiv sin(m \phi)sin(\theta)^m \\
\{ x, y, z \} &amp;= \{ cos \phi sin \theta, sin \phi sin \theta, cos \theta \}
\end{align*}$$
@@ -5664,16 +5664,12 @@ Using the angle sum trigonometric identities:
</p><p>
$$\begin{align*}
cos(m \phi + \phi) &amp;= cos(m \phi) cos(\phi) - sin(m \phi) sin(\phi) \Leftrightarrow C_{m + 1} = \frac{(x C_m - y S_m)}{sin(\theta)^{|m + 1|}} \\
sin(m \phi + \phi) &amp;= sin(m \phi) sin(\phi) + cos(m \phi) sin(\phi) \Leftrightarrow S_{m + 1} = \frac{(x S_m - y C_m)}{sin(\theta)^{|m + 1|}}
cos(m \phi + \phi) &amp;= cos(m \phi) cos(\phi) - sin(m \phi) sin(\phi) \Leftrightarrow C_{m + 1} = x C_m - y S_m \\
sin(m \phi + \phi) &amp;= sin(m \phi) cos(\phi) + cos(m \phi) sin(\phi) \Leftrightarrow S_{m + 1} = x S_m - y C_m
\end{align*}$$
</p><p>
The equations above have an extra term \(sin(\theta)^{-|m + 1|}\) but we can compensate for that in the \(P^{|m|}_l(z)\) recursion by multiplying \(P^l_l(z)\) by \(sin(\theta)^{|m + 1|}\) which greatly simplifies the third equation in \(\ref{shRecursions}\) because \(P^l_l(cos \theta) sin(\theta)^{-l} = (-1)^l(2l - 1)!!\).
</p><p>
<a href="#listing_nonnormalizedshbasis">Listing&nbsp;47</a> shows the C++ code to compute the non-normalized SH basis \(\frac{y^m_l(s)}{\sqrt{2} K^m_l}\):
</p><pre class="listing tilde"><code><span class="line"><span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">inline</span> <span class="hljs-keyword">size_t</span> <span class="hljs-title">SHindex</span><span class="hljs-params">(<span class="hljs-keyword">ssize_t</span> m, <span class="hljs-keyword">size_t</span> l)</span> </span>{</span>
+2 -2
View File
@@ -1143,7 +1143,7 @@ Where $F(v,h)$ is the Fresnel term of the cloth specular BRDF in equation $\ref{
Subsurface scattering is implemented using the wrapped diffuse lighting technique, in its energy conservative form:
$$\begin{equation}
f_{d}(v,h) = \frac{c_{diff}}{\pi}(1 - F(v,h)) \left< \NoL + \frac{w}{(1 + w)^2} \right> \left< c_{subsurface} + \NoL \right>
f_{d}(v,h) = \frac{c_{diff}}{\pi}(1 - F(v,h)) \left< \frac{\NoL + w}{(1 + w)^2} \right> \left< c_{subsurface} + \NoL \right>
\end{equation}$$
Where $w$ is a value between 0 and 1 defining by how much the diffuse light should wrap around the terminator. To avoid introducing another parameter, we fix $w = 0.5$. Note that with wrap diffuse lighting, the diffuse term must not be multiplied by $\NoL$. The effect of this cheap
@@ -1435,7 +1435,7 @@ The term $ \lambda(l) $ in equations $ \ref{spotAbsorber} $ and $ \ref{spotRefle
$ \ref{spotAngleAtt} $ below.
$$\begin{equation}\label{spotAngleAtt}
\lambda(l) = \frac{l \times spotDirection - cos\theta_{outer}}{cos\theta_{inner} - cos\theta_{outer}}
\lambda(l) = \frac{l \cdot spotDirection - cos\theta_{outer}}{cos\theta_{inner} - cos\theta_{outer}}
\end{equation}$$
#### Attenuation function
+118 -69
View File
@@ -86,37 +86,39 @@ counter-increment: h6;margin-right:10px}
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/format/example" class="level3"><span class="tocNumber">4.1.2&nbsp; </span>Example</a><br>
&nbsp;&nbsp;<a href="#materialdefinitions/materialblock" class="level2"><span class="tocNumber">4.2&nbsp; </span>Material block</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/general:name" class="level3"><span class="tocNumber">4.2.1&nbsp; </span>General: name</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/general:shadingmodel" class="level3"><span class="tocNumber">4.2.2&nbsp; </span>General: shadingModel</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/general:parameters" class="level3"><span class="tocNumber">4.2.3&nbsp; </span>General: parameters</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/general:variantfilter" class="level3"><span class="tocNumber">4.2.4&nbsp; </span>General: variantFilter</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/general:flipuv" class="level3"><span class="tocNumber">4.2.5&nbsp; </span>General: flipUV</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/general:quality" class="level3"><span class="tocNumber">4.2.6&nbsp; </span>General: quality</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/general:instanced" class="level3"><span class="tocNumber">4.2.7&nbsp; </span>General: instanced</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/vertexandattributes:requires" class="level3"><span class="tocNumber">4.2.8&nbsp; </span>Vertex and attributes: requires</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/vertexandattributes:variables" class="level3"><span class="tocNumber">4.2.9&nbsp; </span>Vertex and attributes: variables</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/vertexandattributes:vertexdomain" class="level3"><span class="tocNumber">4.2.10&nbsp; </span>Vertex and attributes: vertexDomain</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/vertexandattributes:interpolation" class="level3"><span class="tocNumber">4.2.11&nbsp; </span>Vertex and attributes: interpolation</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/blendingandtransparency:blending" class="level3"><span class="tocNumber">4.2.12&nbsp; </span>Blending and transparency: blending</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/blendingandtransparency:postlightingblending" class="level3"><span class="tocNumber">4.2.13&nbsp; </span>Blending and transparency: postLightingBlending</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/blendingandtransparency:transparency" class="level3"><span class="tocNumber">4.2.14&nbsp; </span>Blending and transparency: transparency</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/blendingandtransparency:maskthreshold" class="level3"><span class="tocNumber">4.2.15&nbsp; </span>Blending and transparency: maskThreshold</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/blendingandtransparency:refractionmode" class="level3"><span class="tocNumber">4.2.16&nbsp; </span>Blending and transparency: refractionMode</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/blendingandtransparency:refractiontype" class="level3"><span class="tocNumber">4.2.17&nbsp; </span>Blending and transparency: refractionType</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/rasterization:culling" class="level3"><span class="tocNumber">4.2.18&nbsp; </span>Rasterization: culling</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/rasterization:colorwrite" class="level3"><span class="tocNumber">4.2.19&nbsp; </span>Rasterization: colorWrite</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/rasterization:depthwrite" class="level3"><span class="tocNumber">4.2.20&nbsp; </span>Rasterization: depthWrite</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/rasterization:depthculling" class="level3"><span class="tocNumber">4.2.21&nbsp; </span>Rasterization: depthCulling</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/rasterization:doublesided" class="level3"><span class="tocNumber">4.2.22&nbsp; </span>Rasterization: doubleSided</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/lighting:reflections" class="level3"><span class="tocNumber">4.2.23&nbsp; </span>Lighting: reflections</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/lighting:shadowmultiplier" class="level3"><span class="tocNumber">4.2.24&nbsp; </span>Lighting: shadowMultiplier</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/lighting:transparentshadow" class="level3"><span class="tocNumber">4.2.25&nbsp; </span>Lighting: transparentShadow</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/lighting:clearcoatiorchange" class="level3"><span class="tocNumber">4.2.26&nbsp; </span>Lighting: clearCoatIorChange</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/lighting:multibounceambientocclusion" class="level3"><span class="tocNumber">4.2.27&nbsp; </span>Lighting: multiBounceAmbientOcclusion</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/lighting:specularambientocclusion" class="level3"><span class="tocNumber">4.2.28&nbsp; </span>Lighting: specularAmbientOcclusion</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/anti-aliasing:specularantialiasing" class="level3"><span class="tocNumber">4.2.29&nbsp; </span>Anti-aliasing: specularAntiAliasing</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/anti-aliasing:specularantialiasingvariance" class="level3"><span class="tocNumber">4.2.30&nbsp; </span>Anti-aliasing: specularAntiAliasingVariance</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/anti-aliasing:specularantialiasingthreshold" class="level3"><span class="tocNumber">4.2.31&nbsp; </span>Anti-aliasing: specularAntiAliasingThreshold</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/shading:customsurfaceshading" class="level3"><span class="tocNumber">4.2.32&nbsp; </span>Shading: customSurfaceShading</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/general:featurelevel" class="level3"><span class="tocNumber">4.2.2&nbsp; </span>General: featureLevel</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/general:shadingmodel" class="level3"><span class="tocNumber">4.2.3&nbsp; </span>General: shadingModel</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/general:parameters" class="level3"><span class="tocNumber">4.2.4&nbsp; </span>General: parameters</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/general:variantfilter" class="level3"><span class="tocNumber">4.2.5&nbsp; </span>General: variantFilter</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/general:flipuv" class="level3"><span class="tocNumber">4.2.6&nbsp; </span>General: flipUV</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/general:quality" class="level3"><span class="tocNumber">4.2.7&nbsp; </span>General: quality</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/general:instanced" class="level3"><span class="tocNumber">4.2.8&nbsp; </span>General: instanced</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/general:vertexdomaindevicejittered" class="level3"><span class="tocNumber">4.2.9&nbsp; </span>General: vertexDomainDeviceJittered</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/vertexandattributes:requires" class="level3"><span class="tocNumber">4.2.10&nbsp; </span>Vertex and attributes: requires</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/vertexandattributes:variables" class="level3"><span class="tocNumber">4.2.11&nbsp; </span>Vertex and attributes: variables</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/vertexandattributes:vertexdomain" class="level3"><span class="tocNumber">4.2.12&nbsp; </span>Vertex and attributes: vertexDomain</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/vertexandattributes:interpolation" class="level3"><span class="tocNumber">4.2.13&nbsp; </span>Vertex and attributes: interpolation</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/blendingandtransparency:blending" class="level3"><span class="tocNumber">4.2.14&nbsp; </span>Blending and transparency: blending</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/blendingandtransparency:postlightingblending" class="level3"><span class="tocNumber">4.2.15&nbsp; </span>Blending and transparency: postLightingBlending</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/blendingandtransparency:transparency" class="level3"><span class="tocNumber">4.2.16&nbsp; </span>Blending and transparency: transparency</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/blendingandtransparency:maskthreshold" class="level3"><span class="tocNumber">4.2.17&nbsp; </span>Blending and transparency: maskThreshold</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/blendingandtransparency:refractionmode" class="level3"><span class="tocNumber">4.2.18&nbsp; </span>Blending and transparency: refractionMode</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/blendingandtransparency:refractiontype" class="level3"><span class="tocNumber">4.2.19&nbsp; </span>Blending and transparency: refractionType</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/rasterization:culling" class="level3"><span class="tocNumber">4.2.20&nbsp; </span>Rasterization: culling</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/rasterization:colorwrite" class="level3"><span class="tocNumber">4.2.21&nbsp; </span>Rasterization: colorWrite</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/rasterization:depthwrite" class="level3"><span class="tocNumber">4.2.22&nbsp; </span>Rasterization: depthWrite</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/rasterization:depthculling" class="level3"><span class="tocNumber">4.2.23&nbsp; </span>Rasterization: depthCulling</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/rasterization:doublesided" class="level3"><span class="tocNumber">4.2.24&nbsp; </span>Rasterization: doubleSided</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/lighting:reflections" class="level3"><span class="tocNumber">4.2.25&nbsp; </span>Lighting: reflections</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/lighting:shadowmultiplier" class="level3"><span class="tocNumber">4.2.26&nbsp; </span>Lighting: shadowMultiplier</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/lighting:transparentshadow" class="level3"><span class="tocNumber">4.2.27&nbsp; </span>Lighting: transparentShadow</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/lighting:clearcoatiorchange" class="level3"><span class="tocNumber">4.2.28&nbsp; </span>Lighting: clearCoatIorChange</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/lighting:multibounceambientocclusion" class="level3"><span class="tocNumber">4.2.29&nbsp; </span>Lighting: multiBounceAmbientOcclusion</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/lighting:specularambientocclusion" class="level3"><span class="tocNumber">4.2.30&nbsp; </span>Lighting: specularAmbientOcclusion</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/anti-aliasing:specularantialiasing" class="level3"><span class="tocNumber">4.2.31&nbsp; </span>Anti-aliasing: specularAntiAliasing</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/anti-aliasing:specularantialiasingvariance" class="level3"><span class="tocNumber">4.2.32&nbsp; </span>Anti-aliasing: specularAntiAliasingVariance</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/anti-aliasing:specularantialiasingthreshold" class="level3"><span class="tocNumber">4.2.33&nbsp; </span>Anti-aliasing: specularAntiAliasingThreshold</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/materialblock/shading:customsurfaceshading" class="level3"><span class="tocNumber">4.2.34&nbsp; </span>Shading: customSurfaceShading</a><br>
&nbsp;&nbsp;<a href="#materialdefinitions/vertexblock" class="level2"><span class="tocNumber">4.3&nbsp; </span>Vertex block</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/vertexblock/materialvertexinputs" class="level3"><span class="tocNumber">4.3.1&nbsp; </span>Material vertex inputs</a><br>
&nbsp;&nbsp;&nbsp;&nbsp;<a href="#materialdefinitions/vertexblock/customvertexattributes" class="level3"><span class="tocNumber">4.3.2&nbsp; </span>Custom vertex attributes</a><br>
@@ -776,7 +778,7 @@ and with (right)</span></center></div></center>
The <code>bentNormal</code> property defines the average unoccluded direction at a point on the surface. It is
used to improve the accuracy of indirect lighting. Bent normals can also improve the quality of
specular ambient occlusion (see section <a href="#toc4.2.28">4.2.28</a> about
specular ambient occlusion (see section <a href="#toc4.2.30">4.2.30</a> about
<code>specularAmbientOcclusion</code>).
</p><p>
@@ -891,7 +893,7 @@ light to bend further away from the initial path.
<p></p><p>
The appearance of a refractive material will greatly depend on the <code>refractionType</code> and
<code>refractionMode</code> settings of the material. Refer to section <a href="#toc4.2.17">4.2.17</a> and section <a href="#toc4.2.16">4.2.16</a>
<code>refractionMode</code> settings of the material. Refer to section <a href="#toc4.2.19">4.2.19</a> and section <a href="#toc4.2.18">4.2.18</a>
for more information.
</p><p>
@@ -1424,7 +1426,35 @@ non-shader data.
<span class="line">material {</span>
<span class="line"> name : <span class="hljs-string">"Wet pavement"</span></span>
<span class="line">}</span></code></pre>
<a class="target" name="general:shadingmodel">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/general:shadingmodel">&nbsp;</a><a class="target" name="toc4.2.2">&nbsp;</a><h3>General: shadingModel</h3>
<a class="target" name="general:featurelevel">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/general:featurelevel">&nbsp;</a><a class="target" name="toc4.2.2">&nbsp;</a><h3>General: featureLevel</h3>
<p>
</p><dl><table><tbody><tr valign="top"><td><dt>Type</dt></td><td><dd><p> <code>number</code>
</p></dd></td></tr><tr valign="top"><td><dt>Value</dt></td><td><dd><p> An integer value, either 1 or 2. Defaults to 1.
</p></dd></td></tr></tbody></table></dl><div class="table">
<table class="table"><tbody><tr><th style="text-align:left"> Feature Level </th><th style="text-align:left"> Guaranteed features </th></tr>
<tr><td style="text-align:left"> 1 </td><td style="text-align:left"> 9 textures per material </td></tr>
<tr><td style="text-align:left"> 2 </td><td style="text-align:left"> 12 textures per material, cubemap arrays, ESSL 3.10 </td></tr>
</tbody></table><center><div class="tablecaption"><a class="target" name="table_featurelevels">&nbsp;</a><b style="font-style:normal;">Table&nbsp;13:</b> Feature levels</div></center></div>
<p></p><p>
</p><dl><dt>Description</dt><dd><p> Sets the feature level of the material. Each feature level defines a set of features the
material can use. If the material uses a feature not supported by the selected level, <code>matc</code>
will generate an error during compilation. A given feature level is guaranteed to support
all features of lower feature levels.
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> featureLevel : <span class="hljs-number">2</span></span>
<span class="line">}</span></code></pre><p>
</p><dl><table><tbody><tr valign="top"><td><dt>Bugs</dt></td><td><dd><p> <code>matc</code> doesn't verify that a material is not using features above its selected feature level.
</p></dd></td></tr></tbody></table></dl><p></p>
<a class="target" name="general:shadingmodel">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/general:shadingmodel">&nbsp;</a><a class="target" name="toc4.2.3">&nbsp;</a><h3>General: shadingModel</h3>
<p>
@@ -1441,7 +1471,7 @@ non-shader data.
<span class="line">material {</span>
<span class="line"> shadingModel : <span class="hljs-string">"subsurface"</span></span>
<span class="line">}</span></code></pre>
<a class="target" name="general:parameters">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/general:parameters">&nbsp;</a><a class="target" name="toc4.2.3">&nbsp;</a><h3>General: parameters</h3>
<a class="target" name="general:parameters">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/general:parameters">&nbsp;</a><a class="target" name="toc4.2.4">&nbsp;</a><h3>General: parameters</h3>
<p>
@@ -1451,7 +1481,7 @@ non-shader data.
name must be a valid GLSL identifier. Entries also 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">table&nbsp;13</a>.
<a href="#table_materialparamstypes">table&nbsp;14</a>.
</p></dd></dl><div class="table">
<table class="table"><tbody><tr><th style="text-align:left"> Type </th><th style="text-align:left"> Description </th></tr>
@@ -1477,7 +1507,7 @@ non-shader data.
<tr><td style="text-align:left"> sampler2dArray </td><td style="text-align:left"> Array of 2D textures </td></tr>
<tr><td style="text-align:left"> samplerExternal </td><td style="text-align:left"> External texture (platform-specific) </td></tr>
<tr><td style="text-align:left"> samplerCubemap </td><td style="text-align:left"> Cubemap texture </td></tr>
</tbody></table><center><div class="tablecaption"><a class="target" name="table_materialparamstypes">&nbsp;</a><b style="font-style:normal;">Table&nbsp;13:</b> Material parameter types</div></center></div>
</tbody></table><center><div class="tablecaption"><a class="target" name="table_materialparamstypes">&nbsp;</a><b style="font-style:normal;">Table&nbsp;14:</b> Material parameter types</div></center></div>
<p></p><p>
@@ -1533,7 +1563,7 @@ non-shader data.
<span class="line"> material.reflectance = materialParams.metallicReflectance.y;</span>
<span class="line"> }</span>
<span class="line">}</span></code></pre>
<a class="target" name="general:variantfilter">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/general:variantfilter">&nbsp;</a><a class="target" name="toc4.2.4">&nbsp;</a><h3>General: variantFilter</h3>
<a class="target" name="general:variantfilter">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/general:variantfilter">&nbsp;</a><a class="target" name="toc4.2.5">&nbsp;</a><h3>General: variantFilter</h3>
<p>
@@ -1575,7 +1605,7 @@ non-shader data.
<span class="line"> blending : transparent,</span>
<span class="line"> variantFilter : [ skinning ]</span>
<span class="line">}</span></code></pre>
<a class="target" name="general:flipuv">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/general:flipuv">&nbsp;</a><a class="target" name="toc4.2.5">&nbsp;</a><h3>General: flipUV</h3>
<a class="target" name="general:flipuv">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/general:flipuv">&nbsp;</a><a class="target" name="toc4.2.6">&nbsp;</a><h3>General: flipUV</h3>
<p>
@@ -1590,7 +1620,7 @@ non-shader data.
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> flipUV : <span class="hljs-literal">false</span></span>
<span class="line">}</span></code></pre>
<a class="target" name="general:quality">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/general:quality">&nbsp;</a><a class="target" name="toc4.2.6">&nbsp;</a><h3>General: quality</h3>
<a class="target" name="general:quality">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/general:quality">&nbsp;</a><a class="target" name="toc4.2.7">&nbsp;</a><h3>General: quality</h3>
<p>
@@ -1606,7 +1636,7 @@ non-shader data.
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> quality : default</span>
<span class="line">}</span></code></pre>
<a class="target" name="general:instanced">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/general:instanced">&nbsp;</a><a class="target" name="toc4.2.7">&nbsp;</a><h3>General: instanced</h3>
<a class="target" name="general:instanced">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/general:instanced">&nbsp;</a><a class="target" name="toc4.2.8">&nbsp;</a><h3>General: instanced</h3>
<p>
@@ -1623,7 +1653,26 @@ non-shader data.
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> instanced : <span class="hljs-literal">true</span></span>
<span class="line">}</span></code></pre>
<a class="target" name="vertexandattributes:requires">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/vertexandattributes:requires">&nbsp;</a><a class="target" name="toc4.2.8">&nbsp;</a><h3>Vertex and attributes: requires</h3>
<a class="target" name="general:vertexdomaindevicejittered">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/general:vertexdomaindevicejittered">&nbsp;</a><a class="target" name="toc4.2.9">&nbsp;</a><h3>General: vertexDomainDeviceJittered</h3>
<p>
</p><dl><dt>Type</dt><dd><p> <code>boolean</code>
</p></dd><dt>Value</dt><dd><p> <code>true</code> or <code>false</code>. Defaults to <code>false</code>.
</p></dd><dt>Description</dt><dd><p> Only meaningful for <code>vertexDomain:Device</code> materials, this parameter specifies whether the
filament clip-space transforms need to be applied or not, which affects TAA and guard bands.
Generally it needs to be applied because by definition <code>vertexDomain:Device</code> materials
vertices are not transformed and used <em class="asterisk">as is</em>.
However, if the vertex shader uses for instance <code>getViewFromClipMatrix()</code> (or other
matrices based on the projection), the clip-space transform is already applied.
Setting this parameter incorrectly can prevent TAA or the guard bands to work correctly.
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> vertexDomainDeviceJittered : <span class="hljs-literal">true</span></span>
<span class="line">}</span></code></pre>
<a class="target" name="vertexandattributes:requires">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/vertexandattributes:requires">&nbsp;</a><a class="target" name="toc4.2.10">&nbsp;</a><h3>Vertex and attributes: requires</h3>
<p>
@@ -1658,7 +1707,7 @@ non-shader data.
<span class="line"> material.baseColor.rgb *= getCustom0().rgb;</span>
<span class="line"> }</span>
<span class="line">}</span></code></pre>
<a class="target" name="vertexandattributes:variables">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/vertexandattributes:variables">&nbsp;</a><a class="target" name="toc4.2.9">&nbsp;</a><h3>Vertex and attributes: variables</h3>
<a class="target" name="vertexandattributes:variables">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/vertexandattributes:variables">&nbsp;</a><a class="target" name="toc4.2.11">&nbsp;</a><h3>Vertex and attributes: variables</h3>
<p>
@@ -1705,7 +1754,7 @@ non-shader data.
<span class="line"> material.eyeDirection.xyz = mulMat3x3Float3(getWorldFromViewMatrix(), u);</span>
<span class="line"> }</span>
<span class="line">}</span></code></pre>
<a class="target" name="vertexandattributes:vertexdomain">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/vertexandattributes:vertexdomain">&nbsp;</a><a class="target" name="toc4.2.10">&nbsp;</a><h3>Vertex and attributes: vertexDomain</h3>
<a class="target" name="vertexandattributes:vertexdomain">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/vertexandattributes:vertexdomain">&nbsp;</a><a class="target" name="toc4.2.12">&nbsp;</a><h3>Vertex and attributes: vertexDomain</h3>
<p>
@@ -1734,7 +1783,7 @@ non-shader data.
<p></p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> vertexDomain : device</span>
<span class="line">}</span></code></pre>
<a class="target" name="vertexandattributes:interpolation">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/vertexandattributes:interpolation">&nbsp;</a><a class="target" name="toc4.2.11">&nbsp;</a><h3>Vertex and attributes: interpolation</h3>
<a class="target" name="vertexandattributes:interpolation">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/vertexandattributes:interpolation">&nbsp;</a><a class="target" name="toc4.2.13">&nbsp;</a><h3>Vertex and attributes: interpolation</h3>
<p>
@@ -1750,7 +1799,7 @@ non-shader data.
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> interpolation : flat</span>
<span class="line">}</span></code></pre>
<a class="target" name="blendingandtransparency:blending">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:blending">&nbsp;</a><a class="target" name="toc4.2.12">&nbsp;</a><h3>Blending and transparency: blending</h3>
<a class="target" name="blendingandtransparency:blending">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:blending">&nbsp;</a><a class="target" name="toc4.2.14">&nbsp;</a><h3>Blending and transparency: blending</h3>
<p>
@@ -1791,7 +1840,7 @@ non-shader data.
<p></p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> blending : transparent</span>
<span class="line">}</span></code></pre>
<a class="target" name="blendingandtransparency:postlightingblending">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:postlightingblending">&nbsp;</a><a class="target" name="toc4.2.13">&nbsp;</a><h3>Blending and transparency: postLightingBlending</h3>
<a class="target" name="blendingandtransparency:postlightingblending">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:postlightingblending">&nbsp;</a><a class="target" name="toc4.2.15">&nbsp;</a><h3>Blending and transparency: postLightingBlending</h3>
<p>
@@ -1821,7 +1870,7 @@ non-shader data.
<p></p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> postLightingBlending : add</span>
<span class="line">}</span></code></pre>
<a class="target" name="blendingandtransparency:transparency">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:transparency">&nbsp;</a><a class="target" name="toc4.2.14">&nbsp;</a><h3>Blending and transparency: transparency</h3>
<a class="target" name="blendingandtransparency:transparency">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:transparency">&nbsp;</a><a class="target" name="toc4.2.16">&nbsp;</a><h3>Blending and transparency: transparency</h3>
<p>
@@ -1868,7 +1917,7 @@ and correctly sorted</span></center></div></center>
and sorting issues are minimized or eliminated</span></center></div></center>
<p></p>
<a class="target" name="blendingandtransparency:maskthreshold">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:maskthreshold">&nbsp;</a><a class="target" name="toc4.2.15">&nbsp;</a><h3>Blending and transparency: maskThreshold</h3>
<a class="target" name="blendingandtransparency:maskthreshold">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:maskthreshold">&nbsp;</a><a class="target" name="toc4.2.17">&nbsp;</a><h3>Blending and transparency: maskThreshold</h3>
<p>
@@ -1885,7 +1934,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
<span class="line"> blending : masked,</span>
<span class="line"> maskThreshold : <span class="hljs-number">0.5</span></span>
<span class="line">}</span></code></pre>
<a class="target" name="blendingandtransparency:refractionmode">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:refractionmode">&nbsp;</a><a class="target" name="toc4.2.16">&nbsp;</a><h3>Blending and transparency: refractionMode</h3>
<a class="target" name="blendingandtransparency:refractionmode">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:refractionmode">&nbsp;</a><a class="target" name="toc4.2.18">&nbsp;</a><h3>Blending and transparency: refractionMode</h3>
<p>
@@ -1906,7 +1955,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> refractionMode : cubemap,</span>
<span class="line">}</span></code></pre>
<a class="target" name="blendingandtransparency:refractiontype">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:refractiontype">&nbsp;</a><a class="target" name="toc4.2.17">&nbsp;</a><h3>Blending and transparency: refractionType</h3>
<a class="target" name="blendingandtransparency:refractiontype">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/blendingandtransparency:refractiontype">&nbsp;</a><a class="target" name="toc4.2.19">&nbsp;</a><h3>Blending and transparency: refractionType</h3>
<p>
@@ -1925,7 +1974,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
<span class="line"> refractionMode : cubemap,</span>
<span class="line"> refractionType : thin,</span>
<span class="line">}</span></code></pre>
<a class="target" name="rasterization:culling">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/rasterization:culling">&nbsp;</a><a class="target" name="toc4.2.18">&nbsp;</a><h3>Rasterization: culling</h3>
<a class="target" name="rasterization:culling">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/rasterization:culling">&nbsp;</a><a class="target" name="toc4.2.20">&nbsp;</a><h3>Rasterization: culling</h3>
<p>
@@ -1939,7 +1988,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
</p></dd></td></tr></tbody></table></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> culling : none</span>
<span class="line">}</span></code></pre>
<a class="target" name="rasterization:colorwrite">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/rasterization:colorwrite">&nbsp;</a><a class="target" name="toc4.2.19">&nbsp;</a><h3>Rasterization: colorWrite</h3>
<a class="target" name="rasterization:colorwrite">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/rasterization:colorwrite">&nbsp;</a><a class="target" name="toc4.2.21">&nbsp;</a><h3>Rasterization: colorWrite</h3>
<p>
@@ -1952,7 +2001,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
</p></dd></td></tr></tbody></table></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> colorWrite : <span class="hljs-literal">false</span></span>
<span class="line">}</span></code></pre>
<a class="target" name="rasterization:depthwrite">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/rasterization:depthwrite">&nbsp;</a><a class="target" name="toc4.2.20">&nbsp;</a><h3>Rasterization: depthWrite</h3>
<a class="target" name="rasterization:depthwrite">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/rasterization:depthwrite">&nbsp;</a><a class="target" name="toc4.2.22">&nbsp;</a><h3>Rasterization: depthWrite</h3>
<p>
@@ -1965,7 +2014,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
</p></dd></td></tr></tbody></table></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> depthWrite : <span class="hljs-literal">false</span></span>
<span class="line">}</span></code></pre>
<a class="target" name="rasterization:depthculling">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/rasterization:depthculling">&nbsp;</a><a class="target" name="toc4.2.21">&nbsp;</a><h3>Rasterization: depthCulling</h3>
<a class="target" name="rasterization:depthculling">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/rasterization:depthculling">&nbsp;</a><a class="target" name="toc4.2.23">&nbsp;</a><h3>Rasterization: depthCulling</h3>
<p>
@@ -1979,7 +2028,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> depthCulling : <span class="hljs-literal">false</span></span>
<span class="line">}</span></code></pre>
<a class="target" name="rasterization:doublesided">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/rasterization:doublesided">&nbsp;</a><a class="target" name="toc4.2.22">&nbsp;</a><h3>Rasterization: doubleSided</h3>
<a class="target" name="rasterization:doublesided">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/rasterization:doublesided">&nbsp;</a><a class="target" name="toc4.2.24">&nbsp;</a><h3>Rasterization: doubleSided</h3>
<p>
@@ -2006,7 +2055,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
<span class="line"> material.baseColor = vec4(0.0, 0.0, 0.0, 0.7);</span>
<span class="line"> }</span>
<span class="line">}</span></code></pre>
<a class="target" name="lighting:reflections">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/lighting:reflections">&nbsp;</a><a class="target" name="toc4.2.23">&nbsp;</a><h3>Lighting: reflections</h3>
<a class="target" name="lighting:reflections">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/lighting:reflections">&nbsp;</a><a class="target" name="toc4.2.25">&nbsp;</a><h3>Lighting: reflections</h3>
<p>
@@ -2023,7 +2072,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
<span class="line"> name : <span class="hljs-string">"Glossy metal"</span>,</span>
<span class="line"> reflections : screenspace</span>
<span class="line">}</span></code></pre>
<a class="target" name="lighting:shadowmultiplier">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/lighting:shadowmultiplier">&nbsp;</a><a class="target" name="toc4.2.24">&nbsp;</a><h3>Lighting: shadowMultiplier</h3>
<a class="target" name="lighting:shadowmultiplier">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/lighting:shadowmultiplier">&nbsp;</a><a class="target" name="toc4.2.26">&nbsp;</a><h3>Lighting: shadowMultiplier</h3>
<p>
@@ -2050,7 +2099,7 @@ and sorting issues are minimized or eliminated</span></center></div></center>
<span class="line"> material.baseColor = vec4(0.0, 0.0, 0.0, 0.7);</span>
<span class="line"> }</span>
<span class="line">}</span></code></pre>
<a class="target" name="lighting:transparentshadow">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/lighting:transparentshadow">&nbsp;</a><a class="target" name="toc4.2.25">&nbsp;</a><h3>Lighting: transparentShadow</h3>
<a class="target" name="lighting:transparentshadow">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/lighting:transparentshadow">&nbsp;</a><a class="target" name="toc4.2.27">&nbsp;</a><h3>Lighting: transparentShadow</h3>
<p>
@@ -2084,7 +2133,7 @@ radius of 4. Model <a href="https://sketchfab.com/3d-models/bottle-of-water-48fd
by <a href="https://sketchfab.com/person-x">T-Art</a>.</span></center></div></center>
<p></p>
<a class="target" name="lighting:clearcoatiorchange">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/lighting:clearcoatiorchange">&nbsp;</a><a class="target" name="toc4.2.26">&nbsp;</a><h3>Lighting: clearCoatIorChange</h3>
<a class="target" name="lighting:clearcoatiorchange">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/lighting:clearcoatiorchange">&nbsp;</a><a class="target" name="toc4.2.28">&nbsp;</a><h3>Lighting: clearCoatIorChange</h3>
<p>
@@ -2106,7 +2155,7 @@ with <code>clearCoatIorChange</code> enabled (left) and disabled
(right).</span></center></div></center>
<p></p>
<a class="target" name="lighting:multibounceambientocclusion">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/lighting:multibounceambientocclusion">&nbsp;</a><a class="target" name="toc4.2.27">&nbsp;</a><h3>Lighting: multiBounceAmbientOcclusion</h3>
<a class="target" name="lighting:multibounceambientocclusion">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/lighting:multibounceambientocclusion">&nbsp;</a><a class="target" name="toc4.2.29">&nbsp;</a><h3>Lighting: multiBounceAmbientOcclusion</h3>
<p>
@@ -2135,7 +2184,7 @@ occclusion enabled (left) and disabled (right).</span></center></div></center>
occclusion enabled and disabled.</span></center></div></center>
<p></p>
<a class="target" name="lighting:specularambientocclusion">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/lighting:specularambientocclusion">&nbsp;</a><a class="target" name="toc4.2.28">&nbsp;</a><h3>Lighting: specularAmbientOcclusion</h3>
<a class="target" name="lighting:specularambientocclusion">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/lighting:specularambientocclusion">&nbsp;</a><a class="target" name="toc4.2.30">&nbsp;</a><h3>Lighting: specularAmbientOcclusion</h3>
<p>
@@ -2161,7 +2210,7 @@ occclusion enabled and disabled.</span></center></div></center>
particularly visible under the hose.</span></center></div></center>
<p></p>
<a class="target" name="anti-aliasing:specularantialiasing">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/anti-aliasing:specularantialiasing">&nbsp;</a><a class="target" name="toc4.2.29">&nbsp;</a><h3>Anti-aliasing: specularAntiAliasing</h3>
<a class="target" name="anti-aliasing:specularantialiasing">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/anti-aliasing:specularantialiasing">&nbsp;</a><a class="target" name="toc4.2.31">&nbsp;</a><h3>Anti-aliasing: specularAntiAliasing</h3>
<p>
@@ -2178,7 +2227,7 @@ particularly visible under the hose.</span></center></div></center>
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> specularAntiAliasing : <span class="hljs-literal">true</span></span>
<span class="line">}</span></code></pre>
<a class="target" name="anti-aliasing:specularantialiasingvariance">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/anti-aliasing:specularantialiasingvariance">&nbsp;</a><a class="target" name="toc4.2.30">&nbsp;</a><h3>Anti-aliasing: specularAntiAliasingVariance</h3>
<a class="target" name="anti-aliasing:specularantialiasingvariance">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/anti-aliasing:specularantialiasingvariance">&nbsp;</a><a class="target" name="toc4.2.32">&nbsp;</a><h3>Anti-aliasing: specularAntiAliasingVariance</h3>
<p>
@@ -2193,7 +2242,7 @@ particularly visible under the hose.</span></center></div></center>
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> specularAntiAliasingVariance : <span class="hljs-number">0.2</span></span>
<span class="line">}</span></code></pre>
<a class="target" name="anti-aliasing:specularantialiasingthreshold">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/anti-aliasing:specularantialiasingthreshold">&nbsp;</a><a class="target" name="toc4.2.31">&nbsp;</a><h3>Anti-aliasing: specularAntiAliasingThreshold</h3>
<a class="target" name="anti-aliasing:specularantialiasingthreshold">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/anti-aliasing:specularantialiasingthreshold">&nbsp;</a><a class="target" name="toc4.2.33">&nbsp;</a><h3>Anti-aliasing: specularAntiAliasingThreshold</h3>
<p>
@@ -2207,7 +2256,7 @@ particularly visible under the hose.</span></center></div></center>
</p></dd></dl><p></p><pre class="listing tilde"><code><span class="line">material {</span>
<span class="line"> specularAntiAliasingThreshold : <span class="hljs-number">0.1</span></span>
<span class="line">}</span></code></pre>
<a class="target" name="shading:customsurfaceshading">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/shading:customsurfaceshading">&nbsp;</a><a class="target" name="toc4.2.32">&nbsp;</a><h3>Shading: customSurfaceShading</h3>
<a class="target" name="shading:customsurfaceshading">&nbsp;</a><a class="target" name="materialdefinitions/materialblock/shading:customsurfaceshading">&nbsp;</a><a class="target" name="toc4.2.34">&nbsp;</a><h3>Shading: customSurfaceShading</h3>
<p>
@@ -2271,7 +2320,7 @@ APIs listed in the <a href="#shaderpublicapis">Shader public APIs</a> section.
<span class="line"> float3 worldNormal; <span class="hljs-comment">// only if the shading model is not unlit</span></span>
<span class="line"> float4 worldPosition; <span class="hljs-comment">// always available (see note below about world-space)</span></span>
<span class="line"></span>
<span class="line"> <span class="hljs-type">mat4</span> clipSpaceTransform; <span class="hljs-comment">// default: identity, transforms the clip-space position</span></span>
<span class="line"> <span class="hljs-type">mat4</span> clipSpaceTransform; <span class="hljs-comment">// default: identity, transforms the clip-space position, only available for `vertexDomain:device`</span></span>
<span class="line"></span>
<span class="line"> <span class="hljs-comment">// variable* names are replaced with actual names</span></span>
<span class="line"> float4 variable0; <span class="hljs-comment">// if 1 or more variables is defined</span></span>
@@ -2766,7 +2815,7 @@ source material definition file.
<p>
The command line flags relevant to application development are described in <a href="#table_matcflags">table&nbsp;14</a>.
The command line flags relevant to application development are described in <a href="#table_matcflags">table&nbsp;15</a>.
</p><div class="table">
<table class="table"><tbody><tr><th style="text-align:right"> Flag </th><th style="text-align:center"> Value </th><th style="text-align:left"> Usage </th></tr>
<tr><td style="text-align:right"> <strong class="asterisk">-o</strong>, <strong class="asterisk">—output</strong> </td><td style="text-align:center"> [path] </td><td style="text-align:left"> Specify the output file path </td></tr>
@@ -2775,7 +2824,7 @@ The command line flags relevant to application development are described in <a h
<tr><td style="text-align:right"> <strong class="asterisk">-S</strong>, <strong class="asterisk">—optimize-size</strong> </td><td style="text-align:center"> N/A </td><td style="text-align:left"> Optimize compiled material for size instead of just performance </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">-r</strong>, <strong class="asterisk">—reflect</strong> </td><td style="text-align:center"> parameters </td><td style="text-align:left"> Outputs the specified metadata as JSON </td></tr>
<tr><td style="text-align:right"> <strong class="asterisk">-v</strong>, <strong class="asterisk">—variant-filter</strong> </td><td style="text-align:center"> [variant] </td><td style="text-align:left"> Filters out the specified, comma-separated variants </td></tr>
</tbody></table><center><div class="tablecaption"><a class="target" name="table_matcflags">&nbsp;</a><b style="font-style:normal;">Table&nbsp;14:</b> List of <code>matc</code> flags</div></center></div>
</tbody></table><center><div class="tablecaption"><a class="target" name="table_matcflags">&nbsp;</a><b style="font-style:normal;">Table&nbsp;15:</b> List of <code>matc</code> flags</div></center></div>
<p></p><p>
+3 -2
View File
@@ -938,12 +938,13 @@ Type
: `number`
Value
: An integer value, either 1 or 2. Defaults to 1.
: An integer value, either 1, 2 or 3. Defaults to 1.
Feature Level | Guaranteed features
:----------------------|:---------------------------------
1 | 9 textures per material
2 | 12 textures per material, cubemap arrays, ESSL 3.10
2 | 9 textures per material, cubemap arrays, ESSL 3.10
3 | 12 textures per material, cubemap arrays, ESSL 3.10
[Table [featureLevels]: Feature levels]
Description
+8 -1
View File
@@ -198,7 +198,7 @@ set(PRIVATE_HDRS
src/materials/fsr/ffx_a.h
src/materials/fsr/ffx_fsr1.h
src/materials/fsr/ffx_fsr1_mobile.fs
src/upcast.h
src/downcast.h
)
set(MATERIAL_SRCS
@@ -537,6 +537,13 @@ target_link_libraries(${TARGET} PUBLIC filaflat)
target_link_libraries(${TARGET} PUBLIC filabridge)
target_link_libraries(${TARGET} PUBLIC ibl-lite)
if (FILAMENT_ENABLE_FGDBG)
target_link_libraries(${TARGET} PUBLIC fgdbg)
add_definitions(-DFILAMENT_ENABLE_FGDBG=1)
else()
add_definitions(-DFILAMENT_ENABLE_FGDBG=0)
endif()
if (FILAMENT_ENABLE_MATDBG)
target_link_libraries(${TARGET} PUBLIC matdbg)
add_definitions(-DFILAMENT_ENABLE_MATDBG=1)
+20
View File
@@ -450,3 +450,23 @@ if (APPLE AND NOT IOS)
set_target_properties(backend_test_mac PROPERTIES FOLDER Tests)
endif()
# ==================================================================================================
# Compute tests
if (NOT IOS AND NOT WEBGL)
add_executable(compute_test
test/ComputeTest.cpp
test/Arguments.cpp
test/test_ComputeBasic.cpp
)
target_link_libraries(compute_test PRIVATE
backend
getopt
gtest
)
set_target_properties(compute_test PROPERTIES FOLDER Tests)
endif()
+32 -12
View File
@@ -48,23 +48,36 @@ static constexpr uint64_t SWAP_CHAIN_CONFIG_ENABLE_XCB = 0x4;
static constexpr uint64_t SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER = 0x8;
static constexpr size_t MAX_VERTEX_ATTRIBUTE_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_VERTEX_SAMPLER_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_FRAGMENT_SAMPLER_COUNT = 16; // This is guaranteed by OpenGL ES.
static constexpr size_t MAX_SAMPLER_COUNT = 32; // 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.
static constexpr size_t MAX_SSBO_COUNT = 4; // This is guaranteed by OpenGL ES.
// Per feature level caps
// Use (int)FeatureLevel to index this array
static constexpr struct {
const size_t MAX_VERTEX_SAMPLER_COUNT;
const size_t MAX_FRAGMENT_SAMPLER_COUNT;
} FEATURE_LEVEL_CAPS[4] = {
{ 0, 0 }, // do not use
{ 16, 16 }, // guaranteed by OpenGL ES, Vulkan and Metal
{ 16, 16 }, // guaranteed by OpenGL ES, Vulkan and Metal
{ 31, 31 }, // guaranteed by Metal
};
static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT,
"The number of buffer objects that can be attached to a VertexBuffer must be "
"less than or equal to the maximum number of vertex attributes.");
static constexpr size_t CONFIG_BINDING_COUNT = 12; // This is guaranteed by OpenGL ES.
static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 10; // This is guaranteed by OpenGL ES.
static constexpr size_t CONFIG_SAMPLER_BINDING_COUNT = 4; // This is guaranteed by OpenGL ES.
/**
* Defines the backend's feature levels.
*/
enum class FeatureLevel : uint8_t {
FEATURE_LEVEL_1 = 1, //!< OpenGL ES 3.0 features (default)
FEATURE_LEVEL_2 //!< OpenGL ES 3.1 features + 31 textures units + cubemap arrays
FEATURE_LEVEL_2, //!< OpenGL ES 3.1 features + 16 textures units + cubemap arrays
FEATURE_LEVEL_3 //!< OpenGL ES 3.1 features + 31 textures units + cubemap arrays
};
/**
@@ -301,7 +314,8 @@ enum class ElementType : uint8_t {
//! Buffer object binding type
enum class BufferObjectBinding : uint8_t {
VERTEX,
UNIFORM
UNIFORM,
SHADER_STORAGE
};
//! Face culling Mode
@@ -883,9 +897,10 @@ struct RasterState {
* \privatesection
*/
enum ShaderType : uint8_t {
enum class ShaderStage : uint8_t {
VERTEX = 0,
FRAGMENT = 1
FRAGMENT = 1,
COMPUTE = 2
};
static constexpr size_t PIPELINE_STAGE_COUNT = 2;
@@ -893,15 +908,18 @@ enum class ShaderStageFlags : uint8_t {
NONE = 0,
VERTEX = 0x1,
FRAGMENT = 0x2,
ALL_SHADER_STAGE_FLAGS = VERTEX | FRAGMENT
COMPUTE = 0x4,
ALL_SHADER_STAGE_FLAGS = VERTEX | FRAGMENT | COMPUTE
};
static inline constexpr bool hasShaderType(ShaderStageFlags flags, ShaderType type) noexcept {
static inline constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage type) noexcept {
switch (type) {
case VERTEX:
case ShaderStage::VERTEX:
return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::VERTEX));
case FRAGMENT:
case ShaderStage::FRAGMENT:
return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::FRAGMENT));
case ShaderStage::COMPUTE:
return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::COMPUTE));
}
}
@@ -1050,6 +1068,8 @@ template<> struct utils::EnableBitMaskOperators<filament::backend::StencilFace>
: public std::true_type {};
template<> struct utils::EnableIntegerOperators<filament::backend::TextureCubemapFace>
: public std::true_type {};
template<> struct utils::EnableIntegerOperators<filament::backend::FeatureLevel>
: public std::true_type {};
#if !defined(NDEBUG)
utils::io::ostream& operator<<(utils::io::ostream& out, filament::backend::BufferUsage usage);
@@ -280,7 +280,7 @@ public:
}
size_t bpr = bpp * stride;
size_t bprAligned = (bpr + (alignment - 1)) & -alignment;
size_t bprAligned = (bpr + (alignment - 1)) & (~alignment + 1);
return bprAligned * height;
}
+27 -8
View File
@@ -27,19 +27,20 @@
#include <backend/DriverEnums.h>
#include <array>
#include <variant>
namespace filament::backend {
class Program {
public:
static constexpr size_t SHADER_TYPE_COUNT = 2;
static constexpr size_t BINDING_COUNT = CONFIG_BINDING_COUNT;
static constexpr size_t SHADER_TYPE_COUNT = 3;
static constexpr size_t UNIFORM_BINDING_COUNT = CONFIG_UNIFORM_BINDING_COUNT;
static constexpr size_t SAMPLER_BINDING_COUNT = CONFIG_SAMPLER_BINDING_COUNT;
struct Sampler {
utils::CString name = {}; // name of the sampler in the shader
uint16_t binding = 0; // binding point of the sampler in the shader
bool strict = false; // if true, this sampler must always have a bound texture
uint32_t binding = 0; // binding point of the sampler in the shader
};
struct SamplerGroupData {
@@ -47,8 +48,8 @@ public:
ShaderStageFlags stageFlags = ShaderStageFlags::ALL_SHADER_STAGE_FLAGS;
};
using UniformBlockInfo = std::array<const char*, BINDING_COUNT>;
using SamplerGroupInfo = std::array<SamplerGroupData, BINDING_COUNT>;
using UniformBlockInfo = std::array<utils::CString, UNIFORM_BINDING_COUNT>;
using SamplerGroupInfo = std::array<SamplerGroupData, SAMPLER_BINDING_COUNT>;
using ShaderBlob = utils::FixedCapacityVector<uint8_t>;
using ShaderSource = std::array<ShaderBlob, SHADER_TYPE_COUNT>;
@@ -69,13 +70,13 @@ public:
// sets one of the program's shader (e.g. vertex, fragment)
// string-based shaders are null terminated, consequently the size parameter must include the
// null terminating character.
Program& shader(ShaderType shader, void const* data, size_t size);
Program& shader(ShaderStage shader, void const* data, size_t size);
// Note: This is only needed for GLES3.0 backends, because the layout(binding=) syntax is
// not permitted in glsl. The backend needs a way to associate a uniform block
// to a binding point.
Program& uniformBlockBindings(
utils::FixedCapacityVector<std::pair<const char*, uint8_t>> const& uniformBlockBindings) noexcept;
utils::FixedCapacityVector<std::pair<utils::CString, uint8_t>> const& uniformBlockBindings) noexcept;
// sets the 'bindingPoint' sampler group descriptor for this program.
// 'samplers' can be destroyed after this call.
@@ -84,10 +85,20 @@ public:
Program& setSamplerGroup(size_t bindingPoint, ShaderStageFlags stageFlags,
Sampler const* samplers, size_t count) noexcept;
struct SpecializationConstant {
uint32_t id; // id set in glsl
std::variant<int32_t, float, bool> value; // value and type
};
Program& specializationConstants(
utils::FixedCapacityVector<SpecializationConstant> specConstants) noexcept;
ShaderSource const& getShadersSource() const noexcept { return mShadersSource; }
ShaderSource& getShadersSource() noexcept { return mShadersSource; }
UniformBlockInfo const& getUniformBlockBindings() const noexcept { return mUniformBlocks; }
UniformBlockInfo& getUniformBlockBindings() noexcept { return mUniformBlocks; }
SamplerGroupInfo const& getSamplerGroupInfo() const { return mSamplerGroups; }
SamplerGroupInfo& getSamplerGroupInfo() { return mSamplerGroups; }
@@ -95,6 +106,13 @@ public:
utils::CString const& getName() const noexcept { return mName; }
utils::CString& getName() noexcept { return mName; }
utils::FixedCapacityVector<SpecializationConstant> const& getSpecializationConstants() const noexcept {
return mSpecializationConstants;
}
utils::FixedCapacityVector<SpecializationConstant>& getSpecializationConstants() noexcept {
return mSpecializationConstants;
}
private:
friend utils::io::ostream& operator<<(utils::io::ostream& out, const Program& builder);
@@ -103,6 +121,7 @@ private:
ShaderSource mShadersSource;
utils::CString mName;
utils::Invocable<utils::io::ostream&(utils::io::ostream& out)> mLogger;
utils::FixedCapacityVector<SpecializationConstant> mSpecializationConstants;
};
} // namespace filament::backend
@@ -21,15 +21,16 @@
#include <backend/PixelBufferDescriptor.h>
#include <string_view>
#include <stddef.h>
namespace filament {
namespace backend {
namespace filament::backend {
/**
* Returns true if the shader string requests the Google-style line directive extension.
*/
bool requestsGoogleLineDirectivesExtension(const char* shader, size_t length) noexcept;
bool requestsGoogleLineDirectivesExtension(std::string_view source) noexcept;
/**
* Edit a GLSL shader string in-place so any Google-style line directives are turned into regular
@@ -48,6 +49,11 @@ void removeGoogleLineDirectives(char* shader, size_t length) noexcept;
*/
size_t getFormatSize(TextureFormat format) noexcept;
/**
* Returns the number of component (1 to 4) for the given format.
*/
size_t getFormatComponentCount(TextureFormat format) noexcept;
/**
* For compressed texture formats, returns the number of horizontal texels per block. Otherwise
* returns 0.
@@ -65,7 +71,6 @@ size_t getBlockHeight(TextureFormat format) noexcept;
*/
bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped);
} // namespace backend
} // namespace filament
#endif // TNT_FILAMENT_BACKEND_PRIVATE_BACKENDUTILS_H
@@ -158,6 +158,9 @@ DECL_DRIVER_API_0(flush)
// flush and wait for the effects to be done
DECL_DRIVER_API_0(finish)
// reset state tracking, if the driver does any state tracking (e.g. GL)
DECL_DRIVER_API_0(resetState)
/*
* Creating driver objects
* -----------------------
@@ -406,12 +409,17 @@ DECL_DRIVER_API_N(bindUniformBuffer,
uint32_t, index,
backend::BufferObjectHandle, ubh)
DECL_DRIVER_API_N(bindUniformBufferRange,
DECL_DRIVER_API_N(bindBufferRange,
BufferObjectBinding, bindingType,
uint32_t, index,
backend::BufferObjectHandle, ubh,
uint32_t, offset,
uint32_t, size)
DECL_DRIVER_API_N(unbindBuffer,
BufferObjectBinding, bindingType,
uint32_t, index)
DECL_DRIVER_API_N(bindSamplers,
uint32_t, index,
backend::SamplerGroupHandle, sbh)
@@ -444,6 +452,12 @@ DECL_DRIVER_API_N(readPixels,
uint32_t, height,
backend::PixelBufferDescriptor&&, data)
DECL_DRIVER_API_N(readBufferSubData,
backend::BufferObjectHandle, src,
uint32_t, offset,
uint32_t, size,
backend::BufferDescriptor&&, data)
/*
* Rendering operations
* --------------------
@@ -462,6 +476,11 @@ DECL_DRIVER_API_N(draw,
backend::RenderPrimitiveHandle, rph,
uint32_t, instanceCount)
DECL_DRIVER_API_N(dispatchCompute,
backend::ProgramHandle, program,
math::uint3, workGroupCount)
#pragma clang diagnostic pop
#undef EXPAND
+143 -23
View File
@@ -22,12 +22,10 @@
#include <string_view>
namespace filament {
namespace backend {
namespace filament::backend {
bool requestsGoogleLineDirectivesExtension(const char* shader, size_t length) noexcept {
std::string_view s(shader, length);
return s.find("GL_GOOGLE_cpp_style_line_directive") != std::string_view::npos;
bool requestsGoogleLineDirectivesExtension(std::string_view source) noexcept {
return source.find("GL_GOOGLE_cpp_style_line_directive") != std::string_view::npos;
}
void removeGoogleLineDirectives(char* shader, size_t length) noexcept {
@@ -211,6 +209,129 @@ size_t getFormatSize(TextureFormat format) noexcept {
}
}
size_t getFormatComponentCount(TextureFormat format) noexcept {
switch (format) {
case TextureFormat::R8:
case TextureFormat::R8_SNORM:
case TextureFormat::R8UI:
case TextureFormat::R8I:
case TextureFormat::R16F:
case TextureFormat::R16UI:
case TextureFormat::R16I:
case TextureFormat::R32F:
case TextureFormat::R32I:
case TextureFormat::R32UI:
case TextureFormat::STENCIL8:
case TextureFormat::DEPTH16:
case TextureFormat::DEPTH24:
case TextureFormat::DEPTH32F:
return 1;
case TextureFormat::RG8:
case TextureFormat::RG8_SNORM:
case TextureFormat::RG8UI:
case TextureFormat::RG8I:
case TextureFormat::RG16F:
case TextureFormat::RG16UI:
case TextureFormat::RG16I:
case TextureFormat::RG32F:
case TextureFormat::RG32UI:
case TextureFormat::RG32I:
case TextureFormat::DEPTH24_STENCIL8:
case TextureFormat::DEPTH32F_STENCIL8:
return 2;
case TextureFormat::RGB565:
case TextureFormat::RGB8:
case TextureFormat::SRGB8:
case TextureFormat::RGB8_SNORM:
case TextureFormat::RGB8UI:
case TextureFormat::RGB8I:
case TextureFormat::R11F_G11F_B10F:
case TextureFormat::RGB16F:
case TextureFormat::RGB16UI:
case TextureFormat::RGB16I:
case TextureFormat::RGB32F:
case TextureFormat::RGB32UI:
case TextureFormat::RGB32I:
return 3;
case TextureFormat::RGB5_A1:
case TextureFormat::RGBA4:
case TextureFormat::RGB9_E5:
case TextureFormat::RGBA8:
case TextureFormat::SRGB8_A8:
case TextureFormat::RGBA8_SNORM:
case TextureFormat::RGB10_A2:
case TextureFormat::RGBA8UI:
case TextureFormat::RGBA8I:
case TextureFormat::RGBA16F:
case TextureFormat::RGBA16UI:
case TextureFormat::RGBA16I:
case TextureFormat::RGBA32F:
case TextureFormat::RGBA32UI:
case TextureFormat::RGBA32I:
return 4;
// Compressed formats ---------------------------------------------------------------------
case TextureFormat::EAC_R11:
case TextureFormat::EAC_R11_SIGNED:
return 1;
case TextureFormat::EAC_RG11:
case TextureFormat::EAC_RG11_SIGNED:
return 2;
case TextureFormat::ETC2_RGB8:
case TextureFormat::ETC2_SRGB8:
case TextureFormat::DXT1_RGB:
case TextureFormat::DXT1_SRGB:
return 3;
case TextureFormat::ETC2_EAC_RGBA8:
case TextureFormat::ETC2_EAC_SRGBA8:
case TextureFormat::ETC2_RGB8_A1:
case TextureFormat::ETC2_SRGB8_A1:
case TextureFormat::DXT1_RGBA:
case TextureFormat::DXT1_SRGBA:
case TextureFormat::DXT3_RGBA:
case TextureFormat::DXT3_SRGBA:
case TextureFormat::DXT5_RGBA:
case TextureFormat::DXT5_SRGBA:
case TextureFormat::RGBA_ASTC_4x4:
case TextureFormat::RGBA_ASTC_5x4:
case TextureFormat::RGBA_ASTC_5x5:
case TextureFormat::RGBA_ASTC_6x5:
case TextureFormat::RGBA_ASTC_6x6:
case TextureFormat::RGBA_ASTC_8x5:
case TextureFormat::RGBA_ASTC_8x6:
case TextureFormat::RGBA_ASTC_8x8:
case TextureFormat::RGBA_ASTC_10x5:
case TextureFormat::RGBA_ASTC_10x6:
case TextureFormat::RGBA_ASTC_10x8:
case TextureFormat::RGBA_ASTC_10x10:
case TextureFormat::RGBA_ASTC_12x10:
case TextureFormat::RGBA_ASTC_12x12:
case TextureFormat::SRGB8_ALPHA8_ASTC_4x4:
case TextureFormat::SRGB8_ALPHA8_ASTC_5x4:
case TextureFormat::SRGB8_ALPHA8_ASTC_5x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_6x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_6x6:
case TextureFormat::SRGB8_ALPHA8_ASTC_8x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_8x6:
case TextureFormat::SRGB8_ALPHA8_ASTC_8x8:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x5:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x6:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x8:
case TextureFormat::SRGB8_ALPHA8_ASTC_10x10:
case TextureFormat::SRGB8_ALPHA8_ASTC_12x10:
case TextureFormat::SRGB8_ALPHA8_ASTC_12x12:
return 4;
case TextureFormat::UNUSED:
return 0;
}
}
size_t getBlockWidth(TextureFormat format) noexcept {
switch (format) {
case TextureFormat::EAC_RG11:
@@ -380,60 +501,59 @@ bool reshape(const PixelBufferDescriptor& data, PixelBufferDescriptor& reshaped)
}
}
} // namespace backend
} // namespace filament
} // namespace backend::filament
namespace utils {
template<>
CString to_string<filament::backend::TextureUsage>(filament::backend::TextureUsage usage) noexcept {
CString to_string<filament::backend::TextureUsage>(filament::backend::TextureUsage value) noexcept {
using namespace filament::backend;
char string[7] = {'-', '-', '-', '-', '-', '-', 0};
if (any(usage & TextureUsage::UPLOADABLE)) {
if (any(value & TextureUsage::UPLOADABLE)) {
string[0]='U';
}
if (any(usage & TextureUsage::SAMPLEABLE)) {
if (any(value & TextureUsage::SAMPLEABLE)) {
string[1]='S';
}
if (any(usage & TextureUsage::COLOR_ATTACHMENT)) {
if (any(value & TextureUsage::COLOR_ATTACHMENT)) {
string[2]='c';
}
if (any(usage & TextureUsage::DEPTH_ATTACHMENT)) {
if (any(value & TextureUsage::DEPTH_ATTACHMENT)) {
string[3]='d';
}
if (any(usage & TextureUsage::STENCIL_ATTACHMENT)) {
if (any(value & TextureUsage::STENCIL_ATTACHMENT)) {
string[4] = 's';
}
if (any(usage & TextureUsage::SUBPASS_INPUT)) {
if (any(value & TextureUsage::SUBPASS_INPUT)) {
string[5]='f';
}
return CString(string, 6);
return { string, 6 };
}
template<>
CString to_string<filament::backend::TargetBufferFlags>(filament::backend::TargetBufferFlags flags) noexcept {
CString to_string<filament::backend::TargetBufferFlags>(filament::backend::TargetBufferFlags value) noexcept {
using namespace filament::backend;
char string[7] = {'-', '-', '-', '-', '-', '-', 0};
if (any(flags & TargetBufferFlags::COLOR0)) {
if (any(value & TargetBufferFlags::COLOR0)) {
string[0]='0';
}
if (any(flags & TargetBufferFlags::COLOR1)) {
if (any(value & TargetBufferFlags::COLOR1)) {
string[1]='1';
}
if (any(flags & TargetBufferFlags::COLOR2)) {
if (any(value & TargetBufferFlags::COLOR2)) {
string[2]='2';
}
if (any(flags & TargetBufferFlags::COLOR3)) {
if (any(value & TargetBufferFlags::COLOR3)) {
string[3]='3';
}
if (any(flags & TargetBufferFlags::DEPTH)) {
if (any(value & TargetBufferFlags::DEPTH)) {
string[4]='D';
}
if (any(flags & TargetBufferFlags::STENCIL)) {
if (any(value & TargetBufferFlags::STENCIL)) {
string[5]='S';
}
return CString(string, 6);
return { string, 6 };
}
} // namespace utils
+22 -1
View File
@@ -59,6 +59,21 @@ public:
}
}
static void copyImage(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 /*width*/, size_t height, bool /*swizzle*/) {
if (srcBytesPerRow == dstBytesPerRow) {
std::memcpy(dest, src, height * srcBytesPerRow);
return;
}
const size_t minBytesPerRow = std::min(srcBytesPerRow, dstBytesPerRow);
for (size_t i = 0; i < height; ++i, src += srcBytesPerRow, dest += dstBytesPerRow) {
std::memcpy(dest, src, minBytesPerRow);
}
}
// Converts a n-channel image of UBYTE, INT, UINT, or FLOAT to a different type.
template<typename dstComponentType, typename srcComponentType>
static void reshapeImage(uint8_t* UTILS_RESTRICT dest, const uint8_t* UTILS_RESTRICT src,
@@ -119,7 +134,13 @@ public:
switch (dst->type) {
case UBYTE:
switch (srcType) {
case UBYTE: reshaper = reshapeImage<uint8_t, uint8_t>; break;
case UBYTE:
reshaper = reshapeImage<uint8_t, uint8_t>;
if (dst->format == PixelDataFormat::RGBA &&
dstChannelCount == srcChannelCount && !swizzle) {
reshaper = copyImage;
}
break;
case FLOAT: reshaper = reshapeImage<uint8_t, float>; break;
case INT: reshaper = reshapeImage<uint8_t, int32_t>; break;
case UINT: reshaper = reshapeImage<uint8_t, uint32_t>; break;
+14 -7
View File
@@ -16,10 +16,10 @@
#include "backend/Program.h"
using namespace utils;
namespace filament::backend {
using namespace utils;
// We want these in the .cpp file, so they're not inlined (not worth it)
Program::Program() noexcept { // NOLINT(modernize-use-equals-default)
}
@@ -37,14 +37,14 @@ Program& Program::operator=(Program&& rhs) noexcept {
Program::~Program() noexcept = default;
Program& Program::diagnostics(utils::CString const& name,
utils::Invocable<io::ostream&(utils::io::ostream&)>&& logger) {
Program& Program::diagnostics(CString const& name,
Invocable<io::ostream&(io::ostream&)>&& logger) {
mName = name;
mLogger = std::move(logger);
return *this;
}
Program& Program::shader(ShaderType shader, void const* data, size_t size) {
Program& Program::shader(ShaderStage shader, void const* data, size_t size) {
ShaderBlob blob(size);
std::copy_n((const uint8_t *)data, size, blob.data());
mShadersSource[size_t(shader)] = std::move(blob);
@@ -52,9 +52,9 @@ Program& Program::shader(ShaderType shader, void const* data, size_t size) {
}
Program& Program::uniformBlockBindings(
utils::FixedCapacityVector<std::pair<const char*, uint8_t>> const& uniformBlockBindings) noexcept {
FixedCapacityVector<std::pair<utils::CString, uint8_t>> const& uniformBlockBindings) noexcept {
for (auto const& item : uniformBlockBindings) {
assert_invariant(item.second < BINDING_COUNT);
assert_invariant(item.second < UNIFORM_BINDING_COUNT);
mUniformBlocks[item.second] = item.first;
}
return *this;
@@ -71,6 +71,12 @@ Program& Program::setSamplerGroup(size_t bindingPoint, ShaderStageFlags stageFla
return *this;
}
Program& Program::specializationConstants(
FixedCapacityVector<SpecializationConstant> specConstants) noexcept {
mSpecializationConstants = std::move(specConstants);
return *this;
}
io::ostream& operator<<(io::ostream& out, const Program& builder) {
out << "Program{";
builder.mLogger(out);
@@ -78,4 +84,5 @@ io::ostream& operator<<(io::ostream& out, const Program& builder) {
return out;
}
} // namespace filament::backend
+134 -11
View File
@@ -24,13 +24,18 @@
#include <Metal/Metal.h>
namespace filament {
namespace backend {
#include <utils/compiler.h>
#include <utility>
#include <memory>
namespace filament::backend {
class MetalBuffer {
public:
MetalBuffer(MetalContext& context, BufferUsage usage, size_t size, bool forceGpuBuffer = false);
MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
size_t size, bool forceGpuBuffer = false);
~MetalBuffer();
MetalBuffer(const MetalBuffer& rhs) = delete;
@@ -57,18 +62,21 @@ public:
void* getCpuBuffer() const noexcept { return mCpuBuffer; }
enum Stage {
VERTEX = 1,
FRAGMENT = 2
enum Stage : uint8_t {
VERTEX = 1u << 0u,
FRAGMENT = 1u << 1u,
COMPUTE = 1u << 2u
};
/**
* Bind multiple buffers to pipeline stages.
*
* bindBuffers binds an array of buffers to the given stage(s) of a MTLRenderCommandEncoder's
* pipeline.
* bindBuffers binds an array of buffers to the given stage(s) of a MTLCommandEncoders's
* pipeline. The encoder must be either a MTLRenderCommandEncoder or a MTLComputeCommandEncoder.
* For MTLRenderCommandEncoders, only the VERTEX and FRAGMENT stages may be specified.
* For MTLComputeCommandEncoders, only the COMPUTE stage may be specified.
*/
static void bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLRenderCommandEncoder> encoder,
static void bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLCommandEncoder> encoder,
size_t bufferStart, uint8_t stages, MetalBuffer* const* buffers, size_t const* offsets,
size_t count);
@@ -80,7 +88,122 @@ private:
MetalContext& mContext;
};
} // namespace backend
} // namespace filament
template <typename TYPE>
static inline TYPE align(TYPE p, size_t alignment) noexcept {
// alignment must be a power-of-two
assert(alignment && !(alignment & alignment-1));
return (TYPE)((p + alignment - 1) & ~(alignment - 1));
}
/**
* Manages a single id<MTLBuffer>, allowing sub-allocations in a "ring" fashion. Each slot in the
* buffer has a fixed size. When a new allocation is made, previous allocations become available
* when the current id<MTLCommandBuffer> has finished executing on the GPU.
*
* If there are no slots available when a new allocation is requested, MetalRingBuffer falls back to
* allocating a new id<MTLBuffer> per allocation until a slot is freed.
*
* All methods must be called from the Metal backend thread.
*/
class MetalRingBuffer {
public:
// In practice, MetalRingBuffer is used for argument buffers, which are kept in the constant
// address space. Constant buffers have specific alignment requirements when specifying an
// offset.
#if defined(IOS)
#if TARGET_OS_SIMULATOR
// The iOS simulator has differing alignment requirements.
static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 256;
#else
static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 4;
#endif // TARGET_OS_SIMULATOR
#else
static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 32;
#endif
static inline auto computeSlotSize(MTLSizeAndAlign layout) {
return align(align(layout.size, layout.align), METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT);
}
MetalRingBuffer(id<MTLDevice> device, MTLResourceOptions options, MTLSizeAndAlign layout,
NSUInteger slotCount)
: mDevice(device),
mAuxBuffer(nil),
mBufferOptions(options),
mSlotSizeBytes(computeSlotSize(layout)),
mSlotCount(slotCount) {
mBuffer = [device newBufferWithLength:mSlotSizeBytes * mSlotCount options:mBufferOptions];
assert_invariant(mBuffer);
}
/**
* Create a new allocation in the buffer.
* @param cmdBuffer When this command buffer has finished executing on the GPU, the previous
* ring buffer allocation will be freed.
* @return the id<MTLBuffer> and offset for the new allocation
*/
std::pair<id<MTLBuffer>, NSUInteger> createNewAllocation(id<MTLCommandBuffer> cmdBuffer) {
const auto occupiedSlots = mOccupiedSlots->load(std::memory_order_relaxed);
assert_invariant(occupiedSlots <= mSlotCount);
if (UTILS_UNLIKELY(occupiedSlots == mSlotCount)) {
// We don't have any room left, so we fall back to creating a one-off aux buffer.
// If we already have an aux buffer, it will get freed here, unless it has been retained
// by a MTLCommandBuffer. In that case, it will be freed when the command buffer
// finishes executing.
mAuxBuffer = [mDevice newBufferWithLength:mSlotSizeBytes options:mBufferOptions];
assert_invariant(mAuxBuffer);
return {mAuxBuffer, 0};
}
mCurrentSlot = (mCurrentSlot + 1) % mSlotCount;
mOccupiedSlots->fetch_add(1, std::memory_order_relaxed);
// Release the previous allocation.
if (UTILS_UNLIKELY(mAuxBuffer)) {
mAuxBuffer = nil;
} else {
// Capture the mOccupiedSlots var via a weak_ptr because the MetalRingBuffer could be
// destructed before the block executes.
std::weak_ptr<AtomicCounterType> slots = mOccupiedSlots;
[cmdBuffer addCompletedHandler:^(id<MTLCommandBuffer> buffer) {
if (auto s = slots.lock()) {
s->fetch_sub(1, std::memory_order_relaxed);
}
}];
}
return getCurrentAllocation();
}
/**
* Returns an allocation (buffer and offset) that is guaranteed not to be in use by the GPU.
* @param cmdBuffer When this command buffer has finished executing on the GPU, the previous
* ring buffer allocation will be freed.
* @return the id<MTLBuffer> and offset for the current allocation
*/
std::pair<id<MTLBuffer>, NSUInteger> getCurrentAllocation() const {
if (UTILS_UNLIKELY(mAuxBuffer)) {
return { mAuxBuffer, 0 };
}
return { mBuffer, mCurrentSlot * mSlotSizeBytes };
}
bool canAccomodateLayout(MTLSizeAndAlign layout) const {
return mSlotSizeBytes >= computeSlotSize(layout);
}
private:
id<MTLDevice> mDevice;
id<MTLBuffer> mBuffer;
id<MTLBuffer> mAuxBuffer;
MTLResourceOptions mBufferOptions;
NSUInteger mSlotSizeBytes;
NSUInteger mSlotCount;
NSUInteger mCurrentSlot = 0;
using AtomicCounterType = std::atomic<NSUInteger>;
std::shared_ptr<AtomicCounterType> mOccupiedSlots = std::make_shared<AtomicCounterType>(1);
};
} // namespace filament::backend
#endif
+50 -20
View File
@@ -21,15 +21,17 @@
namespace filament {
namespace backend {
MetalBuffer::MetalBuffer(MetalContext& context, BufferUsage usage, size_t size, bool forceGpuBuffer)
: mBufferSize(size), mContext(context) {
MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
size_t size, bool forceGpuBuffer) : mBufferSize(size), mContext(context) {
// If the buffer is less than 4K in size and is updated frequently, we don't use an explicit
// buffer. Instead, we use immediate command encoder methods like setVertexBytes:length:atIndex:.
if (size <= 4 * 1024 && usage == BufferUsage::DYNAMIC && !forceGpuBuffer) {
mBuffer = nil;
mCpuBuffer = malloc(size);
return;
}
// This won't work for SSBOs, since they are read/write.
if (size <= 4 * 1024 && bindingType != BufferObjectBinding::SHADER_STORAGE &&
usage == BufferUsage::DYNAMIC && !forceGpuBuffer) {
mBuffer = nil;
mCpuBuffer = malloc(size);
return;
}
// Otherwise, we allocate a private GPU buffer.
mBuffer = [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate];
@@ -93,9 +95,24 @@ id<MTLBuffer> MetalBuffer::getGpuBufferForDraw(id<MTLCommandBuffer> cmdBuffer) n
return mBuffer;
}
void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLRenderCommandEncoder> encoder,
void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLCommandEncoder> encoder,
size_t bufferStart, uint8_t stages, MetalBuffer* const* buffers, size_t const* offsets,
size_t count) {
// Ensure we were given the correct type of encoder:
// either a MTLRenderCommandEncoder or a MTLComputeCommandEncoder.
if (stages & MetalBuffer::Stage::VERTEX || stages & MetalBuffer::Stage::FRAGMENT) {
assert_invariant([encoder respondsToSelector:@selector(setVertexBuffers:offsets:withRange:)]);
assert_invariant([encoder respondsToSelector:@selector(setFragmentBuffers:offsets:withRange:)]);
assert_invariant([encoder respondsToSelector:@selector(setVertexBytes:length:atIndex:)]);
assert_invariant([encoder respondsToSelector:@selector(setFragmentBytes:length:atIndex:)]);
assert_invariant(!(stages & MetalBuffer::Stage::COMPUTE));
}
if (stages & MetalBuffer::Stage::COMPUTE) {
assert_invariant([encoder respondsToSelector:@selector(setBuffers:offsets:withRange:)]);
assert_invariant([encoder respondsToSelector:@selector(setBytes:length:atIndex:)]);
assert_invariant(!(stages & (MetalBuffer::Stage::FRAGMENT | MetalBuffer::Stage::VERTEX)));
}
const NSRange bufferRange = NSMakeRange(bufferStart, count);
constexpr size_t MAX_BUFFERS = 16;
@@ -120,14 +137,19 @@ void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLRenderComman
}
if (stages & Stage::VERTEX) {
[encoder setVertexBuffers:metalBuffers.data()
offsets:metalOffsets.data()
withRange:bufferRange];
[(id<MTLRenderCommandEncoder>) encoder setVertexBuffers:metalBuffers.data()
offsets:metalOffsets.data()
withRange:bufferRange];
}
if (stages & Stage::FRAGMENT) {
[encoder setFragmentBuffers:metalBuffers.data()
offsets:metalOffsets.data()
withRange:bufferRange];
[(id<MTLRenderCommandEncoder>) encoder setFragmentBuffers:metalBuffers.data()
offsets:metalOffsets.data()
withRange:bufferRange];
}
if (stages & Stage::COMPUTE) {
[(id<MTLComputeCommandEncoder>) encoder setBuffers:metalBuffers.data()
offsets:metalOffsets.data()
withRange:bufferRange];
}
for (size_t b = 0; b < count; b++) {
@@ -146,14 +168,22 @@ void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLRenderComman
auto* bytes = static_cast<const uint8_t*>(cpuBuffer);
if (stages & Stage::VERTEX) {
[encoder setVertexBytes:(bytes + offset)
length:(buffer->getSize() - offset)
atIndex:bufferIndex];
[(id<MTLRenderCommandEncoder>) encoder setVertexBytes:(bytes + offset)
length:(buffer->getSize() - offset)
atIndex:bufferIndex];
}
if (stages & Stage::FRAGMENT) {
[encoder setFragmentBytes:(bytes + offset)
length:(buffer->getSize() - offset)
atIndex:bufferIndex];
[(id<MTLRenderCommandEncoder>) encoder setFragmentBytes:(bytes + offset)
length:(buffer->getSize() - offset)
atIndex:bufferIndex];
}
if (stages & Stage::COMPUTE) {
// TODO: using setBytes means the data is read-only, which currently isn't enforced.
// In practice this won't be an issue since MetalBuffer ensures all SSBOs are realized
// through actual id<MTLBuffer> allocations.
[(id<MTLComputeCommandEncoder>) encoder setBytes:(bytes + offset)
length:(buffer->getSize() - offset)
atIndex:bufferIndex];
}
}
}
+15 -4
View File
@@ -25,6 +25,7 @@
#include <QuartzCore/QuartzCore.h>
#include <array>
#include <atomic>
#include <stack>
#if defined(FILAMENT_METAL_PROFILING)
@@ -41,11 +42,12 @@ class MetalDriver;
class MetalBlitter;
class MetalBufferPool;
class MetalRenderTarget;
class MetalSamplerGroup;
class MetalSwapChain;
class MetalTexture;
class MetalTimerQueryInterface;
struct MetalUniformBuffer;
struct MetalIndexBuffer;
struct MetalSamplerGroup;
struct MetalVertexBuffer;
constexpr static uint8_t MAX_SAMPLE_COUNT = 8; // Metal devices support at most 8 MSAA samples
@@ -58,8 +60,11 @@ struct MetalContext {
id<MTLCommandBuffer> pendingCommandBuffer = nullptr;
id<MTLRenderCommandEncoder> currentRenderPassEncoder = nullptr;
std::atomic<bool> memorylessLimitsReached = false;
// Supported features.
bool supportsTextureSwizzling = false;
bool supportsMemorylessRenderTargets = false;
uint8_t maxColorRenderTargets = 4;
struct {
uint8_t common;
@@ -79,7 +84,8 @@ struct MetalContext {
// State trackers.
PipelineStateTracker pipelineState;
DepthStencilStateTracker depthStencilState;
UniformBufferState uniformState[VERTEX_BUFFER_START];
std::array<BufferState, Program::UNIFORM_BINDING_COUNT> uniformState;
std::array<BufferState, MAX_SSBO_COUNT> ssboState;
CullModeStateTracker cullModeState;
WindingStateTracker windingState;
@@ -87,11 +93,16 @@ struct MetalContext {
DepthStencilStateCache depthStencilStateCache;
PipelineStateCache pipelineStateCache;
SamplerStateCache samplerStateCache;
ArgumentEncoderCache argumentEncoderCache;
MetalSamplerGroup* samplerBindings[SAMPLER_BINDING_COUNT] = {};
MetalSamplerGroup* samplerBindings[Program::SAMPLER_BINDING_COUNT] = {};
// Keeps track of all alive sampler groups.
// Keeps track of sampler groups we've finalized for the current render pass.
tsl::robin_set<MetalSamplerGroup*> finalizedSamplerGroups;
// Keeps track of all alive sampler groups, textures.
tsl::robin_set<MetalSamplerGroup*> samplerGroups;
tsl::robin_set<MetalTexture*> textures;
MetalBufferPool* bufferPool;
@@ -103,6 +103,15 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
// all frames and their completion handlers finish before context is deallocated.
[context->pendingCommandBuffer addCompletedHandler:^(id <MTLCommandBuffer> buffer) {
context->resourceTracker.clearResources((__bridge void*) buffer);
auto errorCode = (MTLCommandBufferError)buffer.error.code;
if (@available(macOS 11.0, *)) {
if (errorCode == MTLCommandBufferErrorMemoryless) {
utils::slog.w << "Metal: memoryless geometry limit reached. "
"Continuing with private storage mode." << utils::io::endl;
context->memorylessLimitsReached = true;
}
}
}];
ASSERT_POSTCONDITION(context->pendingCommandBuffer, "Could not obtain command buffer.");
return context->pendingCommandBuffer;
+6 -5
View File
@@ -34,10 +34,12 @@ namespace backend {
class MetalPlatform;
class MetalBuffer;
class MetalSamplerGroup;
class MetalTexture;
struct MetalUniformBuffer;
struct MetalContext;
struct MetalProgram;
struct UniformBufferState;
struct BufferState;
#ifndef FILAMENT_METAL_HANDLE_ARENA_SIZE_IN_MB
#define FILAMENT_METAL_HANDLE_ARENA_SIZE_IN_MB 8
@@ -126,10 +128,9 @@ private:
inline void setRenderPrimitiveRange(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
uint32_t offset, uint32_t minIndex, uint32_t maxIndex, uint32_t count);
void enumerateSamplerGroups(const MetalProgram* program, ShaderType shaderType,
const std::function<void(const SamplerDescriptor*, size_t)>& f);
void enumerateBoundUniformBuffers(const std::function<void(const UniformBufferState&,
MetalBuffer*, uint32_t)>& f);
void finalizeSamplerGroup(MetalSamplerGroup* sg);
void enumerateBoundBuffers(BufferObjectBinding bindingType,
const std::function<void(const BufferState&, MetalBuffer*, uint32_t)>& f);
};
+487 -204
View File
@@ -37,6 +37,8 @@
#include <utils/Log.h>
#include <utils/Panic.h>
#include <algorithm>
namespace filament {
namespace backend {
@@ -90,6 +92,11 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
mContext->highestSupportedGpuFamily.mac >= 2; // newer macOS GPUs
}
// In order to support memoryless render targets, an Apple GPU is needed.
// Available starting macOS 11.0, the first version to run on Apple GPUs.
// On iOS, it's available on all OS versions.
mContext->supportsMemorylessRenderTargets = mContext->highestSupportedGpuFamily.apple >= 1;
mContext->maxColorRenderTargets = 4;
if (mContext->highestSupportedGpuFamily.apple >= 2 ||
mContext->highestSupportedGpuFamily.mac >= 1) {
@@ -107,6 +114,7 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
mContext->pipelineStateCache.setDevice(mContext->device);
mContext->depthStencilStateCache.setDevice(mContext->device);
mContext->samplerStateCache.setDevice(mContext->device);
mContext->argumentEncoderCache.setDevice(mContext->device);
mContext->bufferPool = new MetalBufferPool(*mContext);
mContext->blitter = new MetalBlitter(*mContext);
@@ -188,6 +196,18 @@ void MetalDriver::endFrame(uint32_t frameId) {
assert_invariant(mContext->groupMarkers.empty());
// If we exceeded memoryless limits, turn it off for the rest of the lifetime of the driver.
if (mContext->supportsMemorylessRenderTargets && mContext->memorylessLimitsReached) {
for (MetalTexture* texture : mContext->textures) {
// Release memoryless MTLTexture-s, which are currently only the MSAA sidecars.
// Creation of new render targets is going to trigger the re-allocation of sidecars,
// with private storage mode from now on. Here, at the end of the frame,
// all render targets that have textures with sidecars are assumed to be destroyed.
texture->msaaSidecar = nil;
}
mContext->supportsMemorylessRenderTargets = false;
}
#if defined(FILAMENT_METAL_PROFILING)
os_signpost_interval_end(mContext->log, mContext->signpostId, "Frame encoding");
#endif
@@ -223,7 +243,7 @@ void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elem
void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {
construct_handle<MetalBufferObject>(boh, *mContext, usage, byteCount);
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
}
void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
@@ -233,9 +253,10 @@ void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8
auto& sc = mContext->sampleCountLookup;
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
construct_handle<MetalTexture>(th, *mContext, target, levels, format, samples,
width, height, depth, usage, TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_1,
TextureSwizzle::CHANNEL_2, TextureSwizzle::CHANNEL_3);
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext,
target, levels, format, samples, width, height, depth, usage,
TextureSwizzle::CHANNEL_0, TextureSwizzle::CHANNEL_1,
TextureSwizzle::CHANNEL_2, TextureSwizzle::CHANNEL_3));
}
void MetalDriver::createTextureSwizzledR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
@@ -246,8 +267,8 @@ void MetalDriver::createTextureSwizzledR(Handle<HwTexture> th, SamplerType targe
auto& sc = mContext->sampleCountLookup;
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
construct_handle<MetalTexture>(th, *mContext, target, levels, format, samples,
width, height, depth, usage, r, g, b, a);
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext,
target, levels, format, samples, width, height, depth, usage, r, g, b, a));
}
void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
@@ -268,8 +289,8 @@ void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
ASSERT_PRECONDITION(metalTexture.textureType == filamentMetalType,
"Imported id<MTLTexture> type (%d) != Filament texture type (%d)",
metalTexture.textureType, filamentMetalType);
construct_handle<MetalTexture>(th, *mContext, target, levels, format, samples,
width, height, depth, usage, metalTexture);
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext,
target, levels, format, samples, width, height, depth, usage, metalTexture));
}
void MetalDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, uint32_t size) {
@@ -297,6 +318,8 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
uint8_t samples, MRT color,
TargetBufferInfo depth, TargetBufferInfo stencil) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"createRenderTarget must be called outside of a render pass.");
// Clamp sample count to what the device supports.
auto& sc = mContext->sampleCountLookup;
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
@@ -310,7 +333,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
ASSERT_PRECONDITION(buffer.handle,
"The COLOR%u flag was specified, but invalid color handle provided.", i);
auto colorTexture = handle_cast<MetalTexture>(buffer.handle);
ASSERT_PRECONDITION(colorTexture->texture,
ASSERT_PRECONDITION(colorTexture->getMtlTextureForWrite(),
"Color texture passed to render target has no texture allocation");
colorTexture->updateLodRange(buffer.level);
colorAttachments[i] = { colorTexture, color[i].level, color[i].layer };
@@ -321,7 +344,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
ASSERT_PRECONDITION(depth.handle,
"The DEPTH flag was specified, but invalid depth handle provided.");
auto depthTexture = handle_cast<MetalTexture>(depth.handle);
ASSERT_PRECONDITION(depthTexture->texture,
ASSERT_PRECONDITION(depthTexture->getMtlTextureForWrite(),
"Depth texture passed to render target has no texture allocation.");
depthTexture->updateLodRange(depth.level);
depthAttachment = { depthTexture, depth.level, depth.layer };
@@ -332,7 +355,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
ASSERT_PRECONDITION(stencil.handle,
"The STENCIL flag was specified, but invalid stencil handle provided.");
auto stencilTexture = handle_cast<MetalTexture>(stencil.handle);
ASSERT_PRECONDITION(stencilTexture->texture,
ASSERT_PRECONDITION(stencilTexture->getMtlTextureForWrite(),
"Stencil texture passed to render target has no texture allocation.");
stencilTexture->updateLodRange(stencil.level);
stencilAttachment = { stencilTexture, stencil.level, stencil.layer };
@@ -458,10 +481,20 @@ void MetalDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
return;
}
auto* bo = handle_cast<MetalBufferObject>(boh);
// Unbind this buffer object from any uniform slots it's still bound to.
// Unbind this buffer object from any uniform / SSBO slots it's still bound to.
bo->boundUniformBuffers.forEachSetBit([this](size_t index) {
mContext->uniformState[index].buffer = nullptr;
mContext->uniformState[index].bound = false;
mContext->uniformState[index] = BufferState {
.buffer = nullptr,
.offset = 0,
.bound = false
};
});
bo->boundSsbos.forEachSetBit([this](size_t index) {
mContext->ssboState[index] = BufferState {
.buffer = nullptr,
.offset = 0,
.bound = false
};
});
destruct_handle<MetalBufferObject>(boh);
}
@@ -498,17 +531,7 @@ void MetalDriver::destroyTexture(Handle<HwTexture> th) {
return;
}
// Unbind this texture from any sampler groups that currently reference it.
for (auto* metalSamplerGroup : mContext->samplerGroups) {
const SamplerDescriptor* samplers = metalSamplerGroup->sb->data();
for (size_t i = 0; i < metalSamplerGroup->sb->getSize(); i++) {
const SamplerDescriptor* sampler = samplers + i;
if (sampler->t == th) {
metalSamplerGroup->sb->setSampler(i, {{}, {}});
}
}
}
mContext->textures.erase(handle_cast<MetalTexture>(th));
destruct_handle<MetalTexture>(th);
}
@@ -689,10 +712,14 @@ bool MetalDriver::isWorkaroundNeeded(Workaround workaround) {
}
FeatureLevel MetalDriver::getFeatureLevel() {
// TODO: before we can return FEATURE_LEVEL_2 we need to decouple the samplers from
// the textures. Metal only supports 16 samplers, but at least 31 textures on all
// hardware.
return FeatureLevel::FEATURE_LEVEL_1;
// FEATURE_LEVEL_3 requires >= 31 textures, which all Metal devices support. However, older
// Metal devices only support 16 unique samplers. We could get around this in the future by
// virtualizing samplers.
if (mContext->highestSupportedGpuFamily.apple >= 6 ||
mContext->highestSupportedGpuFamily.mac >= 2) {
return FeatureLevel::FEATURE_LEVEL_3;
}
return FeatureLevel::FEATURE_LEVEL_2;
}
math::float2 MetalDriver::getClipSpaceParams() {
@@ -775,11 +802,15 @@ void MetalDriver::cancelExternalImage(void* image) {
}
void MetalDriver::setExternalImage(Handle<HwTexture> th, void* image) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"setExternalImage must be called outside of a render pass.");
auto texture = handle_cast<MetalTexture>(th);
texture->externalImage.set((CVPixelBufferRef) image);
}
void MetalDriver::setExternalImagePlane(Handle<HwTexture> th, void* image, uint32_t plane) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"setExternalImagePlane must be called outside of a render pass.");
auto texture = handle_cast<MetalTexture>(th);
texture->externalImage.set((CVPixelBufferRef) image, plane);
}
@@ -807,35 +838,137 @@ void MetalDriver::generateMipmaps(Handle<HwTexture> th) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"generateMipmaps must be called outside of a render pass.");
auto tex = handle_cast<MetalTexture>(th);
id <MTLBlitCommandEncoder> blitEncoder = [getPendingCommandBuffer(mContext) blitCommandEncoder];
[blitEncoder generateMipmapsForTexture:tex->texture];
[blitEncoder endEncoding];
tex->minLod = 0;
tex->maxLod = tex->texture.mipmapLevelCount - 1;
tex->generateMipmaps();
}
bool MetalDriver::canGenerateMipmaps() {
return true;
}
void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh,
BufferDescriptor&& data) {
auto sb = handle_cast<MetalSamplerGroup>(sbh);
void MetalDriver::updateSamplerGroup(Handle<HwSamplerGroup> sbh, BufferDescriptor&& data) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"updateSamplerGroup must be called outside of a render pass.");
// FIXME: we shouldn't be using SamplerGroup here, instead the backend should create
// a descriptor or any internal data-structure that represents the textures/samplers.
// It's preferable to do as much work as possible here.
// Here, we emulate the older backend API by re-creating a SamplerGroup from the
// passed data.
SamplerGroup samplerGroup(data.size / sizeof(SamplerDescriptor));
memcpy(samplerGroup.data(), data.buffer, data.size);
*sb->sb = std::move(samplerGroup);
auto sb = handle_cast<MetalSamplerGroup>(sbh);
assert_invariant(sb->size == data.size / sizeof(SamplerDescriptor));
auto const* const samplers = (SamplerDescriptor const*) data.buffer;
#ifndef NDEBUG
// In debug builds, verify that all the textures in the sampler group are still alive.
// These bugs lead to memory corruption and can be difficult to track down.
for (size_t s = 0; s < data.size / sizeof(SamplerDescriptor); s++) {
if (!samplers[s].t) {
continue;
}
auto iter = mContext->textures.find(handle_cast<MetalTexture>(samplers[s].t));
if (iter == mContext->textures.end()) {
utils::slog.e << "updateSamplerGroup: texture #"
<< (int) s << " is dead, texture handle = "
<< samplers[s].t << utils::io::endl;
}
assert_invariant(iter != mContext->textures.end());
}
#endif
// Create a MTLArgumentEncoder for these textures.
// Ideally, we would create this encoder at createSamplerGroup time, but we need to know the
// texture type of each texture. It's also not guaranteed that the types won't change between
// calls to updateSamplerGroup.
utils::FixedCapacityVector<MTLTextureType> textureTypes(sb->size);
std::transform(samplers, samplers + data.size / sizeof(SamplerDescriptor), textureTypes.begin(),
[this](const SamplerDescriptor& sampler) {
if (!sampler.t) {
// Use Type2D for non-bound textures.
return MTLTextureType2D;
}
auto* t = handle_cast<MetalTexture>(sampler.t);
if (t->target == SamplerType::SAMPLER_EXTERNAL) {
// Use Type2D for external image textures.
return MTLTextureType2D;
}
id<MTLTexture> mtlTexture = t->getMtlTextureForRead();
assert_invariant(mtlTexture);
return mtlTexture.textureType;
});
auto& encoderCache = mContext->argumentEncoderCache;
id<MTLArgumentEncoder> encoder =
encoderCache.getOrCreateState(ArgumentEncoderState(std::move(textureTypes)));
sb->reset(getPendingCommandBuffer(mContext), encoder);
// In a perfect world, all the MTLTexture bindings would be known at updateSamplerGroup time.
// However, there are two special cases preventing this:
// 1. External images
// 2. LOD-clamped textures
//
// Both of these cases prevent us from knowing the final id<MTLTexture> that will be bound into
// the argument buffer representing the sampler group. So, we bind what we can now and wait
// until draw call time to bind any special cases (done in finalizeSamplerGroup).
// The good news is that once a render pass has started, the texture bindings won't change.
// A SamplerGroup is "finalized" when all of its textures have been set and is ready for use in
// a draw call.
// Even if we do know all the final textures at this point, we still wait until draw call time
// to call finalizeSamplerGroup, which has one additional responsibility: to call useResources
// for all the textures, which is required by Metal.
for (size_t s = 0; s < data.size / sizeof(SamplerDescriptor); s++) {
if (!samplers[s].t) {
// Assign a default texture / sampler to empty slots.
// Metal requires all samplers referenced in shaders to be bound.
id<MTLTexture> empty = getOrCreateEmptyTexture(mContext);
sb->setFinalizedTexture(s, empty);
id<MTLSamplerState> sampler = mContext->samplerStateCache.getOrCreateState({});
sb->setFinalizedSampler(s, sampler);
continue;
}
// First, bind the sampler state. We always know the full sampler state at
// updateSamplerGroup time.
SamplerState samplerState {
.samplerParams = samplers[s].s,
};
id<MTLSamplerState> sampler = mContext->samplerStateCache.getOrCreateState(samplerState);
sb->setFinalizedSampler(s, sampler);
sb->setTextureHandle(s, samplers[s].t);
auto* t = handle_cast<MetalTexture>(samplers[s].t);
assert_invariant(t);
// If this texture is an external texture, we defer binding the texture until draw call time
// (in finalizeSamplerGroup).
if (t->target == SamplerType::SAMPLER_EXTERNAL) {
continue;
}
if (!t->allLodsValid()) {
// The texture doesn't have all of its LODs loaded, and this could change by the time we
// issue a draw call with this sampler group. So, we defer binding the texture until
// draw call time (in finalizeSamplerGroup).
continue;
}
// If we get here, we know we have a valid MTLTexture that's guaranteed not to change.
id<MTLTexture> mtlTexture = t->getMtlTextureForRead();
assert_invariant(mtlTexture);
sb->setFinalizedTexture(s, mtlTexture);
}
scheduleDestroy(std::move(data));
}
void MetalDriver::beginRenderPass(Handle<HwRenderTarget> rth,
const RenderPassParams& params) {
#if defined(FILAMENT_METAL_PROFILING)
const char* renderPassName = "Unknown";
if (!mContext->groupMarkers.empty()) {
renderPassName = mContext->groupMarkers.top();
}
os_signpost_interval_begin(mContext->log, OS_SIGNPOST_ID_EXCLUSIVE, "Render pass", "%s",
renderPassName);
#endif
auto renderTarget = handle_cast<MetalRenderTarget>(rth);
mContext->currentRenderTarget = renderTarget;
mContext->currentRenderPassFlags = params.flags;
@@ -865,11 +998,17 @@ void MetalDriver::beginRenderPass(Handle<HwRenderTarget> rth,
mContext->depthStencilState.invalidate();
mContext->cullModeState.invalidate();
mContext->windingState.invalidate();
mContext->finalizedSamplerGroups.clear();
}
void MetalDriver::nextSubpass(int dummy) {}
void MetalDriver::endRenderPass(int dummy) {
#if defined(FILAMENT_METAL_PROFILING)
os_signpost_interval_end(mContext->log, OS_SIGNPOST_ID_EXCLUSIVE, "Render pass");
#endif
[mContext->currentRenderPassEncoder endEncoding];
// Command encoders are one time use. Set it to nil to release the encoder and ensure we don't
@@ -915,32 +1054,101 @@ void MetalDriver::commit(Handle<HwSwapChain> sch) {
}
void MetalDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> boh) {
assert_invariant(index < Program::UNIFORM_BINDING_COUNT);
auto* bo = handle_cast<MetalBufferObject>(boh);
auto* currentBo = mContext->uniformState[index].buffer;
if (currentBo) {
currentBo->boundUniformBuffers.unset(index);
}
bo->boundUniformBuffers.set(index);
mContext->uniformState[index] = UniformBufferState{
mContext->uniformState[index] = BufferState{
.buffer = bo,
.offset = 0,
.bound = true
};
}
void MetalDriver::bindUniformBufferRange(uint32_t index, Handle<HwBufferObject> boh,
uint32_t offset, uint32_t size) {
void MetalDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index,
Handle<HwBufferObject> boh, uint32_t offset, uint32_t size) {
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
bindingType == BufferObjectBinding::UNIFORM);
auto* bo = handle_cast<MetalBufferObject>(boh);
auto* currentBo = mContext->uniformState[index].buffer;
if (currentBo) {
currentBo->boundUniformBuffers.unset(index);
switch (bindingType) {
default:
case BufferObjectBinding::UNIFORM: {
assert_invariant(index < Program::UNIFORM_BINDING_COUNT);
auto* currentBo = mContext->uniformState[index].buffer;
if (currentBo) {
currentBo->boundUniformBuffers.unset(index);
}
bo->boundUniformBuffers.set(index);
mContext->uniformState[index] = BufferState {
.buffer = bo,
.offset = offset,
.bound = true
};
break;
}
case BufferObjectBinding::SHADER_STORAGE: {
assert_invariant(index < MAX_SSBO_COUNT);
auto* currentBo = mContext->ssboState[index].buffer;
if (currentBo) {
currentBo->boundSsbos.unset(index);
}
bo->boundSsbos.set(index);
mContext->ssboState[index] = BufferState {
.buffer = bo,
.offset = offset,
.bound = true
};
break;
}
}
}
void MetalDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
bindingType == BufferObjectBinding::UNIFORM);
switch (bindingType) {
default:
case BufferObjectBinding::UNIFORM: {
assert_invariant(index < Program::UNIFORM_BINDING_COUNT);
auto* currentBo = mContext->uniformState[index].buffer;
if (currentBo) {
currentBo->boundUniformBuffers.unset(index);
}
mContext->uniformState[index] = BufferState {
.buffer = nullptr,
.offset = 0,
.bound = false
};
break;
}
case BufferObjectBinding::SHADER_STORAGE: {
assert_invariant(index < MAX_SSBO_COUNT);
auto* currentBo = mContext->ssboState[index].buffer;
if (currentBo) {
currentBo->boundSsbos.unset(index);
}
mContext->ssboState[index] = BufferState {
.buffer = nullptr,
.offset = 0,
.bound = false
};
break;
}
}
bo->boundUniformBuffers.set(index);
mContext->uniformState[index] = UniformBufferState{
.buffer = bo,
.offset = offset,
.bound = true
};
}
void MetalDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
@@ -1070,6 +1278,12 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
}];
}
void MetalDriver::readBufferSubData(backend::BufferObjectHandle boh,
uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) {
// TODO: implement readBufferSubData
scheduleDestroy(std::move(p));
}
void MetalDriver::blit(TargetBufferFlags buffers,
Handle<HwRenderTarget> dst, Viewport dstRect,
Handle<HwRenderTarget> src, Viewport srcRect,
@@ -1149,6 +1363,97 @@ void MetalDriver::blit(TargetBufferFlags buffers,
}
}
void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
// All of the id<MTLSamplerState> objects have already been bound to the argument buffer.
// Here we bind any textures that were unable to be bound in updateSamplerGroup.
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(mContext);
#ifndef NDEBUG
// In debug builds, verify that all the textures in the sampler group are still alive.
// These bugs lead to memory corruption and can be difficult to track down.
const auto& handles = samplerGroup->getTextureHandles();
for (size_t s = 0; s < handles.size(); s++) {
if (!handles[s]) {
continue;
}
auto iter = mContext->textures.find(handle_cast<MetalTexture>(handles[s]));
if (iter == mContext->textures.end()) {
utils::slog.e << "finalizeSamplerGroup: texture #"
<< (int) s << " is dead, texture handle = "
<< handles[s] << utils::io::endl;
}
assert_invariant(iter != mContext->textures.end());
}
#endif
for (size_t binding = 0; binding < samplerGroup->size; binding++) {
auto [th, t] = samplerGroup->getFinalizedTexture(binding);
// This may be an external texture, in which case we can't cache the id<MTLTexture>, we
// need to refetch it in case the external image has changed.
bool isExternalImage = false;
if (th) {
auto* texture = handle_cast<MetalTexture>(th);
isExternalImage = texture->target == SamplerType::SAMPLER_EXTERNAL;
}
// If t is non-nil, then we've already finalized this texture.
if (t && !isExternalImage) {
continue;
}
// It's possible that some texture handles are null, but we should have already handled
// these inside updateSamplerGroup by binding an "empty" texture.
assert_invariant(th);
auto* texture = handle_cast<MetalTexture>(th);
// Determine if this SamplerGroup needs mutation.
// We can't just simply mutate the SamplerGroup, since it could currently be in use by the
// GPU from a prior render pass.
// If the SamplerGroup does need mutation, then there's two cases:
// 1. The SamplerGroup has not been finalized yet (which means it has not yet been used in a
// draw call). We're free to mutate it.
// 2. The SamplerGroup is finalized. We must call mutate(), which will create a new argument
// buffer that we can then mutate freely.
// TODO: don't just always call mutate, check to see if the texture is actually different.
if (samplerGroup->isFinalized()) {
samplerGroup->mutate(cmdBuffer);
}
// External images
if (texture->target == SamplerType::SAMPLER_EXTERNAL) {
if (texture->externalImage.isValid()) {
id<MTLTexture> mtlTexture = texture->externalImage.getMetalTextureForDraw();
assert_invariant(mtlTexture);
samplerGroup->setFinalizedTexture(binding, mtlTexture);
} else {
// Bind an empty texture.
samplerGroup->setFinalizedTexture(binding, getOrCreateEmptyTexture(mContext));
}
continue;
}
samplerGroup->setFinalizedTexture(binding, texture->getMtlTextureForRead());
}
samplerGroup->finalize();
// At this point, all the id<MTLTextures> should be set to valid textures. Some of them will be
// the "empty" texture. Per Apple documentation, the useResource method must be called once per
// render pass.
samplerGroup->useResources(mContext->currentRenderPassEncoder);
// useResources won't retain references to the textures, so we need to do so manually.
for (id<MTLTexture> texture : samplerGroup->textures) {
const void* retainedTexture = CFBridgingRetain(texture);
[cmdBuffer addCompletedHandler:^(id<MTLCommandBuffer> cb) {
CFBridgingRelease(retainedTexture);
}];
}
}
void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t instanceCount) {
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
"Attempted to draw without a valid command encoder.");
@@ -1283,29 +1588,23 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
// Set scissor-rectangle.
// In order to do this, we compute the intersection between:
// 1. the scissor rectangle
// 2. the current viewport
// 3. the render target attachment dimensions (important, as the scissor can't be set larger)
// 2. the render target attachment dimensions (important, as the scissor can't be set larger)
// fmax/min are used below to guard against NaN and because the MTLViewport/MTLRegion
// coordinates are doubles.
MTLRegion scissor = mContext->currentRenderTarget->getRegionFromClientRect(ps.scissor);
const float sleft = scissor.origin.x, sright = scissor.origin.x + scissor.size.width;
const float stop = scissor.origin.y, sbottom = scissor.origin.y + scissor.size.height;
// Viewport extent
const MTLViewport& viewport = mContext->currentViewport;
const float vleft = viewport.originX, vright = viewport.originX + viewport.width;
const float vtop = viewport.originY, vbottom = viewport.originY + viewport.height;
// Attachment extent
const auto attachmentSize = mContext->currentRenderTarget->getAttachmentSize();
const float aleft = 0.0f, atop = 0.0f;
const float aright = static_cast<float>(attachmentSize.x);
const float abottom = static_cast<float>(attachmentSize.y);
const auto left = std::fmax(std::fmax(sleft, vleft), aleft);
const auto right = std::fmin(std::fmin(sright, vright), aright);
const auto top = std::fmax(std::fmax(stop, vtop), atop);
const auto bottom = std::fmin(std::fmin(sbottom, vbottom), abottom);
const auto left = std::fmax(sleft, aleft);
const auto right = std::fmin(sright, aright);
const auto top = std::fmax(stop, atop);
const auto bottom = std::fmin(sbottom, abottom);
MTLScissorRect scissorRect = {
.x = static_cast<NSUInteger>(left),
@@ -1317,121 +1616,54 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
[mContext->currentRenderPassEncoder setScissorRect:scissorRect];
// Bind uniform buffers.
MetalBuffer* uniformsToBind[Program::BINDING_COUNT] = { nil };
NSUInteger offsets[Program::BINDING_COUNT] = { 0 };
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
enumerateBoundUniformBuffers([&uniformsToBind, &offsets](const UniformBufferState& state,
MetalBuffer* buffer, uint32_t index) {
enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
[&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer,
uint32_t index) {
uniformsToBind[index] = buffer;
offsets[index] = state.offset;
});
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder,
0, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT, uniformsToBind, offsets,
Program::BINDING_COUNT);
UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT,
uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT);
// Enumerate all the sampler buffers for the program and check which textures and samplers need
// to be bound.
auto getTextureToBind = [this](const SamplerDescriptor* sampler) {
const auto metalTexture = handle_const_cast<MetalTexture>(sampler->t);
id<MTLTexture> textureToBind = metalTexture->swizzledTextureView ? metalTexture->swizzledTextureView
: metalTexture->texture;
if (metalTexture->externalImage.isValid()) {
textureToBind = metalTexture->externalImage.getMetalTextureForDraw();
// Bind sampler groups (argument buffers).
for (size_t s = 0; s < Program::SAMPLER_BINDING_COUNT; s++) {
MetalSamplerGroup* const samplerGroup = mContext->samplerBindings[s];
if (!samplerGroup) {
continue;
}
return textureToBind;
};
auto getSamplerToBind = [this](const SamplerDescriptor* sampler) {
const auto metalTexture = handle_const_cast<MetalTexture>(sampler->t);
SamplerState s {
.samplerParams = sampler->s,
.minLod = metalTexture->minLod,
.maxLod = metalTexture->maxLod
};
return mContext->samplerStateCache.getOrCreateState(s);
};
id<MTLTexture> texturesToBindVertex[MAX_VERTEX_SAMPLER_COUNT] = {};
id<MTLSamplerState> samplersToBindVertex[MAX_VERTEX_SAMPLER_COUNT] = {};
enumerateSamplerGroups(program, ShaderType::VERTEX,
[this, &getTextureToBind, &getSamplerToBind, &texturesToBindVertex, &samplersToBindVertex](
const SamplerDescriptor* sampler, uint8_t binding) {
// We currently only support a max of MAX_VERTEX_SAMPLER_COUNT samplers. Ignore any additional
// samplers that may be bound.
if (binding >= MAX_VERTEX_SAMPLER_COUNT) {
return;
const auto& stageFlags = program->samplerGroupInfo[s].stageFlags;
if (stageFlags == ShaderStageFlags::NONE) {
continue;
}
auto& textureToBind = texturesToBindVertex[binding];
textureToBind = getTextureToBind(sampler);
if (!textureToBind) {
utils::slog.w << "Warning: no texture bound at binding point " << (size_t) binding
<< " at the vertex shader." << utils::io::endl;
textureToBind = getOrCreateEmptyTexture(mContext);
auto iter = mContext->finalizedSamplerGroups.find(samplerGroup);
if (iter == mContext->finalizedSamplerGroups.end()) {
finalizeSamplerGroup(samplerGroup);
mContext->finalizedSamplerGroups.insert(samplerGroup);
}
auto& samplerToBind = samplersToBindVertex[binding];
samplerToBind = getSamplerToBind(sampler);
});
assert_invariant(samplerGroup->getArgumentBuffer());
// Assign a default sampler to empty slots, in case Filament hasn't bound all samplers.
// Metal requires all samplers referenced in shaders to be bound.
for (auto& sampler : samplersToBindVertex) {
if (!sampler) {
sampler = mContext->samplerStateCache.getOrCreateState({});
if (uint8_t(stageFlags) & uint8_t(ShaderStageFlags::VERTEX)) {
[mContext->currentRenderPassEncoder setVertexBuffer:samplerGroup->getArgumentBuffer()
offset:samplerGroup->getArgumentBufferOffset()
atIndex:(SAMPLER_GROUP_BINDING_START + s)];
}
if (uint8_t(stageFlags) & uint8_t(ShaderStageFlags::FRAGMENT)) {
[mContext->currentRenderPassEncoder setFragmentBuffer:samplerGroup->getArgumentBuffer()
offset:samplerGroup->getArgumentBufferOffset()
atIndex:(SAMPLER_GROUP_BINDING_START + s)];
}
}
NSRange vertexSamplerRange = NSMakeRange(0, MAX_VERTEX_SAMPLER_COUNT);
[mContext->currentRenderPassEncoder setVertexTextures:texturesToBindVertex
withRange:vertexSamplerRange];
[mContext->currentRenderPassEncoder setVertexSamplerStates:samplersToBindVertex
withRange:vertexSamplerRange];
// Bind the user vertex buffers.
id<MTLTexture> texturesToBindFragment[MAX_FRAGMENT_SAMPLER_COUNT] = {};
id<MTLSamplerState> samplersToBindFragment[MAX_FRAGMENT_SAMPLER_COUNT] = {};
enumerateSamplerGroups(program, ShaderType::FRAGMENT,
[this, &getTextureToBind, &getSamplerToBind, &texturesToBindFragment, &samplersToBindFragment](
const SamplerDescriptor* sampler, uint8_t binding) {
// We currently only support a max of MAX_FRAGMENT_SAMPLER_COUNT samplers. Ignore any additional
// samplers that may be bound.
if (binding >= MAX_FRAGMENT_SAMPLER_COUNT) {
return;
}
auto& textureToBind = texturesToBindFragment[binding];
textureToBind = getTextureToBind(sampler);
if (!textureToBind) {
utils::slog.w << "Warning: no texture bound at binding point " << (size_t) binding
<< " at the fragment shader." << utils::io::endl;
textureToBind = getOrCreateEmptyTexture(mContext);
}
auto& samplerToBind = samplersToBindFragment[binding];
samplerToBind = getSamplerToBind(sampler);
});
// Assign a default sampler to empty slots, in case Filament hasn't bound all samplers.
// Metal requires all samplers referenced in shaders to be bound.
for (auto& sampler : samplersToBindFragment) {
if (!sampler) {
sampler = mContext->samplerStateCache.getOrCreateState({});
}
}
NSRange fragmentSamplerRange = NSMakeRange(0, MAX_FRAGMENT_SAMPLER_COUNT);
[mContext->currentRenderPassEncoder setFragmentTextures:texturesToBindFragment
withRange:fragmentSamplerRange];
[mContext->currentRenderPassEncoder setFragmentSamplerStates:samplersToBindFragment
withRange:fragmentSamplerRange];
// Bind the vertex buffers.
MetalBuffer* buffers[MAX_VERTEX_BUFFER_COUNT];
size_t vertexBufferOffsets[MAX_VERTEX_BUFFER_COUNT];
MetalBuffer* buffers[MAX_VERTEX_BUFFER_COUNT] = {};
size_t vertexBufferOffsets[MAX_VERTEX_BUFFER_COUNT] = {};
size_t bufferIndex = 0;
auto vb = primitive->vertexBuffer;
@@ -1449,14 +1681,14 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
const auto bufferCount = bufferIndex;
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder,
VERTEX_BUFFER_START, MetalBuffer::Stage::VERTEX, buffers,
USER_VERTEX_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX, buffers,
vertexBufferOffsets, bufferCount);
// Bind the zero buffer, used for missing vertex attributes.
static const char bytes[16] = { 0 };
[mContext->currentRenderPassEncoder setVertexBytes:bytes
length:16
atIndex:(VERTEX_BUFFER_START + ZERO_VERTEX_BUFFER)];
atIndex:ZERO_VERTEX_BUFFER_BINDING];
MetalIndexBuffer* indexBuffer = primitive->indexBuffer;
@@ -1470,6 +1702,70 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
instanceCount:instanceCount];
}
void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"dispatchCompute must be called outside of a render pass.");
auto mtlProgram = handle_cast<MetalProgram>(program);
// If the material debugger is enabled, avoid fatal (or cascading) errors and that can occur
// during the draw call when the program is invalid. The shader compile error has already been
// dumped to the console at this point, so it's fine to simply return early.
if (FILAMENT_ENABLE_MATDBG && UTILS_UNLIKELY(!mtlProgram->isValid)) {
return;
}
assert_invariant(mtlProgram->isValid && mtlProgram->computeFunction);
id<MTLComputeCommandEncoder> computeEncoder =
[getPendingCommandBuffer(mContext) computeCommandEncoder];
NSError* error = nil;
id<MTLComputePipelineState> computePipelineState =
[mContext->device newComputePipelineStateWithFunction:mtlProgram->computeFunction
error:&error];
if (error) {
auto description = [error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
utils::slog.e << description << utils::io::endl;
}
assert_invariant(!error);
// Bind uniform buffers.
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
NSUInteger uniformOffsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
[&uniformsToBind, &uniformOffsets](const BufferState& state, MetalBuffer* buffer,
uint32_t index) {
uniformsToBind[index] = buffer;
uniformOffsets[index] = state.offset;
});
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), computeEncoder,
UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::COMPUTE, uniformsToBind,
uniformOffsets, Program::UNIFORM_BINDING_COUNT);
// Bind SSBOs.
MetalBuffer* ssbosToBind[MAX_SSBO_COUNT] = { nil };
NSUInteger ssboOffsets[MAX_SSBO_COUNT] = { 0 };
enumerateBoundBuffers(BufferObjectBinding::SHADER_STORAGE,
[&ssbosToBind, &ssboOffsets](const BufferState& state, MetalBuffer* buffer,
uint32_t index) {
ssbosToBind[index] = buffer;
ssboOffsets[index] = state.offset;
});
MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), computeEncoder, SSBO_BINDING_START,
MetalBuffer::Stage::COMPUTE, ssbosToBind, ssboOffsets, MAX_SSBO_COUNT);
[computeEncoder setComputePipelineState:computePipelineState];
MTLSize threadgroupsPerGrid = MTLSizeMake(workGroupCount.x, workGroupCount.y, workGroupCount.z);
// FIXME: the threadgroup size should be specified in the Program
MTLSize threadsPerThreadgroup = MTLSizeMake(16u, 1u, 1u);
[computeEncoder dispatchThreadgroups:threadgroupsPerGrid
threadsPerThreadgroup:threadsPerThreadgroup];
[computeEncoder endEncoding];
}
void MetalDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"beginTimerQuery must be called outside of a render pass.");
@@ -1484,46 +1780,33 @@ void MetalDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
mContext->timerQueryImpl->endTimeElapsedQuery(tq);
}
void MetalDriver::enumerateSamplerGroups(
const MetalProgram* program, ShaderType shaderType,
const std::function<void(const SamplerDescriptor*, size_t)>& f) {
auto& samplerBlockInfo = (shaderType == ShaderType::VERTEX) ?
program->vertexSamplerBlockInfo : program->fragmentSamplerBlockInfo;
auto maxSamplerCount = (shaderType == ShaderType::VERTEX) ?
MAX_VERTEX_SAMPLER_COUNT : MAX_FRAGMENT_SAMPLER_COUNT;
for (size_t bindingIdx = 0; bindingIdx != maxSamplerCount; ++bindingIdx) {
auto& blockInfo = samplerBlockInfo[bindingIdx];
if (blockInfo.samplerGroup == UINT8_MAX) {
continue;
void MetalDriver::enumerateBoundBuffers(BufferObjectBinding bindingType,
const std::function<void(const BufferState&, MetalBuffer*, uint32_t)>& f) {
assert_invariant(bindingType == BufferObjectBinding::UNIFORM ||
bindingType == BufferObjectBinding::SHADER_STORAGE);
auto enumerate = [&](auto arrayType){
for (auto i = 0u; i < arrayType.size(); i++) {
const auto& thisBuffer = arrayType[i];
if (!thisBuffer.bound) {
continue;
}
f(thisBuffer, thisBuffer.buffer->getBuffer(), i);
}
};
const auto* metalSamplerGroup = mContext->samplerBindings[blockInfo.samplerGroup];
if (!metalSamplerGroup) {
// Do not emit warning here. For example this can arise when skinning is enabled
// and the morphing texture is unused.
continue;
}
SamplerGroup* sb = metalSamplerGroup->sb.get();
const SamplerDescriptor* boundSampler = sb->data() + blockInfo.sampler;
if (!boundSampler->t) {
continue;
}
f(boundSampler, bindingIdx);
switch (bindingType) {
default:
case (BufferObjectBinding::UNIFORM):
enumerate(mContext->uniformState);
break;
case (BufferObjectBinding::SHADER_STORAGE):
enumerate(mContext->ssboState);
break;
}
}
void MetalDriver::enumerateBoundUniformBuffers(
const std::function<void(const UniformBufferState&, MetalBuffer*, uint32_t)>& f) {
for (uint32_t i = 0; i < Program::BINDING_COUNT; i++) {
auto& thisUniform = mContext->uniformState[i];
if (!thisUniform.bound) {
continue;
}
f(thisUniform, thisUniform.buffer->getBuffer(), i);
}
void MetalDriver::resetState(int) {
}
// explicit instantiation of the Dispatcher
+128 -26
View File
@@ -118,15 +118,18 @@ private:
class MetalBufferObject : public HwBufferObject {
public:
MetalBufferObject(MetalContext& context, BufferUsage usage, uint32_t byteCount);
MetalBufferObject(MetalContext& context, BufferObjectBinding bindingType, BufferUsage usage,
uint32_t byteCount);
void updateBuffer(void* data, size_t size, uint32_t byteOffset);
void updateBufferUnsynchronized(void* data, size_t size, uint32_t byteOffset);
MetalBuffer* getBuffer() { return &buffer; }
// Tracks which uniform buffers this buffer object is bound into.
static_assert(Program::BINDING_COUNT <= 32);
// Tracks which uniform/ssbo buffers this buffer object is bound into.
static_assert(Program::UNIFORM_BINDING_COUNT <= 32);
static_assert(MAX_SSBO_COUNT <= 32);
utils::bitset32 boundUniformBuffers;
utils::bitset32 boundSsbos;
private:
MetalBuffer buffer;
@@ -163,14 +166,9 @@ struct MetalProgram : public HwProgram {
id<MTLFunction> vertexFunction;
id<MTLFunction> fragmentFunction;
id<MTLFunction> computeFunction;
struct SamplerBlockInfo {
uint8_t samplerGroup = UINT8_MAX;
uint8_t sampler = UINT8_MAX;
};
std::array<SamplerBlockInfo, MAX_VERTEX_SAMPLER_COUNT> vertexSamplerBlockInfo;
std::array<SamplerBlockInfo, MAX_FRAGMENT_SAMPLER_COUNT> fragmentSamplerBlockInfo;
Program::SamplerGroupInfo samplerGroupInfo;
bool isValid = false;
};
@@ -188,7 +186,8 @@ struct PixelBufferShape {
MTLSize size, uint32_t byteOffset);
};
struct MetalTexture : public HwTexture {
class MetalTexture : public HwTexture {
public:
MetalTexture(MetalContext& context, SamplerType target, uint8_t levels, TextureFormat format,
uint8_t samples, uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a)
@@ -201,40 +200,144 @@ struct MetalTexture : public HwTexture {
~MetalTexture();
// Returns an id<MTLTexture> suitable for reading in a shader, taking into account swizzle and
// LOD clamping.
id<MTLTexture> getMtlTextureForRead() noexcept;
// Returns the id<MTLTexture> for attaching to a render pass.
id<MTLTexture> getMtlTextureForWrite() noexcept {
return texture;
}
void loadImage(uint32_t level, MTLRegion region, PixelBufferDescriptor& p) noexcept;
void loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffset, uint32_t slice,
PixelBufferDescriptor const& data) noexcept;
void loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion region, PixelBufferDescriptor const& data,
const PixelBufferShape& shape);
void loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region, PixelBufferDescriptor const& data,
const PixelBufferShape& shape);
void generateMipmaps() noexcept;
// A texture starts out with none of its mip levels (also referred to as LODs) available for
// reading. 3 actions update the range of LODs available:
// - calling loadImage
// - calling generateMipmaps
// - using the texture as a render target attachment
// The range of available mips can only increase, never decrease.
// A texture's available mips are consistent throughout a render pass.
void updateLodRange(uint32_t level);
void updateLodRange(uint32_t minLevel, uint32_t maxLevel);
// Returns true if the texture has all of its mip levels accessible for reading.
// For any MetalTexture, once this is true, will always return true.
// The value returned will remain consistent for an entire render pass.
bool allLodsValid() const {
return minLod == 0 && maxLod == levels - 1;
}
static MTLPixelFormat decidePixelFormat(MetalContext* context, TextureFormat format);
MetalContext& context;
MetalExternalImage externalImage;
id<MTLTexture> texture = nil;
// A "sidecar" texture used to implement automatic MSAA resolve.
// This is created by MetalRenderTarget and stored here so it can be used with multiple
// render targets.
id<MTLTexture> msaaSidecar = nil;
MTLPixelFormat devicePixelFormat;
private:
void loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffset, uint32_t slice,
PixelBufferDescriptor const& data) noexcept;
void loadWithCopyBuffer(uint32_t level, uint32_t slice, MTLRegion region, PixelBufferDescriptor const& data,
const PixelBufferShape& shape);
void loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region, PixelBufferDescriptor const& data,
const PixelBufferShape& shape);
id<MTLTexture> texture = nil;
// If non-nil, a swizzled texture view to use instead of "texture".
// Filament swizzling only affects texture reads, so this should not be used when the texture is
// bound as a render target attachment.
id<MTLTexture> swizzledTextureView = nil;
id<MTLTexture> lodTextureView = nil;
MTLPixelFormat devicePixelFormat;
uint32_t minLod = UINT_MAX;
uint32_t maxLod = 0;
};
struct MetalSamplerGroup : public HwSamplerGroup {
// NOTE: we have to use out-of-line allocation here because the size of a Handle<> is limited
std::unique_ptr<SamplerGroup> sb; // FIXME: this shouldn't depend on filament::SamplerGroup
explicit MetalSamplerGroup(size_t size) noexcept : sb(new SamplerGroup(size)) { }
class MetalSamplerGroup : public HwSamplerGroup {
public:
explicit MetalSamplerGroup(size_t size) noexcept
: size(size),
textureHandles(size, Handle<HwTexture>()),
textures(size, nil),
samplers(size, nil) {}
inline void setTextureHandle(size_t index, Handle<HwTexture> th) {
assert_invariant(!finalized);
textureHandles[index] = th;
}
#ifndef NDEBUG
// This method is only used for debugging, to ensure all texture handles are alive.
const auto& getTextureHandles() const {
return textureHandles;
}
#endif
// Encode a MTLTexture into this SamplerGroup at the given index.
inline void setFinalizedTexture(size_t index, id<MTLTexture> t) {
assert_invariant(!finalized);
textures[index] = t;
}
// Encode a MTLSamplerState into this SamplerGroup at the given index.
inline void setFinalizedSampler(size_t index, id<MTLSamplerState> s) {
assert_invariant(!finalized);
samplers[index] = s;
}
// A SamplerGroup is "finalized" when all of its textures have been set and is ready for use in
// a draw call.
// Once a SamplerGroup is finalized, it must be reset or mutated to be written into again.
void finalize();
bool isFinalized() const noexcept { return finalized; }
// Both of these methods "unfinalize" a SamplerGroup, allowing it to be updated via calls to
// setFinalizedTexture or setFinalizedSampler. The difference is that when reset is called, all
// the samplers/textures must be rebound. The MTLArgumentEncoder must be specified, in case
// the texture types have changed.
// Mutate re-encodes the current set of samplers/textures into the new argument
// buffer.
void reset(id<MTLCommandBuffer> cmdBuffer, id<MTLArgumentEncoder> e);
void mutate(id<MTLCommandBuffer> cmdBuffer);
id<MTLBuffer> getArgumentBuffer() const {
assert_invariant(finalized);
return argBuffer->getCurrentAllocation().first;
}
NSUInteger getArgumentBufferOffset() const {
return argBuffer->getCurrentAllocation().second;
}
inline std::pair<Handle<HwTexture>, id<MTLTexture>> getFinalizedTexture(size_t index) {
return {textureHandles[index], textures[index]};
}
// Calls the Metal useResource:usage:stages: method for all the textures in this SamplerGroup.
void useResources(id<MTLRenderCommandEncoder> renderPassEncoder);
size_t size;
public:
// These vectors are kept in sync with one another.
utils::FixedCapacityVector<Handle<HwTexture>> textureHandles;
utils::FixedCapacityVector<id<MTLTexture>> textures;
utils::FixedCapacityVector<id<MTLSamplerState>> samplers;
id<MTLArgumentEncoder> encoder;
std::unique_ptr<MetalRingBuffer> argBuffer = nullptr;
bool finalized = false;
};
class MetalRenderTarget : public HwRenderTarget {
@@ -248,7 +351,7 @@ public:
Attachment() = default;
Attachment(MetalTexture* metalTexture, uint8_t level = 0, uint16_t layer = 0) :
level(level), layer(layer),
texture(metalTexture->texture),
texture(metalTexture->getMtlTextureForWrite()),
metalTexture(metalTexture) { }
id<MTLTexture> getTexture() const {
@@ -330,8 +433,7 @@ private:
static MTLLoadAction getLoadAction(const RenderPassParams& params, TargetBufferFlags buffer);
static MTLStoreAction getStoreAction(const RenderPassParams& params, TargetBufferFlags buffer);
static id<MTLTexture> createMultisampledTexture(id<MTLDevice> device, MTLPixelFormat format,
uint32_t width, uint32_t height, uint8_t samples);
id<MTLTexture> createMultisampledTexture(MTLPixelFormat format, uint32_t width, uint32_t height, uint8_t samples) const;
MetalContext* context;
bool defaultRenderTarget = false;
+166 -59
View File
@@ -22,8 +22,11 @@
#include <filament/SwapChain.h>
#include <backend/DriverEnums.h>
#include "private/backend/BackendUtils.h"
#include <utils/compiler.h>
#include <utils/Panic.h>
#include <utils/trap.h>
#include <utils/debug.h>
@@ -268,8 +271,9 @@ void MetalSwapChain::scheduleFrameCompletedCallback() {
}];
}
MetalBufferObject::MetalBufferObject(MetalContext& context, BufferUsage usage, uint32_t byteCount)
: HwBufferObject(byteCount), buffer(context, usage, byteCount) {}
MetalBufferObject::MetalBufferObject(MetalContext& context, BufferObjectBinding bindingType,
BufferUsage usage, uint32_t byteCount)
: HwBufferObject(byteCount), buffer(context, bindingType, usage, byteCount) {}
void MetalBufferObject::updateBuffer(void* data, size_t size, uint32_t byteOffset) {
buffer.copyIntoBuffer(data, size, byteOffset);
@@ -285,7 +289,7 @@ MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, uint8_t bufferCount,
MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
buffer(context, usage, elementSize * indexCount, true) { }
buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { }
void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer*
indexBuffer) {
@@ -296,9 +300,8 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalInde
vertexDescription = {};
// Each attribute gets its own vertex buffer.
uint32_t bufferIndex = 0;
// Each attribute gets its own vertex buffer, starting at logical buffer 1.
uint32_t bufferIndex = 1;
for (uint32_t attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) {
const auto& attribute = vertexBuffer->attributes[attributeIndex];
if (attribute.buffer == Attribute::BUFFER_UNUSED) {
@@ -310,10 +313,10 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalInde
// shader to read from missing vertex attributes.
vertexDescription.attributes[attributeIndex] = {
.format = format,
.buffer = ZERO_VERTEX_BUFFER,
.buffer = ZERO_VERTEX_BUFFER_LOGICAL_INDEX,
.offset = 0
};
vertexDescription.layouts[ZERO_VERTEX_BUFFER] = {
vertexDescription.layouts[ZERO_VERTEX_BUFFER_LOGICAL_INDEX] = {
.step = MTLVertexStepFunctionConstant,
.stride = 16
};
@@ -337,12 +340,12 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalInde
MetalProgram::MetalProgram(id<MTLDevice> device, const Program& program) noexcept
: HwProgram(program.getName()), vertexFunction(nil), fragmentFunction(nil),
isValid(false) {
computeFunction(nil), isValid(false) {
using MetalFunctionPtr = __strong id<MTLFunction>*;
static_assert(Program::SHADER_TYPE_COUNT == 2, "Only vertex and fragment shaders expected.");
MetalFunctionPtr shaderFunctions[2] = { &vertexFunction, &fragmentFunction };
static_assert(Program::SHADER_TYPE_COUNT == 3, "Only vertex, fragment, and/or compute shaders expected.");
MetalFunctionPtr shaderFunctions[3] = { &vertexFunction, &fragmentFunction, &computeFunction };
const auto& sources = program.getShadersSource();
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
@@ -373,51 +376,37 @@ MetalProgram::MetalProgram(id<MTLDevice> device, const Program& program) noexcep
return;
}
*shaderFunctions[i] = [library newFunctionWithName:@"main0"];
MTLFunctionConstantValues* constants = [MTLFunctionConstantValues new];
auto const& specializationConstants = program.getSpecializationConstants();
for (auto const& sc : specializationConstants) {
const std::array<MTLDataType, 3> types{
MTLDataTypeInt, MTLDataTypeFloat, MTLDataTypeBool };
std::visit([&sc, constants, type = types[sc.value.index()]](auto&& arg) {
[constants setConstantValue:&arg
type:type
atIndex:sc.id];
}, sc.value);
}
id<MTLFunction> function = [library newFunctionWithName:@"main0"
constantValues:constants
error:&error];
assert_invariant(function);
*shaderFunctions[i] = function;
}
UTILS_UNUSED_IN_RELEASE const bool isRasterizationProgram =
vertexFunction != nil && fragmentFunction != nil;
UTILS_UNUSED_IN_RELEASE const bool isComputeProgram = computeFunction != nil;
// The program must be either a rasterization program XOR a compute program.
assert_invariant(isRasterizationProgram != isComputeProgram);
// All stages of the program have compiled successfully, this is a valid program.
isValid = true;
// This calculates Metal resource binding indices. Filament's sampler bindings range from 0-31
// across both vertex and fragment stages. However, Metal binding indices must be < 16, with a
// separate binding "namespace" for each stage. So, we recompute binding indices for each shader
// stage. This logic should match updateResourceBinding in GLSLPostProcessor.cpp
// This is an example how binding indices each of shader stages is generated from sampler's
// bindings. Below is sampler's bindings.
// 0 shadowMap { fragment }
// 1 structure { fragment }
// 2 targets { vertex }
// 3 color { vertex | fragment }
// 4 depth { vertex | fragment }
// Below is generated vertex sampler binding indices.
// 0 targets
// 1 color
// 2 depth
// Below is generated fragment sampler binding indices.
// 0 shadowMap
// 1 structure
// 2 color
// 3 depth
auto& samplerGroupInfo = program.getSamplerGroupInfo();
for (size_t shaderType = 0; shaderType != PIPELINE_STAGE_COUNT; ++shaderType) {
size_t bindingIdx = 0;
auto& samplerBlockInfo = (shaderType == ShaderType::VERTEX) ? vertexSamplerBlockInfo
: fragmentSamplerBlockInfo;
for (size_t samplerGroupIdx = 0; samplerGroupIdx != SAMPLER_GROUP_COUNT; ++samplerGroupIdx) {
auto& groupData = samplerGroupInfo[samplerGroupIdx];
auto stageFlags = groupData.stageFlags;
if (!hasShaderType(stageFlags, static_cast<ShaderType>(shaderType))) {
continue;
}
auto& samplers = groupData.samplers;
for (size_t samplerIdx = 0, c = samplers.size(); samplerIdx != c; ++samplerIdx) {
samplerBlockInfo[bindingIdx].samplerGroup = samplerGroupIdx;
samplerBlockInfo[bindingIdx].sampler = samplerIdx;
++bindingIdx;
}
}
}
// Save this program's SamplerGroupInfo, it's used during draw calls to bind sampler groups to
// the appropriate stage(s).
samplerGroupInfo = program.getSamplerGroupInfo();
}
MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t levels,
@@ -532,14 +521,33 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
: HwTexture(target, levels, samples, width, height, depth, format, usage), context(context),
externalImage(context) {
texture = metalTexture;
minLod = 0;
maxLod = levels - 1;
updateLodRange(0, levels - 1);
}
MetalTexture::~MetalTexture() {
externalImage.set(nullptr);
}
id<MTLTexture> MetalTexture::getMtlTextureForRead() noexcept {
if (lodTextureView) {
return lodTextureView;
}
// The texture's swizzle remains constant throughout its lifetime, however its LOD range can
// change. We'll cache the LOD view, and set lodTextureView to nil if minLod or maxLod is
// updated.
id<MTLTexture> t = swizzledTextureView ? swizzledTextureView : texture;
if (!t) {
return nil;
}
if (UTILS_UNLIKELY(minLod > maxLod)) {
// If the texture does not have any available LODs, provide a view of only level 0.
// Filament should prevent this from ever occurring.
lodTextureView = createTextureViewWithLodRange(t, 0, 0);
return lodTextureView;
}
lodTextureView = createTextureViewWithLodRange(t, minLod, maxLod);
return lodTextureView;
}
MTLPixelFormat MetalTexture::decidePixelFormat(MetalContext* context, TextureFormat format) {
const MTLPixelFormat metalFormat = getMetalFormat(context, format);
@@ -661,6 +669,13 @@ void MetalTexture::loadImage(uint32_t level, MTLRegion region, PixelBufferDescri
updateLodRange(level);
}
void MetalTexture::generateMipmaps() noexcept {
id <MTLBlitCommandEncoder> blitEncoder = [getPendingCommandBuffer(&context) blitCommandEncoder];
[blitEncoder generateMipmapsForTexture:texture];
[blitEncoder endEncoding];
updateLodRange(0, texture.mipmapLevelCount - 1);
}
void MetalTexture::loadSlice(uint32_t level, MTLRegion region, uint32_t byteOffset, uint32_t slice,
PixelBufferDescriptor const& data) noexcept {
const PixelBufferShape shape = PixelBufferShape::compute(data, format, region.size, byteOffset);
@@ -781,8 +796,94 @@ void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region
}
void MetalTexture::updateLodRange(uint32_t level) {
assert_invariant(!isInRenderPass(&context));
minLod = std::min(minLod, level);
maxLod = std::max(maxLod, level);
lodTextureView = nil;
}
void MetalTexture::updateLodRange(uint32_t min, uint32_t max) {
assert_invariant(!isInRenderPass(&context));
assert_invariant(min <= max);
minLod = std::min(minLod, min);
maxLod = std::max(maxLod, max);
lodTextureView = nil;
}
void MetalSamplerGroup::finalize() {
assert_invariant(encoder);
// TODO: we should be able to encode textures and samplers inside setFinalizedTexture and
// setFinalizedSampler as they become available, but Metal doesn't seem to like this; the arg
// buffer gets encoded incorrectly. This warrants more investigation.
auto [buffer, offset] = argBuffer->getCurrentAllocation();
[encoder setArgumentBuffer:buffer offset:offset];
// Encode all textures and samplers.
for (size_t s = 0; s < size; s++) {
[encoder setTexture:textures[s] atIndex:(s * 2 + 0)];
[encoder setSamplerState:samplers[s] atIndex:(s * 2 + 1)];
}
finalized = true;
}
void MetalSamplerGroup::reset(id<MTLCommandBuffer> cmdBuffer, id<MTLArgumentEncoder> e) {
encoder = e;
// The number of slots in the ring buffer we use to manage argument buffer allocations.
// This number was chosen to avoid running out of slots and having to allocate a "fallback"
// buffer when SamplerGroups are updated multiple times a frame. This value can reduced after
// auditing Filament's calls to updateSamplerGroup, which should be as few times as possible.
// For example, the bloom downsample pass should be refactored to maintain two separate
// MaterialInstances instead of "ping ponging" between two texture bindings, which causes a
// single SamplerGroup to be updated many times a frame.
static constexpr auto METAL_ARGUMENT_BUFFER_SLOTS = 32;
MTLSizeAndAlign argBufferLayout;
argBufferLayout.size = encoder.encodedLength;
argBufferLayout.align = encoder.alignment;
// Chances are, even though the MTLArgumentEncoder might change, the required size and alignment
// probably won't. So we can re-use the previous ring buffer.
if (UTILS_UNLIKELY(!argBuffer || !argBuffer->canAccomodateLayout(argBufferLayout))) {
argBuffer = std::make_unique<MetalRingBuffer>(encoder.device, MTLResourceStorageModeShared,
argBufferLayout, METAL_ARGUMENT_BUFFER_SLOTS);
} else {
argBuffer->createNewAllocation(cmdBuffer);
}
// Clear all textures and samplers.
assert_invariant(textureHandles.size() == textures.size());
assert_invariant(textures.size() == samplers.size());
for (size_t s = 0; s < textureHandles.size(); s++) {
textureHandles[s] = {};
textures[s] = nil;
samplers[s] = nil;
}
finalized = false;
}
void MetalSamplerGroup::mutate(id<MTLCommandBuffer> cmdBuffer) {
assert_invariant(finalized); // only makes sense to mutate if this sampler group is finalized
assert_invariant(argBuffer);
argBuffer->createNewAllocation(cmdBuffer);
finalized = false;
}
void MetalSamplerGroup::useResources(id<MTLRenderCommandEncoder> renderPassEncoder) {
assert_invariant(finalized);
if (@available(iOS 13, *)) {
// TODO: pass only the appropriate stages to useResources.
[renderPassEncoder useResources:textures.data()
count:textures.size()
usage:MTLResourceUsageRead | MTLResourceUsageSample
stages:MTLRenderStageFragment | MTLRenderStageVertex];
} else {
[renderPassEncoder useResources:textures.data()
count:textures.size()
usage:MTLResourceUsageRead | MTLResourceUsageSample];
}
}
MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint32_t height,
@@ -815,7 +916,7 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
if (samples > 1 && color[i].getSampleCount() == 1) {
auto& sidecar = color[i].metalTexture->msaaSidecar;
if (!sidecar) {
sidecar = createMultisampledTexture(context->device, color[i].getPixelFormat(),
sidecar = createMultisampledTexture(color[i].getPixelFormat(),
color[i].metalTexture->width, color[i].metalTexture->height, samples);
}
}
@@ -840,7 +941,7 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
if (samples > 1 && depth.getSampleCount() == 1) {
auto& sidecar = depth.metalTexture->msaaSidecar;
if (!sidecar) {
sidecar = createMultisampledTexture(context->device, depth.getPixelFormat(),
sidecar = createMultisampledTexture(depth.getPixelFormat(),
depth.metalTexture->width, depth.metalTexture->height, samples);
}
}
@@ -865,7 +966,7 @@ MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint
if (samples > 1 && stencil.getSampleCount() == 1) {
auto& sidecar = stencil.metalTexture->msaaSidecar;
if (!sidecar) {
sidecar = createMultisampledTexture(context->device, stencil.getPixelFormat(),
sidecar = createMultisampledTexture(stencil.getPixelFormat(),
stencil.metalTexture->width, stencil.metalTexture->height, samples);
}
}
@@ -1049,8 +1150,8 @@ MTLStoreAction MetalRenderTarget::getStoreAction(const RenderPassParams& params,
return MTLStoreActionStore;
}
id<MTLTexture> MetalRenderTarget::createMultisampledTexture(id<MTLDevice> device,
MTLPixelFormat format, uint32_t width, uint32_t height, uint8_t samples) {
id<MTLTexture> MetalRenderTarget::createMultisampledTexture(MTLPixelFormat format,
uint32_t width, uint32_t height, uint8_t samples) const {
MTLTextureDescriptor* descriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format
width:width
@@ -1061,7 +1162,13 @@ id<MTLTexture> MetalRenderTarget::createMultisampledTexture(id<MTLDevice> device
descriptor.usage = MTLTextureUsageRenderTarget;
descriptor.resourceOptions = MTLResourceStorageModePrivate;
return [device newTextureWithDescriptor:descriptor];
if (context->supportsMemorylessRenderTargets) {
if (@available(macOS 11.0, *)) {
descriptor.resourceOptions = MTLResourceStorageModeMemoryless;
}
}
return [context->device newTextureWithDescriptor:descriptor];
}
math::uint2 MetalRenderTarget::getAttachmentSize() noexcept {
+79 -34
View File
@@ -24,6 +24,8 @@
#include <backend/DriverEnums.h>
#include <utils/FixedCapacityVector.h>
#include <memory>
#include <tsl/robin_map.h>
#include <utils/Hash.h>
@@ -35,17 +37,42 @@ inline bool operator==(const SamplerParams& lhs, const SamplerParams& rhs) {
return lhs.u == rhs.u;
}
static constexpr uint32_t SAMPLER_GROUP_COUNT = Program::BINDING_COUNT;
static constexpr uint32_t SAMPLER_BINDING_COUNT = MAX_SAMPLER_COUNT;
static constexpr uint32_t VERTEX_BUFFER_START = Program::BINDING_COUNT;
// Rasterization Bindings
// ----------------------
// Bindings Buffer name Count
// ------------------------------------------------------
// 0 Zero buffer (placeholder vertex buffer) 1
// 1-16 Filament vertex buffers 16 limited by MAX_VERTEX_BUFFER_COUNT
// 17-26 Uniform buffers 10 Program::UNIFORM_BINDING_COUNT
// 27-30 Sampler groups (argument buffers) 4 Program::SAMPLER_BINDING_COUNT
//
// Total 31
// The "zero" buffer is a small buffer for missing attributes that resides in the vertex slot
// immediately following any user-provided vertex buffers.
static constexpr uint32_t ZERO_VERTEX_BUFFER = MAX_VERTEX_BUFFER_COUNT;
// Compute Bindings
// ----------------------
// Bindings Buffer name Count
// ------------------------------------------------------
// 0-3 SSBO buffers 4 MAX_SSBO_COUNT
// 17-26 Uniform buffers 10 Program::UNIFORM_BINDING_COUNT
// 27-30 Sampler groups (argument buffers) 4 Program::SAMPLER_BINDING_COUNT
//
// Total 18
// The total number of vertex buffer "slots" that the Metal backend can bind.
// + 1 to account for the zero buffer.
static constexpr uint32_t VERTEX_BUFFER_COUNT = MAX_VERTEX_BUFFER_COUNT + 1;
// + 1 to account for the zero buffer, a placeholder buffer used internally by the Metal backend.
// MAX_VERTEX_BUFFER_COUNT represents the max number of vertex buffers Filament can bind.
static constexpr uint32_t LOGICAL_VERTEX_BUFFER_COUNT = MAX_VERTEX_BUFFER_COUNT + 1;
// The "zero" buffer is a small buffer for missing attributes.
static constexpr uint32_t ZERO_VERTEX_BUFFER_LOGICAL_INDEX = 0u;
static constexpr uint32_t ZERO_VERTEX_BUFFER_BINDING = 0u;
static constexpr uint32_t USER_VERTEX_BUFFER_BINDING_START = 1u;
// These constants must match the equivalent in CodeGenerator.h.
static constexpr uint32_t UNIFORM_BUFFER_BINDING_START = 17u;
static constexpr uint32_t SSBO_BINDING_START = 0u;
static constexpr uint32_t SAMPLER_GROUP_BINDING_START = 27u;
// Forward declarations necessary here, definitions at end of file.
inline bool operator==(const MTLViewport& lhs, const MTLViewport& rhs);
@@ -58,7 +85,7 @@ inline bool operator!=(const MTLViewport& lhs, const MTLViewport& rhs);
struct VertexDescription {
struct Attribute {
MTLVertexFormat format; // 8 bytes
uint32_t buffer; // 4 bytes
uint32_t buffer; // 4 bytes a logical vertex buffer index
uint32_t offset; // 4 bytes
};
struct Layout {
@@ -66,7 +93,8 @@ struct VertexDescription {
uint64_t stride; // 8 bytes
};
Attribute attributes[MAX_VERTEX_ATTRIBUTE_COUNT] = {}; // 256 bytes
Layout layouts[VERTEX_BUFFER_COUNT] = {}; // 272 bytes
// layouts[n] represents the layout of the vertex buffer at logical index n
Layout layouts[LOGICAL_VERTEX_BUFFER_COUNT] = {}; // 272 bytes
bool operator==(const VertexDescription& rhs) const noexcept {
bool result = true;
@@ -126,9 +154,11 @@ static_assert(sizeof(BlendState) == 56, "BlendState is unexpected size.");
// StateCache caches Metal state objects using StateType as a key.
// MetalType is the corresponding Metal API type.
// StateCreator is a functor that creates a new state of type MetalType.
// HashFn is a functor that hashes StateType.
template<typename StateType,
typename MetalType,
typename StateCreator>
typename StateCreator,
typename HashFn = utils::hash::MurmurHashFn<StateType>>
class StateCache {
public:
@@ -141,6 +171,8 @@ public:
void setDevice(id<MTLDevice> device) noexcept { mDevice = device; }
MetalType getOrCreateState(const StateType& state) noexcept {
assert_invariant(mDevice);
// Check if a valid state already exists in the cache.
auto iter = mStateCache.find(state);
if (UTILS_LIKELY(iter != mStateCache.end())) {
@@ -150,6 +182,7 @@ public:
// If we reach this point, we couldn't find one in the cache; create a new one.
const auto& metalObject = creator(mDevice, state);
assert_invariant(metalObject);
mStateCache.emplace(std::make_pair(
state,
@@ -164,7 +197,6 @@ private:
StateCreator creator;
id<MTLDevice> mDevice = nil;
using HashFn = utils::hash::MurmurHashFn<StateType>;
tsl::robin_map<StateType, MetalType, HashFn> mStateCache;
};
@@ -316,38 +348,19 @@ using DepthStencilStateCache = StateCache<DepthStencilState, id<MTLDepthStencilS
class MetalBufferObject;
struct UniformBufferState {
struct BufferState {
MetalBufferObject* buffer = nullptr; // 8 bytes
uint32_t offset = 0; // 4 bytes
bool bound = false; // 1 byte
char padding[3] = { 0 }; // 3 bytes
bool operator==(const UniformBufferState& rhs) const noexcept {
return this->bound == rhs.bound &&
this->buffer == rhs.buffer &&
this->offset == rhs.offset;
}
bool operator!=(const UniformBufferState& rhs) const noexcept {
return !operator==(rhs);
}
};
static_assert(sizeof(UniformBufferState) == 16, "UniformBufferState unexpected size.");
using UniformBufferStateTracker = StateTracker<UniformBufferState>;
// Sampler states
struct SamplerState {
SamplerParams samplerParams;
uint32_t minLod = 0;
uint32_t maxLod = UINT_MAX;
bool operator==(const SamplerState& rhs) const noexcept {
return this->samplerParams == rhs.samplerParams &&
this->minLod == rhs.minLod &&
this->maxLod == rhs.maxLod;
return this->samplerParams == rhs.samplerParams;
}
bool operator!=(const SamplerState& rhs) const noexcept {
@@ -355,7 +368,7 @@ struct SamplerState {
}
};
static_assert(sizeof(SamplerState) == 12, "SamplerState unexpected size.");
static_assert(sizeof(SamplerState) == 4, "SamplerState unexpected size.");
struct SamplerStateCreator {
id<MTLSamplerState> operator()(id<MTLDevice> device, const SamplerState& state) noexcept;
@@ -368,6 +381,38 @@ using SamplerStateCache = StateCache<SamplerState, id<MTLSamplerState>, SamplerS
using CullModeStateTracker = StateTracker<MTLCullMode>;
using WindingStateTracker = StateTracker<MTLWinding>;
// Argument encoder
struct ArgumentEncoderState {
utils::FixedCapacityVector<MTLTextureType> textureTypes;
explicit ArgumentEncoderState(utils::FixedCapacityVector<MTLTextureType>&& types)
: textureTypes(std::move(types)) {}
bool operator==(const ArgumentEncoderState& rhs) const noexcept {
return std::equal(textureTypes.begin(), textureTypes.end(), rhs.textureTypes.begin(),
rhs.textureTypes.end());
}
bool operator!=(const ArgumentEncoderState& rhs) const noexcept {
return !operator==(rhs);
}
};
struct ArgumentEncoderHasher {
uint32_t operator()(const ArgumentEncoderState& key) const noexcept {
return utils::hash::murmur3((const uint32_t*)key.textureTypes.data(),
sizeof(MTLTextureType) * key.textureTypes.size() / 4, 0);
}
};
struct ArgumentEncoderCreator {
id<MTLArgumentEncoder> operator()(id<MTLDevice> device, const ArgumentEncoderState& state) noexcept;
};
using ArgumentEncoderCache = StateCache<ArgumentEncoderState, id<MTLArgumentEncoder>,
ArgumentEncoderCreator, ArgumentEncoderHasher>;
} // namespace backend
} // namespace filament
+39 -7
View File
@@ -38,18 +38,18 @@ id<MTLRenderPipelineState> PipelineStateCreator::operator()(id<MTLDevice> device
if (vertexDescription.attributes[i].format > MTLVertexFormatInvalid) {
const auto& attribute = vertexDescription.attributes[i];
vertex.attributes[i].format = attribute.format;
vertex.attributes[i].bufferIndex = VERTEX_BUFFER_START + attribute.buffer;
vertex.attributes[i].bufferIndex = attribute.buffer;
vertex.attributes[i].offset = attribute.offset;
}
}
for (uint32_t i = 0; i < VERTEX_BUFFER_COUNT; i++) {
for (uint32_t i = 0; i < LOGICAL_VERTEX_BUFFER_COUNT; i++) {
if (vertexDescription.layouts[i].stride > 0) {
const auto& layout = vertexDescription.layouts[i];
vertex.layouts[VERTEX_BUFFER_START + i].stride = layout.stride;
vertex.layouts[VERTEX_BUFFER_START + i].stepFunction = layout.step;
vertex.layouts[i].stride = layout.stride;
vertex.layouts[i].stepFunction = layout.step;
if (layout.step == MTLVertexStepFunctionConstant) {
vertex.layouts[VERTEX_BUFFER_START + i].stepRate = 0;
vertex.layouts[i].stepRate = 0;
}
}
}
@@ -137,11 +137,10 @@ id<MTLSamplerState> SamplerStateCreator::operator()(id<MTLDevice> device,
samplerDescriptor.tAddressMode = getAddressMode(params.wrapT);
samplerDescriptor.rAddressMode = getAddressMode(params.wrapR);
samplerDescriptor.maxAnisotropy = 1u << params.anisotropyLog2;
samplerDescriptor.lodMaxClamp = (float) state.maxLod;
samplerDescriptor.lodMinClamp = (float) state.minLod;
samplerDescriptor.compareFunction =
params.compareMode == SamplerCompareMode::NONE ?
MTLCompareFunctionNever : getCompareFunction(params.compareFunc);
samplerDescriptor.supportArgumentBuffers = YES;
#if defined(IOS)
// Older Apple devices (and the simulator) don't support setting a comparison function in
@@ -156,5 +155,38 @@ id<MTLSamplerState> SamplerStateCreator::operator()(id<MTLDevice> device,
return [device newSamplerStateWithDescriptor:samplerDescriptor];
}
id<MTLArgumentEncoder> ArgumentEncoderCreator::operator()(id<MTLDevice> device,
const ArgumentEncoderState &state) noexcept {
const auto& textureTypes = state.textureTypes;
const auto& count = textureTypes.size();
assert_invariant(count > 0);
// Metal has separate data types for textures versus samplers, so the argument buffer layout
// alternates between texture and sampler, i.e.:
// textureA
// samplerA
// textureB
// samplerB
// etc
NSMutableArray<MTLArgumentDescriptor*>* arguments =
[NSMutableArray arrayWithCapacity:(count * 2)];
for (size_t i = 0; i < count; i++) {
MTLArgumentDescriptor* textureArgument = [MTLArgumentDescriptor argumentDescriptor];
textureArgument.index = i * 2 + 0;
textureArgument.dataType = MTLDataTypeTexture;
textureArgument.textureType = textureTypes[i];
textureArgument.access = MTLArgumentAccessReadOnly;
[arguments addObject:textureArgument];
MTLArgumentDescriptor* samplerArgument = [MTLArgumentDescriptor argumentDescriptor];
samplerArgument.index = i * 2 + 1;
samplerArgument.dataType = MTLDataTypeSampler;
textureArgument.access = MTLArgumentAccessReadOnly;
[arguments addObject:samplerArgument];
}
return [device newArgumentEncoderWithArguments:arguments];
}
} // namespace backend
} // namespace filament
+8
View File
@@ -29,6 +29,14 @@ API_AVAILABLE(ios(13.0))
id<MTLTexture> createTextureViewWithSwizzle(id<MTLTexture> texture,
MTLTextureSwizzleChannels swizzle);
/**
* Creates a texture view of the passed-in texture with the given lod range (inclusive). If the
* range represents all mip levels, simply returns texture.
* lodMax must be greater than or equal to lodMin.
*/
id<MTLTexture> createTextureViewWithLodRange(id<MTLTexture> texture, NSUInteger lodMin,
NSUInteger lodMax);
/**
* Creates a texture view of the passed-in texture with the given layer.
*
+20
View File
@@ -16,6 +16,8 @@
#include "MetalUtils.h"
#include <utils/debug.h>
namespace filament::backend {
id<MTLTexture> createTextureViewWithSwizzle(id<MTLTexture> texture,
@@ -37,6 +39,24 @@ id<MTLTexture> createTextureViewWithSwizzle(id<MTLTexture> texture,
swizzle:swizzle];
}
id<MTLTexture> createTextureViewWithLodRange(id<MTLTexture> texture, NSUInteger lodMin,
NSUInteger lodMax) {
assert_invariant(lodMax >= lodMin);
if (lodMin == 0 && lodMax >= texture.mipmapLevelCount - 1) {
return texture;
}
NSUInteger slices = texture.arrayLength;
if (texture.textureType == MTLTextureTypeCube ||
texture.textureType == MTLTextureTypeCubeArray) {
slices *= 6;
}
NSRange levelRange = NSMakeRange(lodMin, lodMax - lodMin + 1);
return [texture newTextureViewWithPixelFormat:texture.pixelFormat
textureType:texture.textureType
levels:levelRange
slices:NSMakeRange(0, slices)];
}
id<MTLTexture> createTextureViewWithSingleSlice(id<MTLTexture> texture, NSUInteger slice) {
if (texture.textureType == MTLTextureType2D) {
return texture;
+16 -2
View File
@@ -273,8 +273,11 @@ void NoopDriver::commit(Handle<HwSwapChain> sch) {
void NoopDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> ubh) {
}
void NoopDriver::bindUniformBufferRange(uint32_t index, Handle<HwBufferObject> ubh,
uint32_t offset, uint32_t size) {
void NoopDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index,
Handle<HwBufferObject> ubh, uint32_t offset, uint32_t size) {
}
void NoopDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
}
void NoopDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
@@ -301,6 +304,11 @@ 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) {
scheduleDestroy(std::move(p));
}
void NoopDriver::blit(TargetBufferFlags buffers,
Handle<HwRenderTarget> dst, Viewport dstRect,
Handle<HwRenderTarget> src, Viewport srcRect,
@@ -311,10 +319,16 @@ void NoopDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph
uint32_t instanceCount) {
}
void NoopDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
}
void NoopDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
}
void NoopDriver::endTimerQuery(Handle<HwTimerQuery> tqh) {
}
void NoopDriver::resetState(int) {
}
} // namespace filament
+2
View File
@@ -119,6 +119,8 @@ constexpr inline GLenum getBufferBindingType(BufferObjectBinding bindingType) no
return GL_ARRAY_BUFFER;
case BufferObjectBinding::UNIFORM:
return GL_UNIFORM_BUFFER;
case BufferObjectBinding::SHADER_STORAGE:
return GL_SHADER_STORAGE_BUFFER;
}
}
+226 -41
View File
@@ -60,21 +60,30 @@ OpenGLContext::OpenGLContext() noexcept {
glGetIntegerv(GL_MAX_SAMPLES, &gets.max_samples);
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &gets.max_draw_buffers);
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &gets.max_texture_image_units);
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gets.max_combined_texture_image_units);
glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &gets.max_transform_feedback_separate_attribs);
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &gets.max_uniform_buffer_bindings);
glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &gets.uniform_buffer_offset_alignment);
constexpr auto const caps3 = FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_3];
constexpr GLint MAX_VERTEX_SAMPLER_COUNT = caps3.MAX_VERTEX_SAMPLER_COUNT;
constexpr GLint MAX_FRAGMENT_SAMPLER_COUNT = caps3.MAX_FRAGMENT_SAMPLER_COUNT;
if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GLES) {
initExtensionsGLES();
if (state.major == 3) {
assert_invariant(gets.max_texture_image_units >= 16);
if (state.minor >= 0) {
mShaderModel = ShaderModel::MOBILE;
}
assert_invariant(gets.max_combined_texture_image_units >= 32);
if (state.minor >= 1) {
features.multisample_texture = true;
// figure out our feature level
if (gets.max_texture_image_units >= 31 &&
ext.EXT_texture_cube_map_array) {
if (ext.EXT_texture_cube_map_array) {
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_2;
if (gets.max_texture_image_units >= MAX_FRAGMENT_SAMPLER_COUNT &&
gets.max_combined_texture_image_units >=
(MAX_FRAGMENT_SAMPLER_COUNT + MAX_VERTEX_SAMPLER_COUNT)) {
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_3;
}
}
}
}
@@ -82,14 +91,16 @@ OpenGLContext::OpenGLContext() noexcept {
// OpenGL version
initExtensionsGL();
if (state.major == 4) {
if (state.minor >= 1) {
mShaderModel = ShaderModel::DESKTOP;
}
assert_invariant(state.minor >= 1);
mShaderModel = ShaderModel::DESKTOP;
if (state.minor >= 3) {
// cubemap arrays are available as of OpenGL 4.0
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_2;
// figure out our feature level
if (gets.max_texture_image_units >= 31) {
// cubemap arrays are available as of OpenGL 4.0
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_2;
if (gets.max_texture_image_units >= MAX_FRAGMENT_SAMPLER_COUNT &&
gets.max_combined_texture_image_units >=
(MAX_FRAGMENT_SAMPLER_COUNT + MAX_VERTEX_SAMPLER_COUNT)) {
mFeatureLevel = FeatureLevel::FEATURE_LEVEL_3;
}
}
features.multisample_texture = true;
@@ -97,8 +108,8 @@ OpenGLContext::OpenGLContext() noexcept {
// feedback loops are allowed on GL desktop as long as writes are disabled
bugs.allow_read_only_ancillary_feedback_loop = true;
assert_invariant(gets.max_texture_image_units >= 16);
assert_invariant(gets.max_combined_texture_image_units >= 32);
}
#ifdef GL_EXT_texture_filter_anisotropic
if (ext.EXT_texture_filter_anisotropic) {
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gets.max_anisotropy);
@@ -213,6 +224,7 @@ OpenGLContext::OpenGLContext() noexcept {
// TODO: see if we could use `bugs.allow_read_only_ancillary_feedback_loop = true`
}
slog.v << "Feature level: " << +mFeatureLevel << '\n';
slog.v << "Active workarounds: " << '\n';
UTILS_NOUNROLL
for (auto [enabled, name, _] : mBugDatabase) {
@@ -237,31 +249,7 @@ OpenGLContext::OpenGLContext() noexcept {
flush(slog.v);
#endif
/*
* Set our default state
*/
// We need to make sure our internal state matches the GL state when we start.
// (some of these calls may be unneeded as they might be the gl defaults)
glDisable(GL_BLEND);
glDisable(GL_CULL_FACE);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glDisable(GL_DITHER);
glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE);
glDisable(GL_SAMPLE_COVERAGE);
glDisable(GL_POLYGON_OFFSET_FILL);
// Point sprite size and seamless cubemap filtering are disabled by default in desktop GL.
// In OpenGL ES, these flags do not exist because they are always on.
#if BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL
enable(GL_PROGRAM_POINT_SIZE);
#endif
#ifdef GL_ARB_seamless_cube_map
enable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
#endif
setDefaultState();
#ifdef GL_EXT_texture_filter_anisotropic
if (ext.EXT_texture_filter_anisotropic) {
@@ -280,10 +268,6 @@ OpenGLContext::OpenGLContext() noexcept {
}
#endif
#ifdef GL_FRAGMENT_SHADER_DERIVATIVE_HINT
glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_NICEST);
#endif
#if !defined(NDEBUG) && defined(GL_KHR_debug)
if (ext.KHR_debug) {
auto cb = [](GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length,
@@ -315,6 +299,50 @@ OpenGLContext::OpenGLContext() noexcept {
glDebugMessageCallback(cb, nullptr);
}
#endif
}
void OpenGLContext::setDefaultState() noexcept {
// We need to make sure our internal state matches the GL state when we start.
// (some of these calls may be unneeded as they might be the gl defaults)
GLenum caps[] = {
GL_BLEND,
GL_CULL_FACE,
GL_SCISSOR_TEST,
GL_DEPTH_TEST,
GL_STENCIL_TEST,
GL_DITHER,
GL_SAMPLE_ALPHA_TO_COVERAGE,
GL_SAMPLE_COVERAGE,
GL_POLYGON_OFFSET_FILL,
};
size_t capsCount = sizeof(caps) / sizeof(caps[0]);
UTILS_NOUNROLL
for (GLenum capi = 0; capi < capsCount; ++capi) {
size_t capIndex = getIndexForCap(caps[capi]);
GLenum cap = caps[capi];
if (state.enables.caps[capIndex]) {
glEnable(cap);
} else {
glDisable(cap);
}
}
// Point sprite size and seamless cubemap filtering are disabled by default in desktop GL.
// In OpenGL ES, these flags do not exist because they are always on.
#if BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL
glEnable(GL_PROGRAM_POINT_SIZE);
enable(GL_PROGRAM_POINT_SIZE);
#endif
#ifdef GL_ARB_seamless_cube_map
glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
enable(GL_TEXTURE_CUBE_MAP_SEAMLESS);
#endif
#ifdef GL_FRAGMENT_SHADER_DERIVATIVE_HINT
glHint(GL_FRAGMENT_SHADER_DERIVATIVE_HINT, GL_NICEST);
#endif
#if defined(GL_EXT_clip_control) || defined(GL_ARB_clip_control) || defined(GL_VERSION_4_5)
if (ext.EXT_clip_control) {
@@ -511,4 +539,161 @@ void OpenGLContext::deleteVertexArrays(GLsizei n, const GLuint* arrays) noexcept
}
}
void OpenGLContext::resetState() noexcept {
// Force GL state to match the Filament state
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, state.draw_fbo);
glBindFramebuffer(GL_READ_FRAMEBUFFER, state.read_fbo);
// state.program
glUseProgram(state.program.use);
// state.vao
if (state.vao.p) {
glBindVertexArray(state.vao.p->vao);
} else {
bindVertexArray(nullptr);
}
// state.raster
glFrontFace(state.raster.frontFace);
glCullFace(state.raster.cullFace);
glBlendEquationSeparate(state.raster.blendEquationRGB, state.raster.blendEquationA);
glBlendFuncSeparate(
state.raster.blendFunctionSrcRGB,
state.raster.blendFunctionDstRGB,
state.raster.blendFunctionSrcA,
state.raster.blendFunctionDstA
);
glColorMask(
state.raster.colorMask,
state.raster.colorMask,
state.raster.colorMask,
state.raster.colorMask
);
glDepthMask(state.raster.depthMask);
glDepthFunc(state.raster.depthFunc);
// state.stencil
glStencilFuncSeparate(
GL_FRONT,
state.stencil.front.func.func,
state.stencil.front.func.ref,
state.stencil.front.func.mask
);
glStencilFuncSeparate(
GL_BACK,
state.stencil.back.func.func,
state.stencil.back.func.ref,
state.stencil.back.func.mask
);
glStencilOpSeparate(
GL_FRONT,
state.stencil.front.op.sfail,
state.stencil.front.op.dpfail,
state.stencil.front.op.dppass
);
glStencilOpSeparate(
GL_BACK,
state.stencil.back.op.sfail,
state.stencil.back.op.dpfail,
state.stencil.back.op.dppass
);
glStencilMaskSeparate(GL_FRONT, state.stencil.front.stencilMask);
glStencilMaskSeparate(GL_BACK, state.stencil.back.stencilMask);
// state.polygonOffset
glPolygonOffset(state.polygonOffset.factor, state.polygonOffset.units);
// state.enables
setDefaultState();
// state.buffers
// Reset state.buffers to its default state to avoid the complexity and error-prone
// nature of resetting the GL state to its existing state
state.buffers = {};
GLenum bufferTargets[] = {
GL_UNIFORM_BUFFER,
GL_TRANSFORM_FEEDBACK_BUFFER,
#if !defined(__EMSCRIPTEN__)
GL_SHADER_STORAGE_BUFFER,
#endif
GL_ARRAY_BUFFER,
GL_COPY_READ_BUFFER,
GL_COPY_WRITE_BUFFER,
GL_ELEMENT_ARRAY_BUFFER,
GL_PIXEL_PACK_BUFFER,
GL_PIXEL_UNPACK_BUFFER,
};
size_t bufferTargetCount = sizeof(bufferTargets) / sizeof(bufferTargets[0]);
for (size_t targetArrayIndex = 0; targetArrayIndex < bufferTargetCount; ++targetArrayIndex) {
GLenum target = bufferTargets[targetArrayIndex];
glBindBuffer(target, 0);
}
for (size_t bufferIndex = 0; bufferIndex < MAX_BUFFER_BINDINGS; ++bufferIndex) {
if (bufferIndex < (size_t)gets.max_uniform_buffer_bindings) {
glBindBufferBase(GL_UNIFORM_BUFFER, bufferIndex, 0);
}
if (bufferIndex < (size_t)gets.max_transform_feedback_separate_attribs) {
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, bufferIndex, 0);
}
}
// state.textures
// Reset state.textures to its default state to avoid the complexity and error-prone
// nature of resetting the GL state to its existing state
state.textures = {};
const GLuint textureTargets[] = {
GL_TEXTURE_2D,
GL_TEXTURE_2D_ARRAY,
GL_TEXTURE_CUBE_MAP,
GL_TEXTURE_3D,
#if !defined(__EMSCRIPTEN__)
GL_TEXTURE_2D_MULTISAMPLE,
GL_TEXTURE_EXTERNAL_OES,
GL_TEXTURE_CUBE_MAP_ARRAY,
#endif
};
const size_t textureTargetCount = sizeof(textureTargets) / sizeof(textureTargets[0]);
for (GLint unit = 0; unit < gets.max_combined_texture_image_units; ++unit) {
glActiveTexture(GL_TEXTURE0 + unit);
glBindSampler(unit, 0);
for (size_t textureTargetArrayIndex = 0; textureTargetArrayIndex < textureTargetCount; ++textureTargetArrayIndex) {
GLuint target = textureTargets[textureTargetArrayIndex];
glBindTexture(target, 0);
}
}
glActiveTexture(GL_TEXTURE0 + state.textures.active);
// state.unpack
glPixelStorei(GL_UNPACK_ALIGNMENT, state.unpack.alignment);
glPixelStorei(GL_UNPACK_ROW_LENGTH, state.unpack.row_length);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, state.unpack.skip_pixels);
glPixelStorei(GL_UNPACK_SKIP_ROWS, state.unpack.skip_row);
// state.pack
glPixelStorei(GL_PACK_ALIGNMENT, state.pack.alignment);
glPixelStorei(GL_PACK_ROW_LENGTH, state.pack.row_length);
glPixelStorei(GL_PACK_SKIP_PIXELS, state.pack.skip_pixels);
glPixelStorei(GL_PACK_SKIP_ROWS, state.pack.skip_row);
// state.window
glScissor(
state.window.scissor.x,
state.window.scissor.y,
state.window.scissor.z,
state.window.scissor.w
);
glViewport(
state.window.viewport.x,
state.window.viewport.y,
state.window.viewport.z,
state.window.viewport.w
);
glDepthRangef(state.window.depthRange.x, state.window.depthRange.y);
}
} // namesapce filament
+19 -10
View File
@@ -28,6 +28,7 @@
#include <array>
#include <set>
#include <tuple>
#include <utility>
namespace filament::backend {
@@ -35,6 +36,7 @@ namespace filament::backend {
class OpenGLContext {
public:
static constexpr const size_t MAX_TEXTURE_UNIT_COUNT = MAX_SAMPLER_COUNT;
static constexpr const size_t DUMMY_TEXTURE_BINDING = 31; // highest binding less than 32
static constexpr const size_t MAX_BUFFER_BINDINGS = 32;
typedef math::details::TVec4<GLint> vec4gli;
typedef math::details::TVec2<GLclampf> vec2glf;
@@ -71,6 +73,7 @@ public:
ShaderModel getShaderModel() const noexcept { return mShaderModel; }
void resetState() noexcept;
inline void useProgram(GLuint program) noexcept;
@@ -126,6 +129,9 @@ public:
GLint max_samples;
GLint max_uniform_block_size;
GLint max_texture_image_units;
GLint max_combined_texture_image_units;
GLint max_transform_feedback_separate_attribs;
GLint max_uniform_buffer_bindings;
GLint uniform_buffer_offset_alignment;
} gets = {};
@@ -308,7 +314,7 @@ public:
GLsizeiptr size = 0;
} buffers[MAX_BUFFER_BINDINGS];
} targets[2]; // there are only 2 indexed buffer target (uniform and transform feedback)
GLuint genericBinding[8] = { 0 };
GLuint genericBinding[9] = { 0 };
} buffers;
struct {
@@ -406,6 +412,8 @@ private:
}
}
void setDefaultState() noexcept;
static constexpr const size_t TEXTURE_TARGET_COUNT =
sizeof(state.textures.units[0].targets) / sizeof(state.textures.units[0].targets[0]);
@@ -457,17 +465,18 @@ constexpr size_t OpenGLContext::getIndexForCap(GLenum cap) noexcept { //NOLINT
constexpr size_t OpenGLContext::getIndexForBufferTarget(GLenum target) noexcept {
size_t index = 0;
switch (target) {
// The indexed buffers MUST be first in this list
// The indexed buffers MUST be first in this list (those usable with bindBufferRange)
case GL_UNIFORM_BUFFER: index = 0; break;
case GL_TRANSFORM_FEEDBACK_BUFFER: index = 1; break;
case GL_SHADER_STORAGE_BUFFER: index = 2; break;
case GL_ARRAY_BUFFER: index = 2; break;
case GL_COPY_READ_BUFFER: index = 3; break;
case GL_COPY_WRITE_BUFFER: index = 4; break;
case GL_ELEMENT_ARRAY_BUFFER: index = 5; break;
case GL_PIXEL_PACK_BUFFER: index = 6; break;
case GL_PIXEL_UNPACK_BUFFER: index = 7; break;
default: index = 8; break; // should never happen
case GL_ARRAY_BUFFER: index = 3; break;
case GL_COPY_READ_BUFFER: index = 4; break;
case GL_COPY_WRITE_BUFFER: index = 5; break;
case GL_ELEMENT_ARRAY_BUFFER: index = 6; break;
case GL_PIXEL_PACK_BUFFER: index = 7; break;
case GL_PIXEL_UNPACK_BUFFER: index = 8; break;
default: index = 9; break; // should never happen
}
assert_invariant(index < sizeof(state.buffers.genericBinding)/sizeof(state.buffers.genericBinding[0])); // NOLINT(misc-redundant-expression)
return index;
@@ -528,7 +537,7 @@ void OpenGLContext::bindVertexArray(RenderPrimitive const* p) noexcept {
void OpenGLContext::bindBufferRange(GLenum target, GLuint index, GLuint buffer,
GLintptr offset, GLsizeiptr size) noexcept {
size_t targetIndex = getIndexForBufferTarget(target);
assert_invariant(targetIndex <= 1); // validity check
assert_invariant(targetIndex <= 2); // validity check
// this ALSO sets the generic binding
if ( state.buffers.targets[targetIndex].buffers[index].name != buffer
+151 -55
View File
@@ -235,6 +235,9 @@ ShaderModel OpenGLDriver::getShaderModel() const noexcept {
// ------------------------------------------------------------------------------------------------
// Change and track GL state
// ------------------------------------------------------------------------------------------------
void OpenGLDriver::resetState(int) {
mContext.resetState();
}
void OpenGLDriver::bindSampler(GLuint unit, GLuint sampler) noexcept {
mContext.bindSampler(unit, sampler);
@@ -309,9 +312,16 @@ void OpenGLDriver::setStencilState(StencilState ss) noexcept {
mRenderPassStencilWrite |= ss.stencilWrite;
// stencil test / operation
// GL_STENCIL_TEST must be enabled if we're testing OR writing to the stencil buffer.
if (UTILS_LIKELY(
ss.front.stencilFunc == StencilState::StencilFunction::A &&
ss.back.stencilFunc == StencilState::StencilFunction::A)) {
ss.back.stencilFunc == StencilState::StencilFunction::A &&
ss.front.stencilOpDepthFail == StencilOperation::KEEP &&
ss.back.stencilOpDepthFail == StencilOperation::KEEP &&
ss.front.stencilOpStencilFail == StencilOperation::KEEP &&
ss.back.stencilOpStencilFail == StencilOperation::KEEP &&
ss.front.stencilOpDepthStencilPass == StencilOperation::KEEP &&
ss.back.stencilOpDepthStencilPass == StencilOperation::KEEP)) {
// that's equivalent to having the stencil test disabled
gl.disable(GL_STENCIL_TEST);
} else {
@@ -505,8 +515,8 @@ void OpenGLDriver::textureStorage(OpenGLDriver::GLTexture* t,
auto& gl = mContext;
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
switch (t->gl.target) {
case GL_TEXTURE_2D:
@@ -1864,8 +1874,8 @@ void OpenGLDriver::setMinMaxLevels(Handle<HwTexture> th, uint32_t minLevel, uint
auto& gl = mContext;
GLTexture* t = handle_cast<GLTexture *>(th);
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
// Must fit within int8_t.
assert_invariant(minLevel <= 0x7f && maxLevel <= 0x7f);
@@ -1901,8 +1911,8 @@ void OpenGLDriver::generateMipmaps(Handle<HwTexture> th) {
assert_invariant(t->gl.target != GL_TEXTURE_2D_MULTISAMPLE);
// Note: glGenerateMimap can also fail if the internal format is not both
// color-renderable and filterable (i.e.: doesn't work for depth)
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
t->gl.baseLevel = 0;
t->gl.maxLevel = static_cast<int8_t>(t->levels - 1);
@@ -1949,8 +1959,8 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
// fallthrough...
case SamplerType::SAMPLER_2D:
// NOTE: GL_TEXTURE_2D_MULTISAMPLE is not allowed
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
assert_invariant(t->gl.target == GL_TEXTURE_2D);
glTexSubImage2D(t->gl.target, GLint(level),
GLint(xoffset), GLint(yoffset),
@@ -1958,8 +1968,8 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
break;
case SamplerType::SAMPLER_3D:
assert_invariant(zoffset + depth <= std::max(1u, t->depth >> level));
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
assert_invariant(t->gl.target == GL_TEXTURE_3D);
glTexSubImage3D(t->gl.target, GLint(level),
GLint(xoffset), GLint(yoffset), GLint(zoffset),
@@ -1969,8 +1979,8 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
assert_invariant(zoffset + depth <= t->depth);
// NOTE: GL_TEXTURE_2D_MULTISAMPLE is not allowed
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
assert_invariant(t->gl.target == GL_TEXTURE_2D_ARRAY ||
t->gl.target == GL_TEXTURE_CUBE_MAP_ARRAY);
glTexSubImage3D(t->gl.target, GLint(level),
@@ -1979,8 +1989,8 @@ void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
break;
case SamplerType::SAMPLER_CUBEMAP: {
assert_invariant(t->gl.target == GL_TEXTURE_CUBE_MAP);
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
assert_invariant(width == height);
const size_t faceSize = PixelBufferDescriptor::computeDataSize(
@@ -2043,8 +2053,8 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
// fallthrough...
case SamplerType::SAMPLER_2D:
// NOTE: GL_TEXTURE_2D_MULTISAMPLE is not allowed
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
assert_invariant(t->gl.target == GL_TEXTURE_2D);
glCompressedTexSubImage2D(t->gl.target, GLint(level),
GLint(xoffset), GLint(yoffset),
@@ -2052,8 +2062,8 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
t->gl.internalFormat, imageSize, p.buffer);
break;
case SamplerType::SAMPLER_3D:
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
assert_invariant(t->gl.target == GL_TEXTURE_3D);
glCompressedTexSubImage3D(t->gl.target, GLint(level),
GLint(xoffset), GLint(yoffset), GLint(zoffset),
@@ -2071,8 +2081,8 @@ void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
break;
case SamplerType::SAMPLER_CUBEMAP: {
assert_invariant(t->gl.target == GL_TEXTURE_CUBE_MAP);
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
assert_invariant(width == height);
const size_t faceSize = PixelBufferDescriptor::computeDataSize(
@@ -2132,8 +2142,8 @@ void OpenGLDriver::setExternalTexture(GLTexture* t, void* image) {
assert_invariant(t->target == SamplerType::SAMPLER_EXTERNAL);
assert_invariant(t->gl.target == GL_TEXTURE_EXTERNAL_OES);
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, t);
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t);
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
#ifdef GL_OES_EGL_image
glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, static_cast<GLeglImageOES>(image));
@@ -2333,9 +2343,9 @@ void OpenGLDriver::beginRenderPass(Handle<HwRenderTarget> rth,
// EXT_multisampled_render_to_texture emulation).
// We would need to perform a "backward" resolve, i.e. load the resolved texture into the
// tile, everything must appear as though the multi-sample buffer was lost.
// However Filament specifies that a non multi-sample attachment to a
// However, Filament specifies that a non multi-sample attachment to a
// multi-sample RenderTarget is always discarded. We do this because implementing
// the load on Metal is not trivial and it's not a feature we rely on at this time.
// the load on Metal is not trivial, and it's not a feature we rely on at this time.
discardFlags |= rt->gl.resolve;
}
@@ -2502,29 +2512,21 @@ GLsizei OpenGLDriver::getAttachments(AttachmentArray& attachments,
}
// Sets up a scissor rectangle that automatically gets clipped against the viewport.
void OpenGLDriver::setViewportScissor(Viewport const& viewportScissor) noexcept {
void OpenGLDriver::setScissor(Viewport const& scissor) noexcept {
constexpr uint32_t maxvalu = std::numeric_limits<int32_t>::max();
auto& gl = mContext;
// In OpenGL, all four scissor parameters are actually signed, so clamp to MAX_INT32.
const int32_t maxval = std::numeric_limits<int32_t>::max();
int32_t left = std::min(viewportScissor.left, maxval);
int32_t bottom = std::min(viewportScissor.bottom, maxval);
uint32_t width = std::min(viewportScissor.width, uint32_t(maxval));
uint32_t height = std::min(viewportScissor.height, uint32_t(maxval));
// Compute the intersection of the requested scissor rectangle with the current viewport.
// Note that the viewport rectangle isn't necessarily equal to the bounds of the current
// Filament View (e.g., when post-processing is enabled).
OpenGLContext::vec4gli scissor;
OpenGLContext::vec4gli viewport = gl.getViewport();
scissor.x = std::max((int32_t)left, viewport[0]);
scissor.y = std::max((int32_t)bottom, viewport[1]);
int32_t right = std::min((int32_t)left + (int32_t)width, viewport[0] + viewport[2]);
int32_t top = std::min((int32_t)bottom + (int32_t)height, viewport[1] + viewport[3]);
// Compute width / height of the intersected region. If there's no intersection, pass zeroes
// rather than negative values to satisfy OpenGL requirements.
scissor.z = std::max(0, right - scissor.x);
scissor.w = std::max(0, top - scissor.y);
gl.setScissor(scissor.x, scissor.y, scissor.z, scissor.w);
// TODO: disable scissor when it is bigger than the current surface?
if (scissor.left == 0 && scissor.bottom == 0 &&
scissor.width >= maxvalu && scissor.height >= maxvalu) {
gl.disable(GL_SCISSOR_TEST);
return;
}
gl.setScissor(
GLint(scissor.left), GLint(scissor.bottom),
GLint(scissor.width), GLint(scissor.height));
gl.enable(GL_SCISSOR_TEST);
}
@@ -2575,21 +2577,37 @@ void OpenGLDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> ubh)
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::bindUniformBufferRange(uint32_t index, Handle<HwBufferObject> ubh,
uint32_t offset, uint32_t size) {
void OpenGLDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index,
Handle<HwBufferObject> ubh, uint32_t offset, uint32_t size) {
DEBUG_MARKER()
auto& gl = mContext;
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
bindingType == BufferObjectBinding::UNIFORM);
GLBufferObject* ub = handle_cast<GLBufferObject *>(ubh);
assert_invariant(ub->gl.binding == GL_UNIFORM_BUFFER);
GLenum target = GLUtils::getBufferBindingType(bindingType);
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
ub->gl.binding == target);
assert_invariant(offset + size <= ub->byteCount);
gl.bindBufferRange(ub->gl.binding, GLuint(index), ub->gl.id, offset, size);
gl.bindBufferRange(target, GLuint(index), ub->gl.id, offset, size);
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
DEBUG_MARKER()
auto& gl = mContext;
GLenum target = GLUtils::getBufferBindingType(bindingType);
gl.bindBufferRange(target, GLuint(index), 0, 0, 0);
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
DEBUG_MARKER()
assert_invariant(index < Program::BINDING_COUNT);
assert_invariant(index < Program::SAMPLER_BINDING_COUNT);
GLSamplerGroup* sb = handle_cast<GLSamplerGroup *>(sbh);
mSamplerBindings[index] = sb;
CHECK_GL_ERROR(utils::slog.e)
@@ -2760,6 +2778,58 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
});
}
void OpenGLDriver::readBufferSubData(backend::BufferObjectHandle boh,
uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) {
auto& gl = mContext;
GLBufferObject const* bo = handle_cast<GLBufferObject const*>(boh);
// TODO: measure the two solutions
if constexpr (true) {
// schedule a copy of the buffer we're reading into a PBO, this *should* happen
// asynchronously without stalling the CPU.
GLuint pbo;
glGenBuffers(1, &pbo);
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER, (GLsizeiptr)size, nullptr, GL_STATIC_DRAW);
gl.bindBuffer(bo->gl.binding, bo->gl.id);
glCopyBufferSubData(bo->gl.binding, GL_PIXEL_PACK_BUFFER, offset, 0, size);
gl.bindBuffer(bo->gl.binding, 0);
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, 0);
CHECK_GL_ERROR(utils::slog.e)
// then, we schedule a mapBuffer of the PBO later, once the fence has signaled
auto* pUserBuffer = new BufferDescriptor(std::move(p));
whenGpuCommandsComplete([this, size, pbo, pUserBuffer]() mutable {
BufferDescriptor& p = *pUserBuffer;
auto& gl = mContext;
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
void* vaddr = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, size, GL_MAP_READ_BIT);
if (vaddr) {
memcpy(p.buffer, vaddr, size);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
}
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, 0);
glDeleteBuffers(1, &pbo);
scheduleDestroy(std::move(p));
delete pUserBuffer;
CHECK_GL_ERROR(utils::slog.e)
});
} else {
gl.bindBuffer(bo->gl.binding, bo->gl.id);
// TODO: this glMapBufferRange may stall. Ideally we want to use whenGpuCommandsComplete
// but that's tricky because boh could be destroyed right after this call.
void* vaddr = glMapBufferRange(bo->gl.binding, offset, size, GL_MAP_READ_BIT);
if (vaddr) {
memcpy(p.buffer, vaddr, size);
glUnmapBuffer(bo->gl.binding);
}
gl.bindBuffer(bo->gl.binding, 0);
scheduleDestroy(std::move(p));
CHECK_GL_ERROR(utils::slog.e)
}
}
void OpenGLDriver::whenGpuCommandsComplete(std::function<void()> fn) noexcept {
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
mGpuCommandCompleteOps.emplace_back(sync, std::move(fn));
@@ -2873,7 +2943,7 @@ void OpenGLDriver::endFrame(uint32_t frameId) {
// at least do some minimal safety things here, such as resetting the VAO to 0.
auto& gl = mContext;
gl.bindVertexArray(nullptr);
for (int unit = OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1; unit >= 0; unit--) {
for (int unit = OpenGLContext::DUMMY_TEXTURE_BINDING; unit >= 0; unit--) {
gl.bindTexture(unit, GL_TEXTURE_2D, 0);
}
gl.disable(GL_CULL_FACE);
@@ -3034,8 +3104,8 @@ void OpenGLDriver::updateTextureLodRange(GLTexture* texture, int8_t targetLevel)
auto& gl = mContext;
if (texture && any(texture->usage & TextureUsage::SAMPLEABLE)) {
if (targetLevel < texture->gl.baseLevel || targetLevel > texture->gl.maxLevel) {
bindTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1, texture);
gl.activeTexture(OpenGLContext::MAX_TEXTURE_UNIT_COUNT - 1);
bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, texture);
gl.activeTexture(OpenGLContext::DUMMY_TEXTURE_BINDING);
if (targetLevel < texture->gl.baseLevel) {
texture->gl.baseLevel = targetLevel;
glTexParameteri(texture->gl.target, GL_TEXTURE_BASE_LEVEL, targetLevel);
@@ -3085,7 +3155,7 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint
gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant);
setViewportScissor(state.scissor);
setScissor(state.scissor);
if (UTILS_LIKELY(instanceCount <= 1)) {
glDrawRangeElements(GLenum(rp->type), rp->minIndex, rp->maxIndex, (GLsizei)rp->count,
@@ -3103,6 +3173,32 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint
#endif
}
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
executeRenderPassOps();
OpenGLProgram* p = handle_cast<OpenGLProgram*>(program);
// If the material debugger is enabled, avoid fatal (or cascading) errors and that can occur
// during the draw call when the program is invalid. The shader compile error has already been
// dumped to the console at this point, so it's fine to simply return early.
if (FILAMENT_ENABLE_MATDBG && UTILS_UNLIKELY(!p->isValid())) {
return;
}
useProgram(p);
#if defined(GL_ES_VERSION_3_1) || defined(GL_VERSION_4_3)
// FIXME: this line is commented-out for now due to issues switching G3 clients to API 21.
// glDispatchCompute(workGroupCount.x, workGroupCount.y, workGroupCount.z);
#endif
#ifdef FILAMENT_ENABLE_MATDBG
CHECK_GL_ERROR_NON_FATAL(utils::slog.e)
#else
CHECK_GL_ERROR(utils::slog.e)
#endif
}
// explicit instantiation of the Dispatcher
template class ConcreteDispatcher<OpenGLDriver>;
+3 -3
View File
@@ -323,7 +323,7 @@ private:
return pos->second;
}
const std::array<GLSamplerGroup*, Program::BINDING_COUNT>& getSamplerBindings() const {
const std::array<GLSamplerGroup*, Program::SAMPLER_BINDING_COUNT>& getSamplerBindings() const {
return mSamplerBindings;
}
@@ -341,10 +341,10 @@ private:
void clearWithRasterPipe(TargetBufferFlags clearFlags,
math::float4 const& linearColor, GLfloat depth, GLint stencil) noexcept;
void setViewportScissor(Viewport const& viewportScissor) noexcept;
void setScissor(Viewport const& scissor) noexcept;
// sampler buffer binding points (nullptr if not used)
std::array<GLSamplerGroup*, Program::BINDING_COUNT> mSamplerBindings = {}; // 12 pointers
std::array<GLSamplerGroup*, Program::SAMPLER_BINDING_COUNT> mSamplerBindings = {}; // 4 pointers
mutable tsl::robin_map<uint32_t, GLuint> mSamplerMap;
mutable std::vector<GLTexture*> mExternalStreams;
+123 -78
View File
@@ -34,7 +34,7 @@ using namespace utils;
using namespace backend;
static void logCompilationError(utils::io::ostream& out,
ShaderType shaderType, const char* name,
ShaderStage shaderType, const char* name,
GLuint shaderId, CString const& sourceCode) noexcept;
static void logProgramLinkError(utils::io::ostream& out,
@@ -51,13 +51,16 @@ OpenGLProgram::OpenGLProgram(OpenGLDriver& gld, Program&& programBuilder) noexce
OpenGLContext& context = gld.getContext();
mLazyInitializationData->uniformBlockInfo = programBuilder.getUniformBlockBindings();
mLazyInitializationData->uniformBlockInfo = std::move(programBuilder.getUniformBlockBindings());
mLazyInitializationData->samplerGroupInfo = std::move(programBuilder.getSamplerGroupInfo());
// this cannot fail because we check compilation status after linking the program
// shaders[] is filled with id of shader stages present.
OpenGLProgram::compileShaders(context, programBuilder.getShadersSource(),
gl.shaders, mLazyInitializationData->shaderSourceCode);
OpenGLProgram::compileShaders(context,
std::move(programBuilder.getShadersSource()),
programBuilder.getSpecializationConstants(),
gl.shaders,
mLazyInitializationData->shaderSourceCode);
gld.runAtNextRenderPass(this, [this]() {
// by this point we must not have a GL program
@@ -98,49 +101,95 @@ OpenGLProgram::~OpenGLProgram() noexcept {
*/
void OpenGLProgram::compileShaders(OpenGLContext& context,
Program::ShaderSource shadersSource,
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
GLuint shaderIds[Program::SHADER_TYPE_COUNT],
std::array<CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept {
std::string specificationConstantString;
for (auto const& sc : specializationConstants) {
specificationConstantString += "#define SPIRV_CROSS_CONSTANT_ID_" + std::to_string(sc.id) + ' ';
specificationConstantString += std::visit([](auto&& arg) {
return std::to_string(arg);
}, sc.value);
specificationConstantString += '\n';
}
if (!specificationConstantString.empty()) {
specificationConstantString += '\n';
}
// build all shaders
UTILS_NOUNROLL
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
ShaderType type = static_cast<ShaderType>(i);
const ShaderStage stage = static_cast<ShaderStage>(i);
GLenum glShaderType;
switch (type) {
case ShaderType::VERTEX:
glShaderType = GL_VERTEX_SHADER;
break;
case ShaderType::FRAGMENT:
glShaderType = GL_FRAGMENT_SHADER;
break;
switch (stage) {
case ShaderStage::VERTEX: glShaderType = GL_VERTEX_SHADER; break;
case ShaderStage::FRAGMENT: glShaderType = GL_FRAGMENT_SHADER; break;
case ShaderStage::COMPUTE: glShaderType = GL_COMPUTE_SHADER; break;
}
if (UTILS_LIKELY(!shadersSource[i].empty())) {
Program::ShaderBlob const& shader = shadersSource[i];
std::string_view shaderView(reinterpret_cast<const char*>(shader.data()), shader.size());
std::string temp;
Program::ShaderBlob& shader = shadersSource[i];
if (!context.ext.GOOGLE_cpp_style_line_directive) {
// If usages of the Google-style line directive are present, remove them, as some
// drivers don't allow the quotation marks.
if (UTILS_UNLIKELY(requestsGoogleLineDirectivesExtension(
shaderView.data(), shaderView.size()))) {
temp = shaderView; // copy string
removeGoogleLineDirectives(temp.data(), temp.size()); // length is unaffected
shaderView = temp;
}
}
// remove GOOGLE_cpp_style_line_directive
std::string_view source = process_GOOGLE_cpp_style_line_directive(context,
reinterpret_cast<char*>(shader.data()), shader.size());
// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 and
// MacOS doesn't support GL_ARB_shading_language_packing
if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) {
if (context.state.major == 4 && context.state.minor == 1 &&
!context.ext.ARB_shading_language_packing) {
if (temp.empty()) {
temp = shaderView; // copy string
}
// add support for ARB_shading_language_packing if needed
auto const packingFunctions = process_ARB_shading_language_packing(context);
std::string unpackHalf2x16{ R"(
// split shader source, so we can insert the specification constants and the packing functions
auto const [prolog, body] = splitShaderSource(source);
const std::array<const char*, 4> sources = {
prolog.data(),
specificationConstantString.c_str(),
packingFunctions.data(),
body.data()
};
const std::array<GLint, 4> lengths = {
(GLint)prolog.length(),
(GLint)specificationConstantString.length(),
(GLint)packingFunctions.length(),
(GLint)body.length() - 1 // null terminated
};
GLuint shaderId = glCreateShader(glShaderType);
glShaderSource(shaderId, sources.size(), sources.data(), lengths.data());
glCompileShader(shaderId);
#ifndef NDEBUG
// for debugging we return the original shader source (without the modifications we
// made here), otherwise the line numbers wouldn't match.
outShaderSourceCode[i] = { source.data(), source.length() };
#endif
shaderIds[i] = shaderId;
}
}
}
// If usages of the Google-style line directive are present, remove them, as some
// drivers don't allow the quotation marks. This happens in-place.
std::string_view OpenGLProgram::process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
char* source, size_t len) noexcept {
if (!context.ext.GOOGLE_cpp_style_line_directive) {
if (UTILS_UNLIKELY(requestsGoogleLineDirectivesExtension({ source, len }))) {
removeGoogleLineDirectives(source, len); // length is unaffected
}
}
return { source, len };
}
// Tragically, OpenGL 4.1 doesn't support unpackHalf2x16 and
// macOS doesn't support GL_ARB_shading_language_packing
std::string_view OpenGLProgram::process_ARB_shading_language_packing(OpenGLContext& context) noexcept {
using namespace std::literals;
if constexpr (BACKEND_OPENGL_VERSION == BACKEND_OPENGL_VERSION_GL) {
if (context.state.major == 4 && context.state.minor == 1 &&
!context.ext.ARB_shading_language_packing) {
return R"(
// these don't handle denormals, NaNs or inf
float u16tofp32(highp uint v) {
@@ -175,33 +224,31 @@ highp uint packHalf2x16(vec2 v) {
highp uint y = fp32tou16(v.y);
return (y << 16) | x;
}
)"};
// a good point for insertion is just before the first occurrence of an uniform block
auto pos = temp.find("layout(std140)");
if (pos != std::string_view::npos) {
temp.insert(pos, unpackHalf2x16);
}
shaderView = temp;
}
}
GLuint shaderId = glCreateShader(glShaderType);
{ // scope for source/length (we don't want them to leak out)
const char* const source = shaderView.data();
// the shader string is null terminated and the length includes the null character
const GLint length = (GLint)shaderView.length() - 1;
assert_invariant( source[length] == '\0' );
glShaderSource(shaderId, 1, &source, &length);
glCompileShader(shaderId);
#ifndef NDEBUG
outShaderSourceCode[i] = { source, static_cast<size_t>(length) };
#endif
}
shaderIds[i] = shaderId;
)"sv;
}
}
return ""sv;
}
// split shader source code in two, the first section goes from the start to the line after the
// last #extension, and the 2nd part goes from there to the end.
std::array<std::string_view, 2> OpenGLProgram::splitShaderSource(std::string_view source) noexcept {
auto start = source.find("#version");
assert_invariant(start != std::string_view::npos);
auto pos = source.rfind("\n#extension");
if (pos == std::string_view::npos) {
pos = start;
} else {
++pos;
}
auto eol = source.find('\n', pos) + 1;
assert_invariant(eol != std::string_view::npos);
std::string_view version = source.substr(start, eol - start);
std::string_view body = source.substr(version.length(), source.length() - version.length());
return { version, body };
}
/*
@@ -237,7 +284,7 @@ bool OpenGLProgram::checkProgramStatus(const char* name,
// only if the link fails, we check the compilation status
UTILS_NOUNROLL
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
const ShaderType type = static_cast<ShaderType>(i);
const ShaderStage type = static_cast<ShaderStage>(i);
const GLuint shader = shaderIds[i];
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
@@ -271,9 +318,7 @@ void OpenGLProgram::initialize(OpenGLContext& context) {
gl.program, gl.shaders, initializationData->shaderSourceCode);
if (UTILS_LIKELY(mValid)) {
initializeProgramState(context, gl.program,
initializationData->uniformBlockInfo,
initializationData->samplerGroupInfo);
initializeProgramState(context, gl.program, *initializationData);
}
// and destroy all temporary init data
@@ -287,16 +332,15 @@ void OpenGLProgram::initialize(OpenGLContext& context) {
* checkProgramStatus() has been successfully called.
*/
void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint program,
Program::UniformBlockInfo const& uniformBlockInfo,
Program::SamplerGroupInfo const& samplerGroupInfo) noexcept {
LazyInitializationData const& lazyInitializationData) noexcept {
// Note: This is only needed, because the layout(binding=) syntax is not permitted in glsl
// (ES3.0 and GL4.1). The backend needs a way to associate a uniform block to a binding point.
UTILS_NOUNROLL
for (GLuint binding = 0, n = uniformBlockInfo.size(); binding < n; binding++) {
const char* name = uniformBlockInfo[binding];
if (name) {
GLint index = glGetUniformBlockIndex(program, name);
for (GLuint binding = 0, n = lazyInitializationData.uniformBlockInfo.size(); binding < n; binding++) {
auto const& name = lazyInitializationData.uniformBlockInfo[binding];
if (!name.empty()) {
GLint index = glGetUniformBlockIndex(program, name.c_str());
if (index >= 0) {
glUniformBlockBinding(program, GLuint(index), binding);
}
@@ -308,8 +352,8 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
uint8_t tmu = 0;
UTILS_NOUNROLL
for (size_t i = 0, c = samplerGroupInfo.size(); i < c; i++) {
auto const& samplers = samplerGroupInfo[i].samplers;
for (size_t i = 0, c = lazyInitializationData.samplerGroupInfo.size(); i < c; i++) {
auto const& samplers = lazyInitializationData.samplerGroupInfo[i].samplers;
if (samplers.empty()) {
// this binding point doesn't have any samplers, skip it.
continue;
@@ -336,7 +380,7 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
// whole group.
if (atLeastOneSamplerUsed) {
// Cache the sampler uniform locations for each interface block
mUsedBindingPoints[usedBindingCount] = i;
mUsedSamplerBindingPoints[usedBindingCount] = i;
usedBindingCount++;
} else {
tmu -= samplers.size();
@@ -345,12 +389,12 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
mUsedBindingsCount = usedBindingCount;
}
void OpenGLProgram::updateSamplers(OpenGLDriver* gld) noexcept {
void OpenGLProgram::updateSamplers(OpenGLDriver* gld) const noexcept {
using GLTexture = OpenGLDriver::GLTexture;
// cache a few member variable locally, outside the loop
auto const& UTILS_RESTRICT samplerBindings = gld->getSamplerBindings();
auto const& UTILS_RESTRICT usedBindingPoints = mUsedBindingPoints;
auto const& UTILS_RESTRICT usedBindingPoints = mUsedSamplerBindingPoints;
for (uint8_t i = 0, tmu = 0, n = mUsedBindingsCount; i < n; i++) {
auto const binding = usedBindingPoints[i];
@@ -374,13 +418,14 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* gld) noexcept {
}
UTILS_NOINLINE
void logCompilationError(io::ostream& out, ShaderType shaderType,
void logCompilationError(io::ostream& out, ShaderStage shaderType,
const char* name, GLuint shaderId, CString const& sourceCode) noexcept {
auto to_string = [](ShaderType type) -> const char* {
auto to_string = [](ShaderStage type) -> const char* {
switch (type) {
case ShaderType::VERTEX: return "vertex";
case ShaderType::FRAGMENT: return "fragment";
case ShaderStage::VERTEX: return "vertex";
case ShaderStage::FRAGMENT: return "fragment";
case ShaderStage::COMPUTE: return "compute";
}
};
+18 -11
View File
@@ -72,11 +72,26 @@ public:
} gl; // 12 bytes
private:
// keep these away from of other class attributes
struct LazyInitializationData {
Program::UniformBlockInfo uniformBlockInfo;
Program::SamplerGroupInfo samplerGroupInfo;
std::array<utils::CString, Program::SHADER_TYPE_COUNT> shaderSourceCode;
};
static void compileShaders(OpenGLContext& context,
Program::ShaderSource shadersSource,
utils::FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
GLuint shaderIds[Program::SHADER_TYPE_COUNT],
std::array<utils::CString, Program::SHADER_TYPE_COUNT>& outShaderSourceCode) noexcept;
static std::string_view process_GOOGLE_cpp_style_line_directive(OpenGLContext& context,
char* source, size_t len) noexcept;
static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept;
static std::array<std::string_view, 2> splitShaderSource(std::string_view source) noexcept;
static GLuint linkProgram(const GLuint shaderIds[Program::SHADER_TYPE_COUNT]) noexcept;
static bool checkProgramStatus(const char* name,
@@ -86,17 +101,9 @@ private:
void initialize(OpenGLContext& context);
void initializeProgramState(OpenGLContext& context, GLuint program,
Program::UniformBlockInfo const& uniformBlockInfo,
Program::SamplerGroupInfo const& samplerGroupInfo) noexcept;
LazyInitializationData const& lazyInitializationData) noexcept;
void updateSamplers(OpenGLDriver* gld) noexcept;
// keep these away from of other class attributes
struct LazyInitializationData {
Program::UniformBlockInfo uniformBlockInfo;
Program::SamplerGroupInfo samplerGroupInfo;
std::array<utils::CString, Program::SHADER_TYPE_COUNT> shaderSourceCode;
};
void updateSamplers(OpenGLDriver* gld) const noexcept;
// number of bindings actually used by this program
uint8_t mUsedBindingsCount = 0u;
@@ -109,7 +116,7 @@ private:
union {
// when mInitialized == true:
// information about each USED sampler buffer per binding (no gaps)
std::array<uint8_t, Program::BINDING_COUNT> mUsedBindingPoints; // 12 bytes
std::array<uint8_t, Program::SAMPLER_BINDING_COUNT> mUsedSamplerBindingPoints; // 4 bytes
// when mInitialized == false:
// lazy initialization data pointer
LazyInitializationData* mLazyInitializationData;
+112 -64
View File
@@ -19,10 +19,14 @@
#if defined(__ANDROID__) || defined(FILAMENT_USE_EXTERNAL_GLES3) || defined(__EMSCRIPTEN__)
#include <GLES3/gl3.h>
#if defined(__EMSCRIPTEN__)
# include <GLES3/gl3.h>
#else
# include <GLES3/gl31.h>
#endif
#include <GLES2/gl2ext.h>
/* The Android NDK doesn't exposes extensions, fake it with eglGetProcAddress */
/* The Android NDK doesn't expose extensions, fake it with eglGetProcAddress */
namespace glext {
// importGLESExtensionsEntryPoints is thread-safe and can be called multiple times.
// it is currently called from PlatformEGL.
@@ -50,49 +54,13 @@
#endif
#ifdef GL_EXT_disjoint_timer_query
extern PFNGLGETQUERYOBJECTUI64VEXTPROC glGetQueryObjectui64v;
#define GL_TIME_ELAPSED 0x88BF
#endif
#ifdef GL_EXT_clip_control
extern PFNGLCLIPCONTROLEXTPROC glClipControl;
#ifndef GL_LOWER_LEFT
# define GL_LOWER_LEFT GL_LOWER_LEFT_EXT
#endif
#ifndef GL_ZERO_TO_ONE
# define GL_ZERO_TO_ONE GL_ZERO_TO_ONE_EXT
#endif
#endif
}
// Prevent lots of #ifdef's between desktop and mobile by providing some suffix-free constants:
#define GL_DEBUG_OUTPUT 0x92E0
#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242
#define GL_DEBUG_SEVERITY_HIGH 0x9146
#define GL_DEBUG_SEVERITY_MEDIUM 0x9147
#define GL_DEBUG_SEVERITY_LOW 0x9148
#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B
#define GL_DEBUG_TYPE_MARKER 0x8268
#define GL_DEBUG_TYPE_ERROR 0x824C
#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D
#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E
#define GL_DEBUG_TYPE_PORTABILITY 0x824F
#define GL_DEBUG_TYPE_PERFORMANCE 0x8250
#define GL_DEBUG_TYPE_OTHER 0x8251
#define glDebugMessageCallback glext::glDebugMessageCallbackKHR
#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009
#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A
#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C
#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D
#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E
#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F
#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054
#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F
#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A
using namespace glext;
using namespace glext;
#elif defined(IOS)
@@ -101,27 +69,6 @@ using namespace glext;
#include <OpenGLES/ES3/gl.h>
#include <OpenGLES/ES3/glext.h>
/* The iOS SDK only provides OpenGL ES headers up to 3.0. Filament works with OpenGL 3.0, but
* requires the following 3.1 declarations in order to compile. */
#define GL_TEXTURE_2D_MULTISAMPLE 0x9100
#define GL_TIME_ELAPSED 0x88BF
#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009
#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A
#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C
#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D
#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E
#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F
#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054
#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F
#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A
#ifdef GL_EXT_multisampled_render_to_texture
extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glRenderbufferStorageMultisampleEXT;
extern PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC glFramebufferTexture2DMultisampleEXT;
#endif
#else
// bluegl exposes symbols prefixed with bluegl_ to avoid clashing with clients that also link
@@ -135,6 +82,111 @@ using namespace glext;
#endif
#if (!defined(GL_ES_VERSION_2_0) && !defined(GL_VERSION_4_1))
#error "Minimum header version must be OpenGL ES 2.0 or OpenGL 4.1"
#endif
/*
* Since we need ES3.1 headers and iOS only has ES3.0, we also define the constants we
* need to avoid many #ifdef in the actual code.
*/
#if defined(GL_ES_VERSION_2_0)
#ifdef GL_EXT_disjoint_timer_query
# ifndef GL_TIME_ELAPSED
# define GL_TIME_ELAPSED GL_TIME_ELAPSED_EXT
# endif
#endif
#ifdef GL_EXT_clip_control
# ifndef GL_LOWER_LEFT
# define GL_LOWER_LEFT GL_LOWER_LEFT_EXT
# endif
# ifndef GL_ZERO_TO_ONE
# define GL_ZERO_TO_ONE GL_ZERO_TO_ONE_EXT
# endif
#endif
#ifndef GL_TEXTURE_CUBE_MAP_ARRAY
# define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009
#endif
// Prevent lots of #ifdef's between desktop and mobile
#if defined(GL_KHR_debug)
# ifndef GL_DEBUG_OUTPUT
# define GL_DEBUG_OUTPUT GL_DEBUG_OUTPUT_KHR
# endif
# ifndef GL_DEBUG_OUTPUT_SYNCHRONOUS
# define GL_DEBUG_OUTPUT_SYNCHRONOUS GL_DEBUG_OUTPUT_SYNCHRONOUS_KHR
# endif
# ifndef GL_DEBUG_SEVERITY_HIGH
# define GL_DEBUG_SEVERITY_HIGH GL_DEBUG_SEVERITY_HIGH_KHR
# endif
# ifndef GL_DEBUG_SEVERITY_MEDIUM
# define GL_DEBUG_SEVERITY_MEDIUM GL_DEBUG_SEVERITY_MEDIUM_KHR
# endif
# ifndef GL_DEBUG_SEVERITY_LOW
# define GL_DEBUG_SEVERITY_LOW GL_DEBUG_SEVERITY_LOW_KHR
# endif
# ifndef GL_DEBUG_SEVERITY_NOTIFICATION
# define GL_DEBUG_SEVERITY_NOTIFICATION GL_DEBUG_SEVERITY_NOTIFICATION_KHR
# endif
# ifndef GL_DEBUG_TYPE_MARKER
# define GL_DEBUG_TYPE_MARKER GL_DEBUG_TYPE_MARKER_KHR
# endif
# ifndef GL_DEBUG_TYPE_ERROR
# define GL_DEBUG_TYPE_ERROR GL_DEBUG_TYPE_ERROR_KHR
# endif
# ifndef GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR
# define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_KHR
# endif
# ifndef GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR
# define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_KHR
# endif
# ifndef GL_DEBUG_TYPE_PORTABILITY
# define GL_DEBUG_TYPE_PORTABILITY GL_DEBUG_TYPE_PORTABILITY_KHR
# endif
# ifndef GL_DEBUG_TYPE_PERFORMANCE
# define GL_DEBUG_TYPE_PERFORMANCE GL_DEBUG_TYPE_PERFORMANCE_KHR
# endif
# ifndef GL_DEBUG_TYPE_OTHER
# define GL_DEBUG_TYPE_OTHER GL_DEBUG_TYPE_OTHER_KHR
# endif
# define glDebugMessageCallback glDebugMessageCallbackKHR
#endif
/* The iOS SDK only provides OpenGL ES headers up to 3.0. Filament works with OpenGL 3.0, but
* requires ES3.1 headers */
#if !defined(GL_ES_VERSION_3_1)
#define GL_SHADER_STORAGE_BUFFER 0x90D2
#define GL_COMPUTE_SHADER 0x91B9
#define GL_TEXTURE_2D_MULTISAMPLE 0x9100
// FIXME: The GL_TIME_ELAPSED define is used unconditionally in Filament, but
// requires extension support.
#ifndef GL_TIME_ELAPSED
#define GL_TIME_ELAPSED 0x88BF
#endif
#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A
#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C
#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D
#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E
#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F
#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054
#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F
#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A
#endif
#endif // GL_ES_VERSION_2_0
// This is an odd duck function that exists in WebGL 2.0 but not in OpenGL ES.
#if defined(__EMSCRIPTEN__)
extern "C" {
@@ -142,10 +194,6 @@ void glGetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, void *d
}
#endif
#if (!defined(GL_ES_VERSION_2_0) && !defined(GL_VERSION_4_1))
#error "Minimum header version must be OpenGL ES 2.0 or OpenGL 4.1"
#endif
#define BACKEND_OPENGL_VERSION_GLES 0
#define BACKEND_OPENGL_VERSION_GL 1
+5 -1
View File
@@ -367,6 +367,7 @@ io::ostream& operator<<(io::ostream& out, BufferObjectBinding binding) {
switch (binding) {
CASE(BufferObjectBinding, VERTEX)
CASE(BufferObjectBinding, UNIFORM)
CASE(BufferObjectBinding, SHADER_STORAGE)
}
return out;
}
@@ -498,8 +499,11 @@ io::ostream& operator<<(io::ostream& stream, ShaderStageFlags stageFlags) {
case ShaderStageFlags::FRAGMENT:
str = "{ fragment }";
break;
case ShaderStageFlags::COMPUTE:
str = "{ compute }";
break;
case ShaderStageFlags::ALL_SHADER_STAGE_FLAGS:
str = "{ vertex | fragment }";
str = "{ vertex | fragment | compute }";
break;
}
return stream << str;
@@ -376,7 +376,8 @@ void VulkanBlitter::blitSlowDepth(VkImageAspectFlags aspect, VkFilter filter,
.imageLayout = getDefaultImageLayout(TextureUsage::DEPTH_ATTACHMENT)
};
mPipelineCache.bindSamplers(samplers);
mPipelineCache.bindSamplers(samplers,
VulkanPipelineCache::getUsageFlags(0, ShaderStageFlags::FRAGMENT));
auto previousUbo = mPipelineCache.getUniformBufferBinding(0);
mPipelineCache.bindUniformBuffer(0, mParamsBuffer->getGpuBuffer());
@@ -111,6 +111,10 @@ void VulkanBuffer::loadFromCpu(VulkanContext& context, VulkanStagePool& stagePoo
VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT,
0, 0, nullptr, 1, &barrier, 0, nullptr);
}
if (mUsage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) {
// TODO: implement me
}
}
} // namespace filament::backend
+73 -28
View File
@@ -885,15 +885,28 @@ bool VulkanDriver::isWorkaroundNeeded(Workaround workaround) {
}
FeatureLevel VulkanDriver::getFeatureLevel() {
const auto supportedSamplerCount = std::min(
mContext.physicalDeviceProperties.limits.maxDescriptorSetSamplers,
mContext.physicalDeviceProperties.limits.maxDescriptorSetSampledImages);
const VkPhysicalDeviceLimits& limits = mContext.physicalDeviceProperties.limits;
const bool imageCubeArray = (bool)mContext.physicalDeviceFeatures.imageCubeArray;
// If cubemap arrays are not supported, then this is an FL1 device.
if (!mContext.physicalDeviceFeatures.imageCubeArray) {
return FeatureLevel::FEATURE_LEVEL_1;
}
return (supportedSamplerCount >= 31 && imageCubeArray) ?
FeatureLevel::FEATURE_LEVEL_2 :
FeatureLevel::FEATURE_LEVEL_1;
// If the max sampler counts do not meet FL2 standards, then this is an FL1 device.
const auto& fl2 = FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_2];
if (fl2.MAX_VERTEX_SAMPLER_COUNT < limits.maxPerStageDescriptorSamplers ||
fl2.MAX_FRAGMENT_SAMPLER_COUNT < limits.maxPerStageDescriptorSamplers) {
return FeatureLevel::FEATURE_LEVEL_1;
}
// If the max sampler counts do not meet FL3 standards, then this is an FL2 device.
const auto& fl3 = FEATURE_LEVEL_CAPS[+FeatureLevel::FEATURE_LEVEL_3];
if (fl3.MAX_VERTEX_SAMPLER_COUNT < limits.maxPerStageDescriptorSamplers ||
fl3.MAX_FRAGMENT_SAMPLER_COUNT < limits.maxPerStageDescriptorSamplers) {
return FeatureLevel::FEATURE_LEVEL_2;
}
return FeatureLevel::FEATURE_LEVEL_3;
}
math::float2 VulkanDriver::getClipSpaceParams() {
@@ -1220,7 +1233,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
.framebuffer = vkfb,
// The renderArea field constrains the LoadOp, but scissoring does not.
// Therefore we do not set the scissor rect here, we only need it in draw().
// Therefore, we do not set the scissor rect here, we only need it in draw().
.renderArea = { .offset = {}, .extent = extent }
};
@@ -1457,12 +1470,22 @@ void VulkanDriver::bindUniformBuffer(uint32_t index, Handle<HwBufferObject> boh)
mPipelineCache.bindUniformBuffer((uint32_t) index, bo->buffer.getGpuBuffer(), offset, size);
}
void VulkanDriver::bindUniformBufferRange(uint32_t index, Handle<HwBufferObject> boh,
uint32_t offset, uint32_t size) {
void VulkanDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t index,
Handle<HwBufferObject> boh, uint32_t offset, uint32_t size) {
assert_invariant(bindingType == BufferObjectBinding::SHADER_STORAGE ||
bindingType == BufferObjectBinding::UNIFORM);
// TODO: implement BufferObjectBinding::SHADER_STORAGE case
auto* bo = handle_cast<VulkanBufferObject*>(boh);
mPipelineCache.bindUniformBuffer((uint32_t)index, bo->buffer.getGpuBuffer(), offset, size);
}
void VulkanDriver::unbindBuffer(BufferObjectBinding bindingType, uint32_t index) {
// TODO: implement unbindBuffer()
}
void VulkanDriver::bindSamplers(uint32_t index, Handle<HwSamplerGroup> sbh) {
auto* hwsb = handle_cast<VulkanSamplerGroup*>(sbh);
mSamplerBindings[index] = hwsb;
@@ -1560,7 +1583,8 @@ void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = memReqs.size,
.memoryTypeIndex = mContext.selectMemoryType(memReqs.memoryTypeBits,
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT |
VK_MEMORY_PROPERTY_HOST_CACHED_BIT)
};
vkAllocateMemory(device, &allocInfo, nullptr, &stagingMemory);
@@ -1676,6 +1700,12 @@ void VulkanDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y
scheduleDestroy(std::move(pbd));
}
void VulkanDriver::readBufferSubData(backend::BufferObjectHandle boh,
uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) {
// TODO: implement readBufferSubData
scheduleDestroy(std::move(p));
}
void VulkanDriver::blit(TargetBufferFlags buffers, Handle<HwRenderTarget> dst, Viewport dstRect,
Handle<HwRenderTarget> src, Viewport srcRect, SamplerMagFilter filter) {
assert_invariant(mContext.currentRenderPass.renderPass == VK_NULL_HANDLE);
@@ -1728,7 +1758,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
Handle<HwProgram> programHandle = pipelineState.program;
RasterState rasterState = pipelineState.rasterState;
PolygonOffset depthOffset = pipelineState.polygonOffset;
const Viewport& viewportScissor = pipelineState.scissor;
const Viewport& scissorBox = pipelineState.scissor;
auto* program = handle_cast<VulkanProgram*>(programHandle);
mDisposer.acquire(program);
@@ -1822,9 +1852,10 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
// where "SamplerBinding" is the integer in the GLSL, and SamplerGroupBinding is the abstract
// Filament concept used to form groups of samplers.
VkDescriptorImageInfo iInfo[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {};
VkDescriptorImageInfo samplerInfo[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {};
VulkanPipelineCache::UsageFlags usage;
for (uint8_t samplerGroupIdx = 0; samplerGroupIdx < Program::BINDING_COUNT; samplerGroupIdx++) {
for (uint8_t samplerGroupIdx = 0; samplerGroupIdx < Program::SAMPLER_BINDING_COUNT; samplerGroupIdx++) {
const auto& samplerGroup = program->samplerGroupInfo[samplerGroupIdx];
const auto& samplers = samplerGroup.samplers;
if (samplers.empty()) {
@@ -1838,7 +1869,6 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
assert_invariant(sb->getSize() == samplers.size());
size_t samplerIdx = 0;
for (auto& sampler : samplers) {
size_t bindingPoint = sampler.binding;
const SamplerDescriptor* boundSampler = sb->data() + samplerIdx;
samplerIdx++;
@@ -1847,11 +1877,13 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
mDisposer.acquire(texture);
// TODO: can this uninitialized check be checked in a higher layer?
// This fallback path is very flaky because the dummy texture might not have
// matching characteristics. (e.g. if the missing texture is a 3D texture)
if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VK_IMAGE_LAYOUT_UNDEFINED)) {
#ifndef NDEBUG
utils::slog.w << "Uninitialized texture bound to '" << sampler.name.c_str() << "'";
utils::slog.w << " in material '" << program->name.c_str() << "'";
utils::slog.w << " at binding point " << +bindingPoint << utils::io::endl;
utils::slog.w << " at binding point " << +sampler.binding << utils::io::endl;
#endif
texture = mContext.emptyTexture;
}
@@ -1859,17 +1891,18 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
const SamplerParams& samplerParams = boundSampler->s;
VkSampler vksampler = mSamplerCache.getSampler(samplerParams);
iInfo[bindingPoint] = {
usage = VulkanPipelineCache::getUsageFlags(sampler.binding, samplerGroup.stageFlags, usage);
samplerInfo[sampler.binding] = {
.sampler = vksampler,
.imageView = texture->getPrimaryImageView(),
.imageLayout = texture->getPrimaryImageLayout()
};
}
}
}
mPipelineCache.bindSamplers(iInfo);
mPipelineCache.bindSamplers(samplerInfo, usage);
// Bind new descriptor sets if they need to change.
// If descriptor set allocation failed, skip the draw call and bail. No need to emit an error
@@ -1879,16 +1912,21 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
}
// Set scissoring.
// Compute the intersection of the requested scissor rectangle with the current viewport.
const int32_t x = std::max(viewportScissor.left, (int32_t)mContext.viewport.x);
const int32_t y = std::max(viewportScissor.bottom, (int32_t)mContext.viewport.y);
const int32_t right = std::min(viewportScissor.left + (int32_t)viewportScissor.width,
(int32_t)(mContext.viewport.x + mContext.viewport.width));
const int32_t top = std::min(viewportScissor.bottom + (int32_t)viewportScissor.height,
(int32_t)(mContext.viewport.y + mContext.viewport.height));
// clamp left-bottom to 0,0 and avoid overflows
constexpr int32_t maxvali = std::numeric_limits<int32_t>::max();
constexpr uint32_t maxvalu = std::numeric_limits<int32_t>::max();
int32_t l = scissorBox.left;
int32_t b = scissorBox.bottom;
uint32_t w = std::min(maxvalu, scissorBox.width);
uint32_t h = std::min(maxvalu, scissorBox.height);
int32_t r = (l > int32_t(maxvalu - w)) ? maxvali : l + int32_t(w);
int32_t t = (b > int32_t(maxvalu - h)) ? maxvali : b + int32_t(h);
l = std::max(0, l);
b = std::max(0, b);
assert_invariant(r >= l && t >= b);
VkRect2D scissor{
.offset = { std::max(0, x), std::max(0, y) },
.extent = { (uint32_t)right - x, (uint32_t)top - y }
.offset = { l, b },
.extent = { uint32_t(r - l), uint32_t(t - b) }
};
rt->transformClientRectToPlatform(&scissor);
@@ -1916,6 +1954,10 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
}
void VulkanDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
// FIXME: implement me
}
void VulkanDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
VulkanCommandBuffer const* commands = &mContext.commands->get();
VulkanTimerQuery* vtq = handle_cast<VulkanTimerQuery*>(tqh);
@@ -1970,6 +2012,9 @@ void VulkanDriver::debugCommandBegin(CommandStream* cmds, bool synchronous, cons
#endif
}
void VulkanDriver::resetState(int) {
}
// explicit instantiation of the Dispatcher
template class ConcreteDispatcher<VulkanDriver>;
+42 -11
View File
@@ -49,14 +49,10 @@ VulkanProgram::VulkanProgram(VulkanContext& context, const Program& builder) noe
HwProgram(builder.getName()), context(context) {
auto const& blobs = builder.getShadersSource();
VkShaderModule* modules[2] = { &bundle.vertex, &bundle.fragment };
bool missing = false;
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
// TODO: handle compute shaders.
for (size_t i = 0; i < 2; i++) {
const auto& blob = blobs[i];
VkShaderModule* module = modules[i];
if (blob.empty()) {
missing = true;
continue;
}
VkShaderModuleCreateInfo moduleInfo = {};
moduleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
moduleInfo.codeSize = blob.size();
@@ -65,11 +61,45 @@ VulkanProgram::VulkanProgram(VulkanContext& context, const Program& builder) noe
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create shader module.");
}
// Output a warning because it's okay to encounter empty blobs, but it's not okay to use
// this program handle in a draw call.
if (missing) {
utils::slog.w << "Missing SPIR-V shader: " << this->name.c_str() << utils::io::endl;
return;
// populate the specialization constants requirements right now
auto const& specializationConstants = builder.getSpecializationConstants();
if (!specializationConstants.empty()) {
// Allocate a single heap block to store all the specialization constants structures
// our supported types are int32, float and bool, so we use 4 bytes per data. bool will
// just use the first byte.
char* pStorage = (char*)malloc(
sizeof(VkSpecializationInfo) +
specializationConstants.size() * sizeof(VkSpecializationMapEntry) +
specializationConstants.size() * 4);
VkSpecializationInfo* const pInfo = (VkSpecializationInfo*)pStorage;
VkSpecializationMapEntry* const pEntries =
(VkSpecializationMapEntry*)(pStorage + sizeof(VkSpecializationInfo));
void* pData = pStorage + sizeof(VkSpecializationInfo) +
specializationConstants.size() * sizeof(VkSpecializationMapEntry);
*pInfo = {
.mapEntryCount = specializationConstants.size(),
.pMapEntries = pEntries,
.dataSize = specializationConstants.size() * 4,
.pData = pData,
};
for (size_t i = 0; i < specializationConstants.size(); i++) {
const uint32_t offset = uint32_t(i) * 4;
std::visit([&](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
pEntries[i] = {
.constantID = specializationConstants[i].id,
.offset = offset,
.size = sizeof(arg)
};
T* const addr = (T*)((char*)pData + offset);
*addr = arg;
}, specializationConstants[i].value);
}
bundle.specializationInfos = pInfo;
}
// Make a copy of the binding map
@@ -90,6 +120,7 @@ VulkanProgram::VulkanProgram(VulkanContext& context, VkShaderModule vs, VkShader
VulkanProgram::~VulkanProgram() {
vkDestroyShaderModule(context.device, bundle.vertex, VKALLOC);
vkDestroyShaderModule(context.device, bundle.fragment, VKALLOC);
free(bundle.specializationInfos);
}
// Creates a special "default" render target (i.e. associated with the swap chain)
+7 -5
View File
@@ -117,18 +117,18 @@ struct VulkanRenderPrimitive : public HwRenderPrimitive {
};
struct VulkanFence : public HwFence {
VulkanFence(const VulkanCommandBuffer& commands) : fence(commands.fence) {}
explicit VulkanFence(const VulkanCommandBuffer& commands) : fence(commands.fence) {}
std::shared_ptr<VulkanCmdFence> fence;
};
struct VulkanSync : public HwSync {
VulkanSync() {}
VulkanSync(const VulkanCommandBuffer& commands) : fence(commands.fence) {}
VulkanSync() = default;
explicit VulkanSync(const VulkanCommandBuffer& commands) : fence(commands.fence) {}
std::shared_ptr<VulkanCmdFence> fence;
};
struct VulkanTimerQuery : public HwTimerQuery {
VulkanTimerQuery(VulkanContext& context);
explicit VulkanTimerQuery(VulkanContext& context);
~VulkanTimerQuery();
uint32_t startingQueryIndex;
uint32_t stoppingQueryIndex;
@@ -143,7 +143,9 @@ inline constexpr VkBufferUsageFlagBits getBufferObjectUsage(
return VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
case BufferObjectBinding::UNIFORM:
return VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT;
// when adding more buffer types here, make sure to update VulkanBuffer::loadFromCpu()
case BufferObjectBinding::SHADER_STORAGE:
return VK_BUFFER_USAGE_STORAGE_BUFFER_BIT;
// when adding more buffer-types here, make sure to update VulkanBuffer::loadFromCpu()
// if necessary.
}
}
@@ -35,32 +35,33 @@ namespace filament::backend {
static VulkanPipelineCache::RasterState createDefaultRasterState();
static VkShaderStageFlags getShaderStageFlags(utils::bitset64 key, uint16_t binding) {
static VkShaderStageFlags getShaderStageFlags(VulkanPipelineCache::UsageFlags key, uint16_t binding) {
// NOTE: if you modify this function, you also need to modify getUsageFlags.
assert_invariant(binding < MAX_SAMPLER_COUNT);
VkShaderStageFlags flags = 0;
if (key.test(binding * 2 + 0)) {
if (key.test(binding)) {
flags |= VK_SHADER_STAGE_VERTEX_BIT;
}
if (key.test(binding * 2 + 1)) {
if (key.test(MAX_SAMPLER_COUNT + binding)) {
flags |= VK_SHADER_STAGE_FRAGMENT_BIT;
}
return flags;
}
utils::bitset64
static getPipelineLayoutKey(const Program::SamplerGroupInfo& samplerGroupInfo) noexcept {
utils::bitset64 key = {};
for (uint32_t binding = 0; binding < Program::BINDING_COUNT; ++binding) {
const auto& stageFlags = samplerGroupInfo[binding].stageFlags;
for (const auto& sampler : samplerGroupInfo[binding].samplers) {
if (any(stageFlags & ShaderStageFlags::VERTEX)) {
key.set(sampler.binding * 2 + 0);
}
if (any(stageFlags & ShaderStageFlags::FRAGMENT)) {
key.set(sampler.binding * 2 + 1);
}
}
VulkanPipelineCache::UsageFlags
VulkanPipelineCache::getUsageFlags(uint16_t binding, ShaderStageFlags flags, UsageFlags src) {
// NOTE: if you modify this function, you also need to modify getShaderStageFlags.
assert_invariant(binding < MAX_SAMPLER_COUNT);
if (any(flags & ShaderStageFlags::VERTEX)) {
src.set(binding);
}
return key;
if (any(flags & ShaderStageFlags::FRAGMENT)) {
src.set(MAX_SAMPLER_COUNT + binding);
}
// TODO: add support for compute by extending SHADER_MODULE_COUNT and ensuring UsageFlags
// has 186 bits (MAX_SAMPLER_COUNT * 3)
// assert_invariant(!any(flags & ~(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT)));
return src;
}
VulkanPipelineCache::VulkanPipelineCache() : mDefaultRasterState(createDefaultRasterState()) {
@@ -73,15 +74,6 @@ VulkanPipelineCache::VulkanPipelineCache() : mDefaultRasterState(createDefaultRa
mDummyBufferWriteInfo.pBufferInfo = &mDummyBufferInfo;
mDummyBufferWriteInfo.pTexelBufferView = nullptr;
mDummySamplerWriteInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
mDummySamplerWriteInfo.pNext = nullptr;
mDummySamplerWriteInfo.dstArrayElement = 0;
mDummySamplerWriteInfo.descriptorCount = 1;
mDummySamplerWriteInfo.pImageInfo = &mDummySamplerInfo;
mDummySamplerWriteInfo.pBufferInfo = nullptr;
mDummySamplerWriteInfo.pTexelBufferView = nullptr;
mDummySamplerWriteInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
mDummyTargetInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
mDummyTargetWriteInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
mDummyTargetWriteInfo.pNext = nullptr;
@@ -106,8 +98,8 @@ void VulkanPipelineCache::setDevice(VkDevice device, VmaAllocator allocator) {
// Formulate some dummy objects and dummy descriptor info used only for clearing out unused
// bindings. This is especially crucial after a texture has been destroyed. Since core Vulkan
// does not allow specifying VK_NULL_HANDLE without the robustness2 extension, we are forced to
// bind dummy objects.
// does not allow specifying VK_NULL_HANDLE without the robustness2 extension, we would need to
// change the pipeline layout more frequently if we wanted to get rid of these dummy objects.
VkBufferCreateInfo bufferInfo {
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
@@ -119,26 +111,6 @@ void VulkanPipelineCache::setDevice(VkDevice device, VmaAllocator allocator) {
mDummyBufferInfo.buffer = mDummyBuffer;
mDummyBufferInfo.range = bufferInfo.size;
mDummySamplerInfo.imageLayout = mDummyTargetInfo.imageLayout;
VkSamplerCreateInfo samplerInfo {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = VK_FILTER_NEAREST,
.minFilter = VK_FILTER_NEAREST,
.mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
.anisotropyEnable = VK_FALSE,
.maxAnisotropy = 1,
.compareEnable = VK_FALSE,
.compareOp = VK_COMPARE_OP_ALWAYS,
.minLod = 0.0f,
.maxLod = 1.0f,
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE
};
vkCreateSampler(mDevice, &samplerInfo, VKALLOC, &mDummySamplerInfo.sampler);
}
bool VulkanPipelineCache::bindDescriptors(VkCommandBuffer cmdbuffer) noexcept {
@@ -204,7 +176,6 @@ bool VulkanPipelineCache::bindPipeline(VkCommandBuffer cmdbuffer) noexcept {
getOrCreatePipelineLayout()->lastUsed = mCurrentTime;
mBoundPipeline = mPipelineRequirements;
mBoundLayout = mLayoutRequirements;
vkCmdBindPipeline(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, cacheEntry->handle);
return true;
@@ -220,7 +191,7 @@ void VulkanPipelineCache::bindScissor(VkCommandBuffer cmdbuffer, VkRect2D scisso
VulkanPipelineCache::DescriptorCacheEntry* VulkanPipelineCache::createDescriptorSets() noexcept {
PipelineLayoutCacheEntry* layoutCacheEntry = getOrCreatePipelineLayout();
DescriptorCacheEntry descriptorCacheEntry = { .pipelineLayout = mLayoutRequirements };
DescriptorCacheEntry descriptorCacheEntry = { .pipelineLayout = mPipelineRequirements.layout };
// Each of the arenas for this particular layout are guaranteed to have the same size. Check
// the first arena to see if any descriptor sets are available that can be re-claimed. If not,
@@ -299,8 +270,8 @@ VulkanPipelineCache::DescriptorCacheEntry* VulkanPipelineCache::createDescriptor
writeInfo.dstBinding = binding;
}
for (uint32_t binding = 0; binding < SAMPLER_BINDING_COUNT; binding++) {
VkWriteDescriptorSet& writeInfo = writes[nwrites++];
if (mDescriptorRequirements.samplers[binding].sampler) {
VkWriteDescriptorSet& writeInfo = writes[nwrites++];
VkDescriptorImageInfo& imageInfo = descriptorSamplers[binding];
imageInfo = mDescriptorRequirements.samplers[binding];
writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
@@ -311,14 +282,9 @@ VulkanPipelineCache::DescriptorCacheEntry* VulkanPipelineCache::createDescriptor
writeInfo.pImageInfo = &imageInfo;
writeInfo.pBufferInfo = nullptr;
writeInfo.pTexelBufferView = nullptr;
} else {
writeInfo = mDummySamplerWriteInfo;
assert_invariant(mDummySamplerWriteInfo.pImageInfo->sampler);
assert_invariant(mDummySamplerInfo.imageView);
writeInfo.dstSet = descriptorCacheEntry.handles[1];
writeInfo.dstBinding = binding;
}
writeInfo.dstSet = descriptorCacheEntry.handles[1];
writeInfo.dstBinding = binding;
}
for (uint32_t binding = 0; binding < TARGET_BINDING_COUNT; binding++) {
VkWriteDescriptorSet& writeInfo = writes[nwrites++];
@@ -371,7 +337,9 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
// If we reach this point, we need to create and stash a brand new pipeline object.
shaderStages[0].module = mPipelineRequirements.shaders[0];
shaderStages[0].pSpecializationInfo = mSpecializationRequirements;
shaderStages[1].module = mPipelineRequirements.shaders[1];
shaderStages[1].pSpecializationInfo = mSpecializationRequirements;
// Expand our size-optimized structs into the proper Vk structs.
uint32_t numVertexAttribs = 0;
@@ -513,7 +481,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
}
VulkanPipelineCache::PipelineLayoutCacheEntry* VulkanPipelineCache::getOrCreatePipelineLayout() noexcept {
auto iter = mPipelineLayouts.find(mLayoutRequirements);
auto iter = mPipelineLayouts.find(mPipelineRequirements.layout);
if (UTILS_LIKELY(iter != mPipelineLayouts.end())) {
return &iter.value();
}
@@ -541,7 +509,7 @@ VulkanPipelineCache::PipelineLayoutCacheEntry* VulkanPipelineCache::getOrCreateP
VkDescriptorSetLayoutBinding sbindings[SAMPLER_BINDING_COUNT];
binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
for (uint32_t i = 0; i < SAMPLER_BINDING_COUNT; i++) {
binding.stageFlags = getShaderStageFlags(mLayoutRequirements, i);
binding.stageFlags = getShaderStageFlags(mPipelineRequirements.layout, i);
binding.binding = i;
sbindings[i] = binding;
}
@@ -571,8 +539,7 @@ VulkanPipelineCache::PipelineLayoutCacheEntry* VulkanPipelineCache::getOrCreateP
if (UTILS_UNLIKELY(result != VK_SUCCESS)) {
return nullptr;
}
return &mPipelineLayouts.emplace(mLayoutRequirements, cacheEntry).first.value();
return &mPipelineLayouts.emplace(mPipelineRequirements.layout, cacheEntry).first.value();
}
void VulkanPipelineCache::bindProgram(const VulkanProgram& program) noexcept {
@@ -580,7 +547,7 @@ void VulkanPipelineCache::bindProgram(const VulkanProgram& program) noexcept {
for (uint32_t ssi = 0; ssi < SHADER_MODULE_COUNT; ssi++) {
mPipelineRequirements.shaders[ssi] = shaders[ssi];
}
mLayoutRequirements = getPipelineLayoutKey(program.samplerGroupInfo);
mSpecializationRequirements = program.bundle.specializationInfos;
}
void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexcept {
@@ -656,10 +623,12 @@ void VulkanPipelineCache::bindUniformBuffer(uint32_t bindingIndex, VkBuffer unif
key.uniformBufferSizes[bindingIndex] = size;
}
void VulkanPipelineCache::bindSamplers(VkDescriptorImageInfo samplers[SAMPLER_BINDING_COUNT]) noexcept {
void VulkanPipelineCache::bindSamplers(VkDescriptorImageInfo samplers[SAMPLER_BINDING_COUNT],
UsageFlags flags) noexcept {
for (uint32_t bindingIndex = 0; bindingIndex < SAMPLER_BINDING_COUNT; bindingIndex++) {
mDescriptorRequirements.samplers[bindingIndex] = samplers[bindingIndex];
}
mPipelineRequirements.layout = flags;
}
void VulkanPipelineCache::bindInputAttachment(uint32_t bindingIndex,
@@ -678,10 +647,6 @@ void VulkanPipelineCache::destroyCache() noexcept {
}
mPipelines.clear();
mBoundPipeline = {};
if (mDummySamplerInfo.sampler) {
vkDestroySampler(mDevice, mDummySamplerInfo.sampler, VKALLOC);
mDummySamplerInfo.sampler = VK_NULL_HANDLE;
}
vmaDestroyBuffer(mAllocator, mDummyBuffer, mDummyMemory);
mDummyBuffer = VK_NULL_HANDLE;
mDummyMemory = VK_NULL_HANDLE;
@@ -697,7 +662,6 @@ void VulkanPipelineCache::onCommandBuffer(const VulkanCommandBuffer& cmdbuffer)
// The Vulkan spec says: "When a command buffer begins recording, all state in that command
// buffer is undefined." Therefore, we need to clear all bindings at this time.
mBoundPipeline = {};
mBoundLayout = {};
mBoundDescriptor = {};
mCurrentScissor = {};
@@ -866,8 +830,12 @@ void VulkanPipelineCache::growDescriptorPool() noexcept {
mDescriptorSets.clear();
}
size_t VulkanPipelineCache::PipelineLayoutKeyHashFn::operator()(const PipelineLayoutKey& key) const {
return key.getValue();
size_t VulkanPipelineCache::PipelineLayoutKeyHashFn::operator()(
const PipelineLayoutKey& key) const {
std::hash<uint64_t> hasher;
auto h0 = hasher(key.getBitsAt(0));
auto h1 = hasher(key.getBitsAt(1));
return h0 ^ (h1 << 1);
}
bool VulkanPipelineCache::PipelineLayoutKeyEqual::operator()(const PipelineLayoutKey& k1,
@@ -875,13 +843,13 @@ bool VulkanPipelineCache::PipelineLayoutKeyEqual::operator()(const PipelineLayou
return k1 == k2;
}
bool VulkanPipelineCache::PipelineEqual::operator()(const VulkanPipelineCache::PipelineKey& k1,
const VulkanPipelineCache::PipelineKey& k2) const {
bool VulkanPipelineCache::PipelineEqual::operator()(const PipelineKey& k1,
const PipelineKey& k2) const {
return 0 == memcmp((const void*) &k1, (const void*) &k2, sizeof(k1));
}
bool VulkanPipelineCache::DescEqual::operator()(const VulkanPipelineCache::DescriptorKey& k1,
const VulkanPipelineCache::DescriptorKey& k2) const {
bool VulkanPipelineCache::DescEqual::operator()(const DescriptorKey& k1,
const DescriptorKey& k2) const {
for (uint32_t i = 0; i < UBUFFER_BINDING_COUNT; i++) {
if (k1.uniformBuffers[i] != k2.uniformBuffers[i] ||
k1.uniformBufferOffsets[i] != k2.uniformBufferOffsets[i] ||
@@ -56,7 +56,7 @@ public:
VulkanPipelineCache(VulkanPipelineCache const&) = delete;
VulkanPipelineCache& operator=(VulkanPipelineCache const&) = delete;
static constexpr uint32_t UBUFFER_BINDING_COUNT = Program::BINDING_COUNT;
static constexpr uint32_t UBUFFER_BINDING_COUNT = Program::UNIFORM_BINDING_COUNT;
static constexpr uint32_t SAMPLER_BINDING_COUNT = MAX_SAMPLER_COUNT;
static constexpr uint32_t TARGET_BINDING_COUNT = MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT;
static constexpr uint32_t SHADER_MODULE_COUNT = 2;
@@ -66,9 +66,6 @@ public:
static constexpr uint32_t DESCRIPTOR_TYPE_COUNT = 3;
static constexpr uint32_t INITIAL_DESCRIPTOR_SET_POOL_SIZE = 512;
#pragma clang diagnostic push
#pragma clang diagnostic warning "-Wpadded"
// The VertexArray POD is an array of buffer targets and an array of attributes that refer to
// those targets. It does not include any references to actual buffers, so you can think of it
// as a vertex assembler configuration. For simplicity it contains fixed-size arrays and does
@@ -82,8 +79,15 @@ public:
struct ProgramBundle {
VkShaderModule vertex;
VkShaderModule fragment;
VkSpecializationInfo* specializationInfos = nullptr;
};
using UsageFlags = utils::bitset128;
static UsageFlags getUsageFlags(uint16_t binding, ShaderStageFlags stages, UsageFlags src = {});
#pragma clang diagnostic push
#pragma clang diagnostic warning "-Wpadded"
// The RasterState POD contains standard graphics-related state like blending, culling, etc.
// The following states are omitted because Filament never changes them:
// >>> depthClampEnable, rasterizerDiscardEnable, depthBoundsTestEnable, stencilTestEnable
@@ -149,7 +153,7 @@ public:
void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept;
void bindUniformBuffer(uint32_t bindingIndex, VkBuffer uniformBuffer,
VkDeviceSize offset = 0, VkDeviceSize size = VK_WHOLE_SIZE) noexcept;
void bindSamplers(VkDescriptorImageInfo samplers[SAMPLER_BINDING_COUNT]) noexcept;
void bindSamplers(VkDescriptorImageInfo samplers[SAMPLER_BINDING_COUNT], UsageFlags flags) noexcept;
void bindInputAttachment(uint32_t bindingIndex, VkDescriptorImageInfo imageInfo) noexcept;
void bindVertexArray(const VertexArray& varray) noexcept;
@@ -178,7 +182,6 @@ public:
// Injects a dummy texture that can be used to clear out old descriptor sets.
void setDummyTexture(VkImageView imageView) {
mDummySamplerInfo.imageView = imageView;
mDummyTargetInfo.imageView = imageView;
}
@@ -187,9 +190,9 @@ private:
// PIPELINE LAYOUT CACHE KEY
// -------------------------
// The cache key for pipeline layouts represents 32 samplers, each with 2 bits (one for each
// shader stage).
using PipelineLayoutKey = utils::bitset64;
using PipelineLayoutKey = utils::bitset128;
static_assert(PipelineLayoutKey::BIT_COUNT >= 2 * MAX_SAMPLER_COUNT);
struct PipelineLayoutKeyHashFn {
size_t operator()(const PipelineLayoutKey& key) const;
@@ -235,9 +238,9 @@ private:
operator VkVertexInputBindingDescription() const {
return { binding, stride, (VkVertexInputRate) inputRate };
}
uint16_t binding;
uint16_t inputRate;
uint32_t stride;
uint16_t binding;
uint16_t inputRate;
uint32_t stride;
};
// The pipeline key is a POD that represents all currently bound states that form the immutable
@@ -251,9 +254,10 @@ private:
VertexInputBindingDescription vertexBuffers[VERTEX_ATTRIBUTE_COUNT]; // 128 : 156
RasterState rasterState; // 16 : 284
uint32_t padding; // 4 : 300
PipelineLayoutKey layout; // 16 : 304
};
static_assert(sizeof(PipelineKey) == 304, "PipelineKey must not have implicit padding.");
static_assert(sizeof(PipelineKey) == 320, "PipelineKey must not have implicit padding.");
using PipelineHashFn = utils::hash::MurmurHashFn<PipelineKey>;
@@ -289,17 +293,17 @@ private:
// Represents all the Vulkan state that comprises a bound descriptor set.
struct DescriptorKey {
VkBuffer uniformBuffers[UBUFFER_BINDING_COUNT]; // 96 0
DescriptorImageInfo samplers[SAMPLER_BINDING_COUNT]; // 768 96
DescriptorImageInfo inputAttachments[TARGET_BINDING_COUNT]; // 192 864
uint32_t uniformBufferOffsets[UBUFFER_BINDING_COUNT]; // 48 1056
uint32_t uniformBufferSizes[UBUFFER_BINDING_COUNT]; // 48 1104
VkBuffer uniformBuffers[UBUFFER_BINDING_COUNT]; // 80 0
DescriptorImageInfo samplers[SAMPLER_BINDING_COUNT]; // 1488 80
DescriptorImageInfo inputAttachments[TARGET_BINDING_COUNT]; // 192 1568
uint32_t uniformBufferOffsets[UBUFFER_BINDING_COUNT]; // 40 1760
uint32_t uniformBufferSizes[UBUFFER_BINDING_COUNT]; // 40 1080
};
static_assert(offsetof(DescriptorKey, samplers) == 96);
static_assert(offsetof(DescriptorKey, inputAttachments) == 864);
static_assert(offsetof(DescriptorKey, uniformBufferOffsets) == 1056);
static_assert(offsetof(DescriptorKey, uniformBufferSizes) == 1104);
static_assert(sizeof(DescriptorKey) == 1152, "DescriptorKey must not have implicit padding.");
static_assert(offsetof(DescriptorKey, samplers) == 80);
static_assert(offsetof(DescriptorKey, inputAttachments) == 1568);
static_assert(offsetof(DescriptorKey, uniformBufferOffsets) == 1760);
static_assert(offsetof(DescriptorKey, uniformBufferSizes) == 1800);
static_assert(sizeof(DescriptorKey) == 1840, "DescriptorKey must not have implicit padding.");
using DescHashFn = utils::hash::MurmurHashFn<DescriptorKey>;
@@ -382,12 +386,11 @@ private:
const RasterState mDefaultRasterState;
// Current requirements for the pipeline layout, pipeline, and descriptor sets.
PipelineLayoutKey mLayoutRequirements = {};
PipelineKey mPipelineRequirements = {};
DescriptorKey mDescriptorRequirements = {};
VkSpecializationInfo* mSpecializationRequirements = {};
// Current bindings for the pipeline layout, pipeline, and descriptor sets.
PipelineLayoutKey mBoundLayout = {};
// Current bindings for the pipeline and descriptor sets.
PipelineKey mBoundPipeline = {};
DescriptorKey mBoundDescriptor = {};
@@ -418,8 +421,6 @@ private:
VkDescriptorBufferInfo mDummyBufferInfo = {};
VkWriteDescriptorSet mDummyBufferWriteInfo = {};
VkDescriptorImageInfo mDummySamplerInfo = {};
VkWriteDescriptorSet mDummySamplerWriteInfo = {};
VkDescriptorImageInfo mDummyTargetInfo = {};
VkWriteDescriptorSet mDummyTargetWriteInfo = {};
+95
View File
@@ -0,0 +1,95 @@
/*
* Copyright (C) 2022 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 "ComputeTest.h"
#include "PlatformRunner.h"
#include <backend/Platform.h>
#include "private/backend/CommandBufferQueue.h"
#include "private/backend/DriverApi.h"
using namespace filament;
using namespace filament::backend;
int main(int argc, char* argv[]) {
auto backend = (Backend)test::parseArgumentsForBackend(argc, argv);
ComputeTest::init(backend);
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
// ------------------------------------------------------------------------------------------------
static constexpr size_t CONFIG_MIN_COMMAND_BUFFERS_SIZE = 1 * 1024 * 1024;
static constexpr size_t CONFIG_COMMAND_BUFFERS_SIZE = 3 * CONFIG_MIN_COMMAND_BUFFERS_SIZE;
Backend ComputeTest::sBackend = Backend::NOOP;
void ComputeTest::init(Backend backend) {
sBackend = backend;
}
ComputeTest::ComputeTest()
: commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE, CONFIG_COMMAND_BUFFERS_SIZE) {
}
ComputeTest::~ComputeTest() = default;
void ComputeTest::SetUp() {
Test::SetUp();
auto backend = sBackend;
DefaultPlatform* platform = DefaultPlatform::create(&backend);
GTEST_ASSERT_EQ(uint8_t(backend), uint8_t(sBackend));
driver = platform->createDriver(nullptr, {});
commandStream = std::make_unique<CommandStream>(*driver, commandBufferQueue.getCircularBuffer());
// We need at least feature level 2 to run the Compute tests
if (driver->getFeatureLevel() < FeatureLevel::FEATURE_LEVEL_2) {
GTEST_SKIP();
}
}
void ComputeTest::TearDown() {
Test::TearDown();
flushAndWait();
driver->terminate();
delete driver;
}
void ComputeTest::executeCommands() {
commandBufferQueue.flush();
auto buffers = commandBufferQueue.waitForCommands();
for (auto& item : buffers) {
if (UTILS_LIKELY(item.begin)) {
getDriverApi().execute(item.begin);
commandBufferQueue.releaseBuffer(item);
}
}
}
void ComputeTest::flushAndWait(uint64_t timeout) {
auto& api = getDriverApi();
auto fence = api.createFence();
api.finish();
executeCommands();
api.wait(fence, timeout);
api.destroyFence(fence);
}
+51
View File
@@ -0,0 +1,51 @@
/*
* Copyright (C) 2022 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_COMPUTE_TEST_H
#define TNT_COMPUTE_TEST_H
#include <gtest/gtest.h>
#include "private/backend/CommandBufferQueue.h"
#include "private/backend/DriverApi.h"
class ComputeTest : public ::testing::Test {
static filament::backend::Backend sBackend;
filament::backend::Driver* driver = nullptr;
filament::backend::CommandBufferQueue commandBufferQueue;
std::unique_ptr<filament::backend::DriverApi> commandStream;
protected:
void SetUp() override;
void TearDown() override;
void executeCommands();
void flushAndWait(uint64_t timeout = 1000);
filament::backend::DriverApi& getDriverApi() { return *commandStream; }
filament::backend::Driver& getDriver() { return *driver; }
static filament::backend::Backend getBackend() noexcept { return sBackend; };
bool isMobile() noexcept { return driver->getShaderModel() == filament::backend::ShaderModel::MOBILE; };
public:
static void init(filament::backend::Backend backend);
ComputeTest();
~ComputeTest() override;
};
#endif // TNT_COMPUTE_TEST_H
+2 -2
View File
@@ -184,8 +184,8 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(
Program ShaderGenerator::getProgram(filament::backend::DriverApi&) noexcept {
Program program;
program.shader(ShaderType::VERTEX, mVertexBlob.data(), mVertexBlob.size());
program.shader(ShaderType::FRAGMENT, mFragmentBlob.data(), mFragmentBlob.size());
program.shader(ShaderStage::VERTEX, mVertexBlob.data(), mVertexBlob.size());
program.shader(ShaderStage::FRAGMENT, mFragmentBlob.data(), mFragmentBlob.size());
return program;
}
+1 -1
View File
@@ -46,7 +46,7 @@ public:
filament::backend::Program getProgram(filament::backend::DriverApi&) noexcept;
private:
using ShaderStage = filament::backend::ShaderType;
using ShaderStage = filament::backend::ShaderStage;
using Blob = std::vector<char>;
static Blob transpileShader(ShaderStage stage, std::string shader, Backend backend,
+9
View File
@@ -107,6 +107,15 @@ const TBuiltInResource DefaultTBuiltInResource = {
/* .maxTaskWorkGroupSizeY_NV = */ 1,
/* .maxTaskWorkGroupSizeZ_NV = */ 1,
/* .maxMeshViewCountNV = */ 4,
/* .max_mesh_output_vertices_ext = */ 256,
/* .max_mesh_output_primitives_ext = */ 256,
/* .max_mesh_work_group_size_x_ext = */ 128,
/* .max_mesh_work_group_size_y_ext = */ 128,
/* .max_mesh_work_group_size_z_ext = */ 128,
/* .max_task_work_group_size_x_ext = */ 128,
/* .max_task_work_group_size_y_ext = */ 128,
/* .max_task_work_group_size_z_ext = */ 128,
/* .max_mesh_view_count_ext = */ 4,
/* .maxDualSourceDrawBuffersEXT = */ 1,
/* .limits = */ {
+3 -3
View File
@@ -417,7 +417,7 @@ TEST_F(BackendTest, DepthMinify) {
{
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("tex"), 0, false };
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
prog.uniformBlockBindings({{"params", 1}});
program = api.createProgram(std::move(prog));
@@ -559,7 +559,7 @@ TEST_F(BackendTest, ColorResolve) {
{
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("tex"), 0, false };
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
prog.uniformBlockBindings({{"params", 1}});
program = api.createProgram(std::move(prog));
@@ -667,7 +667,7 @@ TEST_F(BackendTest, DepthResolve) {
{
ShaderGenerator shaderGen(triangleVs, triangleFs, sBackend, sIsMobilePlatform);
Program prog = shaderGen.getProgram(api);
Program::Sampler psamplers[] = { utils::CString("tex"), 0, false };
Program::Sampler psamplers[] = { utils::CString("tex"), 0 };
prog.setSamplerGroup(0, ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, psamplers, sizeof(psamplers) / sizeof(psamplers[0]));
prog.uniformBlockBindings({{"params", 1}});
program = api.createProgram(std::move(prog));

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