Compare commits

..
48 Commits
Author SHA1 Message Date
Benjamin Doherty 1f039b917a Bump version to 1.66.1 2025-10-14 14:25:29 -07:00
Benjamin Doherty b7dea28cc5 Release Filament 1.66.0 2025-10-14 14:25:20 -07:00
Benjamin Doherty 9aa8e7780b Fix NEW_RELEASE_NOTES 2025-10-14 14:24:52 -07:00
Ben Doherty 271e639abe Revert "Make check-headers test use c++20" (#9316) 2025-10-14 14:12:34 -07:00
Ben Doherty 082a79eebc Fix, revert to C++17 string_view constructor (#9315) 2025-10-13 15:01:40 -07:00
Ben Doherty 7a10c09954 Fix, Abseil compilation error with StaticString (#9314) 2025-10-13 15:01:39 -07:00
Filament Bot 3139a37cc7 [automated] Updating /docs due to commit f8e8c27
Full commit hash is f8e8c27c04

DOCS_ALLOW_DIRECT_EDITS
2025-10-13 19:56:08 +00:00
Mathias AgopianandPowei Feng f8e8c27c04 Add a Renderer API to force skipping frames (#9313)
* Add a Renderer API to force skipping frames

Renderer::skipNextFrames(size_t) can be used to force filament to
pretend the next N frames must be skipped. This is mostly useful for
debugging.

* Add DebugOptions to Settings

We still need to move the "Debug" features of gltf_viewer to this,
but this give us a framework to do it. 
Currently there is one debug option that allows to set a number of
frames to skip.

ViewerGui propose a button to skip 10 frames using this framework

* Update libs/viewer/src/Settings.cpp

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

* Update libs/viewer/src/Settings.cpp

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

---------

Co-authored-by: Powei Feng <powei@google.com>
2025-10-13 12:52:17 -07:00
Mathias Agopian c79d695ffb fix wasm build. (#9312)
The new AsyncJobQueue can't work with wasm, which doesn't support 
threads.
2025-10-13 12:18:28 -07:00
David Neto ad6f6cf149 filamat: remove call to Glslang's SPIR-V remapper (#9310)
The Glslang SPIR-V remapper was removed, and replaced by the CanonicalizeIds
pass in spirv-opt.  See https://github.com/KhronosGroup/SPIRV-Tools/pull/6174

Filament's GLSL post-optimization algorithm already runs CanonicalizeIds.

Bug: b:450891564
2025-10-13 09:46:07 -07:00
Mathias Agopian 8506e94f10 Improvements to FrameInfo (#9293)
* fix a possible deadlock in AsyncJobQueue

drainAndExit() could get stuck because it waited for the job
queue to be empty, but that was not signaled.
in fact, drainAndExit() didn't need to do that.

* Improvements to FrameInfo 

- return the GPU Complete timestamp
- return the app VSYNC timestamp
- works TimerQueries are not supported

The VSYNC time is just a convenience as it is the same value
provided by the application during Renderer::beginFrame() or via
Renderer::setVsyncTime().
2025-10-10 21:40:28 +00:00
Mathias Agopian 5db8e0b9ab fix a regression in FMaterial::setConstant() (#9308)
it used to return false when setting the same value for the constant,
which other code used to invalidate the material or not. but it
was recently changed to always return true on success.

this reestablish the previous behavior.
2025-10-10 12:43:32 -07:00
Powei Feng da55574b32 webgpu: refactor blitter to use struct as keys (#9300)
Using struct as keys simplifies the parameter definitions of the
relevant pipeline, pipelinelayout, shader module methods. This
enables adding or removing fields without adding major changes
to method definitions.

Using utils::hash::MurmurHashFn also aligns with the style of
the rest of filament.
2025-10-10 17:31:47 +00:00
yein 736fed00b3 handle StatusCode::UNSUPPORTED in << operator (#9307) 2025-10-10 00:06:57 +00:00
Evan Mezeske 9b200fc33a Some trivial changes to compile under most recent MSVC as a CMake sub-project (#9275)
* Add a workaround for Filament's breaking change to add_subdirectory for Abseil, see: https://github.com/google/filament/issues/8611

* Fix #pragma warning syntax for MSVC

* Only search for an external Abseil dependency if a new option FILAMENT_USE_EXTERNAL_ABSL is set
2025-10-09 23:06:18 +00:00
yein fa436f1d12 Make JsonishParser use utils::Status (#9302)
* Make JsonishParser use utils::Status

- add unsupported error in utils::Status
- add invalid case in the test

* return a pair of Status and string in resolveEscapes;
use initializer list for JsonishString and move that to the header.
2025-10-09 15:44:03 -07:00
Powei Feng 837b2715a0 Update MATERIAL_VERSION to 66 2025-10-09 11:29:16 -07:00
Jordan Rupprecht 1a04312986 Add missing #include <ios> (#9305)
This is needed to instantiate `std::streampos`. It currently relies on transitive header inclusion to get that, which is going away for libc++ in https://github.com/llvm/llvm-project/commit/ebcf1bf2ecba6b25ece3c2bbddb4485e76189387.
2025-10-09 10:24:00 -07:00
Filament Bot a89711b006 [automated] Updating /docs due to commit 6d061b5
Full commit hash is 6d061b5d01

DOCS_ALLOW_DIRECT_EDITS
2025-10-08 20:04:04 +00:00
Powei Feng 6d061b5d01 renderdiff: add two params to local_test.sh (#9301)
- --no_rebuild will skip building the gltf_viewer again
 - --num_threads will limit the number of threads used in running
   the rendering test.

Both of these options are meant for debugging locally.
2025-10-08 13:01:09 -07:00
Powei Feng 53ddb3dd1c renderdiff: [viewer] enable frontend to be used with runID (#9277) 2025-10-08 18:36:30 +00:00
Powei Feng c7b0a7f441 webgpu: fix queue submission and fence (#9299)
- Add WebGPUQeuueManager to manage queue submissions
 - The manager will keep a "last status" field that will track
   the status of the last submission.
 - Any created HwFence will be associated with a status object.
 - The object updates in a callback that indicates that the
   submitted workfload has been completed.
2025-10-08 17:30:19 +00:00
Filament Bot d7b44a2585 [automated] Updating /docs due to commit caf886d
Full commit hash is caf886df6b

DOCS_ALLOW_DIRECT_EDITS
2025-10-07 22:58:22 +00:00
Powei Feng caf886df6b Release Filament 1.65.4 2025-10-07 15:54:13 -07:00
Sungun Park 287984dd7f gl: Enable shared contexts for Windows (#9298)
This change addresses a platform-specific issue on Windows where shared
OpenGL contexts must be created on the same thread as the primary
context.

To resolve this, we now pre-create a pool of shared contexts when the
driver is initialized. These contexts are then distributed to other
threads as needed, ensuring shared context creation requirement on
Windows. This allows us to be able to use THREAD_POOL mode for shader
compiler service on Windows. So Windows build uses THREAD_POOL mode
instead of ASYNCHRONOUS mode as of this change.

This change also refactors the shader compiler service to catch and
report errors in the same thread where the program compilation and
linking actually performed as it's the correct way to use shared
contexts. Otherwise it may crashes.
2025-10-07 19:30:42 +00:00
Powei Feng 3294bb64a5 Revert "Make matc workarounds default to NONE again" (#9295)
This reverts commit d11a6b4467.

Breakage detailed in following bug

BUGS=449740720
2025-10-07 18:50:34 +00:00
yeinandPowei Feng f52476a323 Use utils::Status in MaterialParser (#9285)
* Use utils::Status in MaterialParser

* Use utils::sstream instead of std::stringstream

* Remove remaining std::cerr and dep; update MaterialParser::reflectParameters

* make error message in utils::Status more generic

---------

Co-authored-by: Powei Feng <powei@google.com>
2025-10-07 11:27:11 -07:00
Powei Feng c839915a00 Add missing optional include for MaterialParser (#9296) 2025-10-06 22:50:02 -07:00
Mathias Agopian 2deafc6b81 fix matc -E (preprocessor) option (#9292)
this option was ignored and treated as -O0.
2025-10-03 20:15:05 -07:00
Powei Feng fdec0f79a2 renderdiff: [viewer] fix magnifier (#9290)
- Fix the magnifier positioning for both compare and standalone
   mode. Simplified a lot of the logic and streamlined passing
   of states between components.
- Slight css adjustments
2025-10-03 21:44:53 +00:00
Mathias Agopian 31d66002a9 optional per-channel depth clear (#9287)
- increase the number of channels from 4 to 8
- new api on View to enable per-channel depth clear
FIXES=[447648764]
2025-10-03 14:25:02 -07:00
Mathias Agopian f2ed382cf1 New ImmutableCString string class (#9291)
ImmutableCString is a string class similar to CString except it's
immutable. ImmutableCString occupies 16 bytes instead of 8 for CString.

However, ImmutableCString is able to avoid memory allocation when
constructed from a string literal, and in that way it us similar
to StaticString.

ImmutableCString can be auto converted from StaticString.

The backend tag tracking is updated to use ImmutableCString and
the FrameGraph resource manager us updated to use StaticString.

Together these changes significantly cut down heap allocations due to
internal tagging.


We also add optional tracking to {Immutable}CString.
2025-10-03 14:23:36 -07:00
yein a4746eab0c Minor changes in utils::Status (#9288)
* Minor changes in utils::Status

- << operator doesn't have to be friend
- simplify getErrorMessage to not use strlen internally

* Replace std::ostream to utils::io::ostream
2025-10-03 12:53:46 -07:00
Filament Bot 0b011fae7d [automated] Updating /docs due to commit a6c9922
Full commit hash is a6c9922d33

DOCS_ALLOW_DIRECT_EDITS
2025-10-03 18:40:22 +00:00
Eliza a6c9922d33 internal: fix some editor indentation rules (#9289)
* internal: fix some editor indentation rules

* internal: update CODE_STYLE.md
2025-10-03 11:38:01 -07:00
Eliza 7fe1ee3fd5 utils: RefCountedInternPool/RefCountedMap (#9284)
* utils: RefCountedInternPool/RefCountedMap

First, introduce RefCountedInternPool, a reference counted intern pool of
Slice<const T>. Just acquire() a slice that you want and you're guaranteed to
get exactly one canonical value-equal Slice<const T> back.

Additionally, introduce the concept of NullValue to RefCountedMap. A NullValue
defines what should be considered an uninitialized value; by default, it's the
default value of that type (0 for ints, nullptr for pointers, etc). This allows
us to lazily-initialize values in the map. A client can acquire() a bunch of
different resources which will be initialized only when get(factory) is called.
If a client attempts to get() a value without specifying a factory, and the
value is not initialized (i.e. equal to NullValue{}()), RefCountedMap will
panic.

* utils: add unit tests for ref-counted collections

* utils: remove C++20 features, fix memory issue

* utils: remove RefCounted from InternPool
2025-10-03 11:21:02 -07:00
Eliza 075726db8b engine: change spec constants to simple list (#9283)
* engine: change spec constants to simple list

For a shader program cache to be keyed on the set of spec constants that a
program has set, we need to know full exact list of constants, including their
default values. If we're going to always hold a list of all constants all the
time, then we may as well store them as a simple list where each index is the ID
number of the spec constant.

As part of this change, we now write the default values of spec constants into
the material file metadata.

* fix indent

* engine: fix sRGB swapchain emulation
2025-10-02 23:48:11 +00:00
Mathias Agopian b22ff3bebb fix exceptions (#9281) 2025-10-02 16:22:01 -07:00
Sungun Park bc794bbf7b Fix public headers check on github (#9286) 2025-10-01 23:36:26 -07:00
Sungun Park 9c29c3d192 Fix memory leaks for xlib (#9282)
This change frees memory returned by `glXGetFBConfigs` and
`glXChooseFBConfig` using `XFree`.
2025-10-01 23:09:55 +00:00
Eliza 8e5dabfa8e utils: split Slice into mutable and constant types (#9276)
* slice: fix memory semantics

* slice: prefer passing slice by value

This lets us do nice things like coercing Slice<T> to Slice<const T>, etc.

* slice: fix unit tests

* slice: fix copy/assignment, hash function

Don't attempt to define a copy constructor/assignment operator which would
convert a constant type to a mutable type.

Additionally, fix the hash function such that we're hashing U instead of const
U.
2025-10-01 22:40:50 +00:00
Mathias Agopian e974989a95 new utility AsyncJobQueue (#9278)
* new utility AsyncJobQueue

this is a very simple job queue, it spawns a thread and runs the jobs
pushed to the queue in sequence.

* use AsyncJobQueue in OpenGLTimerQuery
2025-10-01 14:54:34 -07:00
yein 057ce2ea4b Introduce utils::Status (#9279)
* Introduce utils::Status

* Replace std::string with utils::CString in utils::Status

* Update unit tests
2025-10-01 14:53:51 -07:00
Martin Valgur 2e920f2780 Fix an invalid USE_EXTERNAL_GLES3 define (#9280)
Should be FILAMENT_USE_EXTERNAL_GLES3 to match its usage in code.
2025-10-01 10:59:55 -07:00
Mathias Agopian b41e6dfd5c fix several issues CircularQueue (#9271)
- pop_back() didn't call the object's dtor
- added a dtor so objects can't be leaked
- made it moveable but not copiable
- added unit tests
2025-09-30 16:06:58 -07:00
Filament Bot 8413c84284 [automated] Updating /docs due to commit 10e63bf
Full commit hash is 10e63bf2cf

DOCS_ALLOW_DIRECT_EDITS
2025-09-30 21:44:09 +00:00
Powei Feng 10e63bf2cf renderdiff: [viewer] add Run ID as a way to pull artifacts (#9272)
The viewer supports pulling artifacts based on PR number, and now
we support providing Run ID as an alternative to identify the
renderdiff run on Github CI.
2025-09-30 21:32:36 +00:00
Filament Bot 73ba8fc753 [automated] Updating /docs due to commit f07a44e
Full commit hash is f07a44eb15

DOCS_ALLOW_DIRECT_EDITS
2025-09-30 18:53:20 +00:00
238 changed files with 4816 additions and 2561 deletions
+3 -1
View File
@@ -65,4 +65,6 @@ SpacesInParentheses: false
SpacesInSquareBrackets: false
TabWidth: 4
UseTab: Never
PackConstructorInitializers: Never
PackConstructorInitializers: Never
ConstructorInitializerIndentWidth: 8
IndentWrappedFunctionNames: true
+1 -1
View File
@@ -391,7 +391,7 @@ Alternatively, if you have node installed you can use the
[live-server](https://www.npmjs.com/package/live-server) package, which automatically refreshes the
web page when it detects a change.
Each sample app has its own handwritten html file. Additionally the server folder contains assets
Each sample app has its own handwritten html file. Additionally, the server folder contains assets
such as meshes, textures, and materials.
## Running the native samples
+1 -1
View File
@@ -351,7 +351,7 @@ else()
endif()
if (FILAMENT_USE_EXTERNAL_GLES3)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_EXTERNAL_GLES3")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DFILAMENT_USE_EXTERNAL_GLES3")
endif()
if (FILAMENT_SUPPORTS_EGL_ON_LINUX)
+18 -6
View File
@@ -20,7 +20,7 @@ The guiding principles of the filament code style and code formatting can be res
- class access modifiers are not indented
- last line of `.cpp` or `.h` file must be an empty line
```
```c++
for (int i = 0; i < max; i++) {
}
@@ -73,12 +73,24 @@ src/data.inc
- `public` class attributes *are not* prefixed
- class attributes and methods are lower camelcase
```
```c++
extern int gGlobalWarming;
class FooBar {
public:
void methodName();
FooBar(int attributeName, int sizeInBytes)
: mAttributeName(attributeName),
sizeInBytes(sizeInBytes) {}
void reallyLongMethodNameWithLotsOfArguments(bool argument1,
int someSecondArgument, int bestArgument) {
std::pair<bool, int> pair = {
argument1,
argument2,
};
// etc
}
int sizeInBytes;
private:
int mAttributeName;
@@ -97,7 +109,7 @@ private:
- always include the copyright notice at the top of every file
- make sure the date is correct
```
```c++
/*
* Copyright (C) 2018 The Android Open Source Project
*
@@ -139,7 +151,7 @@ conversely containers and algorithms are not allowed. There are exceptions such
*Sorting the headers is important to help catching missing `#include` directives.*
```
```c++
/*
* Copyright (C) 2018 The Android Open Source Project
*
@@ -183,7 +195,7 @@ conversely containers and algorithms are not allowed. There are exceptions such
### Misc
- Use `auto` only when the type appears on the same line or with iterators and lambdas.
```
```c++
auto foo = new Foo();
for (auto& i : collection) { }
```
-1
View File
@@ -1,7 +1,6 @@
# Filament Release Notes log
**If you are merging a PR into main**: please add the release note below, under the *Release notes
We are chaning the way Vulkan buffers are handled. We need to switch over to a managed (or view-based) model where the data stored inside the object is a proxy to a Vulkan object that can dynamically be swapped around.
**If you are cherry-picking a commit into an rc/ branch**: add the release note under the
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.65.4'
implementation 'com.google.android.filament:filament-android:1.66.1'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.65.4'
pod 'Filament', '~> 1.66.1'
```
## Documentation
+10
View File
@@ -7,6 +7,16 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.66.1
- filamat: Removed a dependency on Glslang's deprecated SPIR-V remapper.
The functionality is already implemented by calling the CanonicalizeIds pass
in the SPIRV-Tools, and should be a non-functional change.
## v1.66.0
- materials: include default values of spec constants in material metadata [⚠️ **Recompile Materials**]
## v1.65.4
@@ -208,3 +208,19 @@ Java_com_google_android_filament_Renderer_nSetVsyncTime(JNIEnv *, jclass,
Renderer *renderer = (Renderer *) nativeRenderer;
renderer->setVsyncTime(steadyClockTimeNano);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_Renderer_nSkipNextFrames(JNIEnv *, jclass ,
jlong nativeRenderer, jint frameCount) {
Renderer *renderer = (Renderer *) nativeRenderer;
renderer->skipNextFrames(frameCount);
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_Renderer_nGetFrameToSkipCount(JNIEnv *, jclass ,
jlong nativeRenderer) {
Renderer *renderer = (Renderer *) nativeRenderer;
return renderer->getFrameToSkipCount();
}
@@ -356,6 +356,7 @@ Java_com_google_android_filament_View_nSetFogOptions(JNIEnv *, jclass , jlong na
view->setFogOptions(options);
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nSetBlendMode(JNIEnv *, jclass , jlong nativeView, jint blendMode) {
View* view = (View*) nativeView;
@@ -554,3 +555,20 @@ Java_com_google_android_filament_View_nClearFrameHistory(JNIEnv *env, jclass cla
Engine *engine = (Engine *) nativeEngine;
view->clearFrameHistory(*engine);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nSetChannelDepthClearEnabled(JNIEnv *, jclass ,
jlong nativeView, jint channel, jboolean enabled) {
View* view = (View*) nativeView;
view->setChannelDepthClearEnabled((uint8_t) channel, (bool) enabled);
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_View_nIsChannelDepthClearEnabled(JNIEnv *env, jclass clazz,
jlong nativeView, jint channel) {
// TODO: implement nIsChannelDepthClearEnabled()
View* view = (View*) nativeView;
return (jboolean)view->isChannelDepthClearEnabled((uint8_t) channel);
}
@@ -332,7 +332,7 @@ public class RenderableManager {
}
/**
* Set the channel this renderable is associated to. There can be 4 channels.
* Set the channel this renderable is associated to. There can be 8 channels.
*
* <p>All renderables in a given channel are rendered together, regardless of anything else.
* They are sorted as usual within a channel.</p>
@@ -342,7 +342,7 @@ public class RenderableManager {
* <p>Channels 0 and 1 may not have render primitives using a material with `refractionType`
* set to `screenspace`.</p>
*
* @param channel clamped to the range [0..3], defaults to 2.
* @param channel clamped to the range [0..7], defaults to 2.
*
* @return Builder reference for chaining calls.
*
@@ -351,7 +351,7 @@ public class RenderableManager {
* @see RenderableManager::setBlendOrderAt()
*/
@NonNull
public Builder channel(@IntRange(from = 0, to = 3) int channel) {
public Builder channel(@IntRange(from = 0, to = 7) int channel) {
nBuilderChannel(mNativeBuilder, channel);
return this;
}
@@ -730,7 +730,7 @@ public class RenderableManager {
*
* @see Builder#channel
*/
public void setChannel(@EntityInstance int i, @IntRange(from = 0, to = 3) int channel) {
public void setChannel(@EntityInstance int i, @IntRange(from = 0, to = 7) int channel) {
nSetChannel(mNativeObject, i, channel);
}
@@ -732,6 +732,22 @@ public class Renderer {
nResetUserTime(getNativeObject());
}
/**
* Requests the next frameCount frames to be skipped. For Debugging.
* @param frameCount number of frames to skip.
*/
public void skipNextFrames(int frameCount) {
nSkipNextFrames(getNativeObject(), frameCount);
}
/**
* Remainder count of frame to be skipped
* @return remaining frames to be skipped
*/
public int getFrameToSkipCount() {
return nGetFrameToSkipCount(getNativeObject());
}
public long getNativeObject() {
if (mNativeObject == 0) {
throw new IllegalStateException("Calling method on destroyed Renderer");
@@ -773,4 +789,7 @@ public class Renderer {
float interval, float headRoomRatio, float scaleRate, int history);
private static native void nSetClearOptions(long nativeRenderer,
float r, float g, float b, float a, boolean clear, boolean discard);
private static native void nSkipNextFrames(long nativeObject, int frameCount);
private static native int nGetFrameToSkipCount(long nativeObject);
}
@@ -291,6 +291,25 @@ public class View {
return mViewport;
}
/**
* Sets whether a channel must clear the depth buffer before all primitives are rendered.
* Channel depth clear is off by default for all channels.
* This is orthogonal to Renderer::setClearOptions().
* @param channel between 0 and 7
* @param enabled true to enable clear, false to disable
*/
void setChannelDepthClearEnabled(@IntRange(from = 0, to = 7) int channel, boolean enabled) {
nSetChannelDepthClearEnabled(getNativeObject(), channel, enabled);
}
/**
* @param channel between 0 and 7
* @return true if this channel has depth clear enabled.
*/
boolean isChannelDepthClearEnabled(@IntRange(from = 0, to = 7) int channel) {
return nIsChannelDepthClearEnabled(getNativeObject(), channel);
}
/**
* Sets the blending mode used to draw the view into the SwapChain.
*
@@ -1318,6 +1337,8 @@ public class View {
boolean lensFlare, boolean starburst, float chromaticAberration, int ghostCount, float ghostSpacing, float ghostThreshold, float haloThickness, float haloRadius, float haloThreshold);
private static native void nSetFogOptions(long nativeView, float distance, float maximumOpacity, float height, float heightFalloff, float cutOffDistance, float v, float v1, float v2, float density, float inScatteringStart, float inScatteringSize, boolean fogColorFromIbl, long skyColorNativeObject, boolean enabled);
private static native void nSetStereoscopicOptions(long nativeView, boolean enabled);
private static native void nSetChannelDepthClearEnabled(long nativeView, int channel, boolean enabled);
private static native boolean nIsChannelDepthClearEnabled(long nativeView, int channel);
private static native void nSetBlendMode(long nativeView, int blendMode);
private static native void nSetDepthOfFieldOptions(long nativeView, float cocScale, float maxApertureDiameter, boolean enabled, int filter,
boolean nativeResolution, int foregroundRingCount, int backgroundRingCount, int fastGatherRingCount, int maxForegroundCOC, int maxBackgroundCOC);
@@ -1615,7 +1636,7 @@ public class View {
*
* This value is used as a tint instead, when fogColorFromIbl is enabled.
*
* @see fogColorFromIbl
* @see #fogColorFromIbl
*/
@NonNull @Size(min = 3)
public float[] color = {1.0f, 1.0f, 1.0f};
@@ -1656,7 +1677,7 @@ public class View {
*
* `fogColorFromIbl` is ignored when skyTexture is specified.
*
* @see skyColor
* @see #skyColor
*/
public boolean fogColorFromIbl = false;
/**
@@ -1674,7 +1695,7 @@ public class View {
* In `linearFog` mode mipmap level 0 is always used.
*
* @see Texture
* @see fogColorFromIbl
* @see #fogColorFromIbl
*/
@Nullable
public Texture skyColor = null;
@@ -1796,7 +1817,8 @@ public class View {
/**
* Structure used to set the precision of the color buffer and related quality settings.
*
* @see setRenderQuality, getRenderQuality
* @see #setRenderQuality
* @see #getRenderQuality
*/
public static class RenderQuality {
/**
@@ -1814,7 +1836,7 @@ public class View {
/**
* Options for screen space Ambient Occlusion (SSAO) and Screen Space Cone Tracing (SSCT)
* @see setAmbientOcclusionOptions()
* @see #setAmbientOcclusionOptions
*/
public static class AmbientOcclusionOptions {
public enum AmbientOcclusionType {
@@ -1966,7 +1988,7 @@ public class View {
/**
* Options for Multi-Sample Anti-aliasing (MSAA)
* @see setMultiSampleAntiAliasingOptions()
* @see #setMultiSampleAntiAliasingOptions
*/
public static class MultiSampleAntiAliasingOptions {
/**
@@ -1996,7 +2018,7 @@ public class View {
* `feedback` of 0.1 effectively accumulates a maximum of 19 samples in steady state.
* see "A Survey of Temporal Antialiasing Techniques" by Lei Yang and all for more information.
*
* @see setTemporalAntiAliasingOptions()
* @see #setTemporalAntiAliasingOptions
*/
public static class TemporalAntiAliasingOptions {
public enum BoxType {
@@ -2098,7 +2120,7 @@ public class View {
/**
* Options for Screen-space Reflections.
* @see setScreenSpaceReflectionsOptions()
* @see #setScreenSpaceReflectionsOptions
*/
public static class ScreenSpaceReflectionsOptions {
/**
@@ -2132,7 +2154,9 @@ public class View {
/**
* List of available post-processing anti-aliasing techniques.
* @see setAntiAliasing, getAntiAliasing, setSampleCount
* @see #setAntiAliasing
* @see #getAntiAliasing
* @see #setSampleCount
*/
public enum AntiAliasing {
/**
@@ -2161,7 +2185,7 @@ public class View {
/**
* List of available shadow mapping techniques.
* @see setShadowType
* @see #setShadowType
*/
public enum ShadowType {
/**
@@ -2185,7 +2209,7 @@ public class View {
/**
* View-level options for VSM Shadowing.
* @see setVsmShadowOptions()
* @see #setVsmShadowOptions
* @warning This API is still experimental and subject to change.
*/
public static class VsmShadowOptions {
@@ -2226,7 +2250,7 @@ public class View {
/**
* View-level options for DPCF and PCSS Shadowing.
* @see setSoftShadowOptions()
* @see #setSoftShadowOptions
* @warning This API is still experimental and subject to change.
*/
public static class SoftShadowOptions {
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.65.4
VERSION_NAME=1.66.1
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+1 -1
View File
@@ -434,7 +434,7 @@ to create a quick localhost server:</p>
<p>Alternatively, if you have node installed you can use the
<a href="https://www.npmjs.com/package/live-server">live-server</a> package, which automatically refreshes the
web page when it detects a change.</p>
<p>Each sample app has its own handwritten html file. Additionally the server folder contains assets
<p>Each sample app has its own handwritten html file. Additionally, the server folder contains assets
such as meshes, textures, and materials.</p>
<h2 id="running-the-native-samples"><a class="header" href="#running-the-native-samples">Running the native samples</a></h2>
<p>The <code>samples/</code> directory contains several examples of how to use Filament with SDL2.</p>
+18 -6
View File
@@ -179,7 +179,7 @@ Google code style and is derived from the Java code style, but not quite.</p>
<li>class access modifiers are not indented</li>
<li>last line of <code>.cpp</code> or <code>.h</code> file must be an empty line</li>
</ul>
<pre><code>for (int i = 0; i &lt; max; i++) {
<pre><code class="language-c++">for (int i = 0; i &lt; max; i++) {
}
class Foo {
@@ -227,11 +227,23 @@ src/data.inc
<li><code>public</code> class attributes <em>are not</em> prefixed</li>
<li>class attributes and methods are lower camelcase</li>
</ul>
<pre><code>extern int gGlobalWarming;
<pre><code class="language-c++">extern int gGlobalWarming;
class FooBar {
public:
void methodName();
FooBar(int attributeName, int sizeInBytes)
: mAttributeName(attributeName),
sizeInBytes(sizeInBytes) {}
void reallyLongMethodNameWithLotsOfArguments(bool argument1,
int someSecondArgument, int bestArgument) {
std::pair&lt;bool, int&gt; pair = {
argument1,
argument2,
};
// etc
}
int sizeInBytes;
private:
int mAttributeName;
@@ -248,7 +260,7 @@ private:
<li>always include the copyright notice at the top of every file</li>
<li>make sure the date is correct</li>
</ul>
<pre><code>/*
<pre><code class="language-c++">/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -293,7 +305,7 @@ conversely containers and algorithms are not allowed. There are exceptions such
</li>
</ul>
<p><em>Sorting the headers is important to help catching missing <code>#include</code> directives.</em></p>
<pre><code>/*
<pre><code class="language-c++">/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -336,7 +348,7 @@ from other string types.</li>
<ul>
<li>Use <code>auto</code> only when the type appears on the same line or with iterators and lambdas.</li>
</ul>
<pre><code>auto foo = new Foo();
<pre><code class="language-c++">auto foo = new Foo();
for (auto&amp; i : collection) { }
</code></pre>
+2 -2
View File
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.65.2'
implementation 'com.google.android.filament:filament-android:1.65.4'
}
</code></pre>
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
@@ -196,7 +196,7 @@ dependencies {
</div>
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
<p>iOS projects can use CocoaPods to install the latest release:</p>
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.65.2'
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.65.4'
</code></pre>
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
<ul>
+16 -5
View File
@@ -206,11 +206,15 @@ golden repo.</li>
</code></pre>
</li>
</ul>
<h2 id="filtering-tests"><a class="header" href="#filtering-tests">Filtering Tests</a></h2>
<p>You can run a subset of the tests by passing the <code>--test_filter</code> flag to <code>local_test.sh</code>. The
filter supports wildcards (<code>*</code>) to match test names. For example, to run all tests in the
<code>ClearCoat</code> suite, you can use the following command:</p>
<pre><code>bash test/renderdiff/local_test.sh --test_filter='ClearCoat.*.*'
<h2 id="command-line-options"><a class="header" href="#command-line-options">Command-line Options</a></h2>
<p>You can control the behavior of the test scripts with the following flags passed to <code>local_test.sh</code>:</p>
<ul>
<li><code>--test_filter=&lt;filter&gt;</code>: Run a subset of tests. The filter supports wildcards (<code>*</code>) to match test names.</li>
<li><code>--no_rebuild</code>: Skip rebuilding the <code>gltf_viewer</code> executable.</li>
<li><code>--num_threads=&lt;number&gt;</code>: Set the number of threads for rendering. If not set, the system's default is used.</li>
</ul>
<p>For example, to run all <code>MSAA</code> tests without rebuilding and using 8 threads:</p>
<pre><code>bash test/renderdiff/local_test.sh --test_filter='MSAA.*.*' --no_rebuild --num_threads=8
</code></pre>
<h2 id="update-the-golden-images"><a class="header" href="#update-the-golden-images">Update the golden images</a></h2>
<p>The golden images are stored in a github repository:
@@ -276,6 +280,13 @@ For example, it could be <code>out/renderdiff/diffs/presubmit</code> for the sta
</code></pre>
<p>where <code>[PR #]</code> is the numeric ID of your pull request, and the <code>[github token]</code> is an acess
token that you (as a github user) needs to generate (<a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens">reference</a>).</p>
<p>To see the results of a specific run, you would do the following</p>
<pre><code>python3 test/renderdiff/src/viewer.py --run_number=[RUN #] --github_token=[github token]
</code></pre>
<p>where <code>[RUN #]</code> is the numeric ID of the run. You can find the run number in the URL of the
GitHub Actions page. For example, in the URL
<code>https://github.com/google/filament/actions/runs/18023632663/job/51286323708?pr=9264</code>,
the run number is <code>18023632663</code>.</p>
</main>
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -1
View File
@@ -249,9 +249,10 @@ set(PRIVATE_HDRS
set(MATERIAL_SRCS
src/materials/antiAliasing/fxaa/fxaa.mat
src/materials/antiAliasing/taa/taa.mat
src/materials/blitArray.mat
src/materials/blitDepth.mat
src/materials/blitLow.mat
src/materials/blitArray.mat
src/materials/clearDepth.mat
src/materials/bloom/bloomDownsample.mat
src/materials/bloom/bloomDownsample2x.mat
src/materials/bloom/bloomDownsample9.mat
+2
View File
@@ -294,6 +294,8 @@ if (FILAMENT_SUPPORTS_WEBGPU)
src/webgpu/WebGPUPipelineLayoutCache.h
src/webgpu/WebGPUProgram.cpp
src/webgpu/WebGPUProgram.h
src/webgpu/WebGPUQueueManager.cpp
src/webgpu/WebGPUQueueManager.h
src/webgpu/WebGPURenderPassMipmapGenerator.cpp
src/webgpu/WebGPURenderPassMipmapGenerator.h
src/webgpu/WebGPURenderPrimitive.h
@@ -713,6 +713,12 @@ enum class Precision : uint8_t {
DEFAULT
};
union ConstantValue {
int32_t i;
float f;
bool b;
};
/**
* Shader compiler priority queue
*
@@ -1697,6 +1703,8 @@ enum class Workaround : uint16_t {
// prevents these stutters by not precaching depth variants of the default material for those
// particular browsers.
DISABLE_DEPTH_PRECACHE_FOR_DEFAULT_MATERIAL,
// Emulate an sRGB swapchain in shader code.
EMULATE_SRGB_SWAPCHAIN,
};
using StereoscopicType = backend::Platform::StereoscopicType;
+1 -1
View File
@@ -236,7 +236,7 @@ public:
* @return nullptr on failure, or a pointer to the newly created driver.
*/
virtual Driver* UTILS_NULLABLE createDriver(void* UTILS_NULLABLE sharedContext,
const DriverConfig& driverConfig) noexcept = 0;
const DriverConfig& driverConfig) = 0;
/**
* Processes the platform's event queue when called from its primary event-handling thread.
+2 -6
View File
@@ -50,11 +50,7 @@ public:
descriptor_binding_t binding;
};
struct SpecializationConstant {
using Type = std::variant<int32_t, float, bool>;
uint32_t id; // id set in glsl
Type value; // value and type
};
using SpecializationConstant = std::variant<int32_t, float, bool>;
struct Uniform { // For ES2 support
utils::CString name; // full qualified name of the uniform field
@@ -118,7 +114,7 @@ public:
Program& multiview(bool multiview) noexcept;
// For ES2 support only...
Program& uniforms(uint32_t index, utils::CString name, UniformInfo uniforms) noexcept;
Program& uniforms(uint32_t index, utils::CString name, UniformInfo uniforms);
Program& attributes(AttributesInfo attributes) noexcept;
//
@@ -112,7 +112,7 @@ public:
*
*/
virtual SwapChain* UTILS_NULLABLE createSwapChain(
void* UTILS_NULLABLE nativeWindow, uint64_t flags) noexcept = 0;
void* UTILS_NULLABLE nativeWindow, uint64_t flags) = 0;
/**
* Called by the driver create a headless SwapChain.
@@ -126,7 +126,7 @@ public:
* A void* might be enough.
*/
virtual SwapChain* UTILS_NULLABLE createSwapChain(
uint32_t width, uint32_t height, uint64_t flags) noexcept = 0;
uint32_t width, uint32_t height, uint64_t flags) = 0;
/**
* Called by the driver to destroys the SwapChain
@@ -40,7 +40,7 @@ protected:
// Platform Interface
Driver* createDriver(void* sharedContext,
const DriverConfig& driverConfig) noexcept override;
const DriverConfig& driverConfig) override;
// Currently returns 0
int getOSVersion() const noexcept override;
@@ -38,7 +38,7 @@ public:
// Platform Interface
Driver* createDriver(void* sharedGLContext,
const DriverConfig& driverConfig) noexcept override;
const DriverConfig& driverConfig) override;
int getOSVersion() const noexcept final { return 0; }
@@ -80,7 +80,7 @@ protected:
* Initializes EGL, creates the OpenGL context and returns a concrete Driver implementation
* that supports OpenGL/OpenGL ES.
*/
Driver* createDriver(void* sharedContext, const DriverConfig& driverConfig) noexcept override;
Driver* createDriver(void* sharedContext, const DriverConfig& driverConfig) override;
/**
* This returns zero. This method can be overridden to return something more useful.
@@ -101,8 +101,8 @@ protected:
bool isSRGBSwapChainSupported() const noexcept override;
bool isMSAASwapChainSupported(uint32_t samples) const noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool isSwapChainProtected(SwapChain* swapChain) noexcept override;
@@ -80,7 +80,7 @@ protected:
int getOSVersion() const noexcept override;
Driver* createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) noexcept override;
const Platform::DriverConfig& driverConfig) override;
// --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface
@@ -29,7 +29,7 @@ public:
PlatformEGLHeadless() noexcept;
Driver* createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) noexcept override;
const Platform::DriverConfig& driverConfig) override;
protected:
bool isOpenGL() const noexcept override;
@@ -39,7 +39,7 @@ protected:
// Platform Interface
Driver* createDriver(void* sharedGLContext,
const DriverConfig& driverConfig) noexcept override;
const DriverConfig& driverConfig) override;
int getOSVersion() const noexcept final override { return 0; }
@@ -55,9 +55,9 @@ protected:
void commit(SwapChain* swapChain) noexcept override;
private:
Display *mGLXDisplay;
GLXContext mGLXContext;
GLXFBConfig* mGLXConfig;
Display* mGLXDisplay;
GLXContext mGLXContext{};
GLXFBConfig mGLXConfig{};
GLXPbuffer mDummySurface;
std::vector<GLXPbuffer> mPBuffers;
};
@@ -36,7 +36,7 @@ public:
PlatformMetal();
~PlatformMetal() noexcept override;
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) override;
int getOSVersion() const noexcept override { return 0; }
/**
@@ -43,7 +43,7 @@ protected:
// --------------------------------------------------------------------------------------------
// Platform Interface
Driver* createDriver(void* sharedGLContext, const DriverConfig& driverConfig) noexcept override;
Driver* createDriver(void* sharedGLContext, const DriverConfig& driverConfig) override;
int getOSVersion() const noexcept final override { return 0; }
@@ -38,7 +38,7 @@ protected:
// Platform Interface
Driver* createDriver(void* sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept override;
const Platform::DriverConfig& driverConfig) override;
int getOSVersion() const noexcept final override { return 0; }
@@ -61,8 +61,12 @@ protected:
HWND mHWnd = NULL;
HDC mWhdc = NULL;
PIXELFORMATDESCRIPTOR mPfd = {};
std::vector<HGLRC> mAdditionalContexts;
std::vector<int> mAttribs;
// For shared contexts
static constexpr int SHARED_CONTEXT_NUM = 1;
std::vector<HGLRC> mAdditionalContexts;
std::atomic<int> mNextFreeSharedContextIndex{0};
};
} // namespace filament::backend
@@ -34,7 +34,7 @@ protected:
// Platform Interface
Driver* createDriver(void* sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept override;
const Platform::DriverConfig& driverConfig) override;
int getOSVersion() const noexcept override;
@@ -119,7 +119,7 @@ public:
~VulkanPlatform() override;
Driver* createDriver(void* sharedContext,
Platform::DriverConfig const& driverConfig) noexcept override;
Platform::DriverConfig const& driverConfig) override;
int getOSVersion() const noexcept override {
return 0;
@@ -72,7 +72,7 @@ public:
protected:
[[nodiscard]] Driver* createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) noexcept override;
const Platform::DriverConfig& driverConfig) override;
private:
// returns adapter request option variations applicable for the particular
@@ -61,7 +61,7 @@ class CommandBase {
protected:
using Execute = Dispatcher::Execute;
constexpr explicit CommandBase(Execute execute) noexcept : mExecute(execute) {}
constexpr explicit CommandBase(Execute const execute) noexcept : mExecute(execute) {}
public:
// alignment of all Commands in the CommandStream
@@ -70,7 +70,7 @@ public:
}
// executes this command and returns the next one
inline CommandBase* execute(Driver& driver) {
CommandBase* execute(Driver& driver) {
// returning the next command by output parameter allows the compiler to perform the
// tail-call optimization in the function called by mExecute, however that comes at
// a cost here (writing and reading the stack at each iteration), in the end it's
@@ -80,7 +80,7 @@ public:
return reinterpret_cast<CommandBase*>(reinterpret_cast<intptr_t>(this) + next);
}
inline ~CommandBase() noexcept = default;
~CommandBase() noexcept = default;
private:
Execute mExecute;
@@ -135,7 +135,7 @@ struct CommandType<void (Driver::*)(ARGS...)> {
public:
template<typename M, typename D>
static inline void execute(M&& method, D&& driver, CommandBase* base, intptr_t* next) {
static void execute(M&& method, D&& driver, CommandBase* base, intptr_t* next) {
Command* self = static_cast<Command*>(base);
*next = align(sizeof(Command));
#if DEBUG_COMMAND_STREAM
@@ -147,15 +147,15 @@ struct CommandType<void (Driver::*)(ARGS...)> {
}
// A command can be moved
inline Command(Command&& rhs) noexcept = default;
Command(Command&& rhs) noexcept = default;
template<typename... A>
inline explicit constexpr Command(Execute execute, A&& ... args)
explicit constexpr Command(Execute const execute, A&& ... args) noexcept
: CommandBase(execute), mArgs(std::forward<A>(args)...) {
}
// placement new declared as "throw" to avoid the compiler's null-check
inline void* operator new(std::size_t, void* ptr) {
void* operator new(std::size_t, void* ptr) noexcept {
assert_invariant(ptr);
return ptr;
}
@@ -171,8 +171,9 @@ class CustomCommand : public CommandBase {
std::function<void()> mCommand;
static void execute(Driver&, CommandBase* base, intptr_t* next);
public:
inline CustomCommand(CustomCommand&& rhs) = default;
inline explicit CustomCommand(std::function<void()> cmd)
CustomCommand(CustomCommand&& rhs) = default;
explicit CustomCommand(std::function<void()> cmd)
: CommandBase(execute), mCommand(std::move(cmd)) { }
};
@@ -184,7 +185,7 @@ class NoopCommand : public CommandBase {
*next = static_cast<NoopCommand*>(self)->mNext;
}
public:
inline constexpr explicit NoopCommand(void* next) noexcept
constexpr explicit NoopCommand(void* next) noexcept
: CommandBase(execute), mNext(intptr_t((char *)next - (char *)this)) { }
};
@@ -207,8 +208,8 @@ class CommandStream {
template<typename T>
struct AutoExecute {
T closure;
inline explicit AutoExecute(T&& closure) : closure(std::forward<T>(closure)) {}
inline ~AutoExecute() { closure(); }
explicit AutoExecute(T&& closure) : closure(std::forward<T>(closure)) {}
~AutoExecute() { closure(); }
};
public:
@@ -221,7 +222,7 @@ public:
public:
#define DECL_DRIVER_API(methodName, paramsDecl, params) \
inline void methodName(paramsDecl) { \
inline void methodName(paramsDecl) noexcept { \
DEBUG_COMMAND_BEGIN(methodName, false, params); \
using Cmd = COMMAND_TYPE(methodName); \
void* const p = allocateCommand(CommandBase::align(sizeof(Cmd))); \
@@ -230,7 +231,7 @@ public:
}
#define DECL_DRIVER_API_SYNCHRONOUS(RetType, methodName, paramsDecl, params) \
inline RetType methodName(paramsDecl) { \
inline RetType methodName(paramsDecl) noexcept { \
DEBUG_COMMAND_BEGIN(methodName, true, params); \
AutoExecute callOnExit([=, this](){ \
DEBUG_COMMAND_END(methodName, true); \
@@ -239,7 +240,7 @@ public:
}
#define DECL_DRIVER_API_RETURN(RetType, methodName, paramsDecl, params) \
inline RetType methodName(paramsDecl) { \
inline RetType methodName(paramsDecl) noexcept { \
DEBUG_COMMAND_BEGIN(methodName, false, params); \
RetType result = mDriver.methodName##S(); \
using Cmd = COMMAND_TYPE(methodName##R); \
@@ -255,7 +256,7 @@ public:
// This is for debugging only. Currently, CircularBuffer can only be written from a
// single thread. In debug builds we assert this condition.
// Call this first in the render loop.
inline void debugThreading() noexcept {
void debugThreading() noexcept {
#ifndef NDEBUG
mThreadId = utils::ThreadUtils::getThreadId();
#endif
@@ -281,11 +282,11 @@ public:
*/
template<typename PodType,
typename = typename std::enable_if<std::is_trivially_destructible<PodType>::value>::type>
inline PodType* allocatePod(
PodType* allocatePod(
size_t count = 1, size_t alignment = alignof(PodType)) noexcept;
private:
inline void* allocateCommand(size_t size) {
void* allocateCommand(size_t const size) noexcept {
assert_invariant(utils::ThreadUtils::isThisThread(mThreadId));
return mCurrentBuffer.allocate(size);
}
@@ -27,6 +27,7 @@
#include <backend/AcquiredImage.h>
#include <utils/CString.h>
#include <utils/ImmutableCString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/compiler.h>
@@ -108,15 +108,15 @@
DECL_DRIVER_API_RETURN(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__), PAIR_ARGS_N(PARAM, ##__VA_ARGS__))
#define DECL_DRIVER_API_TAGGED_R_N(R, N, ...) \
DECL_DRIVER_API_RETURN(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__, utils::CString&&, tag = {}), \
PAIR_ARGS_N(PARAM, ##__VA_ARGS__, utils::CString&&, tag))
DECL_DRIVER_API_RETURN(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__, utils::ImmutableCString&&, tag = {}), \
PAIR_ARGS_N(PARAM, ##__VA_ARGS__, utils::ImmutableCString&&, tag))
#define DECL_DRIVER_API_SYNCHRONOUS_N(R, N, ...) \
DECL_DRIVER_API_SYNCHRONOUS(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__), PAIR_ARGS_N(PARAM, ##__VA_ARGS__))
#define DECL_DRIVER_API_SYNCHRONOUS_TAGGED_N(R, N, ...) \
DECL_DRIVER_API_SYNCHRONOUS(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__, utils::CString, tag = {}), \
PAIR_ARGS_N(PARAM, ##__VA_ARGS__, utils::CString, tag))
DECL_DRIVER_API_SYNCHRONOUS(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__, utils::ImmutableCString, tag = {}), \
PAIR_ARGS_N(PARAM, ##__VA_ARGS__, utils::ImmutableCString, tag))
// on some compilers the ##__VA_ARGS__ hack is not supported, so we can't handle 0-parameter APIs
// with DECL_DRIVER_API_SYNCHRONOUS_N
@@ -417,14 +417,6 @@ DECL_DRIVER_API_N(setExternalStream,
backend::TextureHandle, th,
backend::StreamHandle, sh)
DECL_DRIVER_API_N(beginRenderPass,
backend::RenderTargetHandle, rth,
const backend::RenderPassParams&, params)
DECL_DRIVER_API_0(endRenderPass)
DECL_DRIVER_API_0(nextSubpass)
DECL_DRIVER_API_N(beginTimerQuery,
backend::TimerQueryHandle, tqh)
@@ -457,6 +449,36 @@ DECL_DRIVER_API_N(copyToMemoryMappedBuffer,
size_t, offset,
backend::BufferDescriptor&&, data)
DECL_DRIVER_API_N(setPushConstant,
backend::ShaderStage, stage,
uint8_t, index,
backend::PushConstantVariant, value)
/*
* Setting rendering state
* -----------------------
*/
DECL_DRIVER_API_N(beginRenderPass,
backend::RenderTargetHandle, rth,
const backend::RenderPassParams&, params)
DECL_DRIVER_API_0(endRenderPass)
DECL_DRIVER_API_0(nextSubpass)
DECL_DRIVER_API_N(bindPipeline,
backend::PipelineState const&, state)
DECL_DRIVER_API_N(bindRenderPrimitive,
backend::RenderPrimitiveHandle, rph)
DECL_DRIVER_API_N(bindDescriptorSet,
backend::DescriptorSetHandle, dsh,
backend::descriptor_set_t, set,
backend::DescriptorSetOffsetArray&&, offsets
)
/*
* Swap chain
*/
@@ -474,11 +496,6 @@ DECL_DRIVER_API_N(commit,
* -----------------------
*/
DECL_DRIVER_API_N(setPushConstant,
backend::ShaderStage, stage,
uint8_t, index,
backend::PushConstantVariant, value)
DECL_DRIVER_API_N(insertEventMarker,
const char*, string)
@@ -539,18 +556,6 @@ DECL_DRIVER_API_N(blit,
math::uint2, srcOrigin,
math::uint2, size)
DECL_DRIVER_API_N(bindPipeline,
backend::PipelineState const&, state)
DECL_DRIVER_API_N(bindRenderPrimitive,
backend::RenderPrimitiveHandle, rph)
DECL_DRIVER_API_N(bindDescriptorSet,
backend::DescriptorSetHandle, dsh,
backend::descriptor_set_t, set,
backend::DescriptorSetOffsetArray&&, offsets
)
DECL_DRIVER_API_N(draw2,
uint32_t, indexOffset,
uint32_t, indexCount,
@@ -21,6 +21,7 @@
#include <utils/Allocator.h>
#include <utils/CString.h>
#include <utils/ImmutableCString.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/compiler.h>
@@ -52,16 +53,16 @@ namespace filament::backend {
class DebugTag {
public:
DebugTag();
void writePoolHandleTag(HandleBase::HandleId key, utils::CString&& tag) noexcept;
void writeHeapHandleTag(HandleBase::HandleId key, utils::CString&& tag) noexcept;
utils::CString findHandleTag(HandleBase::HandleId key) const noexcept;
void writePoolHandleTag(HandleBase::HandleId key, utils::ImmutableCString&& tag) noexcept;
void writeHeapHandleTag(HandleBase::HandleId key, utils::ImmutableCString&& tag) noexcept;
utils::ImmutableCString findHandleTag(HandleBase::HandleId key) const noexcept;
private:
// This is used to associate a tag to a handle. mDebugTags is only written the in the main
// driver thread, but it can be accessed from any thread, because it's called from handle_cast<>
// which is used by synchronous calls.
mutable utils::Mutex mDebugTagLock;
tsl::robin_map<HandleBase::HandleId, utils::CString> mDebugTags;
tsl::robin_map<HandleBase::HandleId, utils::ImmutableCString> mDebugTags;
};
/*
@@ -70,12 +71,12 @@ private:
template<size_t P0, size_t P1, size_t P2>
class HandleAllocator : public DebugTag {
public:
HandleAllocator(const char* name, size_t size) noexcept;
HandleAllocator(const char* name, size_t size);
HandleAllocator(const char* name, size_t size,
bool disableUseAfterFreeCheck, bool disableHeapHandleTags) noexcept;
bool disableUseAfterFreeCheck, bool disableHeapHandleTags);
HandleAllocator(HandleAllocator const& rhs) = delete;
HandleAllocator& operator=(HandleAllocator const& rhs) = delete;
~HandleAllocator();
~HandleAllocator() noexcept;
/*
* Constructs a D object and returns a Handle<D>
@@ -106,7 +107,7 @@ public:
*
*/
template<typename D>
Handle<D> allocate() noexcept {
Handle<D> allocate() {
Handle<D> h{ allocateHandle<D>() };
return h;
}
@@ -222,7 +223,7 @@ public:
return static_cast<Dp>(p);
}
utils::CString getHandleTag(HandleBase::HandleId key) const noexcept;
utils::ImmutableCString getHandleTag(HandleBase::HandleId key) const noexcept;
template<typename B>
bool is_valid(Handle<B>& handle) {
@@ -248,7 +249,7 @@ public:
return handle_cast<Dp>(const_cast<Handle<B>&>(handle));
}
void associateTagToHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
void associateTagToHandle(HandleBase::HandleId id, utils::ImmutableCString&& tag) noexcept {
if (tag.empty()) {
return;
}
@@ -342,7 +343,7 @@ private:
// allocation size this is always inlined, because all these do is to call
// allocateHandleInPool()/deallocateHandleFromPool() with the right pool size.
template<typename D>
HandleBase::HandleId allocateHandle() noexcept {
HandleBase::HandleId allocateHandle() {
constexpr size_t BUCKET_SIZE = getBucketSize<D>();
return allocateHandleInPool<BUCKET_SIZE>();
}
@@ -358,7 +359,7 @@ private:
// the code generated is not trivial (even if it's not insane either).
template<size_t SIZE>
UTILS_NOINLINE
HandleBase::HandleId allocateHandleInPool() noexcept {
HandleBase::HandleId allocateHandleInPool() {
uint8_t age;
void* p = mHandleArena.alloc(SIZE, alignof(std::max_align_t), 0, &age);
if (UTILS_LIKELY(p)) {
+1 -1
View File
@@ -20,7 +20,7 @@
namespace filament::backend {
CallbackManager::CallbackManager(DriverBase& driver) noexcept
CallbackManager::CallbackManager(DriverBase& driver)
: mDriver(driver), mCallbacks(1) {
}
+2 -2
View File
@@ -51,7 +51,7 @@ class CallbackManager {
public:
using Handle = Container::const_iterator;
explicit CallbackManager(DriverBase& driver) noexcept;
explicit CallbackManager(DriverBase& driver);
~CallbackManager() noexcept;
@@ -78,7 +78,7 @@ private:
return --mCallbacks.end();
}
Container::iterator allocateNewSlot() noexcept {
Container::iterator allocateNewSlot() {
std::lock_guard const lock(mLock);
auto curr = --mCallbacks.end();
mCallbacks.emplace_back();
+6 -3
View File
@@ -57,12 +57,15 @@ static void printParameterPack(io::ostream& out, const FIRST& first, const REMAI
printParameterPack(out, rest...);
}
static UTILS_NOINLINE UTILS_UNUSED std::string_view extractMethodName(std::string_view command) noexcept {
constexpr const char startPattern[] = "::Command<&filament::backend::Driver::";
static UTILS_NOINLINE UTILS_UNUSED std::string_view extractMethodName(std::string_view command) noexcept { // NOLINT(*-exception-escape)
constexpr char startPattern[] = "::Command<&filament::backend::Driver::";
auto pos = command.rfind(startPattern);
auto end = command.rfind('(');
pos += sizeof(startPattern) - 1;
return command.substr(pos, end-pos);
if (pos > command.size()) {
return { command.data(), command.size() };
}
return command.substr(pos, end-pos); // this can't throw by construction
}
// ------------------------------------------------------------------------------------------------
+8 -1
View File
@@ -18,7 +18,14 @@
#include <private/utils/Tracing.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <algorithm>
#include <iterator>
#include <memory>
#include <mutex>
#include <utility>
namespace filament::backend {
@@ -36,7 +43,7 @@ CompilerThreadPool::~CompilerThreadPool() noexcept {
}
void CompilerThreadPool::init(uint32_t threadCount,
ThreadSetup&& threadSetup, ThreadCleanup&& threadCleanup) noexcept {
ThreadSetup&& threadSetup, ThreadCleanup&& threadCleanup) {
auto setup = std::make_shared<ThreadSetup>(std::move(threadSetup));
auto cleanup = std::make_shared<ThreadCleanup>(std::move(threadCleanup));
+1 -2
View File
@@ -47,8 +47,7 @@ public:
using Job = utils::Invocable<void()>;
using ThreadSetup = utils::Invocable<void()>;
using ThreadCleanup = utils::Invocable<void()>;
void init(uint32_t threadCount,
ThreadSetup&& threadSetup, ThreadCleanup&& threadCleanup) noexcept;
void init(uint32_t threadCount, ThreadSetup&& threadSetup, ThreadCleanup&& threadCleanup);
void terminate() noexcept;
void queue(CompilerPriorityQueue priorityQueue, program_token_t const& token, Job&& job);
Job dequeue(program_token_t const& token);
+2 -2
View File
@@ -131,7 +131,7 @@ void DriverBase::purge() noexcept {
// ------------------------------------------------------------------------------------------------
void DriverBase::scheduleDestroySlow(BufferDescriptor&& buffer) noexcept {
void DriverBase::scheduleDestroySlow(BufferDescriptor&& buffer) {
auto const handler = buffer.getHandler();
scheduleCallback(handler, [buffer = std::move(buffer)]() {
// user callback is called when BufferDescriptor gets destroyed
@@ -140,7 +140,7 @@ void DriverBase::scheduleDestroySlow(BufferDescriptor&& buffer) noexcept {
// This is called from an async driver method so it's in the GL thread, but purge is called
// on the user thread. This is typically called 0 or 1 times per frame.
void DriverBase::scheduleRelease(AcquiredImage const& image) noexcept {
void DriverBase::scheduleRelease(AcquiredImage const& image) {
scheduleCallback(image.handler, [callback = image.callback, image = image.image, userData = image.userData]() {
callback(image, userData);
});
+3 -3
View File
@@ -214,15 +214,15 @@ public:
protected:
class CallbackDataDetails;
void scheduleDestroy(BufferDescriptor&& buffer) noexcept {
void scheduleDestroy(BufferDescriptor&& buffer) {
if (buffer.hasCallback()) {
scheduleDestroySlow(std::move(buffer));
}
}
void scheduleDestroySlow(BufferDescriptor&& buffer) noexcept;
void scheduleDestroySlow(BufferDescriptor&& buffer);
void scheduleRelease(AcquiredImage const& image) noexcept;
void scheduleRelease(AcquiredImage const& image);
void debugCommandBegin(CommandStream* cmds, bool synchronous, const char* methodName) noexcept override;
void debugCommandEnd(CommandStream* cmds, bool synchronous, const char* methodName) noexcept override;
+7 -7
View File
@@ -81,19 +81,19 @@ HandleAllocator<P0, P1, P2>::Allocator::Allocator(AreaPolicy::HeapArea const& ar
template <size_t P0, size_t P1, size_t P2>
HandleAllocator<P0, P1, P2>::HandleAllocator(const char* name, size_t size,
bool disableUseAfterFreeCheck,
bool disableHeapHandleTags) noexcept
bool disableHeapHandleTags)
: mHandleArena(name, size, disableUseAfterFreeCheck),
mUseAfterFreeCheckDisabled(disableUseAfterFreeCheck),
mHeapHandleTagsDisabled(disableHeapHandleTags) {
}
template <size_t P0, size_t P1, size_t P2>
HandleAllocator<P0, P1, P2>::HandleAllocator(const char* name, size_t size) noexcept
HandleAllocator<P0, P1, P2>::HandleAllocator(const char* name, size_t size)
: HandleAllocator(name, size, false, false) {
}
template <size_t P0, size_t P1, size_t P2>
HandleAllocator<P0, P1, P2>::~HandleAllocator() {
HandleAllocator<P0, P1, P2>::~HandleAllocator() noexcept {
auto& overflowMap = mOverflowMap;
if (!overflowMap.empty()) {
PANIC_LOG("Not all handles have been freed. Probably leaking memory.");
@@ -156,7 +156,7 @@ void HandleAllocator<P0, P1, P2>::deallocateHandleSlow(HandleBase::HandleId id,
template<size_t P0, size_t P1, size_t P2>
UTILS_NOINLINE
CString HandleAllocator<P0, P1, P2>::getHandleTag(HandleBase::HandleId id) const noexcept {
ImmutableCString HandleAllocator<P0, P1, P2>::getHandleTag(HandleBase::HandleId id) const noexcept {
uint32_t key = id;
if (UTILS_LIKELY(isPoolHandle(id))) {
// Truncate the age to get the debug tag
@@ -172,7 +172,7 @@ DebugTag::DebugTag() {
}
UTILS_NOINLINE
CString DebugTag::findHandleTag(HandleBase::HandleId key) const noexcept {
ImmutableCString DebugTag::findHandleTag(HandleBase::HandleId key) const noexcept {
std::unique_lock const lock(mDebugTagLock);
if (auto pos = mDebugTags.find(key); pos != mDebugTags.end()) {
return pos->second;
@@ -181,7 +181,7 @@ CString DebugTag::findHandleTag(HandleBase::HandleId key) const noexcept {
}
UTILS_NOINLINE
void DebugTag::writePoolHandleTag(HandleBase::HandleId key, CString&& tag) noexcept {
void DebugTag::writePoolHandleTag(HandleBase::HandleId key, ImmutableCString&& tag) noexcept {
// This line is the costly part. In the future, we could potentially use a custom
// allocator.
std::unique_lock const lock(mDebugTagLock);
@@ -190,7 +190,7 @@ void DebugTag::writePoolHandleTag(HandleBase::HandleId key, CString&& tag) noexc
}
UTILS_NOINLINE
void DebugTag::writeHeapHandleTag(HandleBase::HandleId key, CString&& tag) noexcept {
void DebugTag::writeHeapHandleTag(HandleBase::HandleId key, ImmutableCString&& tag) noexcept {
// This line is the costly part. In the future, we could potentially use a custom
// allocator.
std::unique_lock const lock(mDebugTagLock);
+9 -9
View File
@@ -41,37 +41,37 @@ Program& Program::operator=(Program&& rhs) noexcept = default;
Program::~Program() noexcept = default;
Program& Program::priorityQueue(CompilerPriorityQueue priorityQueue) noexcept {
Program& Program::priorityQueue(CompilerPriorityQueue const priorityQueue) noexcept {
mPriorityQueue = priorityQueue;
return *this;
}
Program& Program::diagnostics(CString const& name,
Invocable<io::ostream&(utils::CString const& name, io::ostream&)>&& logger) {
Invocable<io::ostream&(CString const& name, io::ostream&)>&& logger) {
mName = name;
mLogger = std::move(logger);
return *this;
}
Program& Program::shader(ShaderStage shader, void const* data, size_t size) {
Program& Program::shader(ShaderStage shader, void const* data, size_t const size) {
ShaderBlob blob(size);
std::copy_n((const uint8_t *)data, size, blob.data());
mShadersSource[size_t(shader)] = std::move(blob);
return *this;
}
Program& Program::shaderLanguage(ShaderLanguage shaderLanguage) {
Program& Program::shaderLanguage(ShaderLanguage const shaderLanguage) {
mShaderLanguage = shaderLanguage;
return *this;
}
Program& Program::descriptorBindings(backend::descriptor_set_t set,
Program& Program::descriptorBindings(descriptor_set_t const set,
DescriptorBindingsInfo descriptorBindings) noexcept {
mDescriptorBindings[set] = std::move(descriptorBindings);
return *this;
}
Program& Program::uniforms(uint32_t index, utils::CString name, UniformInfo uniforms) noexcept {
Program& Program::uniforms(uint32_t index, CString name, UniformInfo uniforms) {
mBindingUniformsInfo.reserve(mBindingUniformsInfo.capacity() + 1);
mBindingUniformsInfo.emplace_back(index, std::move(name), std::move(uniforms));
return *this;
@@ -88,17 +88,17 @@ Program& Program::specializationConstants(SpecializationConstantsInfo specConsta
}
Program& Program::pushConstants(ShaderStage stage,
utils::FixedCapacityVector<PushConstant> constants) noexcept {
FixedCapacityVector<PushConstant> constants) noexcept {
mPushConstants[static_cast<uint8_t>(stage)] = std::move(constants);
return *this;
}
Program& Program::cacheId(uint64_t cacheId) noexcept {
Program& Program::cacheId(uint64_t const cacheId) noexcept {
mCacheId = cacheId;
return *this;
}
Program& Program::multiview(bool multiview) noexcept {
Program& Program::multiview(bool const multiview) noexcept {
mMultiview = multiview;
return *this;
}
+1 -1
View File
@@ -192,7 +192,7 @@ public:
void* getCpuBuffer() const noexcept { return mCpuBuffer; }
void setLabel(const utils::CString& label) {
void setLabel(const utils::ImmutableCString& label) {
#if FILAMENT_METAL_DEBUG_LABELS
if (label.empty()) {
return;
+27 -26
View File
@@ -42,6 +42,7 @@
#include <utils/Logger.h>
#include <utils/Panic.h>
#include <utils/sstream.h>
#include <utils/ImmutableCString.h>
#include <algorithm>
@@ -418,14 +419,14 @@ void MetalDriver::finish(int) {
}
void MetalDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes, utils::CString&& tag) {
uint8_t attributeCount, AttributeArray attributes, utils::ImmutableCString&& tag) {
construct_handle<MetalVertexBufferInfo>(vbih, *mContext,
bufferCount, attributeCount, attributes);
mHandleAllocator.associateTagToHandle(vbih.getId(), std::move(tag));
}
void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih, utils::CString&& tag) {
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih, utils::ImmutableCString&& tag) {
MetalVertexBufferInfo const* const vbi = handle_cast<const MetalVertexBufferInfo>(vbih);
construct_handle<MetalVertexBuffer>(vbh, *mContext, vertexCount, vbi->bufferCount, vbih);
mHandleAllocator.associateTagToHandle(vbh.getId(), std::move(tag));
@@ -433,7 +434,7 @@ void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
}
void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage, utils::CString&& tag) {
uint32_t indexCount, BufferUsage usage, utils::ImmutableCString&& tag) {
auto elementSize = (uint8_t)getElementTypeSize(elementType);
auto* indexBuffer =
construct_handle<MetalIndexBuffer>(ibh, *mContext, usage, elementSize, indexCount);
@@ -446,7 +447,7 @@ void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elem
}
void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage, utils::CString&& tag) {
BufferObjectBinding bindingType, BufferUsage usage, utils::ImmutableCString&& tag) {
auto* bufferObject =
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
FILAMENT_CHECK_POSTCONDITION(bufferObject->getBuffer()->wasAllocationSuccessful())
@@ -488,7 +489,7 @@ inline const char* stringify(SamplerType samplerType) {
void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height,
uint32_t depth, TextureUsage usage, utils::CString&& tag) {
uint32_t depth, TextureUsage usage, utils::ImmutableCString&& tag) {
// Clamp sample count to what the device supports.
auto& sc = mContext->sampleCountLookup;
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
@@ -508,7 +509,7 @@ void MetalDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8
}
void MetalDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
uint8_t baseLevel, uint8_t levelCount, utils::CString&& tag) {
uint8_t baseLevel, uint8_t levelCount, utils::ImmutableCString&& tag) {
MetalTexture const* src = handle_cast<MetalTexture>(srch);
MetalTexture* texture =
construct_handle<MetalTexture>(th, *mContext, src, baseLevel, levelCount);
@@ -519,7 +520,7 @@ void MetalDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> src
void MetalDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
backend::TextureSwizzle a, utils::CString&& tag) {
backend::TextureSwizzle a, utils::ImmutableCString&& tag) {
MetalTexture const* src = handle_cast<MetalTexture>(srch);
MetalTexture* texture = construct_handle<MetalTexture>(th, *mContext, src, r, g, b, a);
mContext->textures.insert(texture);
@@ -531,13 +532,13 @@ void MetalDriver::createTextureExternalImage2R(Handle<HwTexture> th,
backend::SamplerType target,
backend::TextureFormat format,
uint32_t width, uint32_t height, backend::TextureUsage usage,
Platform::ExternalImageHandleRef image, utils::CString&& tag) {
Platform::ExternalImageHandleRef image, utils::ImmutableCString&& tag) {
// FIXME: implement createTextureExternalImage2R
}
void MetalDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, utils::CString&& tag) {
void* image, utils::ImmutableCString&& tag) {
MetalTexture* texture = construct_handle<MetalTexture>(th, *mContext, format, width, height,
usage, (CVPixelBufferRef) image);
mContext->textures.insert(texture);
@@ -550,7 +551,7 @@ void MetalDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::Sam
void MetalDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, uint32_t plane, utils::CString&& tag) {
void* image, uint32_t plane, utils::ImmutableCString&& tag) {
MetalTexture* texture = construct_handle<MetalTexture>(th, *mContext, format, width, height,
usage, (CVPixelBufferRef) image, plane);
mContext->textures.insert(texture);
@@ -564,7 +565,7 @@ void MetalDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height,
uint32_t depth, TextureUsage usage, utils::CString&& tag) {
uint32_t depth, TextureUsage usage, utils::ImmutableCString&& tag) {
id<MTLTexture> metalTexture = (id<MTLTexture>) CFBridgingRelease((void*) i);
FILAMENT_CHECK_PRECONDITION(metalTexture.width == width)
<< "Imported id<MTLTexture> width (" << metalTexture.width
@@ -588,13 +589,13 @@ void MetalDriver::importTextureR(Handle<HwTexture> th, intptr_t i,
void MetalDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
PrimitiveType pt, utils::CString&& tag) {
PrimitiveType pt, utils::ImmutableCString&& tag) {
construct_handle<MetalRenderPrimitive>(rph);
MetalDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh);
mHandleAllocator.associateTagToHandle(rph.getId(), std::move(tag));
}
void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program, utils::CString&& tag) {
void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program, utils::ImmutableCString&& tag) {
#if FILAMENT_METAL_DEBUG_LOG
auto handleId = rph.getId();
DEBUG_LOG("createProgramR(rph = %d, program = ", handleId);
@@ -604,7 +605,7 @@ void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program, utils
mHandleAllocator.associateTagToHandle(rph.getId(), std::move(tag));
}
void MetalDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils::CString&& tag) {
void MetalDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils::ImmutableCString&& tag) {
construct_handle<MetalRenderTarget>(rth, mContext);
mHandleAllocator.associateTagToHandle(rth.getId(), std::move(tag));
}
@@ -612,7 +613,7 @@ void MetalDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils::
void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
TargetBufferFlags targetBufferFlags, uint32_t width, uint32_t height,
uint8_t samples, uint8_t layerCount, MRT color,
TargetBufferInfo depth, TargetBufferInfo stencil, utils::CString&& tag) {
TargetBufferInfo depth, TargetBufferInfo stencil, utils::ImmutableCString&& tag) {
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
<< "createRenderTarget must be called outside of a render pass.";
// Clamp sample count to what the device supports.
@@ -659,14 +660,14 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
mHandleAllocator.associateTagToHandle(rth.getId(), std::move(tag));
}
void MetalDriver::createFenceR(Handle<HwFence> fh, utils::CString&& tag) {
void MetalDriver::createFenceR(Handle<HwFence> fh, utils::ImmutableCString&& tag) {
auto* fence = handle_cast<MetalFence>(fh);
fence->encode();
mHandleAllocator.associateTagToHandle(fh.getId(), std::move(tag));
}
void MetalDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags,
utils::CString&& tag) {
utils::ImmutableCString&& tag) {
// TODO: support MSAA swapchain
if (UTILS_UNLIKELY(flags & SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER)) {
@@ -683,18 +684,18 @@ void MetalDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
}
void MetalDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
uint32_t width, uint32_t height, uint64_t flags, utils::CString&& tag) {
uint32_t width, uint32_t height, uint64_t flags, utils::ImmutableCString&& tag) {
construct_handle<MetalSwapChain>(sch, *mContext, mPlatform, width, height, flags);
mHandleAllocator.associateTagToHandle(sch.getId(), std::move(tag));
}
void MetalDriver::createSyncR(Handle<HwSync> sh, utils::CString&& tag) {
void MetalDriver::createSyncR(Handle<HwSync> sh, utils::ImmutableCString&& tag) {
// TODO: Ensure sync is active, and then invoke and clear all pending
// callbacks.
mHandleAllocator.associateTagToHandle(sh.getId(), std::move(tag));
}
void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::CString&& tag) {
void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::ImmutableCString&& tag) {
// nothing to do, timer query was constructed in createTimerQueryS
mHandleAllocator.associateTagToHandle(tqh.getId(), std::move(tag));
}
@@ -733,12 +734,12 @@ const char* toString(DescriptorFlags flags) {
}
void MetalDriver::createDescriptorSetLayoutR(
Handle<HwDescriptorSetLayout> dslh, DescriptorSetLayout&& info, utils::CString&& tag) {
Handle<HwDescriptorSetLayout> dslh, DescriptorSetLayout&& info, utils::ImmutableCString&& tag) {
#if FILAMENT_METAL_DEBUG_LOG == 1
const char* labelStr = "";
std::visit([&labelStr](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, utils::CString> || std::is_same_v<T, utils::StaticString>) {
if constexpr (std::is_same_v<T, utils::ImmutableCString> || std::is_same_v<T, utils::StaticString>) {
labelStr = arg.c_str();
}
}, info.label);
@@ -758,7 +759,7 @@ void MetalDriver::createDescriptorSetLayoutR(
}
void MetalDriver::createDescriptorSetR(
Handle<HwDescriptorSet> dsh, Handle<HwDescriptorSetLayout> dslh, utils::CString&& tag) {
Handle<HwDescriptorSet> dsh, Handle<HwDescriptorSetLayout> dslh, utils::ImmutableCString&& tag) {
DEBUG_LOG("createDescriptorSetR(dsh = %d, dslh = %d)\n", dsh.getId(), dslh.getId());
MetalDescriptorSetLayout* layout = handle_cast<MetalDescriptorSetLayout>(dslh);
MetalDescriptorSet* ds = construct_handle<MetalDescriptorSet>(dsh, layout);
@@ -1010,11 +1011,11 @@ utils::FixedCapacityVector<ShaderLanguage> MetalDriver::getShaderLanguages(
return { backend::ShaderLanguage::METAL_LIBRARY, backend::ShaderLanguage::MSL };
}
Handle<HwStream> MetalDriver::createStreamNative(void* stream, utils::CString tag) {
Handle<HwStream> MetalDriver::createStreamNative(void* stream, utils::ImmutableCString tag) {
return {};
}
Handle<HwStream> MetalDriver::createStreamAcquired(utils::CString tag) {
Handle<HwStream> MetalDriver::createStreamAcquired(utils::ImmutableCString tag) {
return {};
}
@@ -2231,7 +2232,7 @@ MemoryMappedBufferHandle MetalDriver::mapBufferS() noexcept {
void MetalDriver::mapBufferR(MemoryMappedBufferHandle mmbh,
BufferObjectHandle boh, size_t offset,
size_t size, MapBufferAccessFlags access, utils::CString&& tag) {
size_t size, MapBufferAccessFlags access, utils::ImmutableCString&& tag) {
construct_handle<MetalMemoryMappedBuffer>(mmbh, boh, offset, size, access);
mHandleAllocator.associateTagToHandle(mmbh.getId(), std::move(tag));
}
+3 -3
View File
@@ -267,7 +267,7 @@ public:
static MTLPixelFormat decidePixelFormat(MetalContext* context, TextureFormat format);
void setLabel(const utils::CString& label) {
void setLabel(const utils::ImmutableCString& label) {
#if FILAMENT_METAL_DEBUG_LABELS
if (label.empty()) {
return;
@@ -494,7 +494,7 @@ struct MetalDescriptorSet : public HwDescriptorSet {
void finalize(MetalDriver* driver);
void setLabel(const utils::CString& l) {
void setLabel(const utils::ImmutableCString& l) {
#if FILAMENT_METAL_DEBUG_LABELS
if (l.empty()) {
return;
@@ -527,7 +527,7 @@ struct MetalDescriptorSet : public HwDescriptorSet {
std::array<TrackedMetalBuffer, Program::SHADER_TYPE_COUNT> cachedBuffer = { nil };
#if FILAMENT_METAL_DEBUG_LABELS
utils::CString label;
utils::ImmutableCString label;
#endif
};
@@ -153,14 +153,15 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
MTLFunctionConstantValues* constants = [MTLFunctionConstantValues new];
auto const& specializationConstants = program.getSpecializationConstants();
for (auto const& sc : specializationConstants) {
for (size_t i = 0; i < specializationConstants.size(); i++) {
auto const& sc = specializationConstants[i];
const std::array<MTLDataType, 3> types{
MTLDataTypeInt, MTLDataTypeFloat, MTLDataTypeBool };
std::visit([&sc, constants, type = types[sc.value.index()]](auto&& arg) {
std::visit([i, constants, type = types[sc.index()]](auto&& arg) {
[constants setConstantValue:&arg
type:type
atIndex:sc.id];
}, sc.value);
atIndex:i];
}, sc);
}
id<MTLFunction> function = [library newFunctionWithName:@"main0"
+1 -1
View File
@@ -52,7 +52,7 @@ PlatformMetal::~PlatformMetal() noexcept {
delete pImpl;
}
Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::DriverConfig& driverConfig) noexcept {
Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::DriverConfig& driverConfig) {
pImpl->mDrawableFailureBehavior = driverConfig.metalDisablePanicOnDrawableFailure
? DrawableFailureBehavior::ABORT_FRAME
: DrawableFailureBehavior::PANIC;
+4 -2
View File
@@ -21,6 +21,8 @@
#include "noop/NoopDriver.h"
#include "CommandStreamDispatcher.h"
#include <utils/ImmutableCString.h>
#include <stdint.h>
namespace filament::backend {
@@ -135,11 +137,11 @@ void NoopDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh) {
void NoopDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) {
}
Handle<HwStream> NoopDriver::createStreamNative(void* nativeStream, utils::CString tag) {
Handle<HwStream> NoopDriver::createStreamNative(void* nativeStream, utils::ImmutableCString tag) {
return {};
}
Handle<HwStream> NoopDriver::createStreamAcquired(utils::CString tag) {
Handle<HwStream> NoopDriver::createStreamAcquired(utils::ImmutableCString tag) {
return {};
}
+1 -1
View File
@@ -20,7 +20,7 @@
namespace filament::backend {
Driver* PlatformNoop::createDriver(void* sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
Driver* PlatformNoop::createDriver(void* sharedGLContext, const Platform::DriverConfig& driverConfig) {
return NoopDriver::create();
}
+1 -1
View File
@@ -31,7 +31,7 @@ public:
protected:
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) override;
};
} // namespace filament
@@ -33,6 +33,7 @@
#include <utils/BitmaskEnum.h>
#include <utils/Log.h>
#include <utils/Logger.h>
#include <utils/Panic.h>
#include <utils/bitset.h>
#include <utils/compiler.h>
@@ -49,7 +50,7 @@
namespace filament::backend {
GLDescriptorSet::GLDescriptorSet(OpenGLContext& gl, DescriptorSetLayoutHandle dslh,
GLDescriptorSetLayout const* layout) noexcept
GLDescriptorSetLayout const* layout)
: descriptors(layout->maxDescriptorBinding + 1),
dslh(std::move(dslh)) {
@@ -156,8 +157,9 @@ void GLDescriptorSet::update(OpenGLContext&,
arg.bo = bo;
arg.offset = uint32_t(offset);
} else {
// API usage error. User asked to update the wrong type of descriptor.
PANIC_PRECONDITION("descriptor %d is not a buffer", +binding);
// User asked to update the wrong type of descriptor. This should never happen
// because we're checking that on the filament side
LOG(ERROR) << "descriptor " << +binding << " is not a buffer";
}
}, descriptors[binding].desc);
}
@@ -214,8 +216,9 @@ void GLDescriptorSet::update(OpenGLContext& gl, HandleAllocatorGL& handleAllocat
arg.params = params;
}
} else {
// API usage error. User asked to update the wrong type of descriptor.
PANIC_PRECONDITION("descriptor %d is not a texture", +binding);
// User asked to update the wrong type of descriptor. This should never happen
// because we're checking that on the filament side
LOG(ERROR) << "descriptor " << +binding << " is not a texture";
}
}, descriptors[binding].desc);
}
@@ -52,7 +52,7 @@ struct GLDescriptorSet : public HwDescriptorSet {
using HwDescriptorSet::HwDescriptorSet;
GLDescriptorSet(OpenGLContext& gl, DescriptorSetLayoutHandle dslh,
GLDescriptorSetLayout const* layout) noexcept;
GLDescriptorSetLayout const* layout);
// update a buffer descriptor in the set
void update(OpenGLContext& gl,
@@ -277,7 +277,7 @@ void OpenGLContext::terminate() noexcept {
}
void OpenGLContext::destroyWithContext(
size_t index, std::function<void(OpenGLContext&)> const& closure) noexcept {
size_t index, std::function<void(OpenGLContext&)> const& closure) {
if (index == 0) {
// Note: we only need to delay the destruction of objects on the unprotected context
// (index 0) because the protected context is always immediately destroyed and all its
@@ -296,7 +296,7 @@ void OpenGLContext::unbindEverything() noexcept {
// worry about.
}
void OpenGLContext::synchronizeStateAndCache(size_t index) noexcept {
void OpenGLContext::synchronizeStateAndCache(size_t index) {
// if we're just switching back to context 0, run all the pending destructors
if (index == 0) {
@@ -865,10 +865,12 @@ GLuint OpenGLContext::bindFramebuffer(GLenum target, GLuint buffer) noexcept {
if (UTILS_UNLIKELY(buffer == 0)) {
// we're binding the default frame buffer, resolve its actual name
auto& defaultFboForThisContext = mDefaultFbo[contextIndex];
if (UTILS_UNLIKELY(!defaultFboForThisContext.has_value())) {
defaultFboForThisContext = GLuint(mPlatform.getDefaultFramebufferObject());
}
buffer = defaultFboForThisContext.value();
// by construction, defaultFboForThisContext has a value. value_or() avoids a throwing call.
buffer = defaultFboForThisContext.value_or(0);
}
bindFramebufferResolved(target, buffer);
return buffer;
+2 -2
View File
@@ -193,7 +193,7 @@ public:
void deleteBuffer(GLuint buffer, GLenum target) noexcept;
void deleteVertexArray(GLuint vao) noexcept;
void destroyWithContext(size_t index, std::function<void(OpenGLContext&)> const& closure) noexcept;
void destroyWithContext(size_t index, std::function<void(OpenGLContext&)> const& closure);
// glGet*() values
struct Gets {
@@ -498,7 +498,7 @@ public:
} procs{};
void unbindEverything() noexcept;
void synchronizeStateAndCache(size_t index) noexcept;
void synchronizeStateAndCache(size_t index);
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
GLuint getSamplerSlow(SamplerParams sp) const noexcept;
+38 -58
View File
@@ -50,6 +50,7 @@
#include <type_traits>
#include <utils/BitmaskEnum.h>
#include <utils/CString.h>
#include <utils/ImmutableCString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Invocable.h>
#include <utils/Logger.h>
@@ -394,7 +395,7 @@ void OpenGLDriver::setPushConstant(ShaderStage const stage, uint8_t const index,
}
#endif
Slice<std::pair<GLint, ConstantType>> constants;
Slice<const std::pair<GLint, ConstantType>> constants;
if (stage == ShaderStage::VERTEX) {
constants = mCurrentPushConstants->vertexConstants;
} else if (stage == ShaderStage::FRAGMENT) {
@@ -661,7 +662,7 @@ void OpenGLDriver::createVertexBufferInfoR(
uint8_t bufferCount,
uint8_t attributeCount,
AttributeArray attributes,
CString&& tag) {
ImmutableCString&& tag) {
DEBUG_MARKER()
construct<GLVertexBufferInfo>(vbih, bufferCount, attributeCount, attributes);
mHandleAllocator.associateTagToHandle(vbih.getId(), std::move(tag));
@@ -671,7 +672,7 @@ void OpenGLDriver::createVertexBufferR(
Handle<HwVertexBuffer> vbh,
uint32_t vertexCount,
Handle<HwVertexBufferInfo> vbih,
CString&& tag) {
ImmutableCString&& tag) {
DEBUG_MARKER()
construct<GLVertexBuffer>(vbh, vertexCount, vbih);
mHandleAllocator.associateTagToHandle(vbh.getId(), std::move(tag));
@@ -682,7 +683,7 @@ void OpenGLDriver::createIndexBufferR(
ElementType const elementType,
uint32_t indexCount,
BufferUsage const usage,
CString&& tag) {
ImmutableCString&& tag) {
DEBUG_MARKER()
auto& gl = mContext;
@@ -698,7 +699,7 @@ void OpenGLDriver::createIndexBufferR(
}
void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage, CString&& tag) {
BufferObjectBinding bindingType, BufferUsage usage, ImmutableCString&& tag) {
DEBUG_MARKER()
assert_invariant(byteCount > 0);
@@ -725,7 +726,7 @@ void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byte
void OpenGLDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
PrimitiveType const pt, CString&& tag) {
PrimitiveType const pt, ImmutableCString&& tag) {
DEBUG_MARKER()
auto& gl = mContext;
@@ -761,32 +762,9 @@ void OpenGLDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
mHandleAllocator.associateTagToHandle(rph.getId(), std::move(tag));
}
void OpenGLDriver::createProgramR(Handle<HwProgram> ph, Program&& program, CString&& tag) {
void OpenGLDriver::createProgramR(Handle<HwProgram> ph, Program&& program, ImmutableCString&& tag) {
DEBUG_MARKER()
if (UTILS_UNLIKELY(mContext.isES2())) {
// Here we patch the specialization constants to enable or not the rec709 output
// color space emulation in this program. Obviously, the backend shouldn't know about
// specific spec-constants, so we need to handle failures gracefully. This cannot be
// done at Material creation time because only the backend has access to
// Platform.isSRGBSwapChainSupported().
if (!mPlatform.isSRGBSwapChainSupported()) {
auto& specializationConstants = program.getSpecializationConstants();
auto const pos = std::find_if(specializationConstants.begin(), specializationConstants.end(),
[](auto&& sc) {
// This constant must match
// ReservedSpecializationConstants::CONFIG_SRGB_SWAPCHAIN_EMULATION
// which we can't use here because it's defined in EngineEnums.h.
// (we're breaking layering here, but it's for the good cause).
return sc.id == 3;
});
if (pos != specializationConstants.end()) {
pos->value = true;
}
}
}
construct<OpenGLProgram>(ph, *this, std::move(program));
CHECK_GL_ERROR()
mHandleAllocator.associateTagToHandle(ph.getId(), std::move(tag));
@@ -894,7 +872,7 @@ void OpenGLDriver::textureStorage(GLTexture* t,
void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth,
TextureUsage usage, CString&& tag) {
TextureUsage usage, ImmutableCString&& tag) {
DEBUG_MARKER()
GLenum internalFormat = getInternalFormat(format);
@@ -987,7 +965,7 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
}
void OpenGLDriver::createTextureViewR(Handle<HwTexture> th,
Handle<HwTexture> srch, uint8_t const baseLevel, uint8_t const levelCount, CString&& tag) {
Handle<HwTexture> srch, uint8_t const baseLevel, uint8_t const levelCount, ImmutableCString&& tag) {
DEBUG_MARKER()
GLTexture const* const src = handle_cast<GLTexture const*>(srch);
@@ -1035,7 +1013,7 @@ void OpenGLDriver::createTextureViewR(Handle<HwTexture> th,
void OpenGLDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
TextureSwizzle const r, TextureSwizzle const g, TextureSwizzle const b, TextureSwizzle const a,
CString&& tag) {
ImmutableCString&& tag) {
DEBUG_MARKER()
GLTexture const* const src = handle_cast<GLTexture const*>(srch);
@@ -1101,7 +1079,7 @@ void OpenGLDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwText
void OpenGLDriver::createTextureExternalImage2R(Handle<HwTexture> th, SamplerType target,
TextureFormat format, uint32_t width, uint32_t height, TextureUsage usage,
Platform::ExternalImageHandleRef image, CString&& tag) {
Platform::ExternalImageHandleRef image, ImmutableCString&& tag) {
DEBUG_MARKER()
usage |= TextureUsage::SAMPLEABLE;
@@ -1153,7 +1131,7 @@ void OpenGLDriver::createTextureExternalImage2R(Handle<HwTexture> th, SamplerTyp
void OpenGLDriver::createTextureExternalImageR(Handle<HwTexture> th, SamplerType target,
TextureFormat format, uint32_t width, uint32_t height, TextureUsage usage, void* image,
CString&& tag) {
ImmutableCString&& tag) {
DEBUG_MARKER()
usage |= TextureUsage::SAMPLEABLE;
@@ -1204,13 +1182,13 @@ void OpenGLDriver::createTextureExternalImageR(Handle<HwTexture> th, SamplerType
void OpenGLDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
TextureFormat format, uint32_t width, uint32_t height, TextureUsage usage,
void* image, uint32_t plane, CString&&) {
void* image, uint32_t plane, ImmutableCString&&) {
// not relevant for the OpenGL backend
}
void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t const id,
SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage, CString&& tag) {
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage, ImmutableCString&& tag) {
DEBUG_MARKER()
auto const& gl = mContext;
@@ -1665,7 +1643,7 @@ void OpenGLDriver::renderBufferStorage(GLuint const rbo, GLenum internalformat,
}
void OpenGLDriver::createDefaultRenderTargetR(
Handle<HwRenderTarget> rth, CString&& tag) {
Handle<HwRenderTarget> rth, ImmutableCString&& tag) {
DEBUG_MARKER()
construct<GLRenderTarget>(rth, 0, 0); // FIXME: we don't know the width/height
@@ -1688,7 +1666,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
MRT color,
TargetBufferInfo depth,
TargetBufferInfo stencil,
CString&& tag) {
ImmutableCString&& tag) {
DEBUG_MARKER()
GLRenderTarget* rt = construct<GLRenderTarget>(rth, width, height);
@@ -1804,7 +1782,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
mHandleAllocator.associateTagToHandle(rth.getId(), std::move(tag));
}
void OpenGLDriver::createFenceR(Handle<HwFence> fh, CString&& tag) {
void OpenGLDriver::createFenceR(Handle<HwFence> fh, ImmutableCString&& tag) {
DEBUG_MARKER()
mHandleAllocator.associateTagToHandle(fh.getId(), std::move(tag));
@@ -1838,7 +1816,7 @@ void OpenGLDriver::createFenceR(Handle<HwFence> fh, CString&& tag) {
#endif
}
void OpenGLDriver::createSyncR(Handle<HwSync> sh, CString&& tag) {
void OpenGLDriver::createSyncR(Handle<HwSync> sh, ImmutableCString&& tag) {
DEBUG_MARKER()
GLSyncFence* s = handle_cast<GLSyncFence*>(sh);
@@ -1857,7 +1835,7 @@ void OpenGLDriver::createSyncR(Handle<HwSync> sh, CString&& tag) {
}
void OpenGLDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t const flags,
CString&& tag) {
ImmutableCString&& tag) {
DEBUG_MARKER()
GLSwapChain* sc = handle_cast<GLSwapChain*>(sch);
@@ -1879,7 +1857,7 @@ void OpenGLDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
}
void OpenGLDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
uint32_t const width, uint32_t const height, uint64_t const flags, CString&& tag) {
uint32_t const width, uint32_t const height, uint64_t const flags, ImmutableCString&& tag) {
DEBUG_MARKER()
GLSwapChain* sc = handle_cast<GLSwapChain*>(sch);
@@ -1901,7 +1879,7 @@ void OpenGLDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
mHandleAllocator.associateTagToHandle(sch.getId(), std::move(tag));
}
void OpenGLDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, CString&& tag) {
void OpenGLDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, ImmutableCString&& tag) {
DEBUG_MARKER()
GLTimerQuery* tq = handle_cast<GLTimerQuery*>(tqh);
mContext.createTimerQuery(tq);
@@ -1909,14 +1887,14 @@ void OpenGLDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, CString&& tag) {
}
void OpenGLDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
DescriptorSetLayout&& info, CString&& tag) {
DescriptorSetLayout&& info, ImmutableCString&& tag) {
DEBUG_MARKER()
construct<GLDescriptorSetLayout>(dslh, std::move(info));
mHandleAllocator.associateTagToHandle(dslh.getId(), std::move(tag));
}
void OpenGLDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
Handle<HwDescriptorSetLayout> dslh, CString&& tag) {
Handle<HwDescriptorSetLayout> dslh, ImmutableCString&& tag) {
DEBUG_MARKER()
GLDescriptorSetLayout const* dsl = handle_cast<GLDescriptorSetLayout*>(dslh);
construct<GLDescriptorSet>(dsh, mContext, dslh, dsl);
@@ -1925,7 +1903,7 @@ void OpenGLDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
void OpenGLDriver::mapBufferR(MemoryMappedBufferHandle mmbh,
BufferObjectHandle boh, size_t offset,
size_t size, MapBufferAccessFlags access, CString&& tag) {
size_t size, MapBufferAccessFlags access, ImmutableCString&& tag) {
DEBUG_MARKER()
construct<GLMemoryMappedBuffer>(mmbh, mContext, mHandleAllocator, boh, offset, size, access);
mHandleAllocator.associateTagToHandle(mmbh.getId(), std::move(tag));
@@ -2195,14 +2173,14 @@ void OpenGLDriver::unmapBuffer(MemoryMappedBufferHandle mmbh) {
// These are called on the application's thread
// ------------------------------------------------------------------------------------------------
Handle<HwStream> OpenGLDriver::createStreamNative(void* nativeStream, CString tag) {
Handle<HwStream> OpenGLDriver::createStreamNative(void* nativeStream, ImmutableCString tag) {
Platform::Stream* stream = mPlatform.createStream(nativeStream);
auto handle = initHandle<GLStream>(stream);
mHandleAllocator.associateTagToHandle(handle.getId(), std::move(tag));
return handle;
}
Handle<HwStream> OpenGLDriver::createStreamAcquired(CString tag) {
Handle<HwStream> OpenGLDriver::createStreamAcquired(ImmutableCString tag) {
auto handle = initHandle<GLStream>();
mHandleAllocator.associateTagToHandle(handle.getId(), std::move(tag));
return handle;
@@ -2640,6 +2618,8 @@ bool OpenGLDriver::isWorkaroundNeeded(Workaround const workaround) {
return mContext.bugs.powervr_shader_workarounds;
case Workaround::DISABLE_DEPTH_PRECACHE_FOR_DEFAULT_MATERIAL:
return mContext.bugs.disable_depth_precache_for_default_material;
case Workaround::EMULATE_SRGB_SWAPCHAIN:
return mContext.isES2() && !mPlatform.isSRGBSwapChainSupported();
default:
return false;
}
@@ -3213,7 +3193,7 @@ void OpenGLDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh)
}
UTILS_NOINLINE
void OpenGLDriver::attachStream(GLTexture* t, GLStream* hwStream) noexcept {
void OpenGLDriver::attachStream(GLTexture* t, GLStream* hwStream) {
mTexturesWithStreamsAttached.push_back(t);
switch (hwStream->streamType) {
@@ -3799,16 +3779,16 @@ void OpenGLDriver::readBufferSubData(BufferObjectHandle boh,
}
void OpenGLDriver::runEveryNowAndThen(std::function<bool()> fn) noexcept {
void OpenGLDriver::runEveryNowAndThen(std::function<bool()> fn) {
mEveryNowAndThenOps.push_back(std::move(fn));
}
void OpenGLDriver::executeEveryNowAndThenOps() noexcept {
void OpenGLDriver::executeEveryNowAndThenOps() noexcept { // NOLINT(*-exception-escape)
auto& v = mEveryNowAndThenOps;
auto it = v.begin();
while (it != v.end()) {
if ((*it)()) {
it = v.erase(it);
it = v.erase(it); // cannot throw by construction
} else {
++it;
}
@@ -3816,17 +3796,17 @@ void OpenGLDriver::executeEveryNowAndThenOps() noexcept {
}
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
void OpenGLDriver::whenFrameComplete(const std::function<void()>& fn) noexcept {
void OpenGLDriver::whenFrameComplete(const std::function<void()>& fn) {
mFrameCompleteOps.push_back(fn);
}
void OpenGLDriver::whenGpuCommandsComplete(const std::function<void()>& fn) noexcept {
void OpenGLDriver::whenGpuCommandsComplete(const std::function<void()>& fn) {
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
mGpuCommandCompleteOps.emplace_back(sync, fn);
CHECK_GL_ERROR()
}
void OpenGLDriver::executeGpuCommandsCompleteOps() noexcept {
void OpenGLDriver::executeGpuCommandsCompleteOps() noexcept { // NOLINT(*-exception-escape)
auto& v = mGpuCommandCompleteOps;
auto it = v.begin();
while (it != v.end()) {
@@ -3842,13 +3822,13 @@ void OpenGLDriver::executeGpuCommandsCompleteOps() noexcept {
// ready
it->second();
glDeleteSync(sync);
it = v.erase(it);
it = v.erase(it); // cannot throw by construction
break;
default:
// This should never happen, but is very problematic if it does, as we might leak
// some data depending on what the callback does. However, we clean up our own state.
glDeleteSync(sync);
it = v.erase(it);
it = v.erase(it); // cannot throw by construction
break;
}
}
+5 -4
View File
@@ -45,6 +45,7 @@
#include <utils/FixedCapacityVector.h>
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/ImmutableCString.h>
#include <utils/debug.h>
#include <math/vec4.h>
@@ -395,23 +396,23 @@ private:
std::unordered_map<GLuint, BufferObjectStreamDescriptor> mStreamUniformDescriptors;
void attachStream(GLTexture* t, GLStream* stream) noexcept;
void attachStream(GLTexture* t, GLStream* stream);
void detachStream(GLTexture* t) noexcept;
void replaceStream(GLTexture* t, GLStream* stream) noexcept;
math::mat3f getStreamTransformMatrix(Handle<HwStream> sh);
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
// tasks executed on the main thread after the fence signaled
void whenGpuCommandsComplete(const std::function<void()>& fn) noexcept;
void whenGpuCommandsComplete(const std::function<void()>& fn);
void executeGpuCommandsCompleteOps() noexcept;
std::vector<std::pair<GLsync, std::function<void()>>> mGpuCommandCompleteOps;
void whenFrameComplete(const std::function<void()>& fn) noexcept;
void whenFrameComplete(const std::function<void()>& fn);
std::vector<std::function<void()>> mFrameCompleteOps;
#endif
// tasks regularly executed on the main thread at until they return true
void runEveryNowAndThen(std::function<bool()> fn) noexcept;
void runEveryNowAndThen(std::function<bool()> fn);
void executeEveryNowAndThenOps() noexcept;
std::vector<std::function<bool()>> mEveryNowAndThenOps;
@@ -121,7 +121,7 @@ void OpenGLProgram::initialize(OpenGLDriver& gld) {
* checkProgramStatus() has been successfully called.
*/
void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint program,
LazyInitializationData& lazyInitializationData) noexcept {
LazyInitializationData& lazyInitializationData) {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
// from the pipeline layout we compute a mapping from {set, binding} to {binding}
+5 -5
View File
@@ -43,8 +43,8 @@ namespace filament::backend {
class OpenGLDriver;
struct PushConstantBundle {
utils::Slice<std::pair<GLint, ConstantType>> vertexConstants;
utils::Slice<std::pair<GLint, ConstantType>> fragmentConstants;
utils::Slice<const std::pair<GLint, ConstantType>> vertexConstants;
utils::Slice<const std::pair<GLint, ConstantType>> fragmentConstants;
};
class OpenGLProgram : public HwProgram {
@@ -94,8 +94,8 @@ public:
PushConstantBundle getPushConstants() {
auto fragBegin = mPushConstants.begin() + mPushConstantFragmentStageOffset;
return {
.vertexConstants = utils::Slice(mPushConstants.begin(), fragBegin),
.fragmentConstants = utils::Slice(fragBegin, mPushConstants.end()),
.vertexConstants = { mPushConstants.begin(), fragBegin },
.fragmentConstants = { fragBegin, mPushConstants.end() },
};
}
@@ -106,7 +106,7 @@ private:
void initialize(OpenGLDriver& gld);
void initializeProgramState(OpenGLContext& context, GLuint program,
LazyInitializationData& lazyInitializationData) noexcept;
LazyInitializationData& lazyInitializationData);
BindingMap mBindingMap; // 8 bytes + out-of-line 256 bytes
@@ -51,7 +51,7 @@ class OpenGLDriver;
bool TimerQueryFactory::mGpuTimeSupported = false;
TimerQueryFactoryInterface* TimerQueryFactory::init(
OpenGLPlatform& platform, OpenGLContext& context) noexcept {
OpenGLPlatform& platform, OpenGLContext& context) {
(void)context;
TimerQueryFactoryInterface* impl = nullptr;
@@ -172,46 +172,12 @@ void TimerQueryNativeFactory::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQ
// ------------------------------------------------------------------------------------------------
TimerQueryFenceFactory::TimerQueryFenceFactory(OpenGLPlatform& platform)
: mPlatform(platform) {
mQueue.reserve(2);
mThread = std::thread([this]() {
utils::JobSystem::setThreadName("OpenGLTimerQueryFence");
utils::JobSystem::setThreadPriority(utils::JobSystem::Priority::URGENT_DISPLAY);
auto& queue = mQueue;
bool exitRequested;
do {
std::unique_lock<utils::Mutex> lock(mLock);
mCondition.wait(lock, [this, &queue]() -> bool {
return mExitRequested || !queue.empty();
});
exitRequested = mExitRequested;
if (!queue.empty()) {
Job const job(queue.front());
queue.erase(queue.begin());
lock.unlock();
job();
}
} while (!exitRequested);
});
: mPlatform(platform),
mJobQueue("OpenGLTimerQueryFence", utils::AsyncJobQueue::Priority::URGENT_DISPLAY) {
}
TimerQueryFenceFactory::~TimerQueryFenceFactory() {
assert_invariant(mQueue.empty());
if (mThread.joinable()) {
std::unique_lock<utils::Mutex> lock(mLock);
mExitRequested = true;
mCondition.notify_one();
lock.unlock();
if (mThread.joinable()) {
mThread.join();
}
}
}
void TimerQueryFenceFactory::push(TimerQueryFenceFactory::Job&& job) {
std::unique_lock<utils::Mutex> const lock(mLock);
mQueue.push_back(std::move(job));
mCondition.notify_one();
mJobQueue.drainAndExit();
}
void TimerQueryFenceFactory::createTimerQuery(GLTimerQuery* tq) {
@@ -236,7 +202,7 @@ void TimerQueryFenceFactory::beginTimeElapsedQuery(GLTimerQuery* tq) {
// on a dummy target for instance, or somehow latch the begin time at the next renderpass
// start.
push([&platform = mPlatform, fence = mPlatform.createFence(), weak]() {
mJobQueue.push([&platform = mPlatform, fence = mPlatform.createFence(), weak]() {
auto state = weak.lock();
if (state) {
platform.waitFence(fence, FENCE_WAIT_FOR_EVER);
@@ -252,7 +218,7 @@ void TimerQueryFenceFactory::endTimeElapsedQuery(OpenGLDriver&, GLTimerQuery* tq
assert_invariant(tq->state);
std::weak_ptr<GLTimerQuery::State> const weak = tq->state;
push([&platform = mPlatform, fence = mPlatform.createFence(), weak]() {
mJobQueue.push([&platform = mPlatform, fence = mPlatform.createFence(), weak]() {
auto state = weak.lock();
if (state) {
platform.waitFence(fence, FENCE_WAIT_FOR_EVER);
+3 -13
View File
@@ -21,8 +21,7 @@
#include "DriverBase.h"
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <utils/AsyncJobQueue.h>
#include "gl_headers.h"
@@ -65,7 +64,7 @@ class TimerQueryFactory {
static bool mGpuTimeSupported;
public:
static TimerQueryFactoryInterface* init(
OpenGLPlatform& platform, OpenGLContext& context) noexcept;
OpenGLPlatform& platform, OpenGLContext& context);
static bool isGpuTimeSupported() noexcept {
return mGpuTimeSupported;
@@ -108,22 +107,13 @@ public:
explicit TimerQueryFenceFactory(OpenGLPlatform& platform);
~TimerQueryFenceFactory() override;
private:
using Job = std::function<void()>;
using Container = std::vector<Job>;
void createTimerQuery(GLTimerQuery* query) override;
void destroyTimerQuery(GLTimerQuery* query) override;
void beginTimeElapsedQuery(GLTimerQuery* tq) override;
void endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQuery* tq) override;
void push(Job&& job);
OpenGLPlatform& mPlatform;
std::thread mThread;
mutable utils::Mutex mLock;
mutable utils::Condition mCondition;
Container mQueue;
bool mExitRequested = false;
utils::AsyncJobQueue mJobQueue;
};
class TimerQueryFallbackFactory final : public TimerQueryFactoryInterface {
@@ -71,7 +71,7 @@ static CString to_string(int const i) { return utils::to_string(i); }
static CString to_string(float const f) { return "float(" + utils::to_string(f) + ")"; }
static void logCompilationError(ShaderStage shaderType, const char* name, GLuint shaderId,
Program::ShaderBlob const& sourceCode) noexcept;
Program::ShaderBlob const& sourceCode);
static void logProgramLinkError(char const* name, GLuint program) noexcept;
static void process_GOOGLE_cpp_style_line_directive(OpenGLContext const& context, char* source,
@@ -79,7 +79,7 @@ static void process_GOOGLE_cpp_style_line_directive(OpenGLContext const& context
static void process_OVR_multiview2(OpenGLContext const& context, int32_t eyeCount, char* source,
size_t len) noexcept;
static std::string_view process_ARB_shading_language_packing(OpenGLContext& context) noexcept;
static std::array<std::string_view, 3> splitShaderSource(std::string_view source) noexcept;
static std::array<std::string_view, 3> splitShaderSource(std::string_view source);
// ------------------------------------------------------------------------------------------------
@@ -312,7 +312,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
}
void ShaderCompilerService::compileProgram(
program_token_t const& token, Program&& program) noexcept {
program_token_t const& token, Program&& program) {
auto& gl = mDriver.getContext();
CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
switch (mMode) {
@@ -343,6 +343,12 @@ void ShaderCompilerService::compileProgram(
program.getSpecializationConstants(), program.isMultiview(),
token);
linkProgram(gl, token);
// Check status of program linking. If it failed, errors will be logged.
bool const linked = checkLinkStatusAndCleanupShaders(token);
// We panic if it failed to create the program.
FILAMENT_CHECK_POSTCONDITION(linked)
<< "OpenGL program " << token->name.c_str_safe()
<< " failed to link or compile";
}
// Now `token->gl.program` must be populated, so we signal the completion
// of the linking. We don't need to check the result of the program here
@@ -475,16 +481,14 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) {
ensureTokenIsReady(token);
assert_invariant(token->gl.program);
// Check status of program linking. If it failed, errors will be logged.
bool const linked = checkLinkStatusAndCleanupShaders(token);
// We panic if it failed to create the program.
FILAMENT_CHECK_POSTCONDITION(linked)
<< "OpenGL program " << token->name.c_str_safe() << " failed to link or compile";
GLuint const program = token->gl.program;
if (mMode != Mode::THREAD_POOL) {
// Check status of program linking. If it failed, errors will be logged.
bool const linked = checkLinkStatusAndCleanupShaders(token);
// We panic if it failed to create the program.
FILAMENT_CHECK_POSTCONDITION(linked)
<< "OpenGL program " << token->name.c_str_safe() << " failed to link or compile";
// The program has been successfully created. Try caching the program blob for
// non-THREAD_POOL modes. In the THREAD_POOL mode, caching is performed in the pool.
tryCachingProgram(mBlobCache, mDriver.mPlatform, token);
@@ -586,7 +590,7 @@ void ShaderCompilerService::handleCanceledTokensForThreadPool() {
// ------------------------------------------------------------------------------------------------
void ShaderCompilerService::runAtNextTick(CompilerPriorityQueue priority,
program_token_t const& token, Job job) noexcept {
program_token_t const& token, Job job) {
// insert items in order of priority and at the end of the range
auto& ops = mRunAtNextTickOps;
auto const pos = std::lower_bound(ops.begin(), ops.end(), priority,
@@ -614,7 +618,7 @@ bool ShaderCompilerService::cancelTickOp(program_token_t const& token) noexcept
return false;
}
void ShaderCompilerService::executeTickOps() noexcept {
void ShaderCompilerService::executeTickOps() {
auto& ops = mRunAtNextTickOps;
auto it = ops.begin();
while (it != ops.end()) {
@@ -636,7 +640,7 @@ bool ShaderCompilerService::shouldCompileSynchronousProgramThisTick() const noex
mNumTicksUntilNextSynchronousProgram == 0);
}
void ShaderCompilerService::compilePendingSynchronousPrograms() noexcept {
void ShaderCompilerService::compilePendingSynchronousPrograms() {
if (mDriver.getDriverConfig().disableAmortizedShaderCompile) {
return;
}
@@ -655,7 +659,7 @@ void ShaderCompilerService::compilePendingSynchronousPrograms() noexcept {
}
void ShaderCompilerService::compilePendingSynchronousProgramNow(
program_token_t const& token) noexcept {
program_token_t const& token) {
if (mDriver.getDriverConfig().disableAmortizedShaderCompile) {
return;
}
@@ -692,27 +696,28 @@ void ShaderCompilerService::cancelPendingSynchronousProgram(program_token_t cons
/* static */ void ShaderCompilerService::compileShaders(OpenGLContext& context,
Program::ShaderSource shadersSource,
FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
bool multiview, program_token_t const& token) noexcept {
bool multiview, program_token_t const& token) {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
auto const appendSpecConstantString = +[](CString& s, Program::SpecializationConstant const& sc) {
s += "#define SPIRV_CROSS_CONSTANT_ID_" + utils::to_string(sc.id) + ' ';
s += std::visit([](auto&& arg) { return to_string(arg); }, sc.value);
s += '\n';
return s;
};
auto const appendSpecConstantString =
+[](CString& s, size_t id, Program::SpecializationConstant const& sc) {
s += "#define SPIRV_CROSS_CONSTANT_ID_" + utils::to_string(id) + ' ';
s += std::visit([](auto&& arg) { return to_string(arg); }, sc);
s += '\n';
return s;
};
CString specializationConstantString;
int32_t numViews = 2;
for (auto const& sc: specializationConstants) {
appendSpecConstantString(specializationConstantString, sc);
if (sc.id == 8) {
// This constant must match
// ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT
// which we can't use here because it's defined in EngineEnums.h.
// (we're breaking layering here, but it's for the good cause).
numViews = std::get<int32_t>(sc.value);
}
// This constant must match
// ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT
// which we can't use here because it's defined in EngineEnums.h.
// (we're breaking layering here, but it's for the good cause).
constexpr size_t CONFIG_STEREO_EYE_COUNT = 8;
int32_t numViews = specializationConstants.size() >= CONFIG_STEREO_EYE_COUNT
? std::get<int32_t>(specializationConstants[CONFIG_STEREO_EYE_COUNT])
: 2;
for (size_t id = 0; id < specializationConstants.size(); id++) {
appendSpecConstantString(specializationConstantString, id, specializationConstants[id]);
}
if (!specializationConstantString.empty()) {
specializationConstantString += '\n';
@@ -817,7 +822,7 @@ void ShaderCompilerService::cancelPendingSynchronousProgram(program_token_t cons
return true;
}
/* static */ void ShaderCompilerService::checkCompileStatus(program_token_t const& token) noexcept {
/* static */ void ShaderCompilerService::checkCompileStatus(program_token_t const& token) {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
UTILS_NOUNROLL
@@ -839,7 +844,7 @@ void ShaderCompilerService::cancelPendingSynchronousProgram(program_token_t cons
}
/* static */ void ShaderCompilerService::linkProgram(OpenGLContext const& context,
program_token_t const& token) noexcept {
program_token_t const& token) {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
// Shader compilation should be completed by now. Check the status and log errors on failure.
@@ -933,7 +938,7 @@ void ShaderCompilerService::cancelPendingSynchronousProgram(program_token_t cons
UTILS_NOINLINE
/* static */ void logCompilationError(ShaderStage shaderType, const char* name,
GLuint const shaderId,
UTILS_UNUSED_IN_RELEASE Program::ShaderBlob const& sourceCode) noexcept {
UTILS_UNUSED_IN_RELEASE Program::ShaderBlob const& sourceCode) {
// Collects the current GL error for additional context. While often redundant,
// errors like `GL_CONTEXT_LOST` can occur asynchronously after `glCompileShader`.
@@ -1179,7 +1184,7 @@ mediump vec4 unpackSnorm4x8(highp uint v) {
// - the version line
// - extensions
// - everything else
/* static */ std::array<std::string_view, 3> splitShaderSource(std::string_view source) noexcept {
/* static */ std::array<std::string_view, 3> splitShaderSource(std::string_view source) {
auto const version_start = source.find("#version");
assert_invariant(version_start != std::string_view::npos);
@@ -148,16 +148,16 @@ private:
// Methods for SYNCHRONOUS and ASYNCHRONOUS modes.
void runAtNextTick(CompilerPriorityQueue priority, program_token_t const& token,
Job job) noexcept;
void executeTickOps() noexcept;
Job job);
void executeTickOps();
bool cancelTickOp(program_token_t const& token) noexcept;
bool shouldCompileSynchronousProgramThisTick() const noexcept;
void compilePendingSynchronousPrograms() noexcept;
void compilePendingSynchronousProgramNow(program_token_t const& token) noexcept;
void compilePendingSynchronousPrograms();
void compilePendingSynchronousProgramNow(program_token_t const& token);
void cancelPendingSynchronousProgram(program_token_t const& token) noexcept;
void compileProgram(program_token_t const& token, Program&& program) noexcept;
void compileProgram(program_token_t const& token, Program&& program);
// Compile shaders with the given `shaderSource`. `gl.shaders` is always populated with valid
// shader IDs after this method. But this doesn't necessarily mean the shaders are successfully
@@ -165,20 +165,20 @@ private:
static void compileShaders(OpenGLContext& context, Program::ShaderSource shadersSource,
utils::FixedCapacityVector<Program::SpecializationConstant> const&
specializationConstants,
bool multiview, program_token_t const& token) noexcept;
bool multiview, program_token_t const& token);
// Check if the shader compilation is completed. You may want to call this when the extension
// `KHR_parallel_shader_compile` is enabled.
static bool isCompileCompleted(program_token_t const& token) noexcept;
// Check compilation status of the shaders and log errors on failure.
static void checkCompileStatus(program_token_t const& token) noexcept;
static void checkCompileStatus(program_token_t const& token);
// Create a program by linking the compiled shaders. `gl.program` is always populated with a
// valid program ID after this method. But this doesn't necessarily mean the program is
// successfully linked. Errors can be checked by calling `checkLinkStatusAndCleanupShaders`
// later.
static void linkProgram(OpenGLContext const& context, program_token_t const& token) noexcept;
static void linkProgram(OpenGLContext const& context, program_token_t const& token);
// Check if the program link is completed. You may want to call this when the extension
// `KHR_parallel_shader_compile` is enabled.
@@ -148,7 +148,7 @@ PlatformCocoaGL::~PlatformCocoaGL() noexcept {
delete pImpl;
}
Driver* PlatformCocoaGL::createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept {
Driver* PlatformCocoaGL::createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) {
// NSOpenGLPFAColorSize: when unspecified, a format that matches the screen is preferred
NSOpenGLPixelFormatAttribute pixelFormatAttributes[] = {
NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core,
@@ -64,7 +64,7 @@ PlatformCocoaTouchGL::~PlatformCocoaTouchGL() noexcept {
delete pImpl;
}
Driver* PlatformCocoaTouchGL::createDriver(void* sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
Driver* PlatformCocoaTouchGL::createDriver(void* sharedGLContext, const Platform::DriverConfig& driverConfig) {
EAGLSharegroup* sharegroup = (__bridge EAGLSharegroup*) sharedGLContext;
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3 sharegroup:sharegroup];
@@ -119,7 +119,7 @@ bool PlatformEGL::isOpenGL() const noexcept {
PlatformEGL::ExternalImageEGL::~ExternalImageEGL() = default;
Driver* PlatformEGL::createDriver(void* sharedContext, const DriverConfig& driverConfig) noexcept {
Driver* PlatformEGL::createDriver(void* sharedContext, const DriverConfig& driverConfig) {
mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
assert_invariant(mEGLDisplay != EGL_NO_DISPLAY);
@@ -500,7 +500,7 @@ bool PlatformEGL::isMSAASwapChainSupported(uint32_t samples) const noexcept {
}
Platform::SwapChain* PlatformEGL::createSwapChain(
void* nativeWindow, uint64_t flags) noexcept {
void* nativeWindow, uint64_t flags) {
Config attribs;
@@ -561,7 +561,7 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
}
Platform::SwapChain* PlatformEGL::createSwapChain(
uint32_t width, uint32_t height, uint64_t flags) noexcept {
uint32_t width, uint32_t height, uint64_t flags) {
Config attribs = {
{ EGL_WIDTH, EGLint(width) },
@@ -198,7 +198,7 @@ void backend::PlatformEGLAndroid::preCommit() noexcept {
}
Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) noexcept {
const Platform::DriverConfig& driverConfig) {
// the refresh rate default value doesn't matter, we change it later
int32_t const tid = gettid();
@@ -39,7 +39,7 @@ bool PlatformEGLHeadless::isOpenGL() const noexcept {
}
backend::Driver* PlatformEGLHeadless::createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) noexcept {
const Platform::DriverConfig& driverConfig) {
EGLBoolean bindAPI = eglBindAPI(EGL_OPENGL_API);
if (UTILS_UNLIKELY(!bindAPI)) {
LOG(ERROR) << "eglBindAPI EGL_OPENGL_API failed";
@@ -31,6 +31,7 @@
// Function pointer types for X11 functions
typedef Display* (* X11_OPEN_DISPLAY)(const char*);
typedef Display* (* X11_CLOSE_DISPLAY)(Display*);
typedef int (* X11_FREE)(void*);
// Function pointer types for GLX functions
typedef void (* GLX_DESTROY_CONTEXT)(Display*, GLXContext);
@@ -43,7 +44,7 @@ struct GLXFunctions {
PFNGLXDESTROYPBUFFERPROC destroyPbuffer;
PFNGLXMAKECONTEXTCURRENTPROC setCurrentContext;
/*
/*
When creating a shared GL context, we query the used
GLX_FBCONFIG_ID to make sure our display framebuffer
attributes match; otherwise making our context current
@@ -52,7 +53,7 @@ struct GLXFunctions {
*/
PFNGLXQUERYCONTEXTPROC queryContext;
/*
/*
When creating a shared GL context, we select the matching
GLXFBConfig that is used by the shared GL context. `getFBConfigs`
will return all the available GLXFBConfigs.
@@ -76,6 +77,7 @@ struct GLXFunctions {
struct X11Functions {
X11_OPEN_DISPLAY openDisplay;
X11_CLOSE_DISPLAY closeDisplay;
X11_FREE free;
void* library;
} g_x11;
@@ -121,6 +123,7 @@ static bool loadLibraries() {
g_x11.openDisplay = (X11_OPEN_DISPLAY)dlsym(g_x11.library, "XOpenDisplay");
g_x11.closeDisplay = (X11_CLOSE_DISPLAY)dlsym(g_x11.library, "XCloseDisplay");
g_x11.free = (X11_FREE)dlsym(g_x11.library, "XFree");
return true;
}
@@ -129,7 +132,7 @@ namespace filament::backend {
using namespace backend;
Driver* PlatformGLX::createDriver(void* sharedGLContext,
const DriverConfig& driverConfig) noexcept {
const DriverConfig& driverConfig) {
loadLibraries();
// Get the display device
mGLXDisplay = g_x11.openDisplay(NULL);
@@ -151,16 +154,13 @@ Driver* PlatformGLX::createDriver(void* sharedGLContext,
int numConfigs = 0;
GLXFBConfig* fbConfigs = g_glx.getFbConfigs(mGLXDisplay, 0, &numConfigs);
if (fbConfigs == nullptr) {
LOG(ERROR) << "Failed to get the available GLXFBConfigs.";
return nullptr;
}
int fbId = 0;
int fbIndex = -1;
for (int i = 0; i < numConfigs; ++i) {
int fbId = 0;
r = g_glx.getFbConfigAttrib(mGLXDisplay, fbConfigs[i], GLX_FBCONFIG_ID, &fbId);
if (r != 0) {
LOG(ERROR) << "Failed to get GLX_FBCONFIG_ID for entry " << i << ".";
@@ -168,17 +168,17 @@ Driver* PlatformGLX::createDriver(void* sharedGLContext,
}
if (fbId == usedFbId) {
fbIndex = i;
mGLXConfig = fbConfigs[i];
break;
}
}
if (fbIndex < 0) {
g_x11.free(fbConfigs);
if (!mGLXConfig) {
LOG(ERROR) << "Failed to find an `GLXFBConfig` with the requested ID.";
return nullptr;
}
mGLXConfig = fbConfigs + fbIndex;
} else {
// Create a context
static int attribs[] = {
@@ -188,17 +188,17 @@ Driver* PlatformGLX::createDriver(void* sharedGLContext,
};
int configCount = 0;
mGLXConfig = g_glx.chooseFbConfig(mGLXDisplay, DefaultScreen(mGLXDisplay),
GLXFBConfig* fbConfigs = g_glx.chooseFbConfig(mGLXDisplay, DefaultScreen(mGLXDisplay),
attribs, &configCount);
if (mGLXConfig == nullptr || configCount == 0) {
if (fbConfigs == nullptr || configCount == 0) {
return nullptr;
}
mGLXConfig = fbConfigs[0];
g_x11.free(fbConfigs);
}
PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs = (PFNGLXCREATECONTEXTATTRIBSARBPROC)
getProcAddress((GLubyte*)"glXCreateContextAttribsARB");
if (glXCreateContextAttribs == nullptr) {
if (g_glx.createContext == nullptr) {
LOG(INFO) << "Unable to retrieve function pointer for `glXCreateContextAttribs()`.";
return nullptr;
}
@@ -209,7 +209,7 @@ Driver* PlatformGLX::createDriver(void* sharedGLContext,
GL_NONE
};
mGLXContext = g_glx.createContext(mGLXDisplay, mGLXConfig[0],
mGLXContext = g_glx.createContext(mGLXDisplay, mGLXConfig,
(GLXContext)sharedGLContext, True, contextAttribs);
int pbufferAttribs[] = {
@@ -218,7 +218,7 @@ Driver* PlatformGLX::createDriver(void* sharedGLContext,
GL_NONE
};
mDummySurface = g_glx.createPbuffer(mGLXDisplay, mGLXConfig[0], pbufferAttribs);
mDummySurface = g_glx.createPbuffer(mGLXDisplay, mGLXConfig, pbufferAttribs);
g_glx.setCurrentContext(mGLXDisplay, mDummySurface, mDummySurface, mGLXContext);
int result = bluegl::bind();
@@ -246,7 +246,7 @@ Platform::SwapChain* PlatformGLX::createSwapChain(
GLX_PBUFFER_HEIGHT, int(height),
GL_NONE
};
GLXPbuffer sur = g_glx.createPbuffer(mGLXDisplay, mGLXConfig[0], pbufferAttribs);
GLXPbuffer sur = g_glx.createPbuffer(mGLXDisplay, mGLXConfig, pbufferAttribs);
if (sur) {
mPBuffers.push_back(sur);
}
@@ -113,7 +113,7 @@ private:
}// anonymous namespace
Driver* PlatformOSMesa::createDriver(void* sharedGLContext,
const DriverConfig& driverConfig) noexcept {
const DriverConfig& driverConfig) {
OSMesaAPI* api = new OSMesaAPI();
mOsMesaApi = api;
@@ -75,7 +75,7 @@ struct WGLSwapChain {
static PFNWGLCREATECONTEXTATTRIBSARBPROC wglCreateContextAttribs = nullptr;
Driver* PlatformWGL::createDriver(void* sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept {
const Platform::DriverConfig& driverConfig) {
int result = 0;
int pixelFormat = 0;
DWORD dwError = 0;
@@ -124,8 +124,6 @@ Driver* PlatformWGL::createDriver(void* sharedGLContext,
(PFNWGLCREATECONTEXTATTRIBSARBPROC) wglGetProcAddress("wglCreateContextAttribsARB");
// try all versions down, from GL 4.5 to 4.1
for (int minor = 5; minor >= 1; minor--) {
mAttribs = {
WGL_CONTEXT_MAJOR_VERSION_ARB, 4,
@@ -144,6 +142,18 @@ Driver* PlatformWGL::createDriver(void* sharedGLContext,
goto error;
}
// Create shared contexts here for use by other threads. This is a Windows specific workaround
// necessitated by the requirement that shared contexts must be initialized on the same thread
// as the primary context. If more shared contexts are necessary, the constant
// SHARED_CONTEXT_NUM must be updated.
for (int i = 0; i < SHARED_CONTEXT_NUM; ++i) {
HGLRC context = wglCreateContextAttribs(mWhdc, mContext, mAttribs.data());
if (context) {
mAdditionalContexts.push_back(context);
}
}
// Delete the temporary context
wglMakeCurrent(NULL, NULL);
wglDeleteContext(tempContext);
tempContext = NULL;
@@ -169,13 +179,17 @@ error:
}
bool PlatformWGL::isExtraContextSupported() const noexcept {
return false;
return true;
}
void PlatformWGL::createContext(bool shared) {
HGLRC context = wglCreateContextAttribs(mWhdc, shared ? mContext : nullptr, mAttribs.data());
wglMakeCurrent(mWhdc, context);
mAdditionalContexts.push_back(context);
int nextIndex = mNextFreeSharedContextIndex.fetch_add(1, std::memory_order_relaxed);
FILAMENT_CHECK_PRECONDITION(nextIndex < SHARED_CONTEXT_NUM)
<< "Shared context index out of range. Increase SHARED_CONTEXT_NUM.";
HGLRC context = mAdditionalContexts[nextIndex];
BOOL result = wglMakeCurrent(mWhdc, context);
FILAMENT_CHECK_POSTCONDITION(result) << "Failed to make current.";
}
void PlatformWGL::terminate() noexcept {
@@ -21,7 +21,7 @@ namespace filament::backend {
using namespace backend;
Driver* PlatformWebGL::createDriver(void* sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept {
const Platform::DriverConfig& driverConfig) {
return OpenGLPlatform::createDefaultDriver(this, sharedGLContext, driverConfig);
}
+26 -25
View File
@@ -37,6 +37,7 @@
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/ImmutableCString.h>
#include <utils/Panic.h>
#ifndef NDEBUG
@@ -524,7 +525,7 @@ void VulkanDriver::finish(int dummy) {
void VulkanDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
PrimitiveType pt, utils::CString&& tag) {
PrimitiveType pt, utils::ImmutableCString&& tag) {
FVK_SYSTRACE_SCOPE();
auto vb = resource_ptr<VulkanVertexBuffer>::cast(&mResourceManager, vbh);
auto ib = resource_ptr<VulkanIndexBuffer>::cast(&mResourceManager, ibh);
@@ -543,7 +544,7 @@ void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
}
void VulkanDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes, utils::CString&& tag) {
uint8_t attributeCount, AttributeArray attributes, utils::ImmutableCString&& tag) {
FVK_SYSTRACE_SCOPE();
auto vbi = resource_ptr<VulkanVertexBufferInfo>::make(&mResourceManager, vbih, bufferCount,
attributeCount, attributes);
@@ -561,7 +562,7 @@ void VulkanDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
}
void VulkanDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint32_t vertexCount,
Handle<HwVertexBufferInfo> vbih, utils::CString&& tag) {
Handle<HwVertexBufferInfo> vbih, utils::ImmutableCString&& tag) {
FVK_SYSTRACE_SCOPE();
auto vbi = resource_ptr<VulkanVertexBufferInfo>::cast(&mResourceManager, vbih);
auto vb = resource_ptr<VulkanVertexBuffer>::make(&mResourceManager, vbh, mContext, mStagePool,
@@ -580,7 +581,7 @@ void VulkanDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
}
void VulkanDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage, utils::CString&& tag) {
uint32_t indexCount, BufferUsage usage, utils::ImmutableCString&& tag) {
FVK_SYSTRACE_SCOPE();
auto elementSize = (uint8_t) getElementTypeSize(elementType);
auto ib = resource_ptr<VulkanIndexBuffer>::make(&mResourceManager, ibh, mContext, mAllocator,
@@ -599,7 +600,7 @@ void VulkanDriver::destroyIndexBuffer(Handle<HwIndexBuffer> ibh) {
}
void VulkanDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage, utils::CString&& tag) {
BufferObjectBinding bindingType, BufferUsage usage, utils::ImmutableCString&& tag) {
FVK_SYSTRACE_SCOPE();
auto bo = resource_ptr<VulkanBufferObject>::make(&mResourceManager, boh, mContext, mAllocator,
mStagePool, mBufferCache, byteCount, bindingType, usage);
@@ -618,7 +619,7 @@ void VulkanDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
void VulkanDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage usage, utils::CString&& tag) {
TextureUsage usage, utils::ImmutableCString&& tag) {
FVK_SYSTRACE_SCOPE();
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mResourceManager, &mCommands,
@@ -635,7 +636,7 @@ void VulkanDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
}
void VulkanDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
uint8_t baseLevel, uint8_t levelCount, utils::CString&& tag) {
uint8_t baseLevel, uint8_t levelCount, utils::ImmutableCString&& tag) {
auto src = resource_ptr<VulkanTexture>::cast(&mResourceManager, srch);
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, src, baseLevel,
@@ -646,7 +647,7 @@ void VulkanDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> sr
void VulkanDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
backend::TextureSwizzle a, utils::CString&& tag) {
backend::TextureSwizzle a, utils::ImmutableCString&& tag) {
TextureSwizzle const swizzleArray[] = { r, g, b, a };
VkComponentMapping const swizzle = fvkutils::getSwizzleMap(swizzleArray);
auto src = resource_ptr<VulkanTexture>::cast(&mResourceManager, srch);
@@ -658,7 +659,7 @@ void VulkanDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwText
void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
Platform::ExternalImageHandleRef externalImage, utils::CString&& tag) {
Platform::ExternalImageHandleRef externalImage, utils::ImmutableCString&& tag) {
FVK_SYSTRACE_SCOPE();
auto metadata = mPlatform->extractExternalImageMetadata(externalImage);
@@ -709,7 +710,7 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* externalImage, utils::CString&& tag) {
void* externalImage, utils::ImmutableCString&& tag) {
FVK_SYSTRACE_SCOPE();
assert_invariant(false && "Not supported in Vulkan backend");
// not supported in this backend
@@ -717,14 +718,14 @@ void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::Sa
void VulkanDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, uint32_t plane, utils::CString&& tag) {
void* image, uint32_t plane, utils::ImmutableCString&& tag) {
assert_invariant(false && "Not supported in Vulkan backend");
}
void VulkanDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage usage, utils::CString&& tag) {
TextureUsage usage, utils::ImmutableCString&& tag) {
// not supported in this backend
assert_invariant(false && "Not supported in Vulkan backend");
mResourceManager.associateHandle(th.getId(), std::move(tag));
@@ -740,7 +741,7 @@ void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
mExternalImageManager.removeExternallySampledTexture(texture);
}
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program, utils::CString&& tag) {
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program, utils::ImmutableCString&& tag) {
FVK_SYSTRACE_SCOPE();
auto vprogram = resource_ptr<VulkanProgram>::make(&mResourceManager, ph, mPlatform->getDevice(),
program);
@@ -756,7 +757,7 @@ void VulkanDriver::destroyProgram(Handle<HwProgram> ph) {
vprogram.dec();
}
void VulkanDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils::CString&& tag) {
void VulkanDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils::ImmutableCString&& tag) {
assert_invariant(mDefaultRenderTarget); // Default render target should already exist.
auto renderTarget = resource_ptr<VulkanRenderTarget>::make(&mResourceManager, rth,
@@ -769,7 +770,7 @@ void VulkanDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils:
void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
TargetBufferFlags targets, uint32_t width, uint32_t height, uint8_t samples,
uint8_t layerCount, MRT color, TargetBufferInfo depth, TargetBufferInfo stencil,
utils::CString&& tag) {
utils::ImmutableCString&& tag) {
FVK_SYSTRACE_SCOPE();
@@ -846,7 +847,7 @@ void VulkanDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
}
}
void VulkanDriver::createFenceR(Handle<HwFence> fh, utils::CString&& tag) {
void VulkanDriver::createFenceR(Handle<HwFence> fh, utils::ImmutableCString&& tag) {
VulkanCommandBuffer* cmdbuf;
if (mCurrentRenderPass.commandBuffer) {
cmdbuf = mCurrentRenderPass.commandBuffer;
@@ -861,7 +862,7 @@ void VulkanDriver::createFenceR(Handle<HwFence> fh, utils::CString&& tag) {
mResourceManager.associateHandle(fh.getId(), std::move(tag));
}
void VulkanDriver::createSyncR(Handle<HwSync> sh, utils::CString&& tag) {
void VulkanDriver::createSyncR(Handle<HwSync> sh, utils::ImmutableCString&& tag) {
auto sync = resource_ptr<VulkanSync>::cast(&mResourceManager, sh);
VkFence fence = VK_NULL_HANDLE;
std::shared_ptr<VulkanCmdFence> fenceStatus;
@@ -892,7 +893,7 @@ void VulkanDriver::createSyncR(Handle<HwSync> sh, utils::CString&& tag) {
}
void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags,
utils::CString&& tag) {
utils::ImmutableCString&& tag) {
FVK_SYSTRACE_SCOPE();
// Running gc() to guard against an edge case where the old swapchains need to have been
// destroyed before the new swapchain can be created. Otherwise, we would fail
@@ -921,7 +922,7 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
}
void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
uint32_t height, uint64_t flags, utils::CString&& tag) {
uint32_t height, uint64_t flags, utils::ImmutableCString&& tag) {
if ((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0 && !isSRGBSwapChainSupported()) {
FVK_LOGW << "sRGB swapchain requested, but Platform does not support it";
flags = flags | ~(backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE);
@@ -934,13 +935,13 @@ void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t wi
mResourceManager.associateHandle(sch.getId(), std::move(tag));
}
void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::CString&& tag) {
void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::ImmutableCString&& tag) {
// nothing to do, timer query was constructed in createTimerQueryS
mResourceManager.associateHandle(tqh.getId(), std::move(tag));
}
void VulkanDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
backend::DescriptorSetLayout&& info, utils::CString&& tag) {
backend::DescriptorSetLayout&& info, utils::ImmutableCString&& tag) {
FVK_SYSTRACE_SCOPE();
auto layout = mDescriptorSetLayoutCache.createLayout(dslh, std::move(info));
layout.inc();
@@ -948,7 +949,7 @@ void VulkanDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh
}
void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
Handle<HwDescriptorSetLayout> dslh, utils::CString&& tag) {
Handle<HwDescriptorSetLayout> dslh, utils::ImmutableCString&& tag) {
FVK_SYSTRACE_SCOPE();
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout =
fvkmemory::resource_ptr<VulkanDescriptorSetLayout>::cast(&mResourceManager, dslh);
@@ -964,7 +965,7 @@ void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
void VulkanDriver::mapBufferR(MemoryMappedBufferHandle mmbh,
BufferObjectHandle boh, size_t offset,
size_t size, MapBufferAccessFlags access, utils::CString&& tag) {
size_t size, MapBufferAccessFlags access, utils::ImmutableCString&& tag) {
FVK_SYSTRACE_SCOPE();
auto mmb = resource_ptr<VulkanMemoryMappedBuffer>::make(&mResourceManager, mmbh,
boh, offset, size, access);
@@ -1131,11 +1132,11 @@ void VulkanDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
}
}
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream, utils::CString tag) {
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream, utils::ImmutableCString tag) {
return {};
}
Handle<HwStream> VulkanDriver::createStreamAcquired(utils::CString tag) {
Handle<HwStream> VulkanDriver::createStreamAcquired(utils::ImmutableCString tag) {
return {};
}
@@ -24,6 +24,7 @@
#include <private/backend/HandleAllocator.h>
#include <utils/Panic.h>
#include <utils/ImmutableCString.h>
namespace filament::backend::fvkmemory {
@@ -36,7 +37,7 @@ public:
return mHandleAllocatorImpl.allocate<D>();
}
inline void associateHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
inline void associateHandle(HandleBase::HandleId id, utils::ImmutableCString&& tag) noexcept {
mHandleAllocatorImpl.associateTagToHandle(id, std::move(tag));
}
@@ -713,7 +713,7 @@ void VulkanPlatform::terminate() {
// This is the main entry point for context creation.
Driver* VulkanPlatform::createDriver(void* sharedContext,
Platform::DriverConfig const& driverConfig) noexcept {
Platform::DriverConfig const& driverConfig) {
// Load Vulkan entry points.
FILAMENT_CHECK_POSTCONDITION(bluevk::initialize()) << "BlueVK is unable to load entry points.";
+7 -25
View File
@@ -32,7 +32,7 @@ namespace {
// This function transforms an OpSpecConstant instruction into just a OpConstant instruction.
// Additionally, it will adjust the value of the constant as given by the program.
void getTransformedConstantInst(SpecConstantValue* value, uint32_t* inst) {
void getTransformedConstantInst(Program::SpecializationConstant const& value, uint32_t* inst) {
// The first word is the size of the instruction and the instruction type.
// The second word is the type of the instruction.
@@ -42,25 +42,15 @@ void getTransformedConstantInst(SpecConstantValue* value, uint32_t* inst) {
constexpr size_t const OP = 0;
constexpr size_t const VALUE = 3;
if (!value) {
// If a specialization wasn't provided, just switch the opcode.
uint32_t const op = inst[OP] & 0x0000FFFF;
if (op == spv::Op::OpSpecConstantFalse) {
inst[OP] = (3 << 16) | spv::Op::OpConstantFalse;
} else if (op == spv::Op::OpSpecConstantTrue) {
inst[OP] = (3 << 16) | spv::Op::OpConstantTrue;
} else if (op == spv::Op::OpSpecConstant) {
inst[OP] = (4 << 16) | spv::Op::OpConstant;
}
} else if (std::holds_alternative<bool>(*value)) {
if (std::holds_alternative<bool>(value)) {
inst[OP] = (3 << 16)
| (std::get<bool>(*value) ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse);
} else if (std::holds_alternative<float>(*value)) {
float const fval = std::get<float>(*value);
| (std::get<bool>(value) ? spv::Op::OpConstantTrue : spv::Op::OpConstantFalse);
} else if (std::holds_alternative<float>(value)) {
float const fval = std::get<float>(value);
inst[OP] = (4 << 16) | spv::Op::OpConstant;
inst[VALUE] = *reinterpret_cast<uint32_t const*>(&fval);
} else {
int const ival = std::get<int>(*value);
int const ival = std::get<int>(value);
inst[OP] = (4 << 16) | spv::Op::OpConstant;
inst[VALUE] = *reinterpret_cast<uint32_t const*>(&ival);
}
@@ -72,15 +62,9 @@ void workaroundSpecConstant(Program::ShaderBlob const& blob,
utils::FixedCapacityVector<Program::SpecializationConstant> const& specConstants,
std::vector<uint32_t>& output) {
using WordMap = std::unordered_map<uint32_t, uint32_t>;
using SpecValueMap = std::unordered_map<uint32_t, SpecConstantValue>;
constexpr size_t HEADER_SIZE = 5;
WordMap varToIdMap;
SpecValueMap idToValue;
for (auto spec: specConstants) {
idToValue[spec.id] = spec.value;
}
// The follow loop will iterate through all the instructions and look for instructions of the
// form:
@@ -116,9 +100,7 @@ void workaroundSpecConstant(Program::ShaderBlob const& blob,
assert_invariant(idItr != varToIdMap.end() &&
"Cannot find variable previously decorated with SpecId");
auto valItr = idToValue.find(idItr->second);
SpecConstantValue* val = (valItr != idToValue.end()) ? &valItr->second : nullptr;
auto const& val = specConstants[idItr->second];
std::memcpy(&outputData[outputCursor], &data[cursor], wordCount * 4);
getTransformedConstantInst(val, &outputData[outputCursor]);
outputCursor += wordCount;
+1 -4
View File
@@ -21,13 +21,10 @@
#include <utils/FixedCapacityVector.h>
#include <tuple>
#include <vector>
namespace filament::backend::fvkutils {
using SpecConstantValue = Program::SpecializationConstant::Type;
// For certain drivers, using spec constant can lead to compile errors [1] or undesirable behaviors
// [2]. In those instances, we simply change the spirv and set them to constants.
//
@@ -44,7 +41,7 @@ void workaroundSpecConstant(Program::ShaderBlob const& blob,
std::vector<uint32_t>& output);
// bindings for UBO, samplers, input attachment
// This is no longer needed after the descriptor set refactor, but the code is good for reference.
// This is no longer needed after the descriptor set refactor, but the code is good for reference.
// std::tuple<uint32_t, uint32_t, uint32_t> getProgramBindings(Program::ShaderBlob const& blob);
} // namespace filament::backend::fvkutils
+73 -106
View File
@@ -17,19 +17,19 @@
#include "WebGPUBlitter.h"
#include "WebGPUConstants.h"
#include "WebGPUStrings.h"
#include "WebGPUTextureHelpers.h"
#include "webgpu/utils/StringPlaceholderTemplateProcessor.h"
#include <backend/DriverEnums.h>
#include <webgpu/webgpu_cpp.h>
#include <math/vec2.h>
#include <utils/Hash.h>
#include <utils/Panic.h>
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
#include <string.h>
#include <string>
#include <string_view>
#include <unordered_map>
@@ -395,10 +395,15 @@ void WebGPUBlitter::blit(wgpu::Queue const& queue, wgpu::CommandEncoder const& c
const bool multisampledSource{ args.source.texture.GetSampleCount() > 1 };
const bool depthSource{ hasDepth(args.source.texture.GetFormat()) };
const bool depthDestination{ hasDepth(args.destination.texture.GetFormat()) };
const PipelineLayoutKey pipelineLayoutKey{
.sourceDimension = sourceDimension,
.filterType = args.filter,
.multisampledSource = multisampledSource,
.depthSource = depthSource,
};
const wgpu::BindGroupDescriptor textureBindGroupDescriptor{
.label = "blit_texture_bind_group",
.layout = getOrCreateTextureBindGroupLayout(args.filter, sourceDimension,
multisampledSource, depthSource),
.layout = getOrCreateTextureBindGroupLayout(pipelineLayoutKey),
.entryCount = textureBindGroupEntriesCount,
.entries = textureBindGroupEntries,
};
@@ -447,9 +452,14 @@ void WebGPUBlitter::blit(wgpu::Queue const& queue, wgpu::CommandEncoder const& c
&renderPassDescriptor) };
FILAMENT_CHECK_POSTCONDITION(renderPassEncoder)
<< "Failed to create render pass encoder for blit.";
renderPassEncoder.SetPipeline(getOrCreateRenderPipeline(args.filter, sourceDimension,
args.source.texture.GetSampleCount(), depthSource,
args.destination.texture.GetFormat()));
const RenderPipelineKey renderPipelineKey{
.sourceDimension = sourceDimension,
.destinationTextureFormat = args.destination.texture.GetFormat(),
.sourceSampleCount = static_cast<uint8_t>(args.source.texture.GetSampleCount()),
.filterType = args.filter,
.depthSource = depthSource,
};
renderPassEncoder.SetPipeline(getOrCreateRenderPipeline(renderPipelineKey));
renderPassEncoder.SetBindGroup(TEXTURE_BIND_GROUP_INDEX, textureBindGroup);
renderPassEncoder.Draw(3); // draw the full-screen triangle
// with hard-coded vertices in the shader
@@ -527,14 +537,9 @@ void WebGPUBlitter::createSampler(const SamplerMagFilter filter) {
// Caches and returns a render pipeline for a given blit configuration.
// If a pipeline for the given configuration does not exist, it creates and caches one.
wgpu::RenderPipeline const& WebGPUBlitter::getOrCreateRenderPipeline(
const SamplerMagFilter filterType, const wgpu::TextureViewDimension sourceDimension,
const uint32_t sourceSampleCount, const bool depthSource,
const wgpu::TextureFormat destinationTextureFormat) {
const size_t key{ hashRenderPipelineKey(filterType, sourceDimension, sourceSampleCount,
depthSource, destinationTextureFormat) };
RenderPipelineKey const& key) {
if (mRenderPipelines.find(key) == mRenderPipelines.end()) {
mRenderPipelines[key] = createRenderPipeline(filterType, sourceDimension, sourceSampleCount,
depthSource, destinationTextureFormat);
mRenderPipelines[key] = createRenderPipeline(key);
}
return mRenderPipelines[key];
}
@@ -542,18 +547,21 @@ wgpu::RenderPipeline const& WebGPUBlitter::getOrCreateRenderPipeline(
// Creates a render pipeline for a blit operation.
// The pipeline is configured with the appropriate vertex and fragment shaders,
// pipeline layout, and render target state based on the blit parameters.
wgpu::RenderPipeline WebGPUBlitter::createRenderPipeline(const SamplerMagFilter filterType,
const wgpu::TextureViewDimension sourceDimension, const uint32_t sourceSampleCount,
const bool depthSource, const wgpu::TextureFormat destinationTextureFormat) {
const bool depthDestination{ hasDepth(destinationTextureFormat) };
const wgpu::ColorTargetState colorTargetState{ .format = destinationTextureFormat };
wgpu::RenderPipeline WebGPUBlitter::createRenderPipeline(RenderPipelineKey const& key) {
const bool depthDestination{ hasDepth(key.destinationTextureFormat) };
const wgpu::ColorTargetState colorTargetState{ .format = key.destinationTextureFormat };
const wgpu::DepthStencilState depthStencilState{
.format = destinationTextureFormat,
.format = key.destinationTextureFormat,
.depthWriteEnabled = true,
.depthCompare = wgpu::CompareFunction::Always,
};
wgpu::ShaderModule const& shaderModule{ getOrCreateShaderModule(sourceDimension,
sourceSampleCount > 1, depthSource, hasDepth(destinationTextureFormat)) };
const ShaderModuleKey shaderModuleKey{
.sourceDimension = key.sourceDimension,
.multisampledSource = key.sourceSampleCount > 1,
.depthSource = key.depthSource,
.depthDestination = hasDepth(key.destinationTextureFormat),
};
wgpu::ShaderModule const& shaderModule{ getOrCreateShaderModule(shaderModuleKey) };
const wgpu::FragmentState fragmentState{
.module = shaderModule,
.entryPoint = FRAGMENT_SHADER_ENTRY_POINT,
@@ -562,9 +570,15 @@ wgpu::RenderPipeline WebGPUBlitter::createRenderPipeline(const SamplerMagFilter
.targetCount = depthDestination ? 0u : 1u,
.targets = depthDestination ? nullptr : &colorTargetState,
};
const PipelineLayoutKey pipelineLayoutKey{
.sourceDimension = key.sourceDimension,
.filterType = key.filterType,
.multisampledSource = key.sourceSampleCount > 1,
.depthSource = key.depthSource,
};
const wgpu::RenderPipelineDescriptor pipelineDescriptor{
.label = "render_pass_blit_pipeline",
.layout = getOrCreatePipelineLayout(filterType, sourceDimension, sourceSampleCount > 1, depthSource),
.layout = getOrCreatePipelineLayout(pipelineLayoutKey),
.vertex = {
.module = shaderModule,
.entryPoint = VERTEX_SHADER_ENTRY_POINT,
@@ -586,7 +600,7 @@ wgpu::RenderPipeline WebGPUBlitter::createRenderPipeline(const SamplerMagFilter
},
.depthStencil = depthDestination ? &depthStencilState : nullptr,
.multisample = {
.count = sourceSampleCount,
.count = key.sourceSampleCount,
.mask = 0xFFFFFFFF,
.alphaToCoverageEnabled = false,
},
@@ -597,41 +611,27 @@ wgpu::RenderPipeline WebGPUBlitter::createRenderPipeline(const SamplerMagFilter
return pipeline;
}
size_t WebGPUBlitter::hashRenderPipelineKey(const SamplerMagFilter filterType,
const wgpu::TextureViewDimension sourceDimension, const uint32_t sourceSampleCount,
const bool depthSource, const wgpu::TextureFormat destinationTextureFormat) {
size_t seed{ std::hash<uint8_t>{}(static_cast<uint8_t>(filterType)) };
utils::hash::combine(seed, static_cast<uint32_t>(sourceDimension));
utils::hash::combine(seed, sourceSampleCount);
utils::hash::combine(seed, depthSource);
utils::hash::combine(seed, static_cast<uint32_t>(destinationTextureFormat));
return seed;
bool WebGPUBlitter::RenderPipelineKey::operator==(RenderPipelineKey const& other) const {
return memcmp(this, &other, sizeof(RenderPipelineKey)) == 0;
}
// Caches and returns a pipeline layout for a given blit configuration.
// If a layout for the given configuration does not exist, it creates and caches one.
wgpu::PipelineLayout const& WebGPUBlitter::getOrCreatePipelineLayout(
const SamplerMagFilter filterType, const wgpu::TextureViewDimension sourceDimension,
const bool multisampledSource, const bool depthSource) {
const size_t key{ hashPipelineLayoutKey(filterType, sourceDimension, multisampledSource,
depthSource) };
PipelineLayoutKey const& key) {
if (mPipelineLayouts.find(key) == mPipelineLayouts.end()) {
mPipelineLayouts[key] =
createPipelineLayout(filterType, sourceDimension, multisampledSource, depthSource);
mPipelineLayouts[key] = createPipelineLayout(key);
}
return mPipelineLayouts[key];
}
// Creates a pipeline layout for a blit operation.
// The layout defines the bind group layouts used by the blit pipeline.
wgpu::PipelineLayout WebGPUBlitter::createPipelineLayout(const SamplerMagFilter filterType,
const wgpu::TextureViewDimension sourceDimension, const bool multisampledSource,
const bool depthSource) {
wgpu::PipelineLayout WebGPUBlitter::createPipelineLayout(PipelineLayoutKey const& key) {
const wgpu::PipelineLayoutDescriptor pipelineLayoutDescriptor{
.label = "render_pass_blit_pipeline_layout",
.bindGroupLayoutCount = 1,
.bindGroupLayouts = &getOrCreateTextureBindGroupLayout(filterType, sourceDimension,
multisampledSource, depthSource),
.bindGroupLayouts = &getOrCreateTextureBindGroupLayout(key),
};
const wgpu::PipelineLayout pipelineLayout{ mDevice.CreatePipelineLayout(
&pipelineLayoutDescriptor) };
@@ -640,26 +640,16 @@ wgpu::PipelineLayout WebGPUBlitter::createPipelineLayout(const SamplerMagFilter
return pipelineLayout;
}
size_t WebGPUBlitter::hashPipelineLayoutKey(const SamplerMagFilter filterType,
const wgpu::TextureViewDimension sourceDimension, const bool multisampledSource,
const bool depthSource) {
size_t seed{ std::hash<uint8_t>{}(static_cast<uint8_t>(filterType)) };
utils::hash::combine(seed, static_cast<uint32_t>(sourceDimension));
utils::hash::combine(seed, multisampledSource);
utils::hash::combine(seed, depthSource);
return seed;
bool WebGPUBlitter::PipelineLayoutKey::operator==(const PipelineLayoutKey& other) const {
return memcmp(this, &other, sizeof(PipelineLayoutKey)) == 0;
}
// Caches and returns a bind group layout for a given blit configuration.
// If a layout for the given configuration does not exist, it creates and caches one.
wgpu::BindGroupLayout const& WebGPUBlitter::getOrCreateTextureBindGroupLayout(
const SamplerMagFilter filterType, const wgpu::TextureViewDimension sourceDimension,
const bool multisampledSource, const bool depthSource) {
const size_t key{ hashTextureBindGroupLayoutKey(filterType, sourceDimension, multisampledSource,
depthSource) };
PipelineLayoutKey const& key) {
if (mTextureBindGroupLayouts.find(key) == mTextureBindGroupLayouts.end()) {
mTextureBindGroupLayouts[key] = createTextureBindGroupLayout(filterType, sourceDimension,
multisampledSource, depthSource);
mTextureBindGroupLayouts[key] = createTextureBindGroupLayout(key);
}
return mTextureBindGroupLayouts[key];
}
@@ -667,23 +657,21 @@ wgpu::BindGroupLayout const& WebGPUBlitter::getOrCreateTextureBindGroupLayout(
// Creates a bind group layout for the blit operation's texture resources.
// This layout specifies the bindings for the source texture, a uniform buffer with blit parameters,
// and optionally a sampler.
wgpu::BindGroupLayout WebGPUBlitter::createTextureBindGroupLayout(const SamplerMagFilter filterType,
const wgpu::TextureViewDimension sourceDimension, const bool multisampledSource,
const bool depthSource) {
const wgpu::BindGroupLayoutEntry bindGroupLayoutEntries[MAX_TEXTURE_BIND_GROUP_ENTRY_SIZE] {
wgpu::BindGroupLayout WebGPUBlitter::createTextureBindGroupLayout(PipelineLayoutKey const& key) {
const wgpu::BindGroupLayoutEntry bindGroupLayoutEntries[MAX_TEXTURE_BIND_GROUP_ENTRY_SIZE]{
{
.binding = TEXTURE_BINDING_INDEX,
.visibility = wgpu::ShaderStage::Fragment,
.texture = {
.sampleType = depthSource
.sampleType = key.depthSource
? wgpu::TextureSampleType::Depth
: (multisampledSource
: (key.multisampledSource
? wgpu::TextureSampleType::UnfilterableFloat
: wgpu::TextureSampleType::Float), // only F32 scalar sample
// type supported for now
// (aside from depth)
.viewDimension = sourceDimension,
.multisampled = multisampledSource,
.viewDimension = key.sourceDimension,
.multisampled = key.multisampledSource,
},
},
{
@@ -699,7 +687,7 @@ wgpu::BindGroupLayout WebGPUBlitter::createTextureBindGroupLayout(const SamplerM
.binding = SAMPLER_BINDING_INDEX,
.visibility = wgpu::ShaderStage::Fragment,
.sampler = {
.type =filterType == SamplerMagFilter::LINEAR
.type = key.filterType == SamplerMagFilter::LINEAR
? wgpu::SamplerBindingType::Filtering
: wgpu::SamplerBindingType::NonFiltering,
},
@@ -708,8 +696,8 @@ wgpu::BindGroupLayout WebGPUBlitter::createTextureBindGroupLayout(const SamplerM
const wgpu::BindGroupLayoutDescriptor textureBindGroupLayoutDescriptor{
.label = "render_pass_blit_texture_bind_group_layout",
// TODO, doesnt make any sense but gets rid of the error. Are the entries 0 based?
.entryCount = multisampledSource ? (MAX_TEXTURE_BIND_GROUP_ENTRY_SIZE - 1)
: (MAX_TEXTURE_BIND_GROUP_ENTRY_SIZE),
.entryCount = key.multisampledSource ? (MAX_TEXTURE_BIND_GROUP_ENTRY_SIZE - 1)
: (MAX_TEXTURE_BIND_GROUP_ENTRY_SIZE),
.entries = bindGroupLayoutEntries,
};
const wgpu::BindGroupLayout textureBindGroupLayout{ mDevice.CreateBindGroupLayout(
@@ -719,26 +707,11 @@ wgpu::BindGroupLayout WebGPUBlitter::createTextureBindGroupLayout(const SamplerM
return textureBindGroupLayout;
}
size_t WebGPUBlitter::hashTextureBindGroupLayoutKey(const SamplerMagFilter filterType,
const wgpu::TextureViewDimension sourceDimension, const bool multisampledSource,
const bool depthSource) {
size_t seed{ std::hash<uint8_t>{}(static_cast<uint8_t>(filterType)) };
utils::hash::combine(seed, static_cast<uint32_t>(sourceDimension));
utils::hash::combine(seed, multisampledSource);
utils::hash::combine(seed, depthSource);
return seed;
}
// Caches and returns a shader module for a given blit configuration.
// If a shader module for the given configuration does not exist, it creates and caches one.
wgpu::ShaderModule const& WebGPUBlitter::getOrCreateShaderModule(
const wgpu::TextureViewDimension sourceDimension, const bool multisampledSource,
const bool depthSource, const bool depthDestination) {
const size_t key{ hashShaderModuleKey(sourceDimension, multisampledSource, depthSource,
depthDestination) };
wgpu::ShaderModule const& WebGPUBlitter::getOrCreateShaderModule(ShaderModuleKey const& key) {
if (mShaderModules.find(key) == mShaderModules.end()) {
mShaderModules[key] = createShaderModule(sourceDimension, multisampledSource, depthSource,
depthDestination);
mShaderModules[key] = createShaderModule(key);
}
return mShaderModules[key];
}
@@ -746,46 +719,45 @@ wgpu::ShaderModule const& WebGPUBlitter::getOrCreateShaderModule(
// Creates a shader module containing the vertex and fragment shaders for the blit operation.
// The shader source is generated from a template, with placeholders filled in based on the
// blit configuration (e.g., texture type, sample count).
wgpu::ShaderModule WebGPUBlitter::createShaderModule(
const wgpu::TextureViewDimension sourceDimension, const bool multisampledSource,
const bool depthSource, const bool depthDestination) {
wgpu::ShaderModule WebGPUBlitter::createShaderModule(ShaderModuleKey const& key) {
std::string_view textureType;
if (depthSource) {
if (multisampledSource) {
if (key.depthSource) {
if (key.multisampledSource) {
textureType = "texture_depth_multisampled_2d";
} else {
textureType = "texture_depth_2d";
}
} else {
if (multisampledSource) {
if (key.multisampledSource) {
textureType = "texture_multisampled_2d<f32>";
} else {
if (sourceDimension == wgpu::TextureViewDimension::e3D) {
if (key.sourceDimension == wgpu::TextureViewDimension::e3D) {
textureType = "texture_3d<f32>";
} else {
textureType = "texture_2d<f32>";
}
}
}
const std::string_view texture2dType{ depthSource ? "texture_depth_2d" : "texture_2d<f32>" };
const std::string_view texture2dType{ key.depthSource ? "texture_depth_2d"
: "texture_2d<f32>" };
// we don't declare a sampler in the shader or pipeline for the multisampled case
const std::string_view samplerDeclaration{
multisampledSource ? "" : "@group(0) @binding(2) var sourceSampler: sampler;"
key.multisampledSource ? "" : "@group(0) @binding(2) var sourceSampler: sampler;"
};
const std::string_view fragmentReturnAttributeAndType{
depthDestination ? "@builtin(frag_depth) f32" : "@location(0) vec4<f32>"
key.depthDestination ? "@builtin(frag_depth) f32" : "@location(0) vec4<f32>"
};
const std::unordered_map<std::string_view, std::string_view>
valueByPlaceholderNameForFragmentSnippet{
{ FRAGMENT_RETURN_ATTRIBUTE_AND_TYPE_PLACEHOLDER, fragmentReturnAttributeAndType },
};
std::string fragmentShaderSnippet;
if (multisampledSource) {
if (key.multisampledSource) {
fragmentShaderSnippet = webgpuutils::processPlaceholderTemplate(
FRAGMENT_SHADER_SNIPPET_MSAA_INPUT_TEMPLATE, PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX,
valueByPlaceholderNameForFragmentSnippet);
} else {
if (sourceDimension == wgpu::TextureViewDimension::e3D) {
if (key.sourceDimension == wgpu::TextureViewDimension::e3D) {
fragmentShaderSnippet = webgpuutils::processPlaceholderTemplate(
FRAGMENT_SHADER_SNIPPET_3D_INPUT_TEMPLATE, PLACEHOLDER_PREFIX,
PLACEHOLDER_SUFFIX, valueByPlaceholderNameForFragmentSnippet);
@@ -815,13 +787,8 @@ wgpu::ShaderModule WebGPUBlitter::createShaderModule(
return shaderModule;
}
size_t WebGPUBlitter::hashShaderModuleKey(const wgpu::TextureViewDimension sourceDimension,
const bool multisampledSource, const bool depthSource, const bool depthDestination) {
size_t seed{ std::hash<uint32_t>{}(static_cast<uint32_t>(sourceDimension)) };
utils::hash::combine(seed, multisampledSource);
utils::hash::combine(seed, depthSource);
utils::hash::combine(seed, depthDestination);
return seed;
bool WebGPUBlitter::ShaderModuleKey::operator==(const ShaderModuleKey& other) const {
return memcmp(this, &other, sizeof(ShaderModuleKey)) == 0;
}
} // namespace filament::backend
+53 -33
View File
@@ -76,55 +76,75 @@ private:
// The following methods are used to create and cache WebGPU objects.
// The pattern is to have a `getOrCreate...` method that looks up the object in a cache,
// and if it's not found, it calls a `create...` method to create it and then stores it in the
// cache. A `hash...` method is used to generate a key for the cache.
// cache.
[[nodiscard]] wgpu::RenderPipeline const& getOrCreateRenderPipeline(SamplerMagFilter,
wgpu::TextureViewDimension sourceDimension, uint32_t sourceSampleCount,
bool depthSource, wgpu::TextureFormat destinationTextureFormat);
struct RenderPipelineKey;
struct PipelineLayoutKey;
struct ShaderModuleKey;
[[nodiscard]] wgpu::RenderPipeline createRenderPipeline(SamplerMagFilter,
wgpu::TextureViewDimension sourceDimension, uint32_t sourceSampleCount,
bool depthSource, wgpu::TextureFormat destinationTextureFormat);
[[nodiscard]] wgpu::RenderPipeline const& getOrCreateRenderPipeline(RenderPipelineKey const&);
[[nodiscard]] wgpu::RenderPipeline createRenderPipeline(RenderPipelineKey const&);
[[nodiscard]] static size_t hashRenderPipelineKey(SamplerMagFilter,
wgpu::TextureViewDimension sourceDimension, uint32_t sourceSampleCount,
bool depthSource, wgpu::TextureFormat destinationTextureFormat);
[[nodiscard]] wgpu::PipelineLayout const& getOrCreatePipelineLayout(PipelineLayoutKey const&);
[[nodiscard]] wgpu::PipelineLayout createPipelineLayout(PipelineLayoutKey const&);
[[nodiscard]] wgpu::PipelineLayout const& getOrCreatePipelineLayout(SamplerMagFilter,
wgpu::TextureViewDimension, bool multisampledSource, bool depthSource);
[[nodiscard]] wgpu::BindGroupLayout const& getOrCreateTextureBindGroupLayout(
PipelineLayoutKey const&);
[[nodiscard]] wgpu::BindGroupLayout createTextureBindGroupLayout(PipelineLayoutKey const&);
[[nodiscard]] wgpu::PipelineLayout createPipelineLayout(SamplerMagFilter,
wgpu::TextureViewDimension sourceDimension, bool multisampledSource, bool depthSource);
[[nodiscard]] wgpu::ShaderModule const& getOrCreateShaderModule(ShaderModuleKey const&);
[[nodiscard]] wgpu::ShaderModule createShaderModule(ShaderModuleKey const&);
[[nodiscard]] static size_t hashPipelineLayoutKey(SamplerMagFilter,
wgpu::TextureViewDimension sourceDimension, bool multisampledSource, bool depthSource);
struct RenderPipelineKey {
wgpu::TextureViewDimension sourceDimension; // 4 bytes
wgpu::TextureFormat destinationTextureFormat; // 4
uint8_t sourceSampleCount; // 1
SamplerMagFilter filterType; // 1
bool depthSource; // 1
uint8_t padding = 0; // 1
[[nodiscard]] wgpu::BindGroupLayout const& getOrCreateTextureBindGroupLayout(SamplerMagFilter,
wgpu::TextureViewDimension sourceDimension, bool multisampledSource, bool depthSource);
bool operator==(const RenderPipelineKey& other) const;
using Hasher = utils::hash::MurmurHashFn<RenderPipelineKey>;
};
[[nodiscard]] wgpu::BindGroupLayout createTextureBindGroupLayout(SamplerMagFilter,
wgpu::TextureViewDimension sourceDimension, bool multisampledSource, bool depthSource);
struct PipelineLayoutKey {
wgpu::TextureViewDimension sourceDimension; // 4 bytes
SamplerMagFilter filterType; // 1
bool multisampledSource; // 1
bool depthSource; // 1
uint8_t padding = 0; // 1
[[nodiscard]] static size_t hashTextureBindGroupLayoutKey(SamplerMagFilter,
wgpu::TextureViewDimension sourceDimension, bool multisampledSource, bool depthSource);
bool operator==(const PipelineLayoutKey& other) const;
using Hasher = utils::hash::MurmurHashFn<PipelineLayoutKey>;
};
[[nodiscard]] wgpu::ShaderModule const& getOrCreateShaderModule(
wgpu::TextureViewDimension sourceDimension, bool multisampledSource, bool depthSource,
bool depthDestination);
struct ShaderModuleKey {
wgpu::TextureViewDimension sourceDimension; // 4 bytes
bool multisampledSource; // 1
bool depthSource; // 1
bool depthDestination; // 1
uint8_t padding = 0; // 1
[[nodiscard]] wgpu::ShaderModule createShaderModule(wgpu::TextureViewDimension sourceDimension,
bool multisampledSource, bool depthSource, bool depthDestination);
bool operator==(const ShaderModuleKey& other) const;
using Hasher = utils::hash::MurmurHashFn<ShaderModuleKey>;
};
[[nodiscard]] static size_t hashShaderModuleKey(wgpu::TextureViewDimension sourceDimension,
bool multisampledSource, bool depthSource, bool depthDestination);
static_assert(sizeof(RenderPipelineKey) == 12,
"RenderPipelineKey must not have implicit padding.");
static_assert(sizeof(PipelineLayoutKey) == 8,
"PipelineLayoutKey must not have implicit padding.");
static_assert(sizeof(ShaderModuleKey) == 8, "ShaderModuleKey must not have implicit padding.");
wgpu::Device mDevice;
wgpu::Sampler mNearestSampler{ nullptr };
wgpu::Sampler mLinearSampler{ nullptr };
tsl::robin_map<size_t, wgpu::RenderPipeline> mRenderPipelines{};
tsl::robin_map<size_t, wgpu::PipelineLayout> mPipelineLayouts{};
tsl::robin_map<size_t, wgpu::BindGroupLayout> mTextureBindGroupLayouts{};
tsl::robin_map<size_t, wgpu::ShaderModule> mShaderModules{};
tsl::robin_map<RenderPipelineKey, wgpu::RenderPipeline, RenderPipelineKey::Hasher>
mRenderPipelines{};
tsl::robin_map<PipelineLayoutKey, wgpu::PipelineLayout, PipelineLayoutKey::Hasher>
mPipelineLayouts{};
tsl::robin_map<PipelineLayoutKey, wgpu::BindGroupLayout, PipelineLayoutKey::Hasher>
mTextureBindGroupLayouts{};
tsl::robin_map<ShaderModuleKey, wgpu::ShaderModule, ShaderModuleKey::Hasher> mShaderModules{};
};
} // namespace filament::backend
+73 -275
View File
@@ -48,6 +48,7 @@
#include <math/mat3.h>
#include <utils/debug.h>
#include <utils/CString.h>
#include <utils/ImmutableCString.h>
#include <utils/Hash.h>
#include <utils/Panic.h>
#include <utils/compiler.h>
@@ -56,11 +57,9 @@
#include <algorithm>
#include <array>
#include <condition_variable>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <sstream>
#include <utility>
using namespace std::chrono_literals;
@@ -107,10 +106,10 @@ WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform,
: mPlatform{ platform },
mAdapter{ mPlatform.requestAdapter(nullptr) },
mDevice{ mPlatform.requestDevice(mAdapter) },
mQueue{ mDevice.GetQueue() },
mQueueManager{ mDevice },
mPipelineLayoutCache{ mDevice },
mPipelineCache{ mDevice },
mRenderPassMipmapGenerator{ mDevice },
mRenderPassMipmapGenerator{ mDevice, &mQueueManager },
mSpdComputePassMipmapGenerator{ mDevice },
mBlitter{ mDevice },
mHandleAllocator{ "Handles", driverConfig.handleArenaSize,
@@ -142,6 +141,7 @@ template class ConcreteDispatcher<WebGPUDriver>;
void WebGPUDriver::terminate() {
finish();
}
void WebGPUDriver::tick(int) {
@@ -182,62 +182,13 @@ void WebGPUDriver::endFrame(const uint32_t /* frameId */) {
// If a command encoder is in flight then the encoder is finished and submitted to the GPU queue.
// A new command encoder is created fresh after it.
// If a command encoder is not in flight (nullptr), then this is a noop.
void WebGPUDriver::flush(int /* dummy */) {
FWGPU_SYSTRACE_SCOPE();
if (mCommandEncoder == nullptr) {
return;
}
// Submit the commands recorded so far.
assert_invariant(mRenderPassEncoder == nullptr);
wgpu::CommandBufferDescriptor commandBufferDescriptor{
.label = "frame_command_after_flush",
};
mCommandBuffer = mCommandEncoder.Finish(&commandBufferDescriptor);
assert_invariant(mCommandBuffer);
mQueue.Submit(1, &mCommandBuffer);
mCommandBuffer = nullptr;
// create a new command buffer encoder to continue recording the next command for frame...
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = {
.label = "frame_command_after_flush"
};
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
assert_invariant(mCommandEncoder);
void WebGPUDriver::flush(int) {
mQueueManager.flush();
}
// Submits the currently recorded commands and waits for them to complete on the GPU.
// This is a synchronous operation and should be used sparingly.
void WebGPUDriver::finish(int /* dummy */) {
FWGPU_SYSTRACE_SCOPE();
mDevice.Tick();
mAdapter.GetInstance().ProcessEvents();
mReadPixelMapsCounter.waitForAllToFinish();
flush();
// Some applications/samples might call finish in the very beginning of an application before
// creating any resources/tasks to flush out the system on any pending work from previous runs.
// Currently, gltf_viewer sample on android exhibit this behavior.
// Bail out early to safeguard against those cases. Otherwise, OnSubmittedWorkDone will
// hang on work that was never submitted.
if (mCommandEncoder == nullptr) {
return;
}
// Wait for all previously submitted work to finish.
std::mutex syncPoint;
std::condition_variable syncCondition;
bool done = false;
mQueue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowSpontaneous,
[&syncPoint, &syncCondition, &done](wgpu::QueueWorkDoneStatus status, wgpu::StringView message) {
if (status != wgpu::QueueWorkDoneStatus::Success)
{
FWGPU_LOGW << "WebGPUDriver::finish: QueueWorkDoneStatus was not successful. " << message;
}
assert_invariant(status == wgpu::QueueWorkDoneStatus::Success);
std::unique_lock<std::mutex> lock(syncPoint);
done = true;
syncCondition.notify_one();
});
std::unique_lock<std::mutex> lock(syncPoint);
syncCondition.wait(lock, [&done] { return done; });
mQueueManager.finish();
}
void WebGPUDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
@@ -451,7 +402,7 @@ Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
// ------------------------------------------------------------------------------------------------
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
const uint64_t flags, utils::CString&& tag) {
const uint64_t flags, utils::ImmutableCString&& tag) {
FWGPU_SYSTRACE_SCOPE();
// TODO: support MSAA swapchain
@@ -481,7 +432,7 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
}
void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
uint32_t height, uint64_t flags, utils::CString&& tag) {
uint32_t height, uint64_t flags, utils::ImmutableCString&& tag) {
wgpu::Extent2D extent = { .width = width, .height = height };
mSwapChain = constructHandle<WebGPUSwapChain>(sch, extent, mAdapter,
mDevice, flags);
@@ -494,7 +445,7 @@ void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t wi
void WebGPUDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vertexBufferInfoHandle,
const uint8_t bufferCount, const uint8_t attributeCount, const AttributeArray attributes,
utils::CString&& tag) {
utils::ImmutableCString&& tag) {
FWGPU_SYSTRACE_SCOPE();
constructHandle<WebGPUVertexBufferInfo>(vertexBufferInfoHandle, bufferCount, attributeCount,
attributes, mDeviceLimits);
@@ -503,7 +454,7 @@ void WebGPUDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vertexBuff
void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vertexBufferHandle,
const uint32_t vertexCount, Handle<HwVertexBufferInfo> vertexBufferInfoHandle,
utils::CString&& tag) {
utils::ImmutableCString&& tag) {
FWGPU_SYSTRACE_SCOPE();
const auto vertexBufferInfo = handleCast<WebGPUVertexBufferInfo>(vertexBufferInfoHandle);
constructHandle<WebGPUVertexBuffer>(vertexBufferHandle, vertexCount,
@@ -513,7 +464,7 @@ void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vertexBufferHandle
void WebGPUDriver::createIndexBufferR(Handle<HwIndexBuffer> indexBufferHandle,
const ElementType elementType, const uint32_t indexCount, const BufferUsage usage,
utils::CString&& tag) {
utils::ImmutableCString&& tag) {
FWGPU_SYSTRACE_SCOPE();
const auto elementSize = static_cast<uint8_t>(getElementTypeSize(elementType));
constructHandle<WebGPUIndexBuffer>(indexBufferHandle, mDevice, elementSize, indexCount);
@@ -522,7 +473,7 @@ void WebGPUDriver::createIndexBufferR(Handle<HwIndexBuffer> indexBufferHandle,
void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> bufferObjectHandle,
const uint32_t byteCount, const BufferObjectBinding bindingType, const BufferUsage usage,
utils::CString&& tag) {
utils::ImmutableCString&& tag) {
FWGPU_SYSTRACE_SCOPE();
constructHandle<WebGPUBufferObject>(bufferObjectHandle, mDevice, bindingType, byteCount);
setDebugTag(bufferObjectHandle.getId(), std::move(tag));
@@ -531,7 +482,7 @@ void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> bufferObjectHandle
void WebGPUDriver::createTextureR(Handle<HwTexture> textureHandle, const SamplerType target,
const uint8_t levels, const TextureFormat format, const uint8_t samples,
const uint32_t width, const uint32_t height, const uint32_t depth,
const TextureUsage usage, utils::CString&& tag) {
const TextureUsage usage, utils::ImmutableCString&& tag) {
FWGPU_SYSTRACE_SCOPE();
constructHandle<WebGPUTexture>(textureHandle, target, levels, format, samples, width, height,
depth, usage, mDevice);
@@ -540,7 +491,7 @@ void WebGPUDriver::createTextureR(Handle<HwTexture> textureHandle, const Sampler
void WebGPUDriver::createTextureViewR(Handle<HwTexture> textureHandle,
Handle<HwTexture> sourceTextureHandle, const uint8_t baseLevel, const uint8_t levelCount,
utils::CString&& tag) {
utils::ImmutableCString&& tag) {
auto source = handleCast<WebGPUTexture>(sourceTextureHandle);
constructHandle<WebGPUTexture>(textureHandle, source, baseLevel, levelCount);
@@ -551,7 +502,7 @@ void WebGPUDriver::createTextureViewR(Handle<HwTexture> textureHandle,
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> textureHandle,
Handle<HwTexture> sourceTextureHandle, const backend::TextureSwizzle r,
const backend::TextureSwizzle g, const backend::TextureSwizzle b,
const backend::TextureSwizzle a, utils::CString&& tag) {
const backend::TextureSwizzle a, utils::ImmutableCString&& tag) {
if (!isTextureSwizzleSupported()) {
FWGPU_LOGW << "WebGPUDriver::createTextureViewSwizzleR called while texture swizzling is "
@@ -578,7 +529,7 @@ void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> textureHandle,
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> textureHandle,
const backend::SamplerType target, const backend::TextureFormat format,
const uint32_t width, const uint32_t height, const backend::TextureUsage usage,
Platform::ExternalImageHandleRef externalImage, utils::CString&& tag) {
Platform::ExternalImageHandleRef externalImage, utils::ImmutableCString&& tag) {
FWGPU_SYSTRACE_SCOPE();
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
}
@@ -586,27 +537,27 @@ void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> textureHandle,
void WebGPUDriver::createTextureExternalImageR(Handle<HwTexture> textureHandle,
const backend::SamplerType target, const backend::TextureFormat format,
const uint32_t width, const uint32_t height, const backend::TextureUsage usage,
void* externalImage, utils::CString&& tag) {
void* externalImage, utils::ImmutableCString&& tag) {
FWGPU_SYSTRACE_SCOPE();
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
}
void WebGPUDriver::createTextureExternalImagePlaneR(Handle<HwTexture> textureHandle,
const backend::TextureFormat format, const uint32_t width, const uint32_t height,
const backend::TextureUsage usage, void* image, const uint32_t plane, utils::CString&& tag) {
const backend::TextureUsage usage, void* image, const uint32_t plane, utils::ImmutableCString&& tag) {
PANIC_POSTCONDITION("External WebGPU Texture is not supported");
}
void WebGPUDriver::importTextureR(Handle<HwTexture> textureHandle, const intptr_t id,
const SamplerType target, const uint8_t levels, const TextureFormat format,
const uint8_t samples, const uint32_t width, const uint32_t height, const uint32_t depth,
const TextureUsage usage, utils::CString&& tag) {
const TextureUsage usage, utils::ImmutableCString&& tag) {
PANIC_POSTCONDITION("Import WebGPU Texture is not supported");
}
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> renderPrimitiveHandle,
Handle<HwVertexBuffer> vertexBufferHandle, Handle<HwIndexBuffer> indexBufferHandle,
const PrimitiveType primitiveType, utils::CString&& tag) {
const PrimitiveType primitiveType, utils::ImmutableCString&& tag) {
FWGPU_SYSTRACE_SCOPE();
assert_invariant(mDevice);
const auto renderPrimitive = constructHandle<WebGPURenderPrimitive>(renderPrimitiveHandle);
@@ -619,14 +570,14 @@ void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> renderPrimit
}
void WebGPUDriver::createProgramR(Handle<HwProgram> programHandle, Program&& program,
utils::CString&& tag) {
utils::ImmutableCString&& tag) {
FWGPU_SYSTRACE_SCOPE();
constructHandle<WebGPUProgram>(programHandle, mDevice, program);
setDebugTag(programHandle.getId(), std::move(tag));
}
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> renderTargetHandle,
utils::CString&& tag) {
utils::ImmutableCString&& tag) {
assert_invariant(!mDefaultRenderTarget);
mDefaultRenderTarget = constructHandle<WebGPURenderTarget>(renderTargetHandle);
assert_invariant(mDefaultRenderTarget);
@@ -639,7 +590,7 @@ void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> renderTarge
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> renderTargetHandle,
const TargetBufferFlags targetFlags, const uint32_t width, const uint32_t height,
const uint8_t samples, const uint8_t layerCount, const MRT color,
const TargetBufferInfo depth, const TargetBufferInfo stencil, utils::CString&& tag) {
const TargetBufferInfo depth, const TargetBufferInfo stencil, utils::ImmutableCString&& tag) {
FWGPU_SYSTRACE_SCOPE();
constructHandle<WebGPURenderTarget>(
renderTargetHandle, width, height, samples, layerCount, color, depth, stencil,
@@ -651,32 +602,31 @@ void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> renderTargetHandle
setDebugTag(renderTargetHandle.getId(), std::move(tag));
}
void WebGPUDriver::createFenceR(Handle<HwFence> fenceHandle, utils::CString&& tag) {
void WebGPUDriver::createFenceR(Handle<HwFence> fenceHandle, utils::ImmutableCString&& tag) {
// The handle is constructed synchronously in createFenceS.
const auto fence = handleCast<WebGPUFence>(fenceHandle);
assert_invariant(mQueue);
fence->addMarkerToQueueState(mQueue);
fence->setSubmissionState(mQueueManager.getLatestSubmissionState());
setDebugTag(fenceHandle.getId(), std::move(tag));
}
void WebGPUDriver::createSyncR(Handle<HwSync> syncHandle, utils::CString&& tag) {
void WebGPUDriver::createSyncR(Handle<HwSync> syncHandle, utils::ImmutableCString&& tag) {
// TODO: Ensure sync is active, and then invoke and clear all pending
// callbacks.
setDebugTag(syncHandle.getId(), std::move(tag));
}
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::CString&& tag) {}
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::ImmutableCString&& tag) {}
void WebGPUDriver::createDescriptorSetLayoutR(
Handle<HwDescriptorSetLayout> descriptorSetLayoutHandle,
backend::DescriptorSetLayout&& info, utils::CString&& tag) {
backend::DescriptorSetLayout&& info, utils::ImmutableCString&& tag) {
FWGPU_SYSTRACE_SCOPE();
constructHandle<WebGPUDescriptorSetLayout>(descriptorSetLayoutHandle, std::move(info), mDevice);
setDebugTag(descriptorSetLayoutHandle.getId(), std::move(tag));
}
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> descriptorSetHandle,
Handle<HwDescriptorSetLayout> descriptorSetLayoutHandle, utils::CString&& tag) {
Handle<HwDescriptorSetLayout> descriptorSetLayoutHandle, utils::ImmutableCString&& tag) {
FWGPU_SYSTRACE_SCOPE();
auto layout = handleCast<WebGPUDescriptorSetLayout>(descriptorSetLayoutHandle);
constructHandle<WebGPUDescriptorSet>(descriptorSetHandle, layout->getLayout(),
@@ -684,13 +634,13 @@ void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> descriptorSetHan
setDebugTag(descriptorSetHandle.getId(), std::move(tag));
}
Handle<HwStream> WebGPUDriver::createStreamNative(void* nativeStream, utils::CString tag) {
Handle<HwStream> WebGPUDriver::createStreamNative(void* nativeStream, utils::ImmutableCString tag) {
return {
//todo
};
}
Handle<HwStream> WebGPUDriver::createStreamAcquired(utils::CString tag) {
Handle<HwStream> WebGPUDriver::createStreamAcquired(utils::ImmutableCString tag) {
return {
//todo
};
@@ -898,7 +848,7 @@ void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> indexBufferHandle,
// draw calls are made.
flush();
handleCast<WebGPUIndexBuffer>(indexBufferHandle)
->updateGPUBuffer(bufferDescriptor, byteOffset, mQueue);
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice.GetQueue());
scheduleDestroy(std::move(bufferDescriptor));
}
@@ -909,18 +859,14 @@ void WebGPUDriver::updateBufferObject(Handle<HwBufferObject> bufferObjectHandle,
// draw calls are made.
flush();
handleCast<WebGPUBufferObject>(bufferObjectHandle)
->updateGPUBuffer(bufferDescriptor, byteOffset, mQueue);
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice.GetQueue());
scheduleDestroy(std::move(bufferDescriptor));
}
void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> bufferObjectHandle,
BufferDescriptor&& bufferDescriptor, const uint32_t byteOffset) {
// make sure command elements (draws, etc.) prior to the buffer update are processed before the
// update on the GPU, otherwise the expected data may not be available at the time certain
// draw calls are made.
flush();
handleCast<WebGPUBufferObject>(bufferObjectHandle)
->updateGPUBuffer(bufferDescriptor, byteOffset, mQueue);
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice.GetQueue());
scheduleDestroy(std::move(bufferDescriptor));
}
@@ -1042,23 +988,14 @@ void WebGPUDriver::update3DImage(Handle<HwTexture> textureHandle, const uint32_t
const wgpu::Texture stagingTexture{ mDevice.CreateTexture(&stagingTextureDescriptor) };
FILAMENT_CHECK_POSTCONDITION(stagingTexture)
<< "Failed to create staging input texture for blit?";
const auto copyInfo{ wgpu::TexelCopyTextureInfo{
const wgpu::TexelCopyTextureInfo copyInfo{
.texture = stagingTexture,
.mipLevel = 0,
.origin = { .x = 0, .y = 0, .z = 0 },
.aspect = texture->getAspect(),
} };
mQueue.WriteTexture(&copyInfo, dataBuff, dataSize, &layout, &extent);
bool reusedCommandEncoder{ true };
if (!mCommandEncoder) {
reusedCommandEncoder = false;
const wgpu::CommandEncoderDescriptor commandEncoderDescriptor{
.label = "blit_command",
};
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
FILAMENT_CHECK_POSTCONDITION(mCommandEncoder)
<< "Failed to create command encoder for blit?";
}
};
mDevice.GetQueue().WriteTexture(&copyInfo, dataBuff, dataSize, &layout, &extent);
auto commandEncoder = mQueueManager.getCommandEncoder();
WebGPUBlitter::BlitArgs blitArgs{
.source = {
.texture = stagingTexture,
@@ -1077,27 +1014,7 @@ void WebGPUDriver::update3DImage(Handle<HwTexture> textureHandle, const uint32_t
for (uint32_t layerIndex{ 0 }; layerIndex < depth; ++layerIndex) {
blitArgs.source.layerOrDepth = layerIndex;
blitArgs.destination.layerOrDepth = layerIndex + zoffset;
mBlitter.blit(mQueue, mCommandEncoder, blitArgs);
}
if (!reusedCommandEncoder) {
// TODO consider removing encoder finish + submit once we have test coverage and can
// safely verify doing so will not result in strange errors. We don't think this
// submit is necessary, as the encoder should get finished and the command
// submitted in commit(), finish(), and/or flush() in this frame
// or the next anyway. And, the blit bits will be encoded before other things.
// Thus, not submitting here _seems_ ok. Nonetheless, doing the submit here likely
// represents unnecessary inefficiency and higher bandwidth
// between the CPU and GPU, but we are worried about functional correctness at this
// stage.
const wgpu::CommandBufferDescriptor commandBufferDescriptor{
.label = "blit_command_buffer",
};
const wgpu::CommandBuffer blitCommand{ mCommandEncoder.Finish(
&commandBufferDescriptor) };
FILAMENT_CHECK_POSTCONDITION(blitCommand)
<< "Failed to create command buffer for blit?";
mQueue.Submit(1, &blitCommand);
mCommandEncoder = nullptr;
mBlitter.blit(mDevice.GetQueue(), commandEncoder, blitArgs);
}
} else {
// Direct copy without a blit.
@@ -1107,7 +1024,7 @@ void WebGPUDriver::update3DImage(Handle<HwTexture> textureHandle, const uint32_t
.origin = { .x = xoffset, .y = yoffset, .z = zoffset, },
.aspect = texture->getAspect(),
}};
mQueue.WriteTexture(&copyInfo, dataBuff, dataSize, &layout, &extent);
mDevice.GetQueue().WriteTexture(&copyInfo, dataBuff, dataSize, &layout, &extent);
}
scheduleDestroy(std::move(pixelBufferDescriptor));
}
@@ -1154,7 +1071,7 @@ void WebGPUDriver::generateMipmaps(Handle<HwTexture> textureHandle) {
FILAMENT_CHECK_PRECONDITION(usage & wgpu::TextureUsage::RenderAttachment)
<< "Texture for mipmap generation via render pass blit must have "
"RenderAttachment usage.";
mRenderPassMipmapGenerator.generateMipmaps(mQueue, texture->getTexture());
mRenderPassMipmapGenerator.generateMipmaps(texture->getTexture());
break;
case WebGPUTexture::MipmapGenerationStrategy::NONE:
FWGPU_LOGW << "Trying to generate mipmaps for a texture we already know we cannot do "
@@ -1172,11 +1089,8 @@ void WebGPUDriver::generateMipmaps(Handle<HwTexture> textureHandle) {
<< webGPUTextureFormatToString(wgpuTexture.GetFormat()) << " is "
<< WebGPUTexture::supportsMultipleMipLevelsViaStorageBinding(
wgpuTexture.GetFormat());
// We will record all passes into a single command encoder.
wgpu::CommandEncoderDescriptor encoderDesc = {};
encoderDesc.label = "Mipmap Command Encoder";
wgpu::CommandEncoder encoder = mDevice.CreateCommandEncoder(&encoderDesc);
wgpu::CommandEncoder encoder = mQueueManager.getCommandEncoder();
spd::SPDPassConfig spdConfig = { .filter = spd::SPDFilter::Average,
.targetTexture = wgpuTexture,
.numMips = totalMipLevels,
@@ -1184,23 +1098,6 @@ void WebGPUDriver::generateMipmaps(Handle<HwTexture> textureHandle) {
.sourceMipLevel = 0 };
mSpdComputePassMipmapGenerator.Generate(encoder, wgpuTexture, spdConfig);
// Finish the encoder and submit all the passes at once.
// TODO consider reusing the existing command encoder or creating it in this function
// if it doesn't already exist and removing encoder finish + submit here once
// we have test coverage and can safely verify doing so will not result in
// strange errors. We don't think this submit is necessary, as the encoder
// should get finished and the command submitted in
// commit(), finish(), and/or flush() in this frame or the next anyway.
// And, the mipmap generation bits will be encoded before other things.
// Thus, not submitting here _seems_ ok. Nonetheless, doing the submit here likely
// represents unnecessary inefficiency and higher bandwidth
// between the CPU and GPU, but we are worried about functional correctness at this
// stage.
wgpu::CommandBufferDescriptor cmdBufferDesc = {};
cmdBufferDesc.label = "Mipmap Command Buffer";
wgpu::CommandBuffer commandBuffer = encoder.Finish(&cmdBufferDesc);
mQueue.Submit(1, &commandBuffer);
break;
}
}
@@ -1218,12 +1115,8 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
// resolve targets if necessary.
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
RenderPassParams const& params) {
FWGPU_SYSTRACE_SCOPE();
if (!mCommandEncoder) {
wgpu::CommandEncoderDescriptor commandEncoderDescriptor{ .label = "frame_command_encoder" };
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
}
assert_invariant(mCommandEncoder);
auto commandEncoder = mQueueManager.getCommandEncoder();
assert_invariant(commandEncoder);
auto renderTarget = handleCast<WebGPURenderTarget>(renderTargetHandle);
wgpu::TextureView defaultColorView = nullptr;
@@ -1356,7 +1249,7 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
customDepthStencilView,
customDepthStencilMsaaSidecarTextureView);
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
mRenderPassEncoder = commandEncoder.BeginRenderPass(&renderPassDescriptor);
// Ensure viewport dimensions are not 0
FILAMENT_CHECK_POSTCONDITION(params.viewport.width > 0) << "viewport width is 0?";
@@ -1395,55 +1288,11 @@ void WebGPUDriver::makeCurrent(Handle<HwSwapChain> drawSwapChain,
} else {
mTextureView = mSwapChain->getNextTextureView(mPlatform.getSurfaceExtent(mNativeWindow));
}
assert_invariant(mTextureView);
if (!mCommandEncoder) {
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = {
.label = "frame_command_encoder"
};
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
}
assert_invariant(mCommandEncoder);
}
void WebGPUDriver::commit(Handle<HwSwapChain> swapChainHandle) {
FWGPU_SYSTRACE_SCOPE();
if (UTILS_VERY_UNLIKELY(!mCommandEncoder)) {
// nothing to submit
if (UTILS_LIKELY(mSwapChain)) {
mSwapChain->present(*this);
}
return;
}
wgpu::CommandBufferDescriptor commandBufferDescriptor{
.label = "frame_command",
};
mCommandBuffer = mCommandEncoder.Finish(&commandBufferDescriptor);
assert_invariant(mCommandBuffer);
mCommandEncoder = nullptr;
mQueue.Submit(1, &mCommandBuffer);
mCommandBuffer = nullptr;
static bool firstRender = true;
// For the first frame rendered, we need to make sure the work is done before presenting or we
// get a purple flash
if (UTILS_UNLIKELY(firstRender)) {
auto f = mQueue.OnSubmittedWorkDone(wgpu::CallbackMode::WaitAnyOnly,
[](wgpu::QueueWorkDoneStatus status, wgpu::StringView message) {
// This lambda does nothing, but the signature must match.
if (status != wgpu::QueueWorkDoneStatus::Success){
FWGPU_LOGE << "Waiting for first frame work to finish resulted in an error";
}
});
const wgpu::Instance instance = mAdapter.GetInstance();
auto wStatus = instance.WaitAny(f,
std::chrono::duration_cast<std::chrono::nanoseconds>(1s).count());
if (wStatus != wgpu::WaitStatus::Success) {
FWGPU_LOGW << "Waiting for first frame work to finish resulted in an error"
<< static_cast<uint32_t>(wStatus);
}
firstRender = false;
}
void WebGPUDriver::commit(Handle<HwSwapChain> sch) {
mQueueManager.flush();
mTextureView = nullptr;
assert_invariant(mSwapChain);
mSwapChain->present(*this);
@@ -1484,6 +1333,7 @@ void WebGPUDriver::stopCapture(int /* dummy */) {
void WebGPUDriver::readPixels(Handle<HwRenderTarget> sourceRenderTargetHandle, const uint32_t x,
const uint32_t y, const uint32_t width, const uint32_t height,
PixelBufferDescriptor&& pixelBufferDescriptor) {
flush();
const auto srcTarget{ handleCast<WebGPURenderTarget>(sourceRenderTargetHandle) };
assert_invariant(srcTarget);
@@ -1523,14 +1373,12 @@ void WebGPUDriver::readPixels(Handle<HwRenderTarget> sourceRenderTargetHandle, c
return;
}
if (!mCommandEncoder) {
const wgpu::CommandEncoderDescriptor commandEncoderDescriptor{
.label = "read_pixels_command",
};
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
FILAMENT_CHECK_POSTCONDITION(mCommandEncoder)
<< "Failed to create command encoder for readPixels?";
}
const wgpu::CommandEncoderDescriptor commandEncoderDescriptor{
.label = "read_pixels_command",
};
auto commandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
FILAMENT_CHECK_POSTCONDITION(commandEncoder)
<< "Failed to create command encoder for readPixels?";
const wgpu::TextureFormat srcFormat{srcTexture.GetFormat()};
const wgpu::TextureFormat dstFormat{toWebGPUFormat(pixelBufferDescriptor.format,
@@ -1578,7 +1426,7 @@ void WebGPUDriver::readPixels(Handle<HwRenderTarget> sourceRenderTargetHandle, c
},
.filter = SamplerMagFilter::NEAREST, // Use NEAREST for a 1:1 copy
};
mBlitter.blit(mQueue, mCommandEncoder, blitArgs);
mBlitter.blit(mDevice.GetQueue(), commandEncoder, blitArgs);
// The subsequent read will be from the top-left of the new intermediate texture.
readX = 0;
@@ -1624,12 +1472,10 @@ void WebGPUDriver::readPixels(Handle<HwRenderTarget> sourceRenderTargetHandle, c
.height = actualHeight,
.depthOrArrayLayers = 1,
};
mCommandEncoder.CopyTextureToBuffer(&source, &destination, &copySize);
mCommandBuffer = mCommandEncoder.Finish();
assert_invariant(mCommandBuffer);
mCommandEncoder = nullptr;
mQueue.Submit(1, &mCommandBuffer);
mCommandBuffer = nullptr;
commandEncoder.CopyTextureToBuffer(&source, &destination, &copySize);
wgpu::CommandBuffer commandBuffer = commandEncoder.Finish();
assert_invariant(commandBuffer);
mDevice.GetQueue().Submit(1, &commandBuffer);
// Map the buffer to read the data
struct UserData final {
@@ -1651,7 +1497,7 @@ void WebGPUDriver::readPixels(Handle<HwRenderTarget> sourceRenderTargetHandle, c
mReadPixelMapsCounter.startTask();
userData->buffer.MapAsync(
wgpu::MapMode::Read, 0, bufferSize, wgpu::CallbackMode::AllowSpontaneous,
wgpu::MapMode::Read, 0, bufferSize, wgpu::CallbackMode::AllowProcessEvents,
[](wgpu::MapAsyncStatus status, const char* message, UserData* userdata) {
std::unique_ptr<UserData> data(static_cast<UserData*>(userdata));
if (UTILS_LIKELY(status == wgpu::MapAsyncStatus::Success)) {
@@ -1735,15 +1581,8 @@ void WebGPUDriver::blitDEPRECATED(TargetBufferFlags buffers,
const wgpu::Extent2D destinationSize{ static_cast<uint32_t>(destinationViewport.width),
static_cast<uint32_t>(destinationViewport.height) };
bool reusedCommandEncoder{ true };
if (mCommandEncoder) {
flush();
} else {
reusedCommandEncoder = false;
const wgpu::CommandEncoderDescriptor desc{ .label = "blit_deprecated_command" };
mCommandEncoder = mDevice.CreateCommandEncoder(&desc);
}
mQueueManager.flush();
wgpu::CommandEncoder commandEncoder = mQueueManager.getCommandEncoder();
const WebGPUBlitter::BlitArgs blitArgs{
.source = { .texture = sourceTexture->getTexture(),
.aspect = sourceTexture->getAspect(),
@@ -1761,26 +1600,16 @@ void WebGPUDriver::blitDEPRECATED(TargetBufferFlags buffers,
},
.filter = filter,
};
mBlitter.blit(mQueue, mCommandEncoder, blitArgs);
mBlitter.blit(mDevice.GetQueue(), commandEncoder, blitArgs);
if (!reusedCommandEncoder) {
const wgpu::CommandBufferDescriptor desc{ .label = "blit_deprecated_command_buffer" };
const wgpu::CommandBuffer blitCommand{ mCommandEncoder.Finish(&desc) };
mQueue.Submit(1, &blitCommand);
mCommandEncoder = nullptr;
}
mQueueManager.flush();
}
void WebGPUDriver::resolve(Handle<HwTexture> destinationTextureHandle, const uint8_t sourceLevel,
const uint8_t sourceLayer, Handle<HwTexture> sourceTextureHandle,
const uint8_t destinationLevel, const uint8_t destinationLayer) {
FWGPU_SYSTRACE_SCOPE();
if (!mCommandEncoder) {
const wgpu::CommandEncoderDescriptor encoderDescriptor{
.label = "command_created_with_resolve",
};
mCommandEncoder = mDevice.CreateCommandEncoder(&encoderDescriptor);
}
auto commandEncoder = mQueueManager.getCommandEncoder();
FILAMENT_CHECK_PRECONDITION(mRenderPassEncoder == nullptr)
<< "Resolve cannot be called during an existing render pass";
const auto sourceTexture{ handleCast<WebGPUTexture>(sourceTextureHandle) };
@@ -1788,7 +1617,7 @@ void WebGPUDriver::resolve(Handle<HwTexture> destinationTextureHandle, const uin
assert_invariant(sourceTexture);
assert_invariant(destinationTexture);
const WebGPUMsaaTextureResolver::ResolveRequest request{
.commandEncoder = mCommandEncoder,
.commandEncoder = commandEncoder,
.viewFormat = sourceTexture->getViewFormat(),
.source = {
.texture = sourceTexture->getTexture(),
@@ -1813,21 +1642,8 @@ void WebGPUDriver::blit(Handle<HwTexture> destinationTextureHandle, const uint8_
Handle<HwTexture> sourceTextureHandle, const uint8_t destinationLevel,
const uint8_t destinationLayer, const math::uint2 sourceOrigin, const math::uint2 size) {
FWGPU_SYSTRACE_SCOPE();
bool reusedCommandEncoder{ true };
if (mCommandEncoder) {
// make sure command elements (draws, etc.) prior to this blit are processed before the blit
// on the GPU
flush();
} else {
// a command encoder is not in flight, so create one for this blit itself
reusedCommandEncoder = false;
const wgpu::CommandEncoderDescriptor commandEncoderDescriptor{
.label = "blit_command",
};
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
FILAMENT_CHECK_POSTCONDITION(mCommandEncoder)
<< "Failed to create command encoder for blit?";
}
mQueueManager.flush();
wgpu::CommandEncoder commandEncoder = mQueueManager.getCommandEncoder();
const auto sourceTexture{ handleCast<WebGPUTexture>(sourceTextureHandle) };
const auto destinationTexture{ handleCast<WebGPUTexture>(destinationTextureHandle) };
const WebGPUBlitter::BlitArgs blitArgs{
@@ -1849,25 +1665,8 @@ void WebGPUDriver::blit(Handle<HwTexture> destinationTextureHandle, const uint8_
},
.filter = SamplerMagFilter::NEAREST,
};
mBlitter.blit(mQueue, mCommandEncoder, blitArgs);
if (!reusedCommandEncoder) {
// TODO consider removing encoder finish + submit once we have test coverage and can
// safely verify doing so will not result in strange errors. We don't think this
// submit is necessary, as the encoder should get finished and the command submitted in
// commit(), finish(), and/or flush() in this frame or the next anyway.
// And, the blit bits will be encoded before other things.
// Thus, not submitting here _seems_ ok. Nonetheless, doing the submit here likely
// represents unnecessary inefficiency and higher bandwidth
// between the CPU and GPU, but we are worried about functional correctness at this
// stage.
const wgpu::CommandBufferDescriptor commandBufferDescriptor{
.label = "blit_command_buffer",
};
const wgpu::CommandBuffer blitCommand{ mCommandEncoder.Finish(&commandBufferDescriptor) };
FILAMENT_CHECK_POSTCONDITION(blitCommand) << "Failed to create command buffer for blit?";
mQueue.Submit(1, &blitCommand);
mCommandEncoder = nullptr;
}
mBlitter.blit(mDevice.GetQueue(), commandEncoder, blitArgs);
mQueueManager.flush();
}
void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
@@ -2100,7 +1899,7 @@ void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> descriptorSetHandle
.offsets = std::move(offsets) };
}
void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString&& tag) {
void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::ImmutableCString&& tag) {
//todo
}
@@ -2223,7 +2022,7 @@ MemoryMappedBufferHandle WebGPUDriver::mapBufferS() noexcept {
void WebGPUDriver::mapBufferR(MemoryMappedBufferHandle mmbh,
BufferObjectHandle boh, size_t offset,
size_t size, MapBufferAccessFlags access, utils::CString&& tag) {
size_t size, MapBufferAccessFlags access, utils::ImmutableCString&& tag) {
// TODO: MetalDriver::mapBufferR
}
@@ -2236,5 +2035,4 @@ void WebGPUDriver::copyToMemoryMappedBuffer(MemoryMappedBufferHandle mmbh, size_
// TODO: MetalDriver::copyToMemoryMappedBuffer
}
} // namespace filament::backend
+4 -4
View File
@@ -24,6 +24,7 @@
#include "webgpu/WebGPUPipelineCache.h"
#include "webgpu/WebGPUPipelineLayoutCache.h"
#include "webgpu/WebGPURenderPassMipmapGenerator.h"
#include "webgpu/WebGPUQueueManager.h"
#include "webgpu/utils/AsyncTaskCounter.h"
#include <backend/platforms/WebGPUPlatform.h>
@@ -49,6 +50,7 @@
namespace filament::backend {
class WebGPUSwapChain;
class WebGPUQueueManager;
/**
* Implements the private backend driver API for WebGPU specifically (that API is essentially
@@ -70,7 +72,7 @@ private:
ShaderLanguage preferredLanguage) const noexcept final;
[[nodiscard]] wgpu::Sampler makeSampler(SamplerParams const& params);
[[nodiscard]] static wgpu::AddressMode fWrapModeToWAddressMode(const filament::backend::SamplerWrapMode& fUsage);
void setDebugTag(HandleBase::HandleId handleId, utils::CString&& tag);
void setDebugTag(HandleBase::HandleId handleId, utils::ImmutableCString&& tag);
// The platform (e.g. OS) specific aspects of the WebGPU backend are strictly only
// handled in the WebGPUPlatform.
@@ -78,14 +80,12 @@ private:
wgpu::Adapter mAdapter = nullptr;
wgpu::Device mDevice = nullptr;
wgpu::Limits mDeviceLimits = {};
wgpu::Queue mQueue = nullptr;
WebGPUQueueManager mQueueManager;
void* mNativeWindow = nullptr;
WebGPUSwapChain* mSwapChain = nullptr;
uint64_t mNextFakeHandle = 1;
wgpu::CommandEncoder mCommandEncoder = nullptr;
wgpu::TextureView mTextureView = nullptr;
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
wgpu::CommandBuffer mCommandBuffer = nullptr;
WebGPURenderTarget* mDefaultRenderTarget = nullptr;
WebGPURenderTarget* mCurrentRenderTarget = nullptr;
WebGPUPipelineLayoutCache mPipelineLayoutCache;
+29 -54
View File
@@ -15,76 +15,51 @@
*/
#include "WebGPUFence.h"
#include "WebGPUConstants.h"
#include <backend/DriverEnums.h>
#include <memory>
#include <webgpu/webgpu_cpp.h>
#include <condition_variable>
#include <chrono>
#include <cstdint>
#include <mutex>
namespace filament::backend {
WebGPUFence::~WebGPUFence() noexcept {
// if the fence was being waited on, make sure to return with an error
std::lock_guard const lock(state->lock);
state->status = FenceStatus::ERROR;
state->cond.notify_all();
WebGPUFence::WebGPUFence() = default;
WebGPUFence::~WebGPUFence() = default;
void WebGPUFence::setSubmissionState(std::shared_ptr<WebGPUSubmissionState> state) {
std::lock_guard<std::mutex> const lock(mLock);
mState = state;
mCond.notify_one();
}
FenceStatus WebGPUFence::getStatus() const {
std::lock_guard const lock(state->lock);
return state->status;
FenceStatus WebGPUFence::getStatus() {
return wait(0);
}
FenceStatus WebGPUFence::wait(uint64_t const timeout) {
FenceStatus WebGPUFence::wait(uint64_t timeout) {
// we have to take into account that the STL's wait_for() actually works with
// time_points relative to steady_clock::now() internally.
using namespace std::chrono;
auto const now = steady_clock::now();
auto now = steady_clock::now();
steady_clock::time_point until = steady_clock::time_point::max();
if (now <= steady_clock::time_point::max() - nanoseconds(timeout)) {
until = now + nanoseconds(timeout);
}
std::unique_lock lock(state->lock);
state->cond.wait_until(lock, until,
[state = state] { return state->status != FenceStatus::TIMEOUT_EXPIRED; });
return state->status;
}
void WebGPUFence::addMarkerToQueueState(wgpu::Queue const& queue) {
// The lambda function is called when the work is done. It updates the fence status based on the
// result of the work.
// Because the callback holds a weak_ptr to the state of the HwFence object, it can be
// destroyed before the fence signals.
std::weak_ptr const weak = state;
queue.OnSubmittedWorkDone(
wgpu::CallbackMode::AllowSpontaneous,
[weak](const wgpu::QueueWorkDoneStatus status, wgpu::StringView message) {
if (auto const state = weak.lock()) {
std::unique_lock const lock(state->lock);
if (state->status != FenceStatus::ERROR) {
switch (status) {
case wgpu::QueueWorkDoneStatus::Success:
state->status = FenceStatus::CONDITION_SATISFIED;
state->cond.notify_all();
break;
case wgpu::QueueWorkDoneStatus::CallbackCancelled:
case wgpu::QueueWorkDoneStatus::Error:
state->status = FenceStatus::ERROR;
state->cond.notify_all();
FWGPU_LOGW << "WebGPUFence: wgpu::QueueWorkDoneStatus::Error. " <<
message;
break;
}
}
}
});
{
std::unique_lock<std::mutex> lock(mLock);
bool const success = mCond.wait_until(lock, until, [this] {
return bool(mState);
});
if (!success) {
return FenceStatus::TIMEOUT_EXPIRED;
}
}
auto duration_ns = static_cast<uint64_t>(
std::chrono::duration_cast<std::chrono::nanoseconds>(steady_clock::now() - now)
.count());
// In the unlikely event that duration_ns is too close to timeout, we just assume the leftover
// timeout is 0.
if (timeout == 0 || duration_ns > timeout) {
duration_ns = timeout;
}
return mState->waitForCompletion(timeout - duration_ns);
}
} // namespace filament::backend
+14 -25
View File
@@ -17,43 +17,32 @@
#ifndef TNT_FILAMENT_BACKEND_WEBGPUFENCE_H
#define TNT_FILAMENT_BACKEND_WEBGPUFENCE_H
#include "WebGPUQueueManager.h"
#include "DriverBase.h"
#include <backend/DriverEnums.h>
#include <utils/Mutex.h> // NOLINT(*-include-cleaner)
#include <utils/Condition.h> // NOLINT(*-include-cleaner)
#include <cstdint>
#include <memory>
namespace wgpu {
class Queue;
} // namespace wgpu
namespace filament::backend {
/**
* A WebGPU-specific implementation of the HwFence.
* This class is used to synchronize the CPU and GPU. It allows the CPU to know when the GPU has
* finished a set of commands.
*/
class WebGPUFence final : public HwFence {
class WebGPUFence : public HwFence {
public:
~WebGPUFence() noexcept;
[[nodiscard]] FenceStatus getStatus() const;
[[nodiscard]] FenceStatus wait(uint64_t timeout);
WebGPUFence();
~WebGPUFence();
void addMarkerToQueueState(wgpu::Queue const&);
// Initialize the fence with a submission state from the queue manager.
void setSubmissionState(std::shared_ptr<WebGPUSubmissionState> state);
// Note that getStatus cannot be const since it's implemented with wait();
FenceStatus getStatus();
FenceStatus wait(uint64_t timeout);
private:
struct State {
utils::Mutex lock; // NOLINT(*-include-cleaner)
utils::Condition cond; // NOLINT(*-include-cleaner)
FenceStatus status{ FenceStatus::TIMEOUT_EXPIRED };
};
// we need a shared_ptr because the Fence could be destroyed while we're waiting
std::shared_ptr<State> state{ std::make_shared<State>() };
std::mutex mLock;
std::condition_variable mCond;
std::shared_ptr<WebGPUSubmissionState> mState;
};
} // namespace filament::backend
+5 -31
View File
@@ -57,8 +57,7 @@ namespace {
*/
[[nodiscard]] std::string replaceSpecConstants(std::string_view shaderLabel,
std::string_view shaderSource,
std::unordered_map<uint32_t, std::variant<int32_t, float, bool>> const&
specConstants) {
utils::FixedCapacityVector<Program::SpecializationConstant> const& specConstants) {
// this function is not expected to be called at all if no spec constants are to be replaced
assert_invariant(!specConstants.empty());
static constexpr std::string_view specConstantPrefix = "FILAMENT_SPEC_CONST_";
@@ -120,16 +119,7 @@ namespace {
constantId = static_cast<int>(tempConstantId);
}
}
const auto newValueItr = specConstants.find(static_cast<uint32_t>(constantId));
if (newValueItr == specConstants.end()) {
// The constant is not being overridden, so keep the default value.
processedShaderSource << std::string_view(sourceData + pos,
posEndOfStatement + 1 - pos);
pos = posEndOfStatement + 1;
continue;
}
// need to override the constant...
const std::variant<int32_t, float, bool> newValue = newValueItr->second;
const std::variant<int32_t, float, bool> newValue = specConstants[constantId];
// stream up to the equal sign...
processedShaderSource << std::string_view(sourceData + pos, posOfEqual + 1 - pos);
// stream the new value...
@@ -156,15 +146,13 @@ namespace {
* @param program The "program" to compile/create the shader, which includes the shader source
* @param stage The stage (e.g. vertex, fragment, etc.) to create the shader module
* @param specConstants Override constants to apply when creating/compiling the shader module.
* The expectation is that this is consistent with the program's spec constants, just in a map
* format for quick access
* @return the proper WebGPU shader module compiled/created from the input parameters. This might
* wrap a null handle if the shader is not present (if the shader source is empty), such as
* a missing fragment or compute shader.
*/
[[nodiscard]] wgpu::ShaderModule createShaderModule(wgpu::Device const& device,
Program const& program, const ShaderStage stage,
std::unordered_map<uint32_t, std::variant<int32_t, float, bool>> const& specConstants) {
utils::FixedCapacityVector<Program::SpecializationConstant> const& specConstants) {
const char* const programName = program.getName().c_str_safe();
std::array<utils::FixedCapacityVector<uint8_t>, Program::SHADER_TYPE_COUNT> const&
shaderSource = program.getShadersSource();
@@ -260,26 +248,12 @@ namespace {
return shaderModule;
}
/**
* Convenience function to convert the array structure of constants to a map indexed by constant
* id.
* @param specConstants Original spec constant structure (immutable)
* @param outConstantById Output map of spec constants indexed by constant id
*/
void toMap(utils::FixedCapacityVector<Program::SpecializationConstant> const& specConstants,
std::unordered_map<uint32_t, std::variant<int32_t, float, bool>>& outConstantById) {
outConstantById.reserve(specConstants.size());
for (auto const& specConstant: specConstants) {
outConstantById.emplace(specConstant.id, specConstant.value);
}
}
}// namespace
WebGPUProgram::WebGPUProgram(wgpu::Device const& device, Program const& program)
: HwProgram{ program.getName() } {
std::unordered_map<uint32_t, std::variant<int32_t, float, bool>> specConstants;
toMap(program.getSpecializationConstants(), specConstants);
utils::FixedCapacityVector<Program::SpecializationConstant> const& specConstants =
program.getSpecializationConstants();
// TODO: Consider creating/compiling these shaders in parallel.
vertexShaderModule = createShaderModule(device, program, ShaderStage::VERTEX, specConstants);
fragmentShaderModule =
@@ -0,0 +1,123 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "WebGPUQueueManager.h"
#include <utils/Panic.h>
#include <chrono>
#include <cstdint>
#include <thread>
namespace filament::backend {
FenceStatus WebGPUSubmissionState::waitForCompletion(uint64_t timeout) {
using namespace std::chrono;
auto now = steady_clock::now();
steady_clock::time_point until = steady_clock::time_point::max();
if (now <= steady_clock::time_point::max() - nanoseconds(timeout)) {
until = now + nanoseconds(timeout);
}
std::unique_lock<std::mutex> lock(mLock);
mCond.wait_until(lock, until, [this] {
return mStatus == FenceStatus::CONDITION_SATISFIED || mStatus == FenceStatus::ERROR;
});
// Note that mStatus is default to FenceStatus::TIMEOUT_EXPIRED, so if SetStatus() wasn't
// called, then FenceStatus::TIMEOUT_EXPIRED will be returned, which is aligned with the
// condition if the wait_until above times out.
return mStatus;
}
void WebGPUSubmissionState::setStatus(FenceStatus status) {
std::lock_guard<std::mutex> const lock(mLock);
mStatus = status;
mCond.notify_all();
}
WebGPUQueueManager::WebGPUQueueManager(wgpu::Device const& device)
: mDevice(device),
mQueue(device.GetQueue()),
mLatestSubmissionState(
std::make_shared<WebGPUSubmissionState>(FenceStatus::CONDITION_SATISFIED)) {}
WebGPUQueueManager::~WebGPUQueueManager() = default;
wgpu::CommandEncoder WebGPUQueueManager::getCommandEncoder() {
if (!mCommandEncoder) {
wgpu::CommandEncoderDescriptor commandEncoderDescriptor = {
.label = "Filament Command Encoder",
};
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
ASSERT_POSTCONDITION(mCommandEncoder, "Failed to create command encoder.");
}
return mCommandEncoder;
}
void WebGPUQueueManager::flush() {
submit();
}
void WebGPUQueueManager::finish() {
submit();
// This is a strange side-effect of the dawn callback system, where we would need to keep
// calling ProcessEvents() to be able to advance an internal sequence ID and reach our callback.
// This is similar to draining a work queue. We currently have no other way to force the "last"
// callback to be called.
while (mLatestSubmissionState->getStatus() == FenceStatus::TIMEOUT_EXPIRED) {
mDevice.GetAdapter().GetInstance().ProcessEvents();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
}
std::shared_ptr<WebGPUSubmissionState> WebGPUQueueManager::getLatestSubmissionState() {
return mLatestSubmissionState;
}
void WebGPUQueueManager::submit() {
if (!mCommandEncoder) {
return;
}
mLatestSubmissionState = std::make_shared<WebGPUSubmissionState>();
wgpu::CommandBufferDescriptor commandBufferDescriptor{
.label = "Filament Command Buffer",
};
auto cbuf = mCommandEncoder.Finish(&commandBufferDescriptor);
mQueue.Submit(1, &cbuf);
mQueue.OnSubmittedWorkDone(wgpu::CallbackMode::AllowSpontaneous,
[state = mLatestSubmissionState](wgpu::QueueWorkDoneStatus status,
wgpu::StringView sv) {
switch (status) {
case wgpu::QueueWorkDoneStatus::Success:
state->setStatus(FenceStatus::CONDITION_SATISFIED);
break;
case wgpu::QueueWorkDoneStatus::Error:
case wgpu::QueueWorkDoneStatus::CallbackCancelled:
state->setStatus(FenceStatus::ERROR);
break;
}
});
mCommandEncoder = nullptr;
}
} // namespace filament::backend
@@ -0,0 +1,81 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_WEBGPUQUEUEMANAGER_H
#define TNT_FILAMENT_BACKEND_WEBGPUQUEUEMANAGER_H
#include <backend/DriverEnums.h>
#include <webgpu/webgpu_cpp.h>
#include <memory>
#include <mutex>
namespace filament::backend {
// This struct represents the state of a single submission to the GPU queue.
// A shared pointer to this object is held by WebGPUFence objects.
struct WebGPUSubmissionState {
WebGPUSubmissionState()
: mStatus(FenceStatus::TIMEOUT_EXPIRED) {}
WebGPUSubmissionState(FenceStatus status)
: mStatus(status) {}
FenceStatus getStatus() {
std::lock_guard<std::mutex> const lock(mLock);
return mStatus;
}
FenceStatus waitForCompletion(uint64_t timeout = std::numeric_limits<uint64_t>::max());
void setStatus(FenceStatus status);
private:
std::mutex mLock;
std::condition_variable mCond;
FenceStatus mStatus;
};
class WebGPUQueueManager {
public:
WebGPUQueueManager(wgpu::Device const& device);
~WebGPUQueueManager();
// Returns the command encoder for the current workload. Creates one if it doesn't exist.
wgpu::CommandEncoder getCommandEncoder();
// Submits the current command buffer and creates a new submission state.
void flush();
// Submits the current command buffer and blocks until the work is done.
void finish();
// Returns a shared pointer to the latest submission state.
std::shared_ptr<WebGPUSubmissionState> getLatestSubmissionState();
private:
void submit();
wgpu::Device mDevice;
wgpu::Queue mQueue;
wgpu::CommandEncoder mCommandEncoder;
std::shared_ptr<WebGPUSubmissionState> mLatestSubmissionState;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_WEBGPUQUEUEMANAGER_H
@@ -15,6 +15,7 @@
*/
#include "WebGPURenderPassMipmapGenerator.h"
#include "WebGPUQueueManager.h"
#include <utils/Panic.h>
@@ -199,12 +200,14 @@ constexpr std::string_view SHADER_SOURCE{ R"(
} // namespace
WebGPURenderPassMipmapGenerator::WebGPURenderPassMipmapGenerator(wgpu::Device const& device)
: mDevice{ device },
mPreviousMipLevelSampler{ createPreviousMipLevelSampler(mDevice) },
mShaderModule{ createShaderModule(mDevice) },
mTextureBindGroupLayout{ createTextureBindGroupLayout(mDevice) },
mPipelineLayout{ createPipelineLayout(mDevice, mTextureBindGroupLayout) } {}
WebGPURenderPassMipmapGenerator::WebGPURenderPassMipmapGenerator(wgpu::Device const& device,
WebGPUQueueManager* queueManager)
: mDevice{ device },
mQueueManager(queueManager),
mPreviousMipLevelSampler{ createPreviousMipLevelSampler(mDevice) },
mShaderModule{ createShaderModule(mDevice) },
mTextureBindGroupLayout{ createTextureBindGroupLayout(mDevice) },
mPipelineLayout{ createPipelineLayout(mDevice, mTextureBindGroupLayout) } {}
WebGPURenderPassMipmapGenerator::FormatCompatibility
WebGPURenderPassMipmapGenerator::getCompatibilityFor(const wgpu::TextureFormat format,
@@ -415,33 +418,20 @@ WebGPURenderPassMipmapGenerator::getScalarSampleTypeFrom(const wgpu::TextureForm
}
}
void WebGPURenderPassMipmapGenerator::generateMipmaps(wgpu::Queue const& queue,
wgpu::Texture const& texture) {
void WebGPURenderPassMipmapGenerator::generateMipmaps(wgpu::Texture const& texture) {
const uint32_t mipLevelCount{ texture.GetMipLevelCount() };
if (mipLevelCount < 2) {
return; // Nothing to do.
}
auto commandEncoder = mQueueManager->getCommandEncoder();
wgpu::RenderPipeline const& pipeline{ getOrCreatePipelineFor(texture.GetFormat()) };
const wgpu::CommandEncoderDescriptor commandEncoderDescriptor{
.label = "mipmap_generation_render_pass_cmd_encoder",
};
const wgpu::CommandEncoder commandEncoder{ mDevice.CreateCommandEncoder(
&commandEncoderDescriptor) };
FILAMENT_CHECK_POSTCONDITION(commandEncoder)
<< "Failed to create command encoder for render pass mipmap generation.";
const uint32_t layerCount{ texture.GetDepthOrArrayLayers() };
for (uint32_t layer = 0; layer < layerCount; layer++) {
for (uint32_t mipLevel = 1; mipLevel < mipLevelCount; mipLevel++) {
generateMipmap(commandEncoder, texture, pipeline, layer, mipLevel);
}
}
const wgpu::CommandBufferDescriptor commandBufferDescriptor{
.label = "mipmap_generation_render_pass_cmd_buffer",
};
const wgpu::CommandBuffer commandBuffer{ commandEncoder.Finish(&commandBufferDescriptor) };
FILAMENT_CHECK_POSTCONDITION(commandBuffer)
<< "Failed to create command buffer for render pass mipmap generation.";
queue.Submit(1, &commandBuffer);
mQueueManager->flush();
}
void WebGPURenderPassMipmapGenerator::generateMipmap(wgpu::CommandEncoder const& commandEncoder,
@@ -27,6 +27,8 @@
namespace filament::backend {
class WebGPUQueueManager;
/**
* A utility class for generating mipmaps for a texture using a series of render passes.
*/
@@ -37,6 +39,8 @@ public:
std::string_view reason;
};
WebGPURenderPassMipmapGenerator(wgpu::Device const&, WebGPUQueueManager* queueManager);
/**
* Checks if a given texture format is compatible with render pass-based mipmap generation.
* @return A FormatCompatibility struct indicating whether the format is compatible
@@ -45,16 +49,7 @@ public:
[[nodiscard]] static FormatCompatibility getCompatibilityFor(wgpu::TextureFormat,
wgpu::TextureDimension, uint32_t sampleCount);
explicit WebGPURenderPassMipmapGenerator(wgpu::Device const&);
/**
* IMPORTANT NOTE: when reusing a command encoder and/or textures make sure to flush/submit
* pending commands (draws, etc.) to the GPU prior to calling this call, because texture updates
* may otherwise (unintentionally) happen after draw commands encoded in the encoder.
* Submitting any commands up to this point ensures the calls happen in the expected
* sequence.
*/
void generateMipmaps(wgpu::Queue const&, wgpu::Texture const&);
void generateMipmaps(wgpu::Texture const&);
private:
enum class ScalarSampleType : uint8_t {
@@ -72,6 +67,7 @@ private:
wgpu::RenderPipeline const&, uint32_t layer, uint32_t mipLevel);
wgpu::Device mDevice;
WebGPUQueueManager* mQueueManager;
const wgpu::Sampler mPreviousMipLevelSampler{ nullptr };
const wgpu::ShaderModule mShaderModule{ nullptr };
const wgpu::BindGroupLayout mTextureBindGroupLayout{ nullptr };
@@ -694,7 +694,7 @@ wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
}
Driver* WebGPUPlatform::createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) noexcept {
const Platform::DriverConfig& driverConfig) {
if (sharedContext) {
FWGPU_LOGW << "sharedContext is ignored/unused in the WebGPU backend. A non-null "
"sharedContext was provided, but it will be ignored.";
@@ -176,7 +176,7 @@ protected:
// Map the buffer with a specific offset.
MemoryMappedBufferHandle const memoryMappedBuffer =
api.mapBuffer(bufferObject, mapOffset, vertexDataSize + copyOffset,
MapBufferAccessFlags::WRITE_BIT, utils::CString{ screenshotName });
MapBufferAccessFlags::WRITE_BIT, utils::ImmutableCString{ screenshotName });
copyData(vertices.data(), vertexDataSize, copyOffset, memoryMappedBuffer, callbackExecuted);
+6 -6
View File
@@ -19,7 +19,7 @@
#include <utils/compiler.h>
#include <utils/PrivateImplementation.h>
#include <utils/CString.h>
#include <utils/ImmutableCString.h>
#include <utils/StaticString.h>
#include <stddef.h>
@@ -57,7 +57,7 @@ template<typename T>
using BuilderBase = utils::PrivateImplementation<T>;
// This needs to be public because it is used in the following template.
UTILS_PUBLIC void builderMakeName(utils::CString& outName, const char* name, size_t len) noexcept;
UTILS_PUBLIC void builderMakeName(utils::ImmutableCString& outName, const char* name, size_t len) noexcept;
template <typename Builder>
class UTILS_PUBLIC BuilderNameMixin {
@@ -73,18 +73,18 @@ public:
return static_cast<Builder&>(*this);
}
utils::CString const& getName() const noexcept { return mName; }
utils::ImmutableCString const& getName() const noexcept { return mName; }
utils::CString const& getNameOrDefault() const noexcept {
utils::ImmutableCString const& getNameOrDefault() const noexcept {
if (const auto& name = getName(); !name.empty()) {
return name;
}
static const utils::CString sDefaultName = "(none)";
static const utils::ImmutableCString sDefaultName = "(none)";
return sDefaultName;
}
private:
utils::CString mName;
utils::ImmutableCString mName;
};
} // namespace filament
@@ -297,7 +297,7 @@ public:
Builder& priority(uint8_t priority) noexcept;
/**
* Set the channel this renderable is associated to. There can be 4 channels.
* Set the channel this renderable is associated to. There can be 8 channels.
* All renderables in a given channel are rendered together, regardless of anything else.
* They are sorted as usual within a channel.
* Channels work similarly to priorities, except that they enforce the strongest ordering.
@@ -305,7 +305,7 @@ public:
* Channels 0 and 1 may not have render primitives using a material with `refractionType`
* set to `screenspace`.
*
* @param channel clamped to the range [0..3], defaults to 2.
* @param channel clamped to the range [0..7], defaults to 2.
*
* @return Builder reference for chaining calls.
*
+15
View File
@@ -97,6 +97,8 @@ public:
time_point_ns endFrame; //!< Renderer::endFrame() time since epoch [ns]
time_point_ns backendBeginFrame; //!< Backend thread time of frame start since epoch [ns]
time_point_ns backendEndFrame; //!< Backend thread time of frame end since epoch [ns]
time_point_ns gpuFrameComplete; //!< GPU thread time of frame end since epoch [ns] or 0
time_point_ns vsync; //!< VSYNC time of this frame since epoch [ns]
};
/**
@@ -646,6 +648,19 @@ public:
*/
void resetUserTime();
/**
* Requests the next frameCount frames to be skipped. For Debugging.
* @param frameCount number of frames to skip.
*/
void skipNextFrames(size_t frameCount) const noexcept;
/**
* Remainder count of frame to be skipped
* @return remaining frames to be skipped
*/
size_t getFrameToSkipCount() const noexcept;
protected:
// prevent heap allocation
~Renderer() = default;

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