Compare commits

..
Author SHA1 Message Date
Sungun Park 23e1779f1e Fix a compile error
FMaterial accesses a private static method of the MaterialDefinition
class, which incurs a compile error for certain compilers.
2025-09-24 12:07:40 -07:00
384 changed files with 6368 additions and 24433 deletions
+1 -3
View File
@@ -65,6 +65,4 @@ SpacesInParentheses: false
SpacesInSquareBrackets: false
TabWidth: 4
UseTab: Never
PackConstructorInitializers: Never
ConstructorInitializerIndentWidth: 8
IndentWrappedFunctionNames: true
PackConstructorInitializers: Never
+11 -21
View File
@@ -1,53 +1,43 @@
# This action retrieves the latest commit message from a push or pull_request event
# and makes it available as an output variable named 'msg'.
name: 'Get commit message'
outputs:
msg:
value: ${{ steps.action_output.outputs.msg }}
hash:
value: ${{ steps.action_output.outputs.hash }}
runs:
using: "composite"
steps:
- name: Find commit message (on push)
if: github.event_name == 'push'
shell: bash
env:
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
run: |
AUTHOR_NAME="${{ github.event.head_commit.author.name }}"
AUTHOR_EMAIL="${{ github.event.head_commit.author.email }}"
TSTAMP="${{ github.event.head_commit.timestamp }}"
HASH="${{ github.event.head_commit.id }}"
echo "commit $HASH" >> /tmp/commit_msg.txt
echo "commit ${{ github.event.head_commit.id }}" >> /tmp/commit_msg.txt
echo "Author: ${AUTHOR_NAME}<${AUTHOR_EMAIL}>" >> /tmp/commit_msg.txt
echo "Date: ${TSTAMP}" >> /tmp/commit_msg.txt
echo "" >> /tmp/commit_msg.txt
echo "$COMMIT_MESSAGE" >> /tmp/commit_msg.txt
echo "$HASH" > /tmp/commit_hash.txt
echo "${{ github.event.head_commit.message }}" >> /tmp/commit_msg.txt
- name: Find commit message (PR)
shell: bash
id: checkout_code
if: github.event_name == 'pull_request'
run: |
BEFORE_HASH=$(git rev-parse HEAD)
echo "hash=$BEFORE_HASH" >> "$GITHUB_OUTPUT"
# Next we will checkout the actual head (not the merge commits) of the PR
AFTER_HASH="${{ github.event.pull_request.head.sha }}"
git checkout $AFTER_HASH
COMMIT_MESSAGE=$(git log -1 --no-merges)
echo "$COMMIT_MESSAGE" > /tmp/commit_msg.txt
echo "$AFTER_HASH" > /tmp/commit_hash.txt
echo "+++++ head commit message +++++"
echo "$(git log -1 --no-merges)"
echo "+++++++++++++++++++++++++++++++"
echo "hash=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
git checkout ${{ github.event.pull_request.head.sha }}
echo "$(git log -1 --no-merges)" >> /tmp/commit_msg.txt
- shell: bash
id: action_output
run: |
# Get the commit message
DELIMITER="EOF_FILE_CONTENT_$(date +%s)" # Using timestamp to make it more unique
echo "msg<<$DELIMITER" >> "$GITHUB_OUTPUT"
cat /tmp/commit_msg.txt >> "$GITHUB_OUTPUT"
echo "$DELIMITER" >> "$GITHUB_OUTPUT"
# Get the commit hash
echo "hash=$(cat /tmp/commit_hash.txt)" >> "$GITHUB_OUTPUT"
echo "----- got commit message ---"
cat /tmp/commit_msg.txt
echo "----------------------------"
- name: Cleanup Find commit message (PR)
shell: bash
if: github.event_name == 'pull_request'
+2 -2
View File
@@ -23,7 +23,7 @@ jobs:
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
run: |
GOLDEN_BRANCH=$(echo "${{ steps.get_commit_msg.outputs.msg }}" | python3 test/renderdiff/src/commit_msg.py)
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}"
COMMIT_HASH=$(echo "${{ steps.get_commit_msg.outputs.msg }}" | head -n 1 | sed "s/commit //g")
if [[ "${GOLDEN_BRANCH}" != "main" ]]; then
git config --global user.email "filament.bot@gmail.com"
git config --global user.name "Filament Bot"
@@ -51,7 +51,7 @@ jobs:
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
run: |
bash docs_src/build/install_mdbook.sh && source ~/.bashrc
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}"
COMMIT_HASH=$(echo "${{ steps.get_commit_msg.outputs.msg }}" | head -n 1 | sed "s/commit //g")
git config --global user.email "filament.bot@gmail.com"
git config --global user.name "Filament Bot"
git config --global credential.helper cache
+4 -25
View File
@@ -108,7 +108,8 @@ jobs:
uses: ./.github/actions/get-commit-msg
- name: Check for manual edits to /docs
run: |
bash docs_src/build/presubmit_check.sh ${{ steps.get_commit_msg.outputs.hash }}
COMMIT_ID=$(echo "${{ steps.get_commit_msg.outputs.msg }}" | head -n 1 | sed "s/commit //g")
bash docs_src/build/presubmit_check.sh ${COMMIT_ID}
test-renderdiff:
name: test-renderdiff
@@ -120,7 +121,7 @@ jobs:
- uses: ./.github/actions/mac-prereq
- uses: ./.github/actions/get-gltf-assets
- uses: ./.github/actions/get-mesa
- uses: ./.github/actions/get-vulkan-sdk
- uses: ./.github/actions/get-vulkan-sdk
- id: get_commit_msg
uses: ./.github/actions/get-commit-msg
- name: Prerequisites
@@ -131,14 +132,12 @@ jobs:
shell: bash
- name: Render and compare
id: render_compare
env:
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
run: |
ls ./gltf/Models
TEST_DIR=test/renderdiff
source ${TEST_DIR}/src/preamble.sh
start_
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 ${TEST_DIR}/src/commit_msg.py)
GOLDEN_BRANCH=$(echo "${{ steps.get_commit_msg.outputs.msg }}" | python3 ${TEST_DIR}/src/commit_msg.py)
bash ${TEST_DIR}/generate.sh && \
python3 ${TEST_DIR}/src/golden_manager.py \
--branch=${GOLDEN_BRANCH} \
@@ -203,23 +202,3 @@ jobs:
./build.sh -p desktop debug gltf_viewer
- name: Run test
run: bash test/code-correctness/test.sh
test-backend:
name: test-backend
runs-on: macos-14-xlarge
steps:
- uses: actions/checkout@v4.1.6
with:
fetch-depth: 0
- uses: ./.github/actions/mac-prereq
- uses: ./.github/actions/get-mesa
- uses: ./.github/actions/get-vulkan-sdk
- name: Prerequisites
run: |
# Must have at least clang-16 for a webgpu/dawn build.
sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer
shell: bash
- name: Run backend tests
shell: bash
run: |
bash test/backend/test.sh
+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
+17 -22
View File
@@ -56,8 +56,6 @@ option(FILAMENT_ENABLE_FGVIEWER "Enable the frame graph viewer" OFF)
option(FILAMENT_USE_ABSEIL_LOGGING "Use Abseil to log, may increase binary size" OFF)
option(FILAMENT_ENABLE_EXPERIMENTAL_GCC_SUPPORT "Enable GCC support (unsupported)" OFF)
# This is to disable GTAO for the short-term while we investigate a way to better manage size increases.
# On the regular filament build (where size is of less concern), we enable GTAO by default.
option(FILAMENT_DISABLE_GTAO "Disable GTAO" OFF)
@@ -297,10 +295,15 @@ set(TOOLS ${CMAKE_CURRENT_SOURCE_DIR}/tools)
# ==================================================================================================
set(MIN_CLANG_VERSION "6.0")
set(FILAMENT_USING_GCC OFF)
if (CMAKE_C_COMPILER_ID MATCHES "GNU" AND FILAMENT_ENABLE_EXPERIMENTAL_GCC_SUPPORT)
set(FILAMENT_USING_GCC ON)
elseif (CMAKE_C_COMPILER_ID MATCHES "Clang")
if (CMAKE_C_COMPILER_ID MATCHES "Clang")
if (CMAKE_C_COMPILER_VERSION VERSION_LESS MIN_CLANG_VERSION)
message(FATAL_ERROR "Detected C compiler Clang ${CMAKE_C_COMPILER_VERSION} < ${MIN_CLANG_VERSION}")
endif()
elseif (NOT MSVC)
message(FATAL_ERROR "Detected C compiler ${CMAKE_C_COMPILER_ID} is unsupported")
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS MIN_CLANG_VERSION)
message(FATAL_ERROR "Detected CXX compiler Clang ${CMAKE_CXX_COMPILER_VERSION} < ${MIN_CLANG_VERSION}")
endif()
@@ -341,18 +344,14 @@ if (MSVC)
set(CXX_STANDARD "/std:c++latest")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} /W0 /Zc:__cplusplus")
else()
if(FILAMENT_USING_GCC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} -Wno-changes-meaning -Wno-return-type -Wno-attributes -Wno-unknown-pragmas -Wno-class-memaccess -Wno-multichar -Wno-deprecated-declarations -Wno-subobject-linkage -Wno-invalid-constexpr")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} -fstrict-aliasing -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations")
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_STANDARD} -fstrict-aliasing -Wno-unknown-pragmas -Wno-unused-function -Wno-deprecated-declarations")
if (APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-nullability-extension")
endif()
endif()
if (FILAMENT_USE_EXTERNAL_GLES3)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DFILAMENT_USE_EXTERNAL_GLES3")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_EXTERNAL_GLES3")
endif()
if (FILAMENT_SUPPORTS_EGL_ON_LINUX)
@@ -366,14 +365,10 @@ endif()
if (LINUX)
option(USE_STATIC_LIBCXX "Link against the static runtime libraries." ON)
if (${USE_STATIC_LIBCXX})
if (FILAMENT_USING_GCC)
link_libraries("-static-libgcc -static-libstdc++")
else ()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
link_libraries("-static-libgcc -static-libstdc++")
link_libraries(libc++.a)
link_libraries(libc++abi.a)
endif()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
link_libraries("-static-libgcc -static-libstdc++")
link_libraries(libc++.a)
link_libraries(libc++abi.a)
endif()
# Only linux, clang doesn't want to use a shared library that is not PIC.
@@ -398,8 +393,8 @@ if (MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE")
endif()
# Add colors to ninja builds (when not using gcc)
if (UNIX AND CMAKE_GENERATOR STREQUAL "Ninja" AND NOT FILAMENT_USING_GCC)
# Add colors to ninja builds
if (UNIX AND CMAKE_GENERATOR STREQUAL "Ninja")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fcolor-diagnostics")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fcolor-diagnostics")
endif()
+6 -18
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,24 +73,12 @@ src/data.inc
- `public` class attributes *are not* prefixed
- class attributes and methods are lower camelcase
```c++
```
extern int gGlobalWarming;
class FooBar {
public:
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
}
void methodName();
int sizeInBytes;
private:
int mAttributeName;
@@ -109,7 +97,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
*
@@ -151,7 +139,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
*
@@ -195,7 +183,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 -2
View File
@@ -1,10 +1,9 @@
# 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).
## Release notes for next branch cut
- Metal: Add support for the `SwapChain::CONFIG_MSAA_4_SAMPLES` flag.
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.67.0'
implementation 'com.google.android.filament:filament-android:1.65.2'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.67.0'
pod 'Filament', '~> 1.65.2'
```
## Documentation
-21
View File
@@ -7,27 +7,6 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.67.0
- materials: Add a new API getParameterTransformName that will return the value of the transformName field of a sampler
parameter. [⚠️ **Recompile Materials**]
## v1.66.2
## v1.66.1
- filamat: Removed a dependency on Glslang's deprecated SPIR-V remapper.
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
## v1.65.3
@@ -557,8 +557,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
jboolean disableHandleUseAfterFreeCheck,
jint preferredShaderLanguage,
jboolean forceGLES2Context, jboolean assertNativeWindowIsValid,
jint gpuContextPriority,
jlong sharedUboInitialSizeInBytes) {
jint gpuContextPriority) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
Engine::Config config = {
.commandBufferSizeMB = (uint32_t) commandBufferSizeMB,
@@ -577,7 +576,6 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
.forceGLES2Context = (bool) forceGLES2Context,
.assertNativeWindowIsValid = (bool) assertNativeWindowIsValid,
.gpuContextPriority = (Engine::GpuContextPriority) gpuContextPriority,
.sharedUboInitialSizeInBytes = (uint32_t) sharedUboInitialSizeInBytes,
};
builder->config(&config);
}
@@ -279,17 +279,6 @@ Java_com_google_android_filament_Material_nHasParameter(JNIEnv* env, jclass,
return (jboolean) hasParameter;
}
extern "C" JNIEXPORT jstring JNICALL
Java_com_google_android_filament_Material_nGetParameterTransformName(JNIEnv* env, jclass,
jlong nativeMaterial, jstring samplerName_) {
Material* material = (Material*) nativeMaterial;
const char* samplerName = env->GetStringUTFChars(samplerName_, 0);
const char* transformName = material->getParameterTransformName(samplerName);
jstring transformName_ = env->NewStringUTF(transformName ? transformName : "");
env->ReleaseStringUTFChars(samplerName_, samplerName);
return transformName_;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_Material_nCompile(JNIEnv *env, jclass clazz,
@@ -208,19 +208,3 @@ 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,7 +356,6 @@ 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;
@@ -555,20 +554,3 @@ 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);
}
@@ -264,8 +264,7 @@ public class Engine {
config.disableHandleUseAfterFreeCheck,
config.preferredShaderLanguage.ordinal(),
config.forceGLES2Context, config.assertNativeWindowIsValid,
config.gpuContextPriority.ordinal(),
config.sharedUboInitialSizeInBytes);
config.gpuContextPriority.ordinal());
return this;
}
@@ -526,16 +525,6 @@ public class Engine {
* GPU context priority level. Controls GPU work scheduling and preemption.
*/
public GpuContextPriority gpuContextPriority = GpuContextPriority.DEFAULT;
/**
* The initial size in bytes of the shared uniform buffer used for material instance batching.
*
* If the buffer runs out of space during a frame, it will be automatically reallocated
* with a larger capacity. Setting an appropriate initial size can help avoid runtime
* reallocations, which can cause a minor performance stutter, at the cost of higher
* initial memory usage.
*/
public long sharedUboInitialSizeInBytes = 256 * 64;
}
private Engine(long nativeEngine, Config config) {
@@ -1540,8 +1529,7 @@ public class Engine {
boolean disableHandleUseAfterFreeCheck,
int preferredShaderLanguage,
boolean forceGLES2Context, boolean assertNativeWindowIsValid,
int gpuContextPriority,
long sharedUboInitialSizeInBytes);
int gpuContextPriority);
private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal);
private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext);
private static native void nSetBuilderPaused(long nativeBuilder, boolean paused);
@@ -787,21 +787,6 @@ public class Material {
return nHasParameter(getNativeObject(), name);
}
/**
*
* Returns the name of the transform parameter associated with the given sampler parameter.
* In the case the parameter doesn't have a transform name field, it will return an empty string.
*
* @param samplerName the name of the sampler parameter to query.
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/general:parameters">
* General: parameters</a>
*/
public String getParameterTransformName(@NonNull String samplerName) {
return nGetParameterTransformName(getNativeObject(), samplerName);
}
/**
* Sets the value of a bool parameter on this material's default instance.
*
@@ -1126,5 +1111,4 @@ public class Material {
private static native int nGetRequiredAttributes(long nativeMaterial);
private static native boolean nHasParameter(long nativeMaterial, @NonNull String name);
private static native String nGetParameterTransformName(long nativeMaterial, @NonNull String samplerName);
}
@@ -332,7 +332,7 @@ public class RenderableManager {
}
/**
* Set the channel this renderable is associated to. There can be 8 channels.
* Set the channel this renderable is associated to. There can be 4 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..7], defaults to 2.
* @param channel clamped to the range [0..3], 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 = 7) int channel) {
public Builder channel(@IntRange(from = 0, to = 3) 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 = 7) int channel) {
public void setChannel(@EntityInstance int i, @IntRange(from = 0, to = 3) int channel) {
nSetChannel(mNativeObject, i, channel);
}
@@ -732,22 +732,6 @@ 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");
@@ -789,7 +773,4 @@ 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);
}
@@ -294,8 +294,8 @@ public class TextureSampler {
/**
* This controls anisotropic filtering.
*
* @param anisotropy Amount of anisotropy, should be a power-of-two. The default is 1.
* The maximum permissible value is 128.
* @param anisotropy Amount of anisotropy, should be a power-of-two. The default is 0.
* The maximum permissible value is 7.
*/
public void setAnisotropy(float anisotropy) {
mSampler = nSetAnisotropy(mSampler, anisotropy);
@@ -291,25 +291,6 @@ 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.
*
@@ -1337,8 +1318,6 @@ 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);
@@ -1636,7 +1615,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};
@@ -1677,7 +1656,7 @@ public class View {
*
* `fogColorFromIbl` is ignored when skyTexture is specified.
*
* @see #skyColor
* @see skyColor
*/
public boolean fogColorFromIbl = false;
/**
@@ -1695,7 +1674,7 @@ public class View {
* In `linearFog` mode mipmap level 0 is always used.
*
* @see Texture
* @see #fogColorFromIbl
* @see fogColorFromIbl
*/
@Nullable
public Texture skyColor = null;
@@ -1817,8 +1796,7 @@ public class View {
/**
* Structure used to set the precision of the color buffer and related quality settings.
*
* @see #setRenderQuality
* @see #getRenderQuality
* @see setRenderQuality, getRenderQuality
*/
public static class RenderQuality {
/**
@@ -1836,7 +1814,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 {
@@ -1988,7 +1966,7 @@ public class View {
/**
* Options for Multi-Sample Anti-aliasing (MSAA)
* @see #setMultiSampleAntiAliasingOptions
* @see setMultiSampleAntiAliasingOptions()
*/
public static class MultiSampleAntiAliasingOptions {
/**
@@ -2018,7 +1996,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 {
@@ -2120,7 +2098,7 @@ public class View {
/**
* Options for Screen-space Reflections.
* @see #setScreenSpaceReflectionsOptions
* @see setScreenSpaceReflectionsOptions()
*/
public static class ScreenSpaceReflectionsOptions {
/**
@@ -2154,9 +2132,7 @@ public class View {
/**
* List of available post-processing anti-aliasing techniques.
* @see #setAntiAliasing
* @see #getAntiAliasing
* @see #setSampleCount
* @see setAntiAliasing, getAntiAliasing, setSampleCount
*/
public enum AntiAliasing {
/**
@@ -2185,7 +2161,7 @@ public class View {
/**
* List of available shadow mapping techniques.
* @see #setShadowType
* @see setShadowType
*/
public enum ShadowType {
/**
@@ -2209,7 +2185,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 {
@@ -2250,7 +2226,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.67.0
VERSION_NAME=1.65.2
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+2 -2
View File
@@ -165,8 +165,8 @@ Portal</a>.</p>
<p>To publish under the <code>com.google.android</code> namespace, you'll also need to email
<code>central-support@sonatype.com</code> with your account information to request access.</p>
<p>Then, generate a user token through the Sonatype website. Navigate to
<a href="https://central.sonatype.com/usertoken">https://central.sonatype.com/usertoken</a>
and select <strong>Generate User Token</strong>.</p>
<a href="https://central.sonatype.com/account">https://central.sonatype.com/account</a> and select <strong>Generate
User Token</strong>.</p>
<p>Finally, add the generated token credentials to <code>~/.gradle/gradle.properties</code>. (Note: these are
different from the credentials used to log into your Sonatype account):</p>
<pre><code class="language-gradle"># Generated Sonatype token
+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>
+6 -18
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 class="language-c++">for (int i = 0; i &lt; max; i++) {
<pre><code>for (int i = 0; i &lt; max; i++) {
}
class Foo {
@@ -227,23 +227,11 @@ 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 class="language-c++">extern int gGlobalWarming;
<pre><code>extern int gGlobalWarming;
class FooBar {
public:
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
}
void methodName();
int sizeInBytes;
private:
int mAttributeName;
@@ -260,7 +248,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 class="language-c++">/*
<pre><code>/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -305,7 +293,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 class="language-c++">/*
<pre><code>/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -348,7 +336,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 class="language-c++">auto foo = new Foo();
<pre><code>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.66.1'
implementation 'com.google.android.filament:filament-android:1.65.1'
}
</code></pre>
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
@@ -196,7 +196,7 @@ dependencies {
</div>
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
<p>iOS projects can use CocoaPods to install the latest release:</p>
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.66.1'
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.65.1'
</code></pre>
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
<ul>
+53 -60
View File
@@ -159,98 +159,99 @@
<div id="content" class="content">
<main>
<h1 id="rendering-difference-test"><a class="header" href="#rendering-difference-test">Rendering Difference Test</a></h1>
<p>This tool is a collections of scripts to run <code>gltf_viewer</code> and produce headless renderings.</p>
<p>We created a few scripts to run <code>gltf_viewer</code> and produce headless renderings.</p>
<p>This is mainly useful for continuous integration where GPUs are generally not available on cloud
machines. To perform software rasterization, these scripts are centered around <a href="https://docs.mesa3d.org">Mesa</a>'s
software rasterizers, but nothing bars us from using another rasterizer like <a href="https://github.com/google/swiftshader">SwiftShader</a>.
Additionally, we should be able to use GPUs where available (though this is more of a future
work).</p>
machines. To perform software rasterization, these scripts are centered around <a href="https://docs.mesa3d.org">Mesa</a>'s software
rasterizers, but nothing bars us from using another rasterizer like <a href="https://github.com/google/swiftshader">SwiftShader</a>. Additionally,
we should be able to use GPUs where available (though this is more of a future work).</p>
<p>The script <code>render.py</code> contains the core logic for taking input parameters (such as the test
description file) and then running <code>gltf_viewer</code> to produce the renderings.</p>
<p>In the <code>test</code> directory is a list of test descriptions that are specified in json. Please see
<code>sample.json</code> to glean the structure.</p>
description file) and then running gltf_viewer to produce the renderings.</p>
<p>In the <code>test</code> directory is a list of test descriptions that are specified in json. Please see
<code>sample.json</code> to parse the structure.</p>
<h2 id="setting-up-python"><a class="header" href="#setting-up-python">Setting up python</a></h2>
<p>The <code>renderdiff</code> project uses <code>python</code> extensively. To install the dependencies for producing
renderings, do the following step</p>
<ul>
<li>Set up a virtual environment (from the root directory)
<pre><code>python3 -m venv venv
. ./venv/bin/activate
<pre><code> python3 -m venv venv
. ./venv/bin/activate
</code></pre>
</li>
<li>Install the rendering dependencies
<pre><code>pip install -r test/renderdiff/src/rendering_requirements.txt
<pre><code> pip install -r test/renderdiff/src/rendering_requirements.txt
</code></pre>
</li>
<li>Install the viewer depdencies
<pre><code>pip install -r test/renderdiff/src/viewer_requirements.txt
<pre><code> pip install -r test/renderdiff/src/viewer_requirements.txt
</code></pre>
</li>
<li>For the commands in the following section, do not exit the virtual environment. Once you've
completed all your work, you can exit with
<pre><code>deactivate
<pre><code> deactivate
</code></pre>
</li>
</ul>
<h2 id="running-the-test-locally"><a class="header" href="#running-the-test-locally">Running the test locally</a></h2>
<ul>
<li>To run the same presbumit as <a href="presubmit-renderdiff"><code>test-renderdiff</code></a>, you can do
<li>
<p>To run the same presbumit as <a href="presubmit-renderdiff"><code>test-renderdiff</code></a>, you can do</p>
<pre><code>bash test/renderdiff/local_test.sh
</code></pre>
</li>
<li>This script will generate the renderings based on the current state of your repo.
<li>
<p>This script will generate the renderings based on the current state of your repo.
Additionally, it will also compare the generated images with corresponding images in the
golden repo.</li>
<li>To just render without running the test, you could use the following script
golden repo.</p>
</li>
<li>
<p>To just render without running the test, you could use the following script</p>
<pre><code>bash test/renderdiff/generate.sh
</code></pre>
</li>
</ul>
<h2 id="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:
https://github.com/google/filament-assets. Filament team members should have access to write
to the repository. A typical flow for updating the goldens is to upload your changed images
into <strong>branch</strong> of <code>filament-assets</code>. This branch is paired with a PR or commit on the
<code>filament</code> repo.</p>
<p>As an example, imagine I am working on a PR, and I've uploaded my change, which is in a
branch called <code>my-pr-branch</code>, to <code>filament</code>. This PR requires updating the golden. We would do
it in the following fashion</p>
<p>The golden images are stored in a github repository: https://github.com/google/filament-assets.
Filament team members should have access to write to the repository. A typical flow for updating
the goldens is to upload your changed images into <strong>branch</strong> of <code>filament-assets</code>. This branch is
paired with a PR or commit on the <code>filament</code> repo.</p>
<p>As an example, imagine I am working on a PR, and I've uploaded my change, which is in a branch
called <code>my-pr-branch</code>, to <code>filament</code>. This PR requires updating the golden. We would do it
in the following fashion</p>
<h3 id="using-a-script-to-update-the-golden-repo"><a class="header" href="#using-a-script-to-update-the-golden-repo">Using a script to update the golden repo</a></h3>
<ul>
<li>Run interactive mode in the <code>update_golden.py</code> script.
<li>
<p>Run interactive mode in the <code>update_golden.py</code> script.</p>
<pre><code>python3 test/renderdiff/src/update_golden.py
</code></pre>
</li>
<li>This will guide you through a series of steps to push the changes to a remote branch on
<code>filament-assets</code>.</li>
<li>
<p>This will guide you through a series of steps to push the changes to a remote branch
on <code>filament-assets</code>.</p>
</li>
</ul>
<h3 id="manually-updating-the-golden-repo"><a class="header" href="#manually-updating-the-golden-repo">Manually updating the golden repo</a></h3>
<ul>
<li>Check out the golden repo
<li>
<p>Check out the golden repo</p>
<pre><code>git clone git@github.com:google/filament-assets.git
</code></pre>
</li>
<li>Create a branch on the golden repo
<li>
<p>Create a branch on the golden repo</p>
<pre><code>cd filament-assets
git switch -c my-pr-branch-golden
</code></pre>
</li>
<li>Copy the new images to their appropriate place in <code>filament-assets</code></li>
<li>Push the <code>filament-assets</code> working branch to remote
<li>
<p>Copy the new images to their appropriate place in <code>filament-assets</code></p>
</li>
<li>
<p>Push the <code>filament-assets</code> working branch to remote</p>
<pre><code>git push origin my-pr-branch-golden
</code></pre>
</li>
<li>In the commit message of your working branch on <code>filament</code>, add the following line
<li>
<p>In the commit message of your working branch on <code>filament</code>, add the following line</p>
<pre><code>RDIFF_BBRANCH=my-pr-branch-golden
</code></pre>
</li>
@@ -259,17 +260,16 @@ git switch -c my-pr-branch-golden
<ul>
<li>The presubmit test <a href="https://github.com/google/filament/blob/e85dfe75c86106a05019e13ccdbef67e030af675/.github/workflows/presubmit.yml#L118"><code>test-renderdiff</code></a> will test against the provided
branch of the golden repo (i.e. <code>my-pr-branch-golden</code>).</li>
<li>If the PR is merged, then there is another workflow that will merge <code>my-pr-branch-golden</code>
to the <code>main</code> branch of the golden repo.</li>
<li>If the PR is merged, then there is another workflow that will merge <code>my-pr-branch-golden</code> to
the <code>main</code> branch of the golden repo.</li>
</ul>
<h2 id="viewing-test-results"><a class="header" href="#viewing-test-results">Viewing test results</a></h2>
<p>We provide a viewer for looking at the result of a test run. The viewer is a webapp that can
be used by pointing your browser to a localhost port. If you input the viewer with a PR or a
directory, it will parse the test result and show the results and the rendered and/or golden
images.</p>
<p>We provide a viewer for looking at the result of a test run. The viewer is a webapp that can be used by
pointing your browser to a localhost port. If you input the viewer with a PR or a directory, it will
parse the test result and show the results and the rendered and/or golden images.</p>
<p><img src="../images/renderdiff_example.png" alt="Viewer" /></p>
<p>To run the viewer of a test output directory that has been generated locally, you would run
the following</p>
<p>To run the viewer of a test output directory that has been generated locally, you would run the
following</p>
<pre><code>python3 test/renderdiff/src/viewer.py --diff=[test output]
</code></pre>
<p>where <code>[test output]</code> is a directory containing the <code>compare_results.json</code> of the test run.
@@ -278,15 +278,8 @@ For example, it could be <code>out/renderdiff/diffs/presubmit</code> for the sta
<p>To see the results of a Pull Request initiated test run, you would do the following</p>
<pre><code>python3 test/renderdiff/src/viewer.py --pr_number=[PR #] --github_token=[github token]
</code></pre>
<p>where <code>[PR #]</code> is the numeric ID of your pull request, and the <code>[github token]</code> is an acess
token that you (as a github user) needs to generate (<a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens">reference</a>).</p>
<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>
<p>where <code>[PR #]</code> is the numeric ID of your pull request, and the <code>[github token]</code> is an acess token
that you (as a github user) needs to generate (<a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens">reference</a>).</p>
</main>
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -8,10 +8,10 @@ tar -xvzf filament-release-web.tgz
rm filament-release-web.tgz
rm filament-web.zip
rm filament.d.ts
cp ../../../web/samples/remote.html index.html
cp ../../web/samples/remote.html index.html
popd
git status
echo ""
echo "All done! Next, commit the changed files"
echo "All done! Next, make a git commit that updates docs/remote."
+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 -2
View File
@@ -77,11 +77,11 @@ def check_has_source_edits(commit_hash, printing=True):
# Returns true in a given TAG is found in the commit msg
def commit_msg_has_tag(commit_hash, tag, printing=True):
res, ret = execute(f'git log -n1 --pretty=%B {commit_hash}', cwd=ROOT_DIR)
res, ret = execute(f'git log --pretty=%B {commit_hash}', cwd=ROOT_DIR)
for l in ret.split('\n'):
if tag == l.strip():
if printing:
print(f'Found tag={tag} in commit={commit_hash}')
print(f'Found tag={tag} in commit message')
return True
return False
-15
View File
@@ -1,15 +0,0 @@
attrs==25.4.0
certifi==2025.10.5
h11==0.16.0
idna==3.11
outcome==1.3.0.post0
PySocks==1.7.1
selenium==4.37.0
sniffio==1.3.1
sortedcontainers==2.4.0
trio==0.31.0
trio-websocket==0.12.2
typing_extensions==4.15.0
urllib3==2.5.0
websocket-client==1.9.0
wsproto==1.2.0
+1 -3
View File
@@ -140,8 +140,6 @@ if __name__ == "__main__":
res, err = execute('mdbook build', cwd=MDBOOK_DIR)
assert res == 0, f"failed to execute `mdbook`. return-code={res} err=\"{err}\""
RAW_IGNORES = shutil.ignore_patterns('update_remote_ui.sh')
shutil.copytree(RAW_COPIES_DIR, BOOK_OUPUT_DIR, ignore=RAW_IGNORES, dirs_exist_ok=True)
shutil.copytree(RAW_COPIES_DIR, BOOK_OUPUT_DIR, dirs_exist_ok=True)
shutil.copy(os.path.join(MARKDEEP_DIR, FILAMENT_MD), BOOK_OUPUT_DIR)
shutil.copy(os.path.join(MARKDEEP_DIR, MATERIALS_MD), BOOK_OUPUT_DIR)
@@ -9,8 +9,8 @@ To publish under the `com.google.android` namespace, you'll also need to email
`central-support@sonatype.com` with your account information to request access.
Then, generate a user token through the Sonatype website. Navigate to
[https://central.sonatype.com/usertoken](https://central.sonatype.com/usertoken)
and select **Generate User Token**.
[https://central.sonatype.com/account](https://central.sonatype.com/account) and select **Generate
User Token**.
Finally, add the generated token credentials to `~/.gradle/gradle.properties`. (Note: these are
different from the credentials used to log into your Sonatype account):
File diff suppressed because one or more lines are too long
Binary file not shown.
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
pushd "$(dirname "$0")" > /dev/null
curl -OL https://nightly.link/google/filament/workflows/web-continuous/main/filament-web.zip
unzip -q filament-web.zip
tar -xvzf filament-release-web.tgz
rm filament-release-web.tgz
rm filament-web.zip
rm filament.d.ts
cp ../../web/samples/remote.html index.html
popd
git status
echo ""
echo "All done! Next, make a git commit that updates docs/remote."
+1 -23
View File
@@ -117,7 +117,6 @@ set(SRCS
src/components/LightManager.cpp
src/components/RenderableManager.cpp
src/components/TransformManager.cpp
src/details/BufferAllocator.cpp
src/details/BufferObject.cpp
src/details/Camera.cpp
src/details/ColorGrading.cpp
@@ -197,7 +196,6 @@ set(PRIVATE_HDRS
src/components/LightManager.h
src/components/RenderableManager.h
src/components/TransformManager.h
src/details/BufferAllocator.h
src/details/BufferObject.h
src/details/Camera.h
src/details/ColorGrading.h
@@ -251,10 +249,9 @@ 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/clearDepth.mat
src/materials/blitArray.mat
src/materials/bloom/bloomDownsample.mat
src/materials/bloom/bloomDownsample2x.mat
src/materials/bloom/bloomDownsample9.mat
@@ -708,13 +705,6 @@ if (MSVC)
elseif(WEBGL)
# Avoid strict-vtable-pointers here, it is broken in WebAssembly.
set(OPTIMIZATION_FLAGS -fvisibility-inlines-hidden)
elseif(FILAMENT_USING_GCC)
set(OPTIMIZATION_FLAGS
-ffast-math
-fno-finite-math-only
-ffp-contract=fast
-fvisibility-inlines-hidden
)
else()
set(OPTIMIZATION_FLAGS
-ffast-math
@@ -736,18 +726,6 @@ set(LINUX_COMPILER_FLAGS
if (MSVC)
set(FILAMENT_WARNINGS /W3)
elseif (FILAMENT_USING_GCC)
# GCC is not officially supported, so use a weaker set of warnings (incl. not using -Werror).
set(FILAMENT_WARNINGS
-Wall -Wextra-semi -Wundef
-Wno-unused-parameter -Wno-missing-field-initializers
-Wno-unused-but-set-variable -Wno-unused-variable -Wno-unused-function
-Wnon-virtual-dtor -Wimplicit-fallthrough
-Wno-strict-aliasing
-Wno-changes-meaning -Wno-return-type
-Wno-invalid-constexpr -Wno-parentheses
-Wno-sign-compare -Wno-deprecated-declarations
)
else()
set(FILAMENT_WARNINGS
-Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers
-24
View File
@@ -104,8 +104,6 @@ if (FILAMENT_SUPPORTS_OPENGL AND NOT FILAMENT_USE_EXTERNAL_GLES3)
list(APPEND SRCS src/VirtualMachineEnv.cpp)
endif ()
if (ANDROID)
list(APPEND SRCS src/AndroidNativeWindow.cpp)
list(APPEND SRCS src/AndroidSwapChainHelper.cpp)
list(APPEND SRCS src/opengl/platforms/ExternalStreamManagerAndroid.cpp)
list(APPEND SRCS src/opengl/platforms/PlatformEGLAndroid.cpp)
elseif (IOS)
@@ -181,7 +179,6 @@ endif()
if (FILAMENT_SUPPORTS_VULKAN)
list(APPEND SRCS
include/backend/platforms/VulkanPlatform.h
src/vulkan/VulkanAsyncHandles.cpp
src/vulkan/VulkanAsyncHandles.h
src/vulkan/VulkanBlitter.cpp
src/vulkan/VulkanBlitter.h
@@ -220,10 +217,6 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/VulkanReadPixels.h
src/vulkan/VulkanSamplerCache.cpp
src/vulkan/VulkanSamplerCache.h
src/vulkan/VulkanSemaphore.cpp
src/vulkan/VulkanSemaphore.h
src/vulkan/VulkanSemaphoreManager.cpp
src/vulkan/VulkanSemaphoreManager.h
src/vulkan/VulkanStagePool.cpp
src/vulkan/VulkanStagePool.h
src/vulkan/VulkanSwapChain.cpp
@@ -296,8 +289,6 @@ 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
@@ -471,13 +462,6 @@ if (MSVC)
elseif(WEBGL)
# Avoid strict-vtable-pointers here, it is broken in WebAssembly.
set(OPTIMIZATION_FLAGS -fvisibility-inlines-hidden)
elseif(FILAMENT_USING_GCC)
set(OPTIMIZATION_FLAGS
-ffast-math
-fno-finite-math-only
-ffp-contract=fast
-fvisibility-inlines-hidden
)
else()
set(OPTIMIZATION_FLAGS
-ffast-math
@@ -504,13 +488,6 @@ endif()
if (MSVC)
set(FILAMENT_WARNINGS /W3)
elseif (FILAMENT_USING_GCC)
# GCC is not officially supported, so use a weaker set of warnings (incl. not using -Werror)
set(FILAMENT_WARNINGS
-Wall -Wno-unused-parameter -Wno-missing-field-initializers
-Wdeprecated -Wnon-virtual-dtor -Wno-strict-aliasing
-Wno-invalid-constexpr -Wno-parentheses -Wno-unused-variable
)
else()
set(FILAMENT_WARNINGS
-Wall -Wextra -Wno-unused-parameter -Wno-missing-field-initializers
@@ -592,7 +569,6 @@ if (APPLE OR LINUX)
test/test_BufferUpdates.cpp
test/test_Callbacks.cpp
test/test_MemoryMappedBuffer.cpp
test/test_MsaaSwapChain.cpp
test/test_MRT.cpp
test/test_PushConstants.cpp
test/test_LoadImage.cpp
@@ -197,7 +197,6 @@ constexpr std::string_view to_string(Backend const backend) noexcept {
* - The Metal backend can prefer precompiled Metal libraries, while falling back to MSL.
*/
enum class ShaderLanguage {
UNSPECIFIED = -1,
ESSL1 = 0,
ESSL3 = 1,
SPIRV = 2,
@@ -220,8 +219,6 @@ constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) noex
return "Metal precompiled library";
case ShaderLanguage::WGSL:
return "WGSL";
case ShaderLanguage::UNSPECIFIED:
return "Unspecified";
}
return "UNKNOWN";
}
@@ -713,12 +710,6 @@ enum class Precision : uint8_t {
DEFAULT
};
union ConstantValue {
int32_t i;
float f;
bool b;
};
/**
* Shader compiler priority queue
*
@@ -1703,8 +1694,6 @@ 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 -103
View File
@@ -91,46 +91,6 @@ public:
using ExternalImageHandleRef = ExternalImageHandle const&;
struct CompositorTiming {
/** duration in nanosecond since epoch of std::steady_clock */
using time_point_ns = int64_t;
/** duration in nanosecond on the std::steady_clock */
using duration_ns = int64_t;
/**
* The timestamp [ns] since epoch of the next time the compositor will begin composition.
* This is effectively the deadline for when the compositor must receive a newly queued
* frame.
*/
time_point_ns compositeDeadline;
/**
* The time delta [ns] between subsequent composition events.
*/
duration_ns compositeInterval;
/**
* The time delta [ns] between the start of composition and the expected present time of
* that composition. This can be used to estimate the latency of the actual present time.
*/
duration_ns compositeToPresentLatency;
};
struct FrameTimestamps {
/** duration in nanosecond since epoch of std::steady_clock */
using time_point_ns = int64_t;
static constexpr time_point_ns INVALID = -1; //!< value not supported
static constexpr time_point_ns PENDING = -2; //!< value not yet available
time_point_ns requestedPresentTime;
time_point_ns acquireTime;
time_point_ns latchTime;
time_point_ns firstRefreshStartTime;
time_point_ns lastRefreshStartTime;
time_point_ns gpuCompositionDoneTime;
time_point_ns displayPresentTime;
time_point_ns dequeueReadyTime;
time_point_ns releaseTime;
};
/**
* The type of technique for stereoscopic rendering. (Note that the materials used will need to
* be compatible with the chosen technique.)
@@ -276,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) = 0;
const DriverConfig& driverConfig) noexcept = 0;
/**
* Processes the platform's event queue when called from its primary event-handling thread.
@@ -287,65 +247,6 @@ public:
*/
virtual bool pumpEvents() noexcept;
// --------------------------------------------------------------------------------------------
// Swapchain timing APIs
/**
* Whether this platform supports compositor timing querying.
*
* @return true if this Platform supports compositor timings, false otherwise [default]
* @see queryCompositorTiming()
* @see setPresentFrameId()
* @see queryFrameTimestamps()
*/
virtual bool isCompositorTimingSupported() const noexcept;
/**
* If compositor timing is supported, fills the provided CompositorTiming structure
* with timing information form the compositor the swapchain's native window is using.
* The swapchain'snative window must be valid (i.e. not a headless swapchain).
* @param swapchain to query the compositor timing from
* @return true on success, false otherwise (e.g. if not supported)
* @see isCompositorTimingSupported()
*/
virtual bool queryCompositorTiming(SwapChain const* UTILS_NONNULL swapchain,
CompositorTiming* UTILS_NONNULL outCompositorTiming) const noexcept;
/**
* Associate a generic frameId which must be monotonically increasing (albeit not strictly) with
* the next frame to be presented on the specified swapchain.
*
* This must be called from the backend thread.
*
* @param swapchain
* @param frameId
* @return true on success, false otherwise
* @see isCompositorTimingSupported()
* @see queryFrameTimestamps()
*/
virtual bool setPresentFrameId(SwapChain const* UTILS_NONNULL swapchain,
uint64_t frameId) noexcept;
/**
* If compositor timing is supported, fills the provided FrameTimestamps structure
* with timing information of a given frame, identified by the frame id, of the specified
* swapchain. The system only keeps a limited history of frames timings.
*
* This API is thread safe and can be called from any thread.
*
* @param swapchain swapchain to query the timestamps of
* @param frameId frame we're interested it
* @param outFrameTimestamps output structure receiving the timestamps
* @return true if successful, false otherwise
* @see isCompositorTimingSupported()
* @see setPresentFrameId()
*/
virtual bool queryFrameTimestamps(SwapChain const* UTILS_NONNULL swapchain,
uint64_t frameId, FrameTimestamps* UTILS_NONNULL outFrameTimestamps) const noexcept;
// --------------------------------------------------------------------------------------------
// Caching APIs
/**
* InsertBlobFunc is an Invocable to an application-provided function that a
* backend implementation may use to insert a key/value pair into the
@@ -438,9 +339,6 @@ public:
size_t retrieveBlob(const void* UTILS_NONNULL key, size_t keySize,
void* UTILS_NONNULL value, size_t valueSize);
// --------------------------------------------------------------------------------------------
// Debugging APIs
using DebugUpdateStatFunc = utils::Invocable<void(const char* UTILS_NONNULL key, uint64_t value)>;
/**
+6 -2
View File
@@ -50,7 +50,11 @@ public:
descriptor_binding_t binding;
};
using SpecializationConstant = std::variant<int32_t, float, bool>;
struct SpecializationConstant {
using Type = std::variant<int32_t, float, bool>;
uint32_t id; // id set in glsl
Type value; // value and type
};
struct Uniform { // For ES2 support
utils::CString name; // full qualified name of the uniform field
@@ -114,7 +118,7 @@ public:
Program& multiview(bool multiview) noexcept;
// For ES2 support only...
Program& uniforms(uint32_t index, utils::CString name, UniformInfo uniforms);
Program& uniforms(uint32_t index, utils::CString name, UniformInfo uniforms) noexcept;
Program& attributes(AttributesInfo attributes) noexcept;
//
@@ -112,7 +112,7 @@ public:
*
*/
virtual SwapChain* UTILS_NULLABLE createSwapChain(
void* UTILS_NULLABLE nativeWindow, uint64_t flags) = 0;
void* UTILS_NULLABLE nativeWindow, uint64_t flags) noexcept = 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) = 0;
uint32_t width, uint32_t height, uint64_t flags) noexcept = 0;
/**
* Called by the driver to destroys the SwapChain
@@ -430,7 +430,7 @@ public:
virtual bool isExtraContextSupported() const noexcept;
/**
* Creates an OpenGL context with the same configuration as the main context and makes it
* Creates an OpenGL context with the same configuration than the main context and makes it
* current to the current thread. Must not be called from the main driver thread.
* createContext() is only supported if isExtraContextSupported() returns true.
* These additional contexts will be automatically terminated in terminate.
@@ -443,7 +443,7 @@ public:
/**
* Detach and destroy the current context if any and releases all resources associated to
* this thread. This must be called from the same thread where createContext() was called.
* this thread.
*/
virtual void releaseContext() noexcept;
};
@@ -40,7 +40,7 @@ protected:
// Platform Interface
Driver* createDriver(void* sharedContext,
const DriverConfig& driverConfig) override;
const DriverConfig& driverConfig) noexcept override;
// Currently returns 0
int getOSVersion() const noexcept override;
@@ -38,7 +38,7 @@ public:
// Platform Interface
Driver* createDriver(void* sharedGLContext,
const DriverConfig& driverConfig) override;
const DriverConfig& driverConfig) noexcept override;
int getOSVersion() const noexcept final { return 0; }
@@ -25,7 +25,6 @@
#include <EGL/eglext.h>
#include <EGL/eglplatform.h>
#include <utils/compiler.h>
#include <utils/Invocable.h>
#include <initializer_list>
@@ -81,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) override;
Driver* createDriver(void* sharedContext, const DriverConfig& driverConfig) noexcept override;
/**
* This returns zero. This method can be overridden to return something more useful.
@@ -101,6 +100,10 @@ protected:
bool isProtectedContextSupported() const noexcept override;
bool isSRGBSwapChainSupported() const noexcept override;
bool isMSAASwapChainSupported(uint32_t samples) const noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool isSwapChainProtected(SwapChain* swapChain) noexcept override;
ContextType getCurrentContextType() const noexcept override;
@@ -155,9 +158,17 @@ protected:
return egl.makeCurrent(context, mEGLDummySurface, mEGLDummySurface);
}
EGLDisplay getEglDisplay() const noexcept { return mEGLDisplay; }
EGLConfig getEglConfig() const noexcept { return mEGLConfig; }
EGLConfig getSuitableConfigForSwapChain(uint64_t flags, bool window, bool pbuffer) const;
// TODO: this should probably use getters instead.
EGLDisplay mEGLDisplay = EGL_NO_DISPLAY;
EGLContext mEGLContext = EGL_NO_CONTEXT;
EGLContext mEGLContextProtected = EGL_NO_CONTEXT;
EGLSurface mEGLDummySurface = EGL_NO_SURFACE;
ContextType mCurrentContextType = ContextType::NONE;
// mEGLConfig is valid only if ext.egl.KHR_no_config_context is false
EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR;
Config mContextAttribs;
std::vector<EGLContext> mAdditionalContexts;
bool mMSAA4XSupport = false;
// supported extensions detected at runtime
struct {
@@ -174,11 +185,7 @@ protected:
} egl;
} ext;
struct SwapChainEGL : public SwapChain {
SwapChainEGL(PlatformEGL const& platform, void* nativeWindow, uint64_t flags);
SwapChainEGL(PlatformEGL const& platform, uint32_t width, uint32_t height, uint64_t flags);
void terminate(PlatformEGL& platform);
struct SwapChainEGL : public Platform::SwapChain {
EGLSurface sur = EGL_NO_SURFACE;
Config attribs{};
EGLNativeWindowType nativeWindow{};
@@ -194,25 +201,10 @@ protected:
~ExternalImageEGL() override;
};
private:
// prevent derived classes' implementations to call through
[[nodiscard]] SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) override;
[[nodiscard]] SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
protected:
EGLConfig findSwapChainConfig(uint64_t flags, bool window, bool pbuffer) const;
EGLDisplay mEGLDisplay = EGL_NO_DISPLAY;
EGLContext mEGLContext = EGL_NO_CONTEXT;
EGLContext mEGLContextProtected = EGL_NO_CONTEXT;
EGLSurface mEGLDummySurface = EGL_NO_SURFACE;
ContextType mCurrentContextType = ContextType::NONE;
// mEGLConfig is valid only if ext.egl.KHR_no_config_context is false
EGLConfig mEGLConfig = EGL_NO_CONFIG_KHR;
Config mContextAttribs;
std::vector<EGLContext> mAdditionalContexts;
bool mMSAA4XSupport = false;
private:
class EGL {
EGLDisplay& mEGLDisplay;
EGLSurface mCurrentDrawSurface = EGL_NO_SURFACE;
@@ -17,10 +17,7 @@
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_EGL_ANDROID_H
#include "AndroidSwapChainHelper.h"
#include <backend/AcquiredImage.h>
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/platforms/PlatformEGL.h>
@@ -28,8 +25,6 @@
#include <utils/android/PerformanceHintManager.h>
#include <utils/compiler.h>
#include <math/mat3.h>
#include <chrono>
#include <stddef.h>
@@ -72,17 +67,9 @@ public:
* @return `true` on success, `false` on failure. The default implementation
* returns `false`.
*/
bool convertSyncToFd(Sync* sync, int* fd) noexcept;
bool convertSyncToFd(Platform::Sync* sync, int* fd) noexcept;
protected:
struct {
struct {
bool ANDROID_presentation_time = false;
bool ANDROID_get_frame_timestamps = false;
bool ANDROID_native_fence_sync = false;
} egl;
} ext;
// --------------------------------------------------------------------------------------------
// Platform Interface
@@ -93,22 +80,12 @@ protected:
int getOSVersion() const noexcept override;
Driver* createDriver(void* sharedContext,
const DriverConfig& driverConfig) override;
bool isCompositorTimingSupported() const noexcept override;
bool queryCompositorTiming(SwapChain const* swapchain,
CompositorTiming* outCompositorTiming) const noexcept override;
bool setPresentFrameId(SwapChain const* swapchain, uint64_t frameId) noexcept override;
bool queryFrameTimestamps(SwapChain const* swapchain, uint64_t frameId,
FrameTimestamps* outFrameTimestamps) const noexcept override;
const Platform::DriverConfig& driverConfig) noexcept override;
// --------------------------------------------------------------------------------------------
// OpenGLPlatform Interface
struct SyncEGLAndroid : public Sync {
struct SyncEGLAndroid : public Platform::Sync {
EGLSyncKHR sync;
};
@@ -129,8 +106,8 @@ protected:
Stream* createStream(void* nativeStream) noexcept override;
void destroyStream(Stream* stream) noexcept override;
Sync* createSync() noexcept override;
void destroySync(Sync* sync) noexcept override;
Platform::Sync* createSync() noexcept override;
void destroySync(Platform::Sync* sync) noexcept override;
void attach(Stream* stream, intptr_t tname) noexcept override;
void detach(Stream* stream) noexcept override;
void updateTexImage(Stream* stream, int64_t* timestamp) noexcept override;
@@ -143,7 +120,7 @@ protected:
*/
AcquiredImage transformAcquiredImage(AcquiredImage source) noexcept override;
ExternalTexture* createExternalImageTexture() noexcept override;
OpenGLPlatform::ExternalTexture* createExternalImageTexture() noexcept override;
void destroyExternalImageTexture(ExternalTexture* texture) noexcept override;
struct ExternalImageEGLAndroid : public ExternalImageEGL {
@@ -163,28 +140,12 @@ protected:
bool setImage(ExternalImageEGLAndroid const* eglExternalImage,
ExternalTexture* texture) noexcept;
protected:
bool makeCurrent(ContextType type,
SwapChain* drawSwapChain,
SwapChain* readSwapChain) override;
struct SwapChainEGLAndroid : public SwapChainEGL {
SwapChainEGLAndroid(PlatformEGLAndroid const& platform,
void* nativeWindow, uint64_t flags);
SwapChainEGLAndroid(PlatformEGLAndroid const& platform,
uint32_t width, uint32_t height, uint64_t flags);
void terminate(PlatformEGLAndroid& platform);
bool setPresentFrameId(uint64_t frameId) const noexcept;
uint64_t getFrameId(uint64_t frameId) const noexcept;
private:
AndroidSwapChainHelper mImpl{};
};
private:
// prevent derived classes' implementations to call through
[[nodiscard]] SwapChain* createSwapChain(void* nativeWindow, uint64_t flags) override;
[[nodiscard]] SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
struct InitializeJvmForPerformanceManagerIfNeeded {
InitializeJvmForPerformanceManagerIfNeeded();
};
@@ -198,15 +159,13 @@ private:
InitializeJvmForPerformanceManagerIfNeeded const mInitializeJvmForPerformanceManagerIfNeeded;
utils::PerformanceHintManager mPerformanceHintManager;
utils::PerformanceHintManager::Session mPerformanceHintSession;
SwapChainEGLAndroid* mCurrentDrawSwapChain{};
using clock = std::chrono::high_resolution_clock;
clock::time_point mStartTimeOfActualWork;
int32_t (*ANativeWindow_setProducerThrottlingEnabled)(ANativeWindow* window, bool enabled) = nullptr;
int32_t (*ANativeWindow_isProducerThrottlingEnabled)(ANativeWindow* window, bool* outEnabled) = nullptr;
void* mNativeWindowLib = nullptr;
int32_t (*ANativeWindow_getBuffersDefaultDataSpace)(ANativeWindow* window) = nullptr;
bool mAssertNativeWindowIsValid = false;
bool mHasProducerThrottlingControl = false;
};
} // namespace filament::backend
@@ -29,7 +29,7 @@ public:
PlatformEGLHeadless() noexcept;
Driver* createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) override;
const Platform::DriverConfig& driverConfig) noexcept override;
protected:
bool isOpenGL() const noexcept override;
@@ -26,10 +26,7 @@
#include <backend/DriverEnums.h>
#include <shared_mutex>
#include <thread>
#include <vector>
#include <unordered_map>
namespace filament::backend {
@@ -42,7 +39,7 @@ protected:
// Platform Interface
Driver* createDriver(void* sharedGLContext,
const DriverConfig& driverConfig) override;
const DriverConfig& driverConfig) noexcept override;
int getOSVersion() const noexcept final override { return 0; }
@@ -51,10 +48,6 @@ protected:
void terminate() noexcept override;
bool isExtraContextSupported() const noexcept override;
void createContext(bool shared) override;
void releaseContext() noexcept override;
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
@@ -62,15 +55,11 @@ 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;
// Variables for shared contexts
std::unordered_map<std::thread::id, GLXContext> mAdditionalContexts;
std::shared_mutex mAdditionalContextsLock;
};
} // namespace filament::backend
@@ -36,7 +36,7 @@ public:
PlatformMetal();
~PlatformMetal() noexcept override;
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) override;
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
int getOSVersion() const noexcept override { return 0; }
/**
@@ -43,7 +43,7 @@ protected:
// --------------------------------------------------------------------------------------------
// Platform Interface
Driver* createDriver(void* sharedGLContext, const DriverConfig& driverConfig) override;
Driver* createDriver(void* sharedGLContext, const DriverConfig& driverConfig) noexcept override;
int getOSVersion() const noexcept final override { return 0; }
@@ -38,7 +38,7 @@ protected:
// Platform Interface
Driver* createDriver(void* sharedGLContext,
const Platform::DriverConfig& driverConfig) override;
const Platform::DriverConfig& driverConfig) noexcept override;
int getOSVersion() const noexcept final override { return 0; }
@@ -61,12 +61,8 @@ protected:
HWND mHWnd = NULL;
HDC mWhdc = NULL;
PIXELFORMATDESCRIPTOR mPfd = {};
std::vector<int> mAttribs;
// For shared contexts
static constexpr int SHARED_CONTEXT_NUM = 1;
std::vector<HGLRC> mAdditionalContexts;
std::atomic<int> mNextFreeSharedContextIndex{0};
std::vector<int> mAttribs;
};
} // namespace filament::backend
@@ -34,7 +34,7 @@ protected:
// Platform Interface
Driver* createDriver(void* sharedGLContext,
const Platform::DriverConfig& driverConfig) override;
const Platform::DriverConfig& driverConfig) noexcept override;
int getOSVersion() const noexcept override;
@@ -119,7 +119,7 @@ public:
~VulkanPlatform() override;
Driver* createDriver(void* sharedContext,
Platform::DriverConfig const& driverConfig) override;
Platform::DriverConfig const& driverConfig) noexcept override;
int getOSVersion() const noexcept override {
return 0;
@@ -72,7 +72,7 @@ public:
protected:
[[nodiscard]] Driver* createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) override;
const Platform::DriverConfig& driverConfig) noexcept 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 const execute) noexcept : mExecute(execute) {}
constexpr explicit CommandBase(Execute 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
CommandBase* execute(Driver& driver) {
inline 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);
}
~CommandBase() noexcept = default;
inline ~CommandBase() noexcept = default;
private:
Execute mExecute;
@@ -135,7 +135,7 @@ struct CommandType<void (Driver::*)(ARGS...)> {
public:
template<typename M, typename D>
static void execute(M&& method, D&& driver, CommandBase* base, intptr_t* next) {
static inline 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
Command(Command&& rhs) noexcept = default;
inline Command(Command&& rhs) noexcept = default;
template<typename... A>
explicit constexpr Command(Execute const execute, A&& ... args) noexcept
inline explicit constexpr Command(Execute execute, A&& ... args)
: CommandBase(execute), mArgs(std::forward<A>(args)...) {
}
// placement new declared as "throw" to avoid the compiler's null-check
void* operator new(std::size_t, void* ptr) noexcept {
inline void* operator new(std::size_t, void* ptr) {
assert_invariant(ptr);
return ptr;
}
@@ -171,9 +171,8 @@ class CustomCommand : public CommandBase {
std::function<void()> mCommand;
static void execute(Driver&, CommandBase* base, intptr_t* next);
public:
CustomCommand(CustomCommand&& rhs) = default;
explicit CustomCommand(std::function<void()> cmd)
inline CustomCommand(CustomCommand&& rhs) = default;
inline explicit CustomCommand(std::function<void()> cmd)
: CommandBase(execute), mCommand(std::move(cmd)) { }
};
@@ -185,7 +184,7 @@ class NoopCommand : public CommandBase {
*next = static_cast<NoopCommand*>(self)->mNext;
}
public:
constexpr explicit NoopCommand(void* next) noexcept
inline constexpr explicit NoopCommand(void* next) noexcept
: CommandBase(execute), mNext(intptr_t((char *)next - (char *)this)) { }
};
@@ -208,8 +207,8 @@ class CommandStream {
template<typename T>
struct AutoExecute {
T closure;
explicit AutoExecute(T&& closure) : closure(std::forward<T>(closure)) {}
~AutoExecute() { closure(); }
inline explicit AutoExecute(T&& closure) : closure(std::forward<T>(closure)) {}
inline ~AutoExecute() { closure(); }
};
public:
@@ -222,7 +221,7 @@ public:
public:
#define DECL_DRIVER_API(methodName, paramsDecl, params) \
inline void methodName(paramsDecl) noexcept { \
inline void methodName(paramsDecl) { \
DEBUG_COMMAND_BEGIN(methodName, false, params); \
using Cmd = COMMAND_TYPE(methodName); \
void* const p = allocateCommand(CommandBase::align(sizeof(Cmd))); \
@@ -231,7 +230,7 @@ public:
}
#define DECL_DRIVER_API_SYNCHRONOUS(RetType, methodName, paramsDecl, params) \
inline RetType methodName(paramsDecl) noexcept { \
inline RetType methodName(paramsDecl) { \
DEBUG_COMMAND_BEGIN(methodName, true, params); \
AutoExecute callOnExit([=, this](){ \
DEBUG_COMMAND_END(methodName, true); \
@@ -240,7 +239,7 @@ public:
}
#define DECL_DRIVER_API_RETURN(RetType, methodName, paramsDecl, params) \
inline RetType methodName(paramsDecl) noexcept { \
inline RetType methodName(paramsDecl) { \
DEBUG_COMMAND_BEGIN(methodName, false, params); \
RetType result = mDriver.methodName##S(); \
using Cmd = COMMAND_TYPE(methodName##R); \
@@ -256,7 +255,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.
void debugThreading() noexcept {
inline void debugThreading() noexcept {
#ifndef NDEBUG
mThreadId = utils::ThreadUtils::getThreadId();
#endif
@@ -282,11 +281,11 @@ public:
*/
template<typename PodType,
typename = typename std::enable_if<std::is_trivially_destructible<PodType>::value>::type>
PodType* allocatePod(
inline PodType* allocatePod(
size_t count = 1, size_t alignment = alignof(PodType)) noexcept;
private:
void* allocateCommand(size_t const size) noexcept {
inline void* allocateCommand(size_t size) {
assert_invariant(utils::ThreadUtils::isThisThread(mThreadId));
return mCurrentBuffer.allocate(size);
}
@@ -27,8 +27,6 @@
#include <backend/AcquiredImage.h>
#include <utils/CString.h>
#include <utils/ImmutableCString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/compiler.h>
#include <functional>
@@ -73,11 +71,7 @@ public:
virtual ShaderModel getShaderModel() const noexcept = 0;
// The shader languages used for shaders for this driver in order of preference, used to inform
// matdbg.
//
// The `preferredLanguage` is only a hint. If the backend supports it, it will appear first in
// the list.
// The shader language used for shaders for this driver, used to inform matdbg.
//
// For OpenGL, this distinguishes whether the driver's shaders are powered by ESSL1 or ESSL3.
// This information is used by matdbg to display the correct shader code to the web UI and patch
@@ -85,8 +79,7 @@ public:
//
// Metal shaders can either be MSL or Metal libraries, but at time of writing, matdbg can only
// interface with MSL.
virtual utils::FixedCapacityVector<ShaderLanguage> getShaderLanguages(
ShaderLanguage preferredLanguage) const noexcept = 0;
virtual ShaderLanguage getShaderLanguage() const noexcept = 0;
// Returns the dispatcher. This is only called once during initialization of the CommandStream,
// so it doesn't matter that it's virtual.
@@ -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::ImmutableCString&&, tag = {}), \
PAIR_ARGS_N(PARAM, ##__VA_ARGS__, utils::ImmutableCString&&, tag))
DECL_DRIVER_API_RETURN(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__, utils::CString&&, tag = {}), \
PAIR_ARGS_N(PARAM, ##__VA_ARGS__, utils::CString&&, tag))
#define DECL_DRIVER_API_SYNCHRONOUS_N(R, N, ...) \
DECL_DRIVER_API_SYNCHRONOUS(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__), PAIR_ARGS_N(PARAM, ##__VA_ARGS__))
#define DECL_DRIVER_API_SYNCHRONOUS_TAGGED_N(R, N, ...) \
DECL_DRIVER_API_SYNCHRONOUS(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__, utils::ImmutableCString, tag = {}), \
PAIR_ARGS_N(PARAM, ##__VA_ARGS__, utils::ImmutableCString, tag))
DECL_DRIVER_API_SYNCHRONOUS(R, N, PAIR_ARGS_N(ARG, ##__VA_ARGS__, utils::CString, tag = {}), \
PAIR_ARGS_N(PARAM, ##__VA_ARGS__, utils::CString, tag))
// on some compilers the ##__VA_ARGS__ hack is not supported, so we can't handle 0-parameter APIs
// with DECL_DRIVER_API_SYNCHRONOUS_N
@@ -336,7 +336,6 @@ DECL_DRIVER_API_SYNCHRONOUS_N(void, setStreamDimensions, backend::StreamHandle,
DECL_DRIVER_API_SYNCHRONOUS_N(int64_t, getStreamTimestamp, backend::StreamHandle, stream)
DECL_DRIVER_API_SYNCHRONOUS_N(void, updateStreams, backend::DriverApi*, driver)
DECL_DRIVER_API_SYNCHRONOUS_N(backend::FenceStatus, getFenceStatus, backend::FenceHandle, fh)
DECL_DRIVER_API_SYNCHRONOUS_N(backend::FenceStatus, fenceWait, backend::FenceHandle, fh, uint64_t, timeout)
DECL_DRIVER_API_SYNCHRONOUS_N(void, getPlatformSync, backend::SyncHandle, sh,
backend::CallbackHandler*, handler, backend::Platform::SyncCallback, cb, void*, userData)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isTextureFormatSupported, backend::TextureFormat, format)
@@ -366,7 +365,6 @@ DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage, void*, image)
DECL_DRIVER_API_SYNCHRONOUS_N(backend::TimerQueryResult, getTimerQueryValue, backend::TimerQueryHandle, tqh, uint64_t*, elapsedTime)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isWorkaroundNeeded, backend::Workaround, workaround)
DECL_DRIVER_API_SYNCHRONOUS_0(backend::FeatureLevel, getFeatureLevel)
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getUniformBufferOffsetAlignment)
/*
* Updating driver objects
@@ -418,6 +416,14 @@ 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)
@@ -450,36 +456,6 @@ 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
*/
@@ -497,6 +473,11 @@ 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)
@@ -557,6 +538,18 @@ 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,7 +21,6 @@
#include <utils/Allocator.h>
#include <utils/CString.h>
#include <utils/ImmutableCString.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/compiler.h>
@@ -53,16 +52,16 @@ namespace filament::backend {
class DebugTag {
public:
DebugTag();
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;
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;
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::ImmutableCString> mDebugTags;
tsl::robin_map<HandleBase::HandleId, utils::CString> mDebugTags;
};
/*
@@ -71,12 +70,12 @@ private:
template<size_t P0, size_t P1, size_t P2>
class HandleAllocator : public DebugTag {
public:
HandleAllocator(const char* name, size_t size);
HandleAllocator(const char* name, size_t size) noexcept;
HandleAllocator(const char* name, size_t size,
bool disableUseAfterFreeCheck, bool disableHeapHandleTags);
bool disableUseAfterFreeCheck, bool disableHeapHandleTags) noexcept;
HandleAllocator(HandleAllocator const& rhs) = delete;
HandleAllocator& operator=(HandleAllocator const& rhs) = delete;
~HandleAllocator() noexcept;
~HandleAllocator();
/*
* Constructs a D object and returns a Handle<D>
@@ -107,7 +106,7 @@ public:
*
*/
template<typename D>
Handle<D> allocate() {
Handle<D> allocate() noexcept {
Handle<D> h{ allocateHandle<D>() };
return h;
}
@@ -223,7 +222,7 @@ public:
return static_cast<Dp>(p);
}
utils::ImmutableCString getHandleTag(HandleBase::HandleId key) const noexcept;
utils::CString getHandleTag(HandleBase::HandleId key) const noexcept;
template<typename B>
bool is_valid(Handle<B>& handle) {
@@ -249,7 +248,7 @@ public:
return handle_cast<Dp>(const_cast<Handle<B>&>(handle));
}
void associateTagToHandle(HandleBase::HandleId id, utils::ImmutableCString&& tag) noexcept {
void associateTagToHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
if (tag.empty()) {
return;
}
@@ -343,7 +342,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() {
HandleBase::HandleId allocateHandle() noexcept {
constexpr size_t BUCKET_SIZE = getBucketSize<D>();
return allocateHandleInPool<BUCKET_SIZE>();
}
@@ -359,7 +358,7 @@ private:
// the code generated is not trivial (even if it's not insane either).
template<size_t SIZE>
UTILS_NOINLINE
HandleBase::HandleId allocateHandleInPool() {
HandleBase::HandleId allocateHandleInPool() noexcept {
uint8_t age;
void* p = mHandleArena.alloc(SIZE, alignof(std::max_align_t), 0, &age);
if (UTILS_LIKELY(p)) {
@@ -1,78 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "AndroidNativeWindow.h"
#include <android/native_window.h>
#include <utils/compiler.h>
#include <cstdint>
#include <cerrno>
#include <utility>
namespace filament::backend {
std::pair<int, bool> NativeWindow::isValid(ANativeWindow* const anw) noexcept {
if (__builtin_available(android 28, *)) {
// this a proxy for is_valid()
auto const result = ANativeWindow_getBuffersDataSpace(anw);
return { result, result >= 0 };
}
// fallback on using private APIs
NativeWindow const* pWindow = reinterpret_cast<NativeWindow const*>(anw);
if (UTILS_LIKELY(pWindow->query)) {
int isValid = 0;
int const err = pWindow->query(anw, IS_VALID, &isValid);
return { err, bool(isValid) };
}
return { -EINVAL, false };
}
int NativeWindow::getNextFrameId(ANativeWindow* anw, uint64_t* frameId) {
NativeWindow const* pWindow = reinterpret_cast<NativeWindow const*>(anw);
return pWindow->perform(anw, GET_NEXT_FRAME_ID, frameId);
}
int NativeWindow::enableFrameTimestamps(ANativeWindow* anw, bool enable) {
NativeWindow const* pWindow = reinterpret_cast<NativeWindow const*>(anw);
return pWindow->perform(anw, ENABLE_FRAME_TIMESTAMPS, enable);
}
int NativeWindow::getCompositorTiming(ANativeWindow* anw,
int64_t* compositeDeadline, int64_t* compositeInterval,
int64_t* compositeToPresentLatency) {
NativeWindow const* pWindow = reinterpret_cast<NativeWindow const*>(anw);
return pWindow->perform(anw, GET_COMPOSITOR_TIMING,
compositeDeadline, compositeInterval, compositeToPresentLatency);
}
int NativeWindow::getFrameTimestamps(ANativeWindow* anw,
uint64_t frameId,
int64_t* outRequestedPresentTime, int64_t* outAcquireTime,
int64_t* outLatchTime, int64_t* outFirstRefreshStartTime,
int64_t* outLastRefreshStartTime, int64_t* outGpuCompositionDoneTime,
int64_t* outDisplayPresentTime, int64_t* outDequeueReadyTime,
int64_t* outReleaseTime) {
NativeWindow const* pWindow = reinterpret_cast<NativeWindow const*>(anw);
return pWindow->perform(anw, GET_FRAME_TIMESTAMPS,
frameId, outRequestedPresentTime, outAcquireTime, outLatchTime,
outFirstRefreshStartTime, outLastRefreshStartTime,
outGpuCompositionDoneTime, outDisplayPresentTime,
outDequeueReadyTime, outReleaseTime);
}
} // namespace filament::backend
@@ -1,68 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FILAMENT_BACKEND_ANDROIDNATIVEWINDOW_H
#define FILAMENT_BACKEND_ANDROIDNATIVEWINDOW_H
#include <android/native_window.h>
#include <cstdint>
#include <utility>
namespace filament::backend {
struct NativeWindow {
// this class can't be instantiated. It's used by casting ANativeWindow* to it.
NativeWindow() noexcept = delete;
~NativeWindow() noexcept = delete;
// is valid query enum value
enum {
IS_VALID = 17,
GET_NEXT_FRAME_ID = 24,
ENABLE_FRAME_TIMESTAMPS = 25,
GET_COMPOSITOR_TIMING = 26,
GET_FRAME_TIMESTAMPS = 27,
};
#if defined(__LP64__)
uint64_t pad[18];
#else
uint32_t pad[21];
#endif
int (*query)(ANativeWindow const*, int, int*);
int (*perform)(ANativeWindow*, int, ...);
// return whether the ANativeWindow is valid
static std::pair<int, bool> isValid(ANativeWindow* anw) noexcept;
static int getNextFrameId(ANativeWindow* anw, uint64_t* frameId);
static int enableFrameTimestamps(ANativeWindow* anw, bool enable);
static int getCompositorTiming(ANativeWindow* anw,
int64_t* compositeDeadline, int64_t* compositeInterval,
int64_t* compositeToPresentLatency);
static int getFrameTimestamps(ANativeWindow* anw,
uint64_t frameId,
int64_t* outRequestedPresentTime, int64_t* outAcquireTime,
int64_t* outLatchTime, int64_t* outFirstRefreshStartTime,
int64_t* outLastRefreshStartTime, int64_t* outGpuCompositionDoneTime,
int64_t* outDisplayPresentTime, int64_t* outDequeueReadyTime,
int64_t* outReleaseTime);
};
} // namespace filament::backend
#endif //FILAMENT_BACKEND_ANDROIDNATIVEWINDOW_H
@@ -1,63 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "AndroidSwapChainHelper.h"
#include "AndroidNativeWindow.h"
#include <android/native_window.h>
#include <cstddef>
#include <cstdint>
#include <limits>
#include <mutex>
namespace filament::backend {
AndroidSwapChainHelper::AndroidSwapChainHelper() = default;
AndroidSwapChainHelper::~AndroidSwapChainHelper() noexcept = default;
bool AndroidSwapChainHelper::setPresentFrameId(
ANativeWindow* anw, uint64_t frameId) const noexcept {
uint64_t sysFrameId{};
int const status = NativeWindow::getNextFrameId(anw, &sysFrameId);
if (status == 0) {
std::lock_guard const lock(mLock);
auto const pos = mFrameIdToSystemFrameId.find(frameId);
if (pos != mFrameIdToSystemFrameId.end() && pos->second != sysFrameId) {
// we're trying to associate the same frame id to a different frame!
return false;
}
// make space by destroying the oldest entry
if (mFrameIdToSystemFrameId.size() >= MAX_HISTORY_SIZE) {
mFrameIdToSystemFrameId.erase(mFrameIdToSystemFrameId.begin());
}
mFrameIdToSystemFrameId.insert(mFrameIdToSystemFrameId.end(), { frameId, sysFrameId });
return true;
}
return false;
}
uint64_t AndroidSwapChainHelper::getFrameId(uint64_t frameId) const noexcept {
std::lock_guard const lock(mLock);
auto pos = mFrameIdToSystemFrameId.find(frameId);
if (pos != mFrameIdToSystemFrameId.end()) {
return pos->second;
}
return std::numeric_limits<uint64_t>::max();
}
}
@@ -1,47 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_ANDROIDSWAPCHAINHELPER_H
#define TNT_FILAMENT_BACKEND_ANDROIDSWAPCHAINHELPER_H
#include <utils/Mutex.h>
#include <android/native_window.h>
#include <map>
#include <cstddef>
#include <cstdint>
namespace filament::backend {
struct AndroidSwapChainHelper {
AndroidSwapChainHelper();
~AndroidSwapChainHelper() noexcept;
// methods below are thread-safe
bool setPresentFrameId(ANativeWindow* anw, uint64_t frameId) const noexcept;
uint64_t getFrameId(uint64_t frameId) const noexcept;
private:
static constexpr size_t MAX_HISTORY_SIZE = 32;
mutable utils::Mutex mLock; // very low-contention lock
mutable std::map<uint64_t, uint64_t> mFrameIdToSystemFrameId{};
};
}
#endif //TNT_FILAMENT_BACKEND_ANDROIDSWAPCHAINHELPER_H
+1 -1
View File
@@ -20,7 +20,7 @@
namespace filament::backend {
CallbackManager::CallbackManager(DriverBase& driver)
CallbackManager::CallbackManager(DriverBase& driver) noexcept
: mDriver(driver), mCallbacks(1) {
}
+2 -2
View File
@@ -51,7 +51,7 @@ class CallbackManager {
public:
using Handle = Container::const_iterator;
explicit CallbackManager(DriverBase& driver);
explicit CallbackManager(DriverBase& driver) noexcept;
~CallbackManager() noexcept;
@@ -78,7 +78,7 @@ private:
return --mCallbacks.end();
}
Container::iterator allocateNewSlot() {
Container::iterator allocateNewSlot() noexcept {
std::lock_guard const lock(mLock);
auto curr = --mCallbacks.end();
mCallbacks.emplace_back();
+3 -6
View File
@@ -57,15 +57,12 @@ 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 { // NOLINT(*-exception-escape)
constexpr char startPattern[] = "::Command<&filament::backend::Driver::";
static UTILS_NOINLINE UTILS_UNUSED std::string_view extractMethodName(std::string_view command) noexcept {
constexpr const char startPattern[] = "::Command<&filament::backend::Driver::";
auto pos = command.rfind(startPattern);
auto end = command.rfind('(');
pos += sizeof(startPattern) - 1;
if (pos > command.size()) {
return { command.data(), command.size() };
}
return command.substr(pos, end-pos); // this can't throw by construction
return command.substr(pos, end-pos);
}
// ------------------------------------------------------------------------------------------------
+1 -8
View File
@@ -18,14 +18,7 @@
#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 {
@@ -43,7 +36,7 @@ CompilerThreadPool::~CompilerThreadPool() noexcept {
}
void CompilerThreadPool::init(uint32_t threadCount,
ThreadSetup&& threadSetup, ThreadCleanup&& threadCleanup) {
ThreadSetup&& threadSetup, ThreadCleanup&& threadCleanup) noexcept {
auto setup = std::make_shared<ThreadSetup>(std::move(threadSetup));
auto cleanup = std::make_shared<ThreadCleanup>(std::move(threadCleanup));
+2 -1
View File
@@ -47,7 +47,8 @@ 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);
void init(uint32_t threadCount,
ThreadSetup&& threadSetup, ThreadCleanup&& threadCleanup) noexcept;
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) {
void DriverBase::scheduleDestroySlow(BufferDescriptor&& buffer) noexcept {
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) {
// 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) {
void DriverBase::scheduleRelease(AcquiredImage const& image) noexcept {
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) {
void scheduleDestroy(BufferDescriptor&& buffer) noexcept {
if (buffer.hasCallback()) {
scheduleDestroySlow(std::move(buffer));
}
}
void scheduleDestroySlow(BufferDescriptor&& buffer);
void scheduleDestroySlow(BufferDescriptor&& buffer) noexcept;
void scheduleRelease(AcquiredImage const& image);
void scheduleRelease(AcquiredImage const& image) noexcept;
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)
bool disableHeapHandleTags) noexcept
: 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)
HandleAllocator<P0, P1, P2>::HandleAllocator(const char* name, size_t size) noexcept
: HandleAllocator(name, size, false, false) {
}
template <size_t P0, size_t P1, size_t P2>
HandleAllocator<P0, P1, P2>::~HandleAllocator() noexcept {
HandleAllocator<P0, P1, P2>::~HandleAllocator() {
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
ImmutableCString HandleAllocator<P0, P1, P2>::getHandleTag(HandleBase::HandleId id) const noexcept {
CString 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
ImmutableCString DebugTag::findHandleTag(HandleBase::HandleId key) const noexcept {
CString 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 @@ ImmutableCString DebugTag::findHandleTag(HandleBase::HandleId key) const noexcep
}
UTILS_NOINLINE
void DebugTag::writePoolHandleTag(HandleBase::HandleId key, ImmutableCString&& tag) noexcept {
void DebugTag::writePoolHandleTag(HandleBase::HandleId key, CString&& 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, ImmutableCString&& t
}
UTILS_NOINLINE
void DebugTag::writeHeapHandleTag(HandleBase::HandleId key, ImmutableCString&& tag) noexcept {
void DebugTag::writeHeapHandleTag(HandleBase::HandleId key, CString&& tag) noexcept {
// This line is the costly part. In the future, we could potentially use a custom
// allocator.
std::unique_lock const lock(mDebugTagLock);
-16
View File
@@ -122,22 +122,6 @@ bool Platform::pumpEvents() noexcept {
return false;
}
bool Platform::isCompositorTimingSupported() const noexcept {
return false;
}
bool Platform::queryCompositorTiming(SwapChain const*, CompositorTiming*) const noexcept {
return false;
}
bool Platform::setPresentFrameId(SwapChain const*, uint64_t) noexcept {
return false;
}
bool Platform::queryFrameTimestamps(SwapChain const*, uint64_t, FrameTimestamps*) const noexcept {
return false;
}
void Platform::setBlobFunc(InsertBlobFunc&& insertBlob, RetrieveBlobFunc&& retrieveBlob) noexcept {
mInsertBlob = std::move(insertBlob);
mRetrieveBlob = std::move(retrieveBlob);
+9 -9
View File
@@ -41,37 +41,37 @@ Program& Program::operator=(Program&& rhs) noexcept = default;
Program::~Program() noexcept = default;
Program& Program::priorityQueue(CompilerPriorityQueue const priorityQueue) noexcept {
Program& Program::priorityQueue(CompilerPriorityQueue priorityQueue) noexcept {
mPriorityQueue = priorityQueue;
return *this;
}
Program& Program::diagnostics(CString const& name,
Invocable<io::ostream&(CString const& name, io::ostream&)>&& logger) {
Invocable<io::ostream&(utils::CString const& name, io::ostream&)>&& logger) {
mName = name;
mLogger = std::move(logger);
return *this;
}
Program& Program::shader(ShaderStage shader, void const* data, size_t const size) {
Program& Program::shader(ShaderStage shader, void const* data, size_t size) {
ShaderBlob blob(size);
std::copy_n((const uint8_t *)data, size, blob.data());
mShadersSource[size_t(shader)] = std::move(blob);
return *this;
}
Program& Program::shaderLanguage(ShaderLanguage const shaderLanguage) {
Program& Program::shaderLanguage(ShaderLanguage shaderLanguage) {
mShaderLanguage = shaderLanguage;
return *this;
}
Program& Program::descriptorBindings(descriptor_set_t const set,
Program& Program::descriptorBindings(backend::descriptor_set_t set,
DescriptorBindingsInfo descriptorBindings) noexcept {
mDescriptorBindings[set] = std::move(descriptorBindings);
return *this;
}
Program& Program::uniforms(uint32_t index, CString name, UniformInfo uniforms) {
Program& Program::uniforms(uint32_t index, utils::CString name, UniformInfo uniforms) noexcept {
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,
FixedCapacityVector<PushConstant> constants) noexcept {
utils::FixedCapacityVector<PushConstant> constants) noexcept {
mPushConstants[static_cast<uint8_t>(stage)] = std::move(constants);
return *this;
}
Program& Program::cacheId(uint64_t const cacheId) noexcept {
Program& Program::cacheId(uint64_t cacheId) noexcept {
mCacheId = cacheId;
return *this;
}
Program& Program::multiview(bool const multiview) noexcept {
Program& Program::multiview(bool multiview) noexcept {
mMultiview = multiview;
return *this;
}
+12 -3
View File
@@ -19,7 +19,6 @@
#include "MetalContext.h"
#include "MetalFlags.h"
#include "MetalUtils.h"
#include <backend/DriverEnums.h>
#include <backend/platforms/PlatformMetal.h>
@@ -193,7 +192,7 @@ public:
void* getCpuBuffer() const noexcept { return mCpuBuffer; }
void setLabel(const utils::ImmutableCString& label) {
void setLabel(const utils::CString& label) {
#if FILAMENT_METAL_DEBUG_LABELS
if (label.empty()) {
return;
@@ -261,8 +260,18 @@ public:
// In practice, MetalRingBuffer is used for argument buffers, which are kept in the constant
// address space. Constant buffers have specific alignment requirements when specifying an
// offset.
#if defined(FILAMENT_IOS)
#if TARGET_OS_SIMULATOR
// The iOS simulator has differing alignment requirements.
static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 256;
#else
static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 4;
#endif // TARGET_OS_SIMULATOR
#else
static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 32;
#endif
static inline auto computeSlotSize(MTLSizeAndAlign layout) {
return align(align(layout.size, layout.align), getUniformBufferOffsetAlignment());
return align(align(layout.size, layout.align), METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT);
}
MetalRingBuffer(id<MTLDevice> device, MTLResourceOptions options, MTLSizeAndAlign layout,
+2 -33
View File
@@ -95,38 +95,6 @@ void initializeSupportedGpuFamilies(MetalContext* context) {
}
}
void logMTLCommandBufferError(MTLCommandBufferError error) {
#define MTL_COMMAND_ERROR_CASE(ERR) \
if (error == (ERR)) { \
LOG(ERROR) << "Filament Metal error: " #ERR "."; \
return; \
}
#if !defined(FILAMENT_IOS)
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorDeviceRemoved)
#endif
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorNone)
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorInternal)
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorTimeout)
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorPageFault)
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorAccessRevoked)
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorNotPermitted)
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorOutOfMemory)
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorInvalidResource)
if (@available(macOS 11.0, *)) {
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorMemoryless)
}
if (@available(iOS 15.0, macOS 12.0, *)) {
MTL_COMMAND_ERROR_CASE(MTLCommandBufferErrorStackOverflow)
}
LOG(ERROR) << "Filament Metal unknown error.";
#undef MTL_COMMAND_ERROR_CASE
}
id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
if (context->pendingCommandBuffer) {
return context->pendingCommandBuffer;
@@ -152,7 +120,8 @@ id<MTLCommandBuffer> getPendingCommandBuffer(MetalContext* context) {
}
if (UTILS_UNLIKELY(errorCode != MTLCommandBufferErrorNone)) {
logMTLCommandBufferError(errorCode);
LOG(ERROR) << "Filament Metal command buffer errored with code: " << errorCode << " ("
<< stringifyMTLCommandBufferError(errorCode) << ").";
}
}];
FILAMENT_CHECK_POSTCONDITION(context->pendingCommandBuffer)
+2 -4
View File
@@ -25,9 +25,8 @@
#include <backend/SamplerDescriptor.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Log.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/debug.h>
#include <functional>
@@ -70,8 +69,7 @@ private:
MetalContext* mContext;
ShaderModel getShaderModel() const noexcept final;
utils::FixedCapacityVector<ShaderLanguage> getShaderLanguages(
ShaderLanguage preferredLanguage) const noexcept final;
ShaderLanguage getShaderLanguage() const noexcept final;
// Overrides the default implementation by wrapping the call to fn in an @autoreleasepool block.
void execute(std::function<void(void)> const& fn) noexcept final;
+48 -67
View File
@@ -29,7 +29,6 @@
#include "MetalHandles.h"
#include "MetalState.h"
#include "MetalTimerQuery.h"
#include "MetalUtils.h"
#include <backend/platforms/PlatformMetal.h>
#include <backend/platforms/PlatformMetal-ObjC.h>
@@ -43,7 +42,6 @@
#include <utils/Logger.h>
#include <utils/Panic.h>
#include <utils/sstream.h>
#include <utils/ImmutableCString.h>
#include <algorithm>
@@ -420,14 +418,14 @@ void MetalDriver::finish(int) {
}
void MetalDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes, utils::ImmutableCString&& tag) {
uint8_t attributeCount, AttributeArray attributes, utils::CString&& tag) {
construct_handle<MetalVertexBufferInfo>(vbih, *mContext,
bufferCount, attributeCount, attributes);
mHandleAllocator.associateTagToHandle(vbih.getId(), std::move(tag));
}
void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih, utils::ImmutableCString&& tag) {
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih, utils::CString&& tag) {
MetalVertexBufferInfo const* const vbi = handle_cast<const MetalVertexBufferInfo>(vbih);
construct_handle<MetalVertexBuffer>(vbh, *mContext, vertexCount, vbi->bufferCount, vbih);
mHandleAllocator.associateTagToHandle(vbh.getId(), std::move(tag));
@@ -435,7 +433,7 @@ void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
}
void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage, utils::ImmutableCString&& tag) {
uint32_t indexCount, BufferUsage usage, utils::CString&& tag) {
auto elementSize = (uint8_t)getElementTypeSize(elementType);
auto* indexBuffer =
construct_handle<MetalIndexBuffer>(ibh, *mContext, usage, elementSize, indexCount);
@@ -448,7 +446,7 @@ void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elem
}
void MetalDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage, utils::ImmutableCString&& tag) {
BufferObjectBinding bindingType, BufferUsage usage, utils::CString&& tag) {
auto* bufferObject =
construct_handle<MetalBufferObject>(boh, *mContext, bindingType, usage, byteCount);
FILAMENT_CHECK_POSTCONDITION(bufferObject->getBuffer()->wasAllocationSuccessful())
@@ -490,7 +488,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::ImmutableCString&& tag) {
uint32_t depth, TextureUsage usage, utils::CString&& tag) {
// Clamp sample count to what the device supports.
auto& sc = mContext->sampleCountLookup;
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
@@ -510,7 +508,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::ImmutableCString&& tag) {
uint8_t baseLevel, uint8_t levelCount, utils::CString&& tag) {
MetalTexture const* src = handle_cast<MetalTexture>(srch);
MetalTexture* texture =
construct_handle<MetalTexture>(th, *mContext, src, baseLevel, levelCount);
@@ -521,7 +519,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::ImmutableCString&& tag) {
backend::TextureSwizzle a, utils::CString&& tag) {
MetalTexture const* src = handle_cast<MetalTexture>(srch);
MetalTexture* texture = construct_handle<MetalTexture>(th, *mContext, src, r, g, b, a);
mContext->textures.insert(texture);
@@ -533,13 +531,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::ImmutableCString&& tag) {
Platform::ExternalImageHandleRef image, utils::CString&& tag) {
// FIXME: implement createTextureExternalImage2R
}
void MetalDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, utils::ImmutableCString&& tag) {
void* image, utils::CString&& tag) {
MetalTexture* texture = construct_handle<MetalTexture>(th, *mContext, format, width, height,
usage, (CVPixelBufferRef) image);
mContext->textures.insert(texture);
@@ -552,7 +550,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::ImmutableCString&& tag) {
void* image, uint32_t plane, utils::CString&& tag) {
MetalTexture* texture = construct_handle<MetalTexture>(th, *mContext, format, width, height,
usage, (CVPixelBufferRef) image, plane);
mContext->textures.insert(texture);
@@ -566,7 +564,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::ImmutableCString&& tag) {
uint32_t depth, TextureUsage usage, utils::CString&& tag) {
id<MTLTexture> metalTexture = (id<MTLTexture>) CFBridgingRelease((void*) i);
FILAMENT_CHECK_PRECONDITION(metalTexture.width == width)
<< "Imported id<MTLTexture> width (" << metalTexture.width
@@ -590,13 +588,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::ImmutableCString&& tag) {
PrimitiveType pt, utils::CString&& tag) {
construct_handle<MetalRenderPrimitive>(rph);
MetalDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh);
mHandleAllocator.associateTagToHandle(rph.getId(), std::move(tag));
}
void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program, utils::ImmutableCString&& tag) {
void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program, utils::CString&& tag) {
#if FILAMENT_METAL_DEBUG_LOG
auto handleId = rph.getId();
DEBUG_LOG("createProgramR(rph = %d, program = ", handleId);
@@ -606,7 +604,7 @@ void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program, utils
mHandleAllocator.associateTagToHandle(rph.getId(), std::move(tag));
}
void MetalDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils::ImmutableCString&& tag) {
void MetalDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, utils::CString&& tag) {
construct_handle<MetalRenderTarget>(rth, mContext);
mHandleAllocator.associateTagToHandle(rth.getId(), std::move(tag));
}
@@ -614,16 +612,14 @@ 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::ImmutableCString&& tag) {
TargetBufferInfo depth, TargetBufferInfo stencil, utils::CString&& tag) {
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
<< "createRenderTarget must be called outside of a render pass.";
// Clamp sample count to what the device supports.
auto& sc = mContext->sampleCountLookup;
samples = sc[std::min(MAX_SAMPLE_COUNT, samples)];
using AttachmentInfo = MetalRenderTarget::AttachmentInfo;
AttachmentInfo colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = { {} };
MetalRenderTarget::Attachment colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {{}};
for (size_t i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
if (none(targetBufferFlags & getTargetBufferFlagsAt(i))) {
continue;
@@ -637,7 +633,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
colorAttachments[i] = { colorTexture, color[i].level, color[i].layer };
}
AttachmentInfo depthAttachment = {};
MetalRenderTarget::Attachment depthAttachment = {};
if (any(targetBufferFlags & TargetBufferFlags::DEPTH)) {
FILAMENT_CHECK_PRECONDITION(depth.handle)
<< "The DEPTH flag was specified, but invalid depth handle provided.";
@@ -647,7 +643,7 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
depthAttachment = { depthTexture, depth.level, depth.layer };
}
AttachmentInfo stencilAttachment = {};
MetalRenderTarget::Attachment stencilAttachment = {};
if (any(targetBufferFlags & TargetBufferFlags::STENCIL)) {
FILAMENT_CHECK_PRECONDITION(stencil.handle)
<< "The STENCIL flag was specified, but invalid stencil handle provided.";
@@ -663,14 +659,16 @@ void MetalDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
mHandleAllocator.associateTagToHandle(rth.getId(), std::move(tag));
}
void MetalDriver::createFenceR(Handle<HwFence> fh, utils::ImmutableCString&& tag) {
void MetalDriver::createFenceR(Handle<HwFence> fh, utils::CString&& tag) {
auto* fence = handle_cast<MetalFence>(fh);
fence->encode();
mHandleAllocator.associateTagToHandle(fh.getId(), std::move(tag));
}
void MetalDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags,
utils::ImmutableCString&& tag) {
utils::CString&& tag) {
// TODO: support MSAA swapchain
if (UTILS_UNLIKELY(flags & SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER)) {
CVPixelBufferRef pixelBuffer = (CVPixelBufferRef) nativeWindow;
construct_handle<MetalSwapChain>(sch, *mContext, mPlatform, pixelBuffer, flags);
@@ -685,18 +683,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::ImmutableCString&& tag) {
uint32_t width, uint32_t height, uint64_t flags, utils::CString&& tag) {
construct_handle<MetalSwapChain>(sch, *mContext, mPlatform, width, height, flags);
mHandleAllocator.associateTagToHandle(sch.getId(), std::move(tag));
}
void MetalDriver::createSyncR(Handle<HwSync> sh, utils::ImmutableCString&& tag) {
void MetalDriver::createSyncR(Handle<HwSync> sh, utils::CString&& tag) {
// TODO: Ensure sync is active, and then invoke and clear all pending
// callbacks.
mHandleAllocator.associateTagToHandle(sh.getId(), std::move(tag));
}
void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::ImmutableCString&& tag) {
void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, utils::CString&& tag) {
// nothing to do, timer query was constructed in createTimerQueryS
mHandleAllocator.associateTagToHandle(tqh.getId(), std::move(tag));
}
@@ -735,12 +733,12 @@ const char* toString(DescriptorFlags flags) {
}
void MetalDriver::createDescriptorSetLayoutR(
Handle<HwDescriptorSetLayout> dslh, DescriptorSetLayout&& info, utils::ImmutableCString&& tag) {
Handle<HwDescriptorSetLayout> dslh, DescriptorSetLayout&& info, utils::CString&& 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::ImmutableCString> || std::is_same_v<T, utils::StaticString>) {
if constexpr (std::is_same_v<T, utils::CString> || std::is_same_v<T, utils::StaticString>) {
labelStr = arg.c_str();
}
}, info.label);
@@ -760,7 +758,7 @@ void MetalDriver::createDescriptorSetLayoutR(
}
void MetalDriver::createDescriptorSetR(
Handle<HwDescriptorSet> dsh, Handle<HwDescriptorSetLayout> dslh, utils::ImmutableCString&& tag) {
Handle<HwDescriptorSet> dsh, Handle<HwDescriptorSetLayout> dslh, utils::CString&& tag) {
DEBUG_LOG("createDescriptorSetR(dsh = %d, dslh = %d)\n", dsh.getId(), dslh.getId());
MetalDescriptorSetLayout* layout = handle_cast<MetalDescriptorSetLayout>(dslh);
MetalDescriptorSet* ds = construct_handle<MetalDescriptorSet>(dsh, layout);
@@ -1004,19 +1002,15 @@ ShaderModel MetalDriver::getShaderModel() const noexcept {
#endif
}
utils::FixedCapacityVector<ShaderLanguage> MetalDriver::getShaderLanguages(
ShaderLanguage preferredLanguage) const noexcept {
if (preferredLanguage == backend::ShaderLanguage::MSL) {
return { backend::ShaderLanguage::MSL, backend::ShaderLanguage::METAL_LIBRARY };
}
return { backend::ShaderLanguage::METAL_LIBRARY, backend::ShaderLanguage::MSL };
ShaderLanguage MetalDriver::getShaderLanguage() const noexcept {
return ShaderLanguage::MSL;
}
Handle<HwStream> MetalDriver::createStreamNative(void* stream, utils::ImmutableCString tag) {
Handle<HwStream> MetalDriver::createStreamNative(void* stream, utils::CString tag) {
return {};
}
Handle<HwStream> MetalDriver::createStreamAcquired(utils::ImmutableCString tag) {
Handle<HwStream> MetalDriver::createStreamAcquired(utils::CString tag) {
return {};
}
@@ -1039,22 +1033,16 @@ void MetalDriver::updateStreams(DriverApi* driver) {
void MetalDriver::destroyFence(Handle<HwFence> fh) {
if (fh) {
auto* fence = handle_cast<MetalFence>(fh);
fence->cancel();
destruct_handle<MetalFence>(fh);
}
}
FenceStatus MetalDriver::getFenceStatus(Handle<HwFence> fh) {
return fenceWait(fh, 0);
}
FenceStatus MetalDriver::fenceWait(FenceHandle fh, uint64_t const timeout) {
auto* fence = handle_cast<MetalFence>(fh);
if (!fence) {
return FenceStatus::ERROR;
}
return fence->wait(timeout);
return fence->wait(0);
}
void MetalDriver::destroySync(Handle<HwSync> sh) {
@@ -1152,7 +1140,8 @@ bool MetalDriver::isSRGBSwapChainSupported() {
}
bool MetalDriver::isMSAASwapChainSupported(uint32_t) {
return true;
// TODO: support MSAA swapchain
return false;
}
bool MetalDriver::isProtectedContentSupported() {
@@ -1254,10 +1243,6 @@ size_t MetalDriver::getMaxArrayTextureLayers() {
return 256;
}
size_t MetalDriver::getUniformBufferOffsetAlignment() {
return ::filament::backend::getUniformBufferOffsetAlignment();
}
void MetalDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& data,
uint32_t byteOffset) {
FILAMENT_CHECK_PRECONDITION(data.buffer)
@@ -1562,9 +1547,9 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
auto srcTarget = handle_cast<MetalRenderTarget>(src);
// We always readPixels from the COLOR0 attachment.
MetalAttachment color = srcTarget->getDrawColorAttachment(0);
MetalRenderTarget::Attachment color = srcTarget->getDrawColorAttachment(0);
id<MTLTexture> srcTexture = color.getTexture();
size_t miplevel = color.getLevel();
size_t miplevel = color.level;
// Clamp height and width to actual texture's height and width
MTLSize srcTextureSize = MTLSizeMake(srcTexture.width >> miplevel, srcTexture.height >> miplevel, 1);
@@ -1772,8 +1757,8 @@ void MetalDriver::blitDEPRECATED(TargetBufferFlags buffers,
};
// We always blit from/to the COLOR0 attachment.
MetalAttachment const srcColorAttachment = srcTarget->getReadColorAttachment(0);
MetalAttachment const dstColorAttachment = dstTarget->getDrawColorAttachment(0);
MetalRenderTarget::Attachment const srcColorAttachment = srcTarget->getReadColorAttachment(0);
MetalRenderTarget::Attachment const dstColorAttachment = dstTarget->getDrawColorAttachment(0);
if (srcColorAttachment && dstColorAttachment) {
FILAMENT_CHECK_PRECONDITION(
@@ -1785,13 +1770,13 @@ void MetalDriver::blitDEPRECATED(TargetBufferFlags buffers,
args.filter = filter;
args.source.region = srcTarget->getRegionFromClientRect(srcRect);
args.source.texture = srcColorAttachment.getTexture();
args.source.level = srcColorAttachment.getLevel();
args.source.slice = srcColorAttachment.getLayer();
args.source.level = srcColorAttachment.level;
args.source.slice = srcColorAttachment.layer;
args.destination.region = dstTarget->getRegionFromClientRect(dstRect);
args.destination.texture = dstColorAttachment.getTexture();
args.destination.level = dstColorAttachment.getLevel();
args.destination.slice = dstColorAttachment.getLayer();
args.destination.level = dstColorAttachment.level;
args.destination.slice = dstColorAttachment.layer;
mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "blitDEPRECATED");
}
@@ -1827,28 +1812,24 @@ void MetalDriver::bindPipeline(PipelineState const& ps) {
auto [fragment, vertex] = functions.getRasterFunctions();
// Pipeline state
MetalRenderTarget* const rt = mContext->currentRenderTarget;
MTLPixelFormat colorPixelFormat[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = { MTLPixelFormatInvalid };
for (size_t i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
const auto& attachment = rt->getDrawColorAttachment(i);
const auto& attachment = mContext->currentRenderTarget->getDrawColorAttachment(i);
if (!attachment) {
continue;
}
colorPixelFormat[i] = attachment.getPixelFormat();
assert_invariant(attachment.getSampleCount() == rt->getSampleCount());
}
MTLPixelFormat depthPixelFormat = MTLPixelFormatInvalid;
const auto& depthAttachment = rt->getDepthAttachment();
const auto& depthAttachment = mContext->currentRenderTarget->getDepthAttachment();
if (depthAttachment) {
depthPixelFormat = depthAttachment.getPixelFormat();
assert_invariant(depthAttachment.getSampleCount() == rt->getSampleCount());
}
MTLPixelFormat stencilPixelFormat = MTLPixelFormatInvalid;
const auto& stencilAttachment = rt->getStencilAttachment();
const auto& stencilAttachment = mContext->currentRenderTarget->getStencilAttachment();
if (stencilAttachment) {
stencilPixelFormat = stencilAttachment.getPixelFormat();
assert_invariant(isMetalFormatStencil(stencilPixelFormat));
assert_invariant(stencilAttachment.getSampleCount() == rt->getSampleCount());
}
MetalPipelineState const pipelineState {
.vertexFunction = vertex,
@@ -1866,7 +1847,7 @@ void MetalDriver::bindPipeline(PipelineState const& ps) {
},
.depthAttachmentPixelFormat = depthPixelFormat,
.stencilAttachmentPixelFormat = stencilPixelFormat,
.sampleCount = rt->getSampleCount(),
.sampleCount = mContext->currentRenderTarget->getSamples(),
.blendState = BlendState {
.alphaBlendOperation = getMetalBlendOperation(rs.blendEquationAlpha),
.rgbBlendOperation = getMetalBlendOperation(rs.blendEquationRGB),
@@ -2242,7 +2223,7 @@ MemoryMappedBufferHandle MetalDriver::mapBufferS() noexcept {
void MetalDriver::mapBufferR(MemoryMappedBufferHandle mmbh,
BufferObjectHandle boh, size_t offset,
size_t size, MapBufferAccessFlags access, utils::ImmutableCString&& tag) {
size_t size, MapBufferAccessFlags access, utils::CString&& tag) {
construct_handle<MetalMemoryMappedBuffer>(mmbh, boh, offset, size, access);
mHandleAllocator.associateTagToHandle(mmbh.getId(), std::move(tag));
}
+32
View File
@@ -451,6 +451,38 @@ inline MTLTextureSwizzleChannels getSwizzleChannels(TextureSwizzle r, TextureSwi
getSwizzle(a));
}
inline const char* stringifyMTLCommandBufferError(MTLCommandBufferError error) {
#if !defined(FILAMENT_IOS)
if (error == MTLCommandBufferErrorDeviceRemoved) {
return "MTLCommandBufferErrorDeviceRemoved";
}
#endif
switch (error) {
case MTLCommandBufferErrorNone:
return "MTLCommandBufferErrorNone";
case MTLCommandBufferErrorInternal:
return "MTLCommandBufferErrorInternal";
case MTLCommandBufferErrorTimeout:
return "MTLCommandBufferErrorTimeout";
case MTLCommandBufferErrorPageFault:
return "MTLCommandBufferErrorPageFault";
case MTLCommandBufferErrorAccessRevoked:
return "MTLCommandBufferErrorAccessRevoked";
case MTLCommandBufferErrorNotPermitted:
return "MTLCommandBufferErrorNotPermitted";
case MTLCommandBufferErrorOutOfMemory:
return "MTLCommandBufferErrorOutOfMemory";
case MTLCommandBufferErrorInvalidResource:
return "MTLCommandBufferErrorInvalidResource";
case MTLCommandBufferErrorMemoryless:
return "MTLCommandBufferErrorMemoryless";
case MTLCommandBufferErrorStackOverflow:
return "MTLCommandBufferErrorStackOverflow";
default:
return "Unknown";
}
}
} // namespace backend
} // namespace filament
+60 -96
View File
@@ -49,61 +49,6 @@
namespace filament {
namespace backend {
class MetalAttachment {
public:
MetalAttachment() = default;
MetalAttachment(id<MTLTexture> texture, uint8_t level = 0, uint16_t layer = 0)
: mLevel(level),
mLayer(layer),
mTexture(texture) {
assert_invariant(texture);
}
explicit operator bool() const { return mTexture != nil; }
id<MTLTexture> getTexture() const { return mTexture; }
id<MTLTexture> getMsaaTexture() const { return mMsaaTexture; }
MTLPixelFormat getPixelFormat() const {
return mTexture ? mTexture.pixelFormat : MTLPixelFormatInvalid;
}
NSUInteger getSampleCount() const {
if (mMsaaTexture) {
return mMsaaTexture.sampleCount;
}
if (mTexture) {
return mTexture.sampleCount;
}
return 1u;
}
uint8_t getLevel() const { return mLevel; }
uint16_t getLayer() const { return mLayer; }
MetalAttachment withMsaaTexture(id<MTLTexture> msaa) const {
assert_invariant(mTexture != nil);
assert_invariant(mTexture.sampleCount == 1u);
MetalAttachment result = *this;
result.mMsaaTexture = msaa;
return result;
}
static MetalAttachment invalidAttachment() { return MetalAttachment(); }
private:
uint8_t mLevel = 0;
uint16_t mLayer = 0;
// The main texture for this Attachment. May be single-sampled or MSAA.
id<MTLTexture> mTexture = nil;
// The MSAA "sidecar" texture that will resolve into mTexture.
// If this is non-nil, mTexture must be single-sampled.
id<MTLTexture> mMsaaTexture = nil;
};
class MetalSwapChain : public HwSwapChain {
public:
@@ -121,14 +66,14 @@ public:
~MetalSwapChain();
// Acquires a texture that can be used to render into this SwapChain and returns a
// MetalAttachment. The texture source depends on the type of SwapChain:
// Acquires a texture that can be used to render into this SwapChain.
// The texture source depends on the type of SwapChain:
// - CAMetalLayer-backed: acquires the CAMetalDrawable and returns its texture.
// - Headless: lazily creates and returns a headless texture.
// May return an invalid attachemnt if a drawable cannot be acquired.
MetalAttachment acquireDrawable();
MetalAttachment acquireDepthTexture();
MetalAttachment acquireStencilTexture();
id<MTLTexture> acquireDrawable();
id<MTLTexture> acquireDepthTexture();
id<MTLTexture> acquireStencilTexture();
void releaseDrawable();
@@ -143,7 +88,6 @@ public:
NSUInteger getSurfaceWidth() const;
NSUInteger getSurfaceHeight() const;
NSUInteger getSampleCount() const;
bool isPixelBuffer() const { return type == SwapChainType::CVPIXELBUFFERREF; }
@@ -162,24 +106,13 @@ private:
void scheduleFrameScheduledCallback();
void scheduleFrameCompletedCallback();
MetalAttachment acquireBaseDrawable();
id<MTLTexture> ensureDepthStencilTexture(uint32_t width, uint32_t height);
id<MTLTexture> ensureMsaaColorTexture(MTLPixelFormat format, uint32_t width, uint32_t height,
uint8_t samples);
id<MTLTexture> ensureMsaaDepthStencilTexture(MTLPixelFormat format, uint32_t width,
uint32_t height, uint8_t samples);
static MTLPixelFormat decideDepthStencilFormat(uint64_t flags);
static id<MTLTexture> createMultisampledTexture(MetalContext const& context,
MTLPixelFormat format, uint32_t width, uint32_t height, uint8_t samples);
void ensureDepthStencilTexture();
MetalContext& context;
PlatformMetal& platform;
id<CAMetalDrawable> drawable = nil;
id<MTLTexture> depthStencilTexture = nil;
id<MTLTexture> msaaColor = nil;
id<MTLTexture> msaaDepthStencil = nil;
id<MTLTexture> headlessDrawable = nil;
MTLPixelFormat depthStencilFormat = MTLPixelFormatInvalid;
NSUInteger headlessWidth = 0;
@@ -188,7 +121,6 @@ private:
std::shared_ptr<std::mutex> layerDrawableMutex;
MetalExternalImage externalImage;
SwapChainType type;
uint64_t flags;
int64_t abandonedUntilFrame = -1;
@@ -335,7 +267,7 @@ public:
static MTLPixelFormat decidePixelFormat(MetalContext* context, TextureFormat format);
void setLabel(const utils::ImmutableCString& label) {
void setLabel(const utils::CString& label) {
#if FILAMENT_METAL_DEBUG_LABELS
if (label.empty()) {
return;
@@ -375,15 +307,51 @@ private:
class MetalRenderTarget : public HwRenderTarget {
public:
struct AttachmentInfo {
MetalTexture* texture = nullptr;
class Attachment {
public:
friend class MetalRenderTarget;
Attachment() = default;
Attachment(MetalTexture* metalTexture, uint8_t level = 0, uint16_t layer = 0) :
level(level), layer(layer),
texture(metalTexture->getMtlTextureForWrite()),
metalTexture(metalTexture) { }
id<MTLTexture> getTexture() const {
return texture;
}
NSUInteger getSampleCount() const {
return texture ? texture.sampleCount : 0u;
}
MTLPixelFormat getPixelFormat() const {
return texture ? texture.pixelFormat : MTLPixelFormatInvalid;
}
explicit operator bool() const {
return texture != nil;
}
uint8_t level = 0;
uint16_t layer = 0;
private:
id<MTLTexture> getMSAASidecarTexture() const {
// This should only be called from render targets associated with a MetalTexture.
assert_invariant(metalTexture);
return metalTexture->msaaSidecar;
}
id<MTLTexture> texture = nil;
MetalTexture* metalTexture = nullptr;
};
MetalRenderTarget(MetalContext* context, uint32_t width, uint32_t height, uint8_t samples,
AttachmentInfo colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
AttachmentInfo depthAttachment, AttachmentInfo stencilAttachment);
Attachment colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
Attachment depthAttachment, Attachment stencilAttachment);
explicit MetalRenderTarget(MetalContext* context)
: HwRenderTarget(0, 0), context(context), defaultRenderTarget(true) {}
@@ -419,14 +387,12 @@ public:
math::uint2 getAttachmentSize() noexcept;
MetalAttachment getDrawColorAttachment(size_t index);
MetalAttachment getReadColorAttachment(size_t index);
MetalAttachment getDepthAttachment();
MetalAttachment getStencilAttachment();
uint8_t getSamples() const { return samples; }
// Returns the number of samples that should be used in the pipeline state that renders to this
// render target. Takes into account "automagic" resolve and MSAA SwapChains.
NSUInteger getSampleCount() const;
Attachment getDrawColorAttachment(size_t index);
Attachment getReadColorAttachment(size_t index);
Attachment getDepthAttachment();
Attachment getStencilAttachment();
private:
@@ -436,12 +402,12 @@ private:
MetalContext* context;
bool defaultRenderTarget = false;
MetalAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
MetalAttachment depth = {};
MetalAttachment stencil = {};
math::uint2 attachmentSize = {};
uint8_t samples = 1;
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
Attachment depth = {};
Attachment stencil = {};
math::uint2 attachmentSize = {};
};
// MetalFence is used to implement both Fences and Syncs.
@@ -463,8 +429,6 @@ public:
API_AVAILABLE(ios(12.0))
void onSignal(MetalFenceSignalBlock block);
void cancel();
private:
MetalContext& context;
@@ -530,7 +494,7 @@ struct MetalDescriptorSet : public HwDescriptorSet {
void finalize(MetalDriver* driver);
void setLabel(const utils::ImmutableCString& l) {
void setLabel(const utils::CString& l) {
#if FILAMENT_METAL_DEBUG_LABELS
if (l.empty()) {
return;
@@ -563,7 +527,7 @@ struct MetalDescriptorSet : public HwDescriptorSet {
std::array<TrackedMetalBuffer, Program::SHADER_TYPE_COUNT> cachedBuffer = { nil };
#if FILAMENT_METAL_DEBUG_LABELS
utils::ImmutableCString label;
utils::CString label;
#endif
};
+177 -272
View File
@@ -116,8 +116,7 @@ MetalSwapChain::MetalSwapChain(
depthStencilFormat(decideDepthStencilFormat(flags)),
layer(nativeWindow),
layerDrawableMutex(std::make_shared<std::mutex>()),
type(SwapChainType::CAMETALLAYER),
flags(flags) {
type(SwapChainType::CAMETALLAYER) {
FILAMENT_CHECK_PRECONDITION([nativeWindow isKindOfClass:[CAMetalLayer class]])
<< "nativeWindow pointer of class "
@@ -148,8 +147,7 @@ MetalSwapChain::MetalSwapChain(MetalContext& context, PlatformMetal& platform, i
depthStencilFormat(decideDepthStencilFormat(flags)),
headlessWidth(width),
headlessHeight(height),
type(SwapChainType::HEADLESS),
flags(flags) {}
type(SwapChainType::HEADLESS) {}
MetalSwapChain::MetalSwapChain(MetalContext& context, PlatformMetal& platform,
CVPixelBufferRef pixelBuffer, uint64_t flags)
@@ -157,8 +155,7 @@ MetalSwapChain::MetalSwapChain(MetalContext& context, PlatformMetal& platform,
platform(platform),
depthStencilFormat(decideDepthStencilFormat(flags)),
externalImage(MetalExternalImage::createFromImage(context, pixelBuffer)),
type(SwapChainType::CVPIXELBUFFERREF),
flags(flags) {
type(SwapChainType::CVPIXELBUFFERREF) {
assert_invariant(flags & SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER);
MetalExternalImage::assertWritableImage(pixelBuffer);
assert_invariant(externalImage.isValid());
@@ -170,27 +167,6 @@ MTLPixelFormat MetalSwapChain::decideDepthStencilFormat(uint64_t flags) {
: MTLPixelFormatDepth32Float;
}
id<MTLTexture> MetalSwapChain::createMultisampledTexture(MetalContext const& context,
MTLPixelFormat format, uint32_t width, uint32_t height, uint8_t samples) {
MTLTextureDescriptor* descriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:format
width:width
height:height
mipmapped:NO];
descriptor.textureType = MTLTextureType2DMultisample;
descriptor.sampleCount = samples;
descriptor.usage = MTLTextureUsageRenderTarget;
descriptor.resourceOptions = MTLResourceStorageModePrivate;
if (context.supportsMemorylessRenderTargets) {
if (@available(macOS 11.0, *)) {
descriptor.resourceOptions = MTLResourceStorageModeMemoryless;
}
}
return [context.device newTextureWithDescriptor:descriptor];
}
MetalSwapChain::~MetalSwapChain() {
}
@@ -214,18 +190,65 @@ NSUInteger MetalSwapChain::getSurfaceHeight() const {
return (NSUInteger) layer.drawableSize.height;
}
NSUInteger MetalSwapChain::getSampleCount() const {
if (flags & flags & SwapChain::CONFIG_MSAA_4_SAMPLES) {
return 4u;
}
return 1u;
}
bool MetalSwapChain::isAbandoned() const {
return context.currentFrame < abandonedUntilFrame;
}
id<MTLTexture> MetalSwapChain::acquireDrawable() {
if (drawable) {
return drawable.texture;
}
if (isHeadless()) {
if (headlessDrawable) {
return headlessDrawable;
}
// For headless surfaces we construct a "fake" drawable, which is simply a renderable
// texture.
MTLTextureDescriptor* textureDescriptor = [MTLTextureDescriptor new];
textureDescriptor.pixelFormat = MTLPixelFormatBGRA8Unorm;
textureDescriptor.width = headlessWidth;
textureDescriptor.height = headlessHeight;
// Specify MTLTextureUsageShaderRead so the headless surface can be blitted from.
textureDescriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
#if defined(FILAMENT_IOS)
textureDescriptor.storageMode = MTLStorageModeShared;
#else
textureDescriptor.storageMode = MTLStorageModeManaged;
#endif
headlessDrawable = [context.device newTextureWithDescriptor:textureDescriptor];
return headlessDrawable;
}
if (isPixelBuffer()) {
return externalImage.getMtlTexture();
}
assert_invariant(isCaMetalLayer());
// CAMetalLayer's drawable pool is not thread safe. Use a mutex when
// calling -nextDrawable, or when releasing the last known reference
// to any CAMetalDrawable returned from a previous -nextDrawable.
{
std::lock_guard<std::mutex> lock(*layerDrawableMutex);
drawable = [layer nextDrawable];
}
if (UTILS_UNLIKELY(drawable == nil)) {
switch (platform.getDrawableFailureBehavior()) {
case PlatformMetal::DrawableFailureBehavior::PANIC:
FILAMENT_CHECK_POSTCONDITION(drawable != nil) << "Could not obtain drawable.";
break;
case PlatformMetal::DrawableFailureBehavior::ABORT_FRAME:
abandonedUntilFrame = context.currentFrame + 1;
return nil;
}
}
return drawable.texture;
}
void MetalSwapChain::releaseDrawable() {
if (drawable) {
std::lock_guard<std::mutex> lock(*layerDrawableMutex);
@@ -233,45 +256,32 @@ void MetalSwapChain::releaseDrawable() {
}
}
MetalAttachment MetalSwapChain::acquireDrawable() {
MetalAttachment attachment = acquireBaseDrawable();
if (auto texture = attachment.getTexture();
texture && flags & SwapChain::CONFIG_MSAA_4_SAMPLES) {
return attachment.withMsaaTexture(
ensureMsaaColorTexture(texture.pixelFormat, texture.width, texture.height, 4u));
}
return attachment;
id<MTLTexture> MetalSwapChain::acquireDepthTexture() {
ensureDepthStencilTexture();
assert_invariant(depthStencilTexture);
return depthStencilTexture;
}
MetalAttachment MetalSwapChain::acquireDepthTexture() {
MetalAttachment attachment =
MetalAttachment(ensureDepthStencilTexture(getSurfaceWidth(), getSurfaceHeight()));
if (auto texture = attachment.getTexture();
texture && flags & SwapChain::CONFIG_MSAA_4_SAMPLES) {
return attachment.withMsaaTexture(ensureMsaaDepthStencilTexture(texture.pixelFormat,
texture.width, texture.height, 4u));
id<MTLTexture> MetalSwapChain::acquireStencilTexture() {
if (!isMetalFormatStencil(depthStencilFormat)) {
return nil;
}
return attachment;
ensureDepthStencilTexture();
assert_invariant(depthStencilTexture);
return depthStencilTexture;
}
MetalAttachment MetalSwapChain::acquireStencilTexture() {
if (!(flags & SwapChain::CONFIG_HAS_STENCIL_BUFFER)) {
return MetalAttachment::invalidAttachment();
}
MetalAttachment attachment =
MetalAttachment(ensureDepthStencilTexture(getSurfaceWidth(), getSurfaceHeight()));
if (auto texture = attachment.getTexture();
texture && flags & SwapChain::CONFIG_MSAA_4_SAMPLES) {
return attachment.withMsaaTexture(ensureMsaaDepthStencilTexture(texture.pixelFormat,
texture.width, texture.height, 4u));
}
return attachment;
}
id<MTLTexture> MetalSwapChain::ensureDepthStencilTexture(uint32_t width, uint32_t height) {
if (UTILS_LIKELY(depthStencilTexture && depthStencilTexture.width == width &&
depthStencilTexture.height == height)) {
return depthStencilTexture;
void MetalSwapChain::ensureDepthStencilTexture() {
NSUInteger width = getSurfaceWidth();
NSUInteger height = getSurfaceHeight();
if (UTILS_LIKELY(depthStencilTexture)) {
// If the surface size has changed, we'll need to allocate a new depth/stencil texture.
if (UTILS_UNLIKELY(
depthStencilTexture.width != width || depthStencilTexture.height != height)) {
depthStencilTexture = nil;
} else {
return;
}
}
MTLTextureDescriptor* descriptor =
[MTLTextureDescriptor texture2DDescriptorWithPixelFormat:depthStencilFormat
@@ -280,26 +290,7 @@ id<MTLTexture> MetalSwapChain::ensureDepthStencilTexture(uint32_t width, uint32_
mipmapped:NO];
descriptor.usage = MTLTextureUsageRenderTarget;
descriptor.resourceOptions = MTLResourceStorageModePrivate;
return depthStencilTexture = [context.device newTextureWithDescriptor:descriptor];
}
id<MTLTexture> MetalSwapChain::ensureMsaaColorTexture(MTLPixelFormat format, uint32_t width, uint32_t height,
uint8_t samples) {
if (UTILS_LIKELY(msaaColor && msaaColor.pixelFormat == format && msaaColor.width == width &&
msaaColor.height == height && msaaColor.sampleCount == samples)) {
return msaaColor;
}
return msaaColor = createMultisampledTexture(context, format, width, height, samples);
}
id<MTLTexture> MetalSwapChain::ensureMsaaDepthStencilTexture(MTLPixelFormat format, uint32_t width,
uint32_t height, uint8_t samples) {
if (UTILS_LIKELY(msaaDepthStencil && msaaDepthStencil.pixelFormat == format &&
msaaDepthStencil.width == width && msaaDepthStencil.height == height &&
msaaDepthStencil.sampleCount == samples)) {
return msaaDepthStencil;
}
return msaaDepthStencil = createMultisampledTexture(context, format, width, height, samples);
depthStencilTexture = [context.device newTextureWithDescriptor:descriptor];
}
void MetalSwapChain::setFrameScheduledCallback(
@@ -463,62 +454,6 @@ void MetalSwapChain::scheduleFrameCompletedCallback() {
}];
}
MetalAttachment MetalSwapChain::acquireBaseDrawable() {
if (drawable) {
return MetalAttachment(drawable.texture);
}
if (isHeadless()) {
if (headlessDrawable) {
return MetalAttachment(headlessDrawable);
}
// For headless surfaces we construct a "fake" drawable, which is simply a renderable
// texture.
MTLTextureDescriptor* textureDescriptor = [MTLTextureDescriptor new];
textureDescriptor.pixelFormat = MTLPixelFormatBGRA8Unorm;
textureDescriptor.width = headlessWidth;
textureDescriptor.height = headlessHeight;
// Specify MTLTextureUsageShaderRead so the headless surface can be blitted from.
textureDescriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead;
#if defined(FILAMENT_IOS)
textureDescriptor.storageMode = MTLStorageModeShared;
#else
textureDescriptor.storageMode = MTLStorageModeManaged;
#endif
headlessDrawable = [context.device newTextureWithDescriptor:textureDescriptor];
return MetalAttachment(headlessDrawable);
}
if (isPixelBuffer()) {
return MetalAttachment(externalImage.getMtlTexture());
}
assert_invariant(isCaMetalLayer());
// CAMetalLayer's drawable pool is not thread safe. Use a mutex when
// calling -nextDrawable, or when releasing the last known reference
// to any CAMetalDrawable returned from a previous -nextDrawable.
{
std::lock_guard<std::mutex> lock(*layerDrawableMutex);
drawable = [layer nextDrawable];
}
if (UTILS_UNLIKELY(drawable == nil)) {
switch (platform.getDrawableFailureBehavior()) {
case PlatformMetal::DrawableFailureBehavior::PANIC:
FILAMENT_CHECK_POSTCONDITION(drawable != nil) << "Could not obtain drawable.";
break;
case PlatformMetal::DrawableFailureBehavior::ABORT_FRAME:
abandonedUntilFrame = context.currentFrame + 1;
return MetalAttachment::invalidAttachment();
}
}
return MetalAttachment(drawable.texture);
}
MetalBufferObject::MetalBufferObject(MetalContext& context, BufferObjectBinding bindingType,
BufferUsage usage, uint32_t byteCount)
: HwBufferObject(byteCount), buffer(context, bindingType, usage, byteCount) {}
@@ -1035,108 +970,90 @@ void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region
}
MetalRenderTarget::MetalRenderTarget(MetalContext* context, uint32_t width, uint32_t height,
uint8_t samples, AttachmentInfo colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
AttachmentInfo depthAttachment, AttachmentInfo stencilAttachment)
: HwRenderTarget(width, height),
context(context),
samples(samples) {
math::uint2 tmin = { std::numeric_limits<uint32_t>::max() };
UTILS_UNUSED_IN_RELEASE math::uint2 tmax = { 0 };
uint8_t samples, Attachment colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
Attachment depthAttachment, Attachment stencilAttachment) :
HwRenderTarget(width, height), context(context), samples(samples) {
math::uint2 tmin = {std::numeric_limits<uint32_t>::max()};
UTILS_UNUSED_IN_RELEASE math::uint2 tmax = {0};
UTILS_UNUSED_IN_RELEASE size_t attachmentCount = 0;
for (size_t i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
MetalTexture* const texture = colorAttachments[i].texture;
const auto& level = colorAttachments[i].level;
const auto& layer = colorAttachments[i].layer;
if (!texture) {
if (!colorAttachments[i]) {
continue;
}
color[i] = colorAttachments[i];
FILAMENT_CHECK_PRECONDITION(texture->samples <= samples)
FILAMENT_CHECK_PRECONDITION(color[i].getSampleCount() <= samples)
<< "MetalRenderTarget was initialized with a MSAA COLOR" << i
<< " texture, but sample count is " << samples << ".";
const auto twidth = std::max(1u, texture->width >> level);
const auto theight = std::max(1u, texture->height >> level);
auto t = color[i].metalTexture;
const auto twidth = std::max(1u, t->width >> color[i].level);
const auto theight = std::max(1u, t->height >> color[i].level);
tmin = { std::min(tmin.x, twidth), std::min(tmin.y, theight) };
tmax = { std::max(tmax.x, twidth), std::max(tmax.y, theight) };
attachmentCount++;
id<MTLTexture> mtlTexture = texture->getMtlTextureForWrite();
color[i] = MetalAttachment(mtlTexture, level, layer);
// If we were given a single-sampled texture but the samples parameter is > 1, we create
// a multisampled sidecar texture and do a resolve automatically.
if (samples > 1 && texture->samples == 1) {
auto& sidecar = texture->msaaSidecar;
if (samples > 1 && color[i].getSampleCount() == 1) {
auto& sidecar = color[i].metalTexture->msaaSidecar;
if (!sidecar) {
sidecar = createMultisampledTexture(mtlTexture.pixelFormat, texture->width,
texture->height, samples);
sidecar = createMultisampledTexture(color[i].getPixelFormat(),
color[i].metalTexture->width, color[i].metalTexture->height, samples);
}
color[i] = MetalAttachment(mtlTexture, level, layer).withMsaaTexture(sidecar);
}
}
if (depthAttachment.texture) {
MetalTexture* const texture = depthAttachment.texture;
const auto& level = depthAttachment.level;
const auto& layer = depthAttachment.layer;
if (depthAttachment) {
depth = depthAttachment;
FILAMENT_CHECK_PRECONDITION(texture->samples <= samples)
FILAMENT_CHECK_PRECONDITION(depth.getSampleCount() <= samples)
<< "MetalRenderTarget was initialized with a MSAA DEPTH texture, but sample count "
"is "
<< samples << ".";
const auto twidth = std::max(1u, texture->width >> level);
const auto theight = std::max(1u, texture->height >> level);
auto t = depth.metalTexture;
const auto twidth = std::max(1u, t->width >> depth.level);
const auto theight = std::max(1u, t->height >> depth.level);
tmin = { math::min(tmin.x, twidth), math::min(tmin.y, theight) };
tmax = { math::max(tmax.x, twidth), math::max(tmax.y, theight) };
attachmentCount++;
id<MTLTexture> mtlTexture = texture->getMtlTextureForWrite();
depth = MetalAttachment(mtlTexture, level, layer);
// If we were given a single-sampled texture but the samples parameter is > 1, we create
// a multisampled sidecar texture and do a resolve automatically.
if (samples > 1 && texture->samples == 1) {
auto& sidecar = texture->msaaSidecar;
if (samples > 1 && depth.getSampleCount() == 1) {
auto& sidecar = depth.metalTexture->msaaSidecar;
if (!sidecar) {
sidecar = createMultisampledTexture(mtlTexture.pixelFormat, texture->width,
texture->height, samples);
sidecar = createMultisampledTexture(depth.getPixelFormat(),
depth.metalTexture->width, depth.metalTexture->height, samples);
}
depth = MetalAttachment(mtlTexture, level, layer).withMsaaTexture(sidecar);
}
}
if (stencilAttachment.texture) {
// stencil = stencilAttachment;
MetalTexture* const texture = stencilAttachment.texture;
const auto& level = stencilAttachment.level;
const auto& layer = stencilAttachment.layer;
if (stencilAttachment) {
stencil = stencilAttachment;
FILAMENT_CHECK_PRECONDITION(texture->samples <= samples)
FILAMENT_CHECK_PRECONDITION(stencil.getSampleCount() <= samples)
<< "MetalRenderTarget was initialized with a MSAA STENCIL texture, but sample "
"count is "
<< samples << ".";
const auto twidth = std::max(1u, texture->width >> level);
const auto theight = std::max(1u, texture->height >> level);
auto t = stencil.metalTexture;
const auto twidth = std::max(1u, t->width >> stencil.level);
const auto theight = std::max(1u, t->height >> stencil.level);
tmin = { math::min(tmin.x, twidth), math::min(tmin.y, theight) };
tmax = { math::max(tmax.x, twidth), math::max(tmax.y, theight) };
attachmentCount++;
id<MTLTexture> mtlTexture = texture->getMtlTextureForWrite();
stencil = MetalAttachment(mtlTexture, level, layer);
// If we were given a single-sampled texture but the samples parameter is > 1, we create
// a multisampled sidecar texture and do a resolve automatically.
if (samples > 1 && texture->samples == 1) {
auto& sidecar = texture->msaaSidecar;
if (samples > 1 && stencil.getSampleCount() == 1) {
auto& sidecar = stencil.metalTexture->msaaSidecar;
if (!sidecar) {
sidecar = createMultisampledTexture(mtlTexture.pixelFormat, texture->width,
texture->height, samples);
sidecar = createMultisampledTexture(stencil.getPixelFormat(),
stencil.metalTexture->width, stencil.metalTexture->height, samples);
}
stencil = MetalAttachment(mtlTexture, level, layer).withMsaaTexture(sidecar);
}
}
@@ -1159,105 +1076,105 @@ void MetalRenderTarget::setUpRenderPassAttachments(MTLRenderPassDescriptor* desc
const auto discardFlags = params.flags.discardEnd;
for (size_t i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
MetalAttachment attachment = getDrawColorAttachment(i);
Attachment attachment = getDrawColorAttachment(i);
if (!attachment) {
continue;
}
descriptor.colorAttachments[i].texture = attachment.getTexture();
descriptor.colorAttachments[i].level = attachment.getLevel();
descriptor.colorAttachments[i].slice = attachment.getLayer();
descriptor.colorAttachments[i].level = attachment.level;
descriptor.colorAttachments[i].slice = attachment.layer;
descriptor.colorAttachments[i].loadAction = getLoadAction(params, getTargetBufferFlagsAt(i));
descriptor.colorAttachments[i].storeAction = getStoreAction(params,
getTargetBufferFlagsAt(i));
descriptor.colorAttachments[i].clearColor = MTLClearColorMake(
params.clearColor.r, params.clearColor.g, params.clearColor.b, params.clearColor.a);
if (attachment.getMsaaTexture()) {
// Check that the loadAction is valid for MSAA targets: either DontCare or Clear.
const bool automaticResolve = samples > 1 && attachment.getSampleCount() == 1;
if (automaticResolve) {
// We're rendering into our temporary MSAA texture and doing an automatic resolve.
// We should not be attempting to load anything into the MSAA texture.
// This might happen if the user is rendering multiple views into a MSAA SwapChain,
// which is not supported. In that case, ignore the load action.
if (descriptor.colorAttachments[i].loadAction == MTLLoadActionLoad) {
descriptor.colorAttachments[i].loadAction = MTLLoadActionDontCare;
}
assert_invariant(descriptor.colorAttachments[i].loadAction != MTLLoadActionLoad);
assert_invariant(!defaultRenderTarget);
descriptor.colorAttachments[i].texture = attachment.getMsaaTexture();
id<MTLTexture> sidecar = attachment.getMSAASidecarTexture();
assert_invariant(sidecar);
descriptor.colorAttachments[i].texture = sidecar;
descriptor.colorAttachments[i].level = 0;
descriptor.colorAttachments[i].slice = 0;
const bool discard = any(discardFlags & getTargetBufferFlagsAt(i));
if (!discard) {
descriptor.colorAttachments[i].resolveTexture = attachment.getTexture();
descriptor.colorAttachments[i].resolveLevel = attachment.getLevel();
descriptor.colorAttachments[i].resolveSlice = attachment.getLayer();
descriptor.colorAttachments[i].resolveTexture = attachment.texture;
descriptor.colorAttachments[i].resolveLevel = attachment.level;
descriptor.colorAttachments[i].resolveSlice = attachment.layer;
descriptor.colorAttachments[i].storeAction = MTLStoreActionMultisampleResolve;
}
}
}
MetalAttachment depthAttachment = getDepthAttachment();
Attachment depthAttachment = getDepthAttachment();
if (depthAttachment) {
descriptor.depthAttachment.texture = depthAttachment.getTexture();
descriptor.depthAttachment.level = depthAttachment.getLevel();
descriptor.depthAttachment.slice = depthAttachment.getLayer();
descriptor.depthAttachment.level = depthAttachment.level;
descriptor.depthAttachment.slice = depthAttachment.layer;
descriptor.depthAttachment.loadAction = getLoadAction(params, TargetBufferFlags::DEPTH);
descriptor.depthAttachment.storeAction = getStoreAction(params, TargetBufferFlags::DEPTH);
descriptor.depthAttachment.clearDepth = params.clearDepth;
}
if (depthAttachment.getMsaaTexture()) {
// Check that the loadAction is valid for MSAA targets: either DontCare or Clear.
const bool depthAutomaticResolve = samples > 1 && depthAttachment.getSampleCount() == 1;
if (depthAutomaticResolve) {
// We're rendering into our temporary MSAA texture and doing an automatic resolve.
// We should not be attempting to load anything into the MSAA texture.
// This might happen if the user is rendering multiple views into a MSAA SwapChain,
// which is not supported. In that case, ignore the load action.
if (descriptor.depthAttachment.loadAction == MTLLoadActionLoad) {
descriptor.depthAttachment.loadAction = MTLLoadActionDontCare;
}
assert_invariant(descriptor.depthAttachment.loadAction != MTLLoadActionLoad);
assert_invariant(!defaultRenderTarget);
descriptor.depthAttachment.texture = depthAttachment.getMsaaTexture();
id<MTLTexture> sidecar = depthAttachment.getMSAASidecarTexture();
assert_invariant(sidecar);
descriptor.depthAttachment.texture = sidecar;
descriptor.depthAttachment.level = 0;
descriptor.depthAttachment.slice = 0;
const bool discard = any(discardFlags & TargetBufferFlags::DEPTH);
if (!discard) {
assert_invariant(context->supportsAutoDepthResolve);
descriptor.depthAttachment.resolveTexture = depthAttachment.getTexture();
descriptor.depthAttachment.resolveLevel = depthAttachment.getLevel();
descriptor.depthAttachment.resolveSlice = depthAttachment.getLayer();
descriptor.depthAttachment.resolveLevel = depthAttachment.level;
descriptor.depthAttachment.resolveSlice = depthAttachment.layer;
descriptor.depthAttachment.storeAction = MTLStoreActionMultisampleResolve;
}
}
MetalAttachment stencilAttachment = getStencilAttachment();
Attachment stencilAttachment = getStencilAttachment();
if (stencilAttachment) {
descriptor.stencilAttachment.texture = stencilAttachment.getTexture();
descriptor.stencilAttachment.level = stencilAttachment.getLevel();
descriptor.stencilAttachment.slice = stencilAttachment.getLayer();
descriptor.stencilAttachment.level = stencilAttachment.level;
descriptor.stencilAttachment.slice = stencilAttachment.layer;
descriptor.stencilAttachment.loadAction = getLoadAction(params, TargetBufferFlags::STENCIL);
descriptor.stencilAttachment.storeAction = getStoreAction(params, TargetBufferFlags::STENCIL);
descriptor.stencilAttachment.clearStencil = params.clearStencil;
}
if (stencilAttachment.getMsaaTexture()) {
// Check that the loadAction is valid for MSAA targets: either DontCare or Clear.
const bool stencilAutomaticResolve = samples > 1 && stencilAttachment.getSampleCount() == 1;
if (stencilAutomaticResolve) {
// We're rendering into our temporary MSAA texture and doing an automatic resolve.
// We should not be attempting to load anything into the MSAA texture.
// This might happen if the user is rendering multiple views into a MSAA SwapChain,
// which is not supported. In that case, ignore the load action.
if (descriptor.stencilAttachment.loadAction == MTLLoadActionLoad) {
descriptor.stencilAttachment.loadAction = MTLLoadActionDontCare;
}
assert_invariant(descriptor.stencilAttachment.loadAction != MTLLoadActionLoad);
assert_invariant(!defaultRenderTarget);
descriptor.stencilAttachment.texture = stencilAttachment.getMsaaTexture();
id<MTLTexture> sidecar = stencilAttachment.getMSAASidecarTexture();
assert_invariant(sidecar);
descriptor.stencilAttachment.texture = sidecar;
descriptor.stencilAttachment.level = 0;
descriptor.stencilAttachment.slice = 0;
const bool discard = any(discardFlags & TargetBufferFlags::STENCIL);
if (!discard) {
assert_invariant(context->supportsAutoDepthResolve);
descriptor.stencilAttachment.resolveTexture = stencilAttachment.getTexture();
descriptor.stencilAttachment.resolveLevel = stencilAttachment.getLevel();
descriptor.stencilAttachment.resolveSlice = stencilAttachment.getLayer();
descriptor.stencilAttachment.resolveLevel = stencilAttachment.level;
descriptor.stencilAttachment.resolveSlice = stencilAttachment.layer;
descriptor.stencilAttachment.storeAction = MTLStoreActionMultisampleResolve;
if (@available(iOS 12.0, *)) {
descriptor.stencilAttachment.stencilResolveFilter = MTLMultisampleStencilResolveFilterSample0;
@@ -1272,46 +1189,44 @@ bool MetalRenderTarget::involvesAbandonedSwapChain() const noexcept {
return (draw && draw->isAbandoned()) || (read && read->isAbandoned());
}
MetalAttachment MetalRenderTarget::getDrawColorAttachment(size_t index) {
MetalRenderTarget::Attachment MetalRenderTarget::getDrawColorAttachment(size_t index) {
assert_invariant(index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
Attachment result = color[index];
if (index == 0 && defaultRenderTarget) {
assert_invariant(context->currentDrawSwapChain);
return context->currentDrawSwapChain->acquireDrawable();
result.texture = context->currentDrawSwapChain->acquireDrawable();
// acquireDrawable may fail to acquire the drawable, in which case result.texture will be
// nil, and an invalid attachment
}
return color[index];
return result;
}
MetalAttachment MetalRenderTarget::getReadColorAttachment(size_t index) {
MetalRenderTarget::Attachment MetalRenderTarget::getReadColorAttachment(size_t index) {
assert_invariant(index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
Attachment result = color[index];
if (index == 0 && defaultRenderTarget) {
assert_invariant(context->currentReadSwapChain);
return context->currentReadSwapChain->acquireDrawable();
result.texture = context->currentReadSwapChain->acquireDrawable();
// acquireDrawable may fail to acquire the drawable, in which case result.texture will be
// nil, and an invalid attachment
}
return color[index];
return result;
}
MetalAttachment MetalRenderTarget::getDepthAttachment() {
MetalRenderTarget::Attachment MetalRenderTarget::getDepthAttachment() {
Attachment result = depth;
if (defaultRenderTarget) {
assert_invariant(context->currentDrawSwapChain);
return context->currentDrawSwapChain->acquireDepthTexture();
result.texture = context->currentDrawSwapChain->acquireDepthTexture();
}
return depth;
return result;
}
MetalAttachment MetalRenderTarget::getStencilAttachment() {
MetalRenderTarget::Attachment MetalRenderTarget::getStencilAttachment() {
Attachment result = stencil;
if (defaultRenderTarget) {
assert_invariant(context->currentDrawSwapChain);
return context->currentDrawSwapChain->acquireStencilTexture();
result.texture = context->currentDrawSwapChain->acquireStencilTexture();
}
return stencil;
}
NSUInteger MetalRenderTarget::getSampleCount() const {
if (defaultRenderTarget) {
assert_invariant(context->currentDrawSwapChain);
return context->currentDrawSwapChain->getSampleCount();
}
return samples;
return result;
}
MTLLoadAction MetalRenderTarget::getLoadAction(const RenderPassParams& params,
@@ -1395,30 +1310,20 @@ void MetalFence::onSignal(MetalFenceSignalBlock block) {
FenceStatus MetalFence::wait(uint64_t timeoutNs) {
if (@available(iOS 12, *)) {
using ns = std::chrono::nanoseconds;
// keep a reference on the stack so our state cannot be destroyed while we wait
auto state = this->state;
std::unique_lock<std::mutex> guard(state->mutex);
while (state->status == FenceStatus::TIMEOUT_EXPIRED) {
if (timeoutNs == FENCE_WAIT_FOR_EVER) {
state->cv.wait(guard);
} else if (timeoutNs == 0 ||
state->cv.wait_for(guard, ns(timeoutNs)) == std::cv_status::timeout) {
return state->status;
return FenceStatus::TIMEOUT_EXPIRED;
}
}
return state->status;
return FenceStatus::CONDITION_SATISFIED;
}
return FenceStatus::ERROR;
}
void MetalFence::cancel() {
std::unique_lock guard(state->mutex);
state->status = FenceStatus::ERROR;
state->cv.notify_all();
}
MetalDescriptorSetLayout::MetalDescriptorSetLayout(DescriptorSetLayout&& l) noexcept
: mLayout(std::move(l)) {
size_t dynamicBindings = 0;
@@ -134,7 +134,6 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
case ShaderLanguage::ESSL3:
case ShaderLanguage::SPIRV:
case ShaderLanguage::WGSL:
case ShaderLanguage::UNSPECIFIED:
break;
}
@@ -153,15 +152,14 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
MTLFunctionConstantValues* constants = [MTLFunctionConstantValues new];
auto const& specializationConstants = program.getSpecializationConstants();
for (size_t i = 0; i < specializationConstants.size(); i++) {
auto const& sc = specializationConstants[i];
for (auto const& sc : specializationConstants) {
const std::array<MTLDataType, 3> types{
MTLDataTypeInt, MTLDataTypeFloat, MTLDataTypeBool };
std::visit([i, constants, type = types[sc.index()]](auto&& arg) {
std::visit([&sc, constants, type = types[sc.value.index()]](auto&& arg) {
[constants setConstantValue:&arg
type:type
atIndex:i];
}, sc);
atIndex:sc.id];
}, sc.value);
}
id<MTLFunction> function = [library newFunctionWithName:@"main0"
+3 -5
View File
@@ -155,11 +155,9 @@ id<MTLSamplerState> SamplerStateCreator::operator()(id<MTLDevice> device,
// Older Apple devices (and the simulator) don't support setting a comparison function in
// MTLSamplerDescriptor.
// In practice, this means shadows are not supported when running in the simulator.
if (samplerDescriptor.compareFunction != MTLCompareFunctionNever) {
if (![device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) {
LOG(WARNING) << "Warning: sample comparison not supported by this GPU";
samplerDescriptor.compareFunction = MTLCompareFunctionNever;
}
if (![device supportsFeatureSet:MTLFeatureSet_iOS_GPUFamily3_v1]) {
LOG(WARNING) << "Warning: sample comparison not supported by this GPU";
samplerDescriptor.compareFunction = MTLCompareFunctionNever;
}
#endif
-16
View File
@@ -50,22 +50,6 @@ id<MTLTexture> createTextureViewWithLodRange(id<MTLTexture> texture, NSUInteger
*/
id<MTLTexture> createTextureViewWithSingleSlice(id<MTLTexture> texture, NSUInteger slice);
/**
* Returns the required alignment for uniform buffer offsets.
*/
constexpr size_t getUniformBufferOffsetAlignment() {
#if defined(FILAMENT_IOS)
#if TARGET_OS_SIMULATOR
// The iOS simulator has differing alignment requirements.
return 256;
#else
return 4;
#endif // TARGET_OS_SIMULATOR
#else
return 32;
#endif
}
} // namespace filament::backend
#endif //TNT_FILAMENT_DRIVER_METALUTILS_H
-1
View File
@@ -68,5 +68,4 @@ id<MTLTexture> createTextureViewWithSingleSlice(id<MTLTexture> texture, NSUInteg
slices:NSMakeRange(slice, 1)];
}
} // namespace filament::backend
+1 -1
View File
@@ -52,7 +52,7 @@ PlatformMetal::~PlatformMetal() noexcept {
delete pImpl;
}
Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::DriverConfig& driverConfig) {
Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::DriverConfig& driverConfig) noexcept {
pImpl->mDrawableFailureBehavior = driverConfig.metalDisablePanicOnDrawableFailure
? DrawableFailureBehavior::ABORT_FRAME
: DrawableFailureBehavior::PANIC;
+4 -22
View File
@@ -21,8 +21,6 @@
#include "noop/NoopDriver.h"
#include "CommandStreamDispatcher.h"
#include <utils/ImmutableCString.h>
#include <stdint.h>
namespace filament::backend {
@@ -47,16 +45,8 @@ ShaderModel NoopDriver::getShaderModel() const noexcept {
#endif
}
utils::FixedCapacityVector<ShaderLanguage> NoopDriver::getShaderLanguages(
ShaderLanguage /*preferredLanguage*/) const noexcept {
return {
ShaderLanguage::ESSL3,
ShaderLanguage::ESSL1,
ShaderLanguage::SPIRV,
ShaderLanguage::MSL,
ShaderLanguage::METAL_LIBRARY,
ShaderLanguage::WGSL,
};
ShaderLanguage NoopDriver::getShaderLanguage() const noexcept {
return ShaderLanguage::ESSL3;
}
// explicit instantiation of the Dispatcher
@@ -137,11 +127,11 @@ void NoopDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh) {
void NoopDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) {
}
Handle<HwStream> NoopDriver::createStreamNative(void* nativeStream, utils::ImmutableCString tag) {
Handle<HwStream> NoopDriver::createStreamNative(void* nativeStream, utils::CString tag) {
return {};
}
Handle<HwStream> NoopDriver::createStreamAcquired(utils::ImmutableCString tag) {
Handle<HwStream> NoopDriver::createStreamAcquired(utils::CString tag) {
return {};
}
@@ -170,10 +160,6 @@ FenceStatus NoopDriver::getFenceStatus(Handle<HwFence> fh) {
return FenceStatus::CONDITION_SATISFIED;
}
FenceStatus NoopDriver::fenceWait(Handle<HwFence> fh, uint64_t timeout) {
return FenceStatus::ERROR;
}
// We create all textures using VK_IMAGE_TILING_OPTIMAL, so our definition of "supported" is that
// the GPU supports the given texture format with non-zero optimal tiling features.
bool NoopDriver::isTextureFormatSupported(TextureFormat format) {
@@ -274,10 +260,6 @@ size_t NoopDriver::getMaxArrayTextureLayers() {
return 256u;
}
size_t NoopDriver::getUniformBufferOffsetAlignment() {
return 256u;
}
void NoopDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
uint32_t byteOffset) {
scheduleDestroy(std::move(p));
+1 -3
View File
@@ -20,7 +20,6 @@
#include "private/backend/Driver.h"
#include "DriverBase.h"
#include <utils/FixedCapacityVector.h>
#include <utils/compiler.h>
namespace filament::backend {
@@ -35,8 +34,7 @@ public:
private:
ShaderModel getShaderModel() const noexcept final;
utils::FixedCapacityVector<ShaderLanguage> getShaderLanguages(
ShaderLanguage preferredLanguage) const noexcept final;
ShaderLanguage getShaderLanguage() const noexcept final;
uint64_t nextFakeHandle = 1;
+1 -1
View File
@@ -20,7 +20,7 @@
namespace filament::backend {
Driver* PlatformNoop::createDriver(void* sharedGLContext, const Platform::DriverConfig& driverConfig) {
Driver* PlatformNoop::createDriver(void* sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
return NoopDriver::create();
}
+1 -1
View File
@@ -31,7 +31,7 @@ public:
protected:
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) override;
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
};
} // namespace filament
@@ -33,7 +33,6 @@
#include <utils/BitmaskEnum.h>
#include <utils/Log.h>
#include <utils/Logger.h>
#include <utils/Panic.h>
#include <utils/bitset.h>
#include <utils/compiler.h>
@@ -50,7 +49,7 @@
namespace filament::backend {
GLDescriptorSet::GLDescriptorSet(OpenGLContext& gl, DescriptorSetLayoutHandle dslh,
GLDescriptorSetLayout const* layout)
GLDescriptorSetLayout const* layout) noexcept
: descriptors(layout->maxDescriptorBinding + 1),
dslh(std::move(dslh)) {
@@ -157,9 +156,8 @@ void GLDescriptorSet::update(OpenGLContext&,
arg.bo = bo;
arg.offset = uint32_t(offset);
} else {
// 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";
// API usage error. User asked to update the wrong type of descriptor.
PANIC_PRECONDITION("descriptor %d is not a buffer", +binding);
}
}, descriptors[binding].desc);
}
@@ -216,9 +214,8 @@ void GLDescriptorSet::update(OpenGLContext& gl, HandleAllocatorGL& handleAllocat
arg.params = params;
}
} else {
// 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";
// API usage error. User asked to update the wrong type of descriptor.
PANIC_PRECONDITION("descriptor %d is not a texture", +binding);
}
}, descriptors[binding].desc);
}
@@ -303,10 +300,13 @@ void GLDescriptorSet::bind(
offset += offsets[dynamicOffsetIndex++];
}
if (arg.bo) {
p.updateUniforms(bindingPoint, arg.bo->gl.id, arg.bo->gl.buffer, arg.bo->age, offset);
auto buffer = static_cast<char const*>(arg.bo->gl.buffer) + offset;
p.updateUniforms(bindingPoint, arg.bo->gl.id, buffer, arg.bo->age);
}
} else if constexpr (std::is_same_v<T, Sampler>) {
GLuint const unit = p.getTextureUnit(set, binding);
if (arg.handle) {
GLTexture const* const t = handleAllocator.handle_cast<GLTexture*>(arg.handle);
gl.bindTexture(unit, t->gl.target, t->gl.id, t->gl.external);
@@ -52,7 +52,7 @@ struct GLDescriptorSet : public HwDescriptorSet {
using HwDescriptorSet::HwDescriptorSet;
GLDescriptorSet(OpenGLContext& gl, DescriptorSetLayoutHandle dslh,
GLDescriptorSetLayout const* layout);
GLDescriptorSetLayout const* layout) noexcept;
// update a buffer descriptor in the set
void update(OpenGLContext& gl,
+12 -35
View File
@@ -277,7 +277,7 @@ void OpenGLContext::terminate() noexcept {
}
void OpenGLContext::destroyWithContext(
size_t index, std::function<void(OpenGLContext&)> const& closure) {
size_t index, std::function<void(OpenGLContext&)> const& closure) noexcept {
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) {
void OpenGLContext::synchronizeStateAndCache(size_t index) noexcept {
// if we're just switching back to context 0, run all the pending destructors
if (index == 0) {
@@ -507,40 +507,20 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
} else if (strstr(renderer, "Mali")) {
// ARM GPU
bugs->vao_doesnt_store_element_array_buffer_binding = true;
// We have run into several problems with timer queries on Mali-Gxx:
// - timer queries seem to cause memory corruptions in some cases on some devices
// (see b/233754398)
// - appeared at least in: "OpenGL ES 3.2 v1.r26p0-01eac0"
// - wasn't present in: "OpenGL ES 3.2 v1.r32p1-00pxl1"
// - timer queries sometime crash with an NPE (see b/273759031)
// (see b/273759031)
// - possibly not present in: "OpenGL ES 3.2 v1.r46p0"
bugs->dont_use_timer_query = true;
// at least some version of Mali have problems with framebuffer_fetch
// (see b/445721121, https://github.com/google/filament/issues/7794)
bugs->disable_framebuffer_fetch_extension = true;
if (strstr(renderer, "Mali-T")) {
bugs->disable_glFlush = true;
bugs->disable_shared_context_draws = true;
// We have not verified that timer queries work on Mali-T, so we disable to be safe.
bugs->dont_use_timer_query = true;
}
if (strstr(renderer, "Mali-G")) {
int maj, min, driverVersion, driverRelease;
int const c = sscanf(version, "OpenGL ES %d.%d v%d.r%d",
&maj, &min, &driverVersion, &driverRelease);
if (UTILS_LIKELY(c == 4)) {
// we were able to parse the version string
if (driverVersion > 1 || (driverVersion == 1 && driverRelease >= 46)) {
// this driver version is known to be good
bugs->dont_use_timer_query = false;
}
if (driverVersion > 1 || (driverVersion == 1 && driverRelease >= 53)) {
// this driver version is known to be good
bugs->disable_framebuffer_fetch_extension = false;
}
}
// We have run into several problems with timer queries on Mali-Gxx:
// - timer queries seem to cause memory corruptions in some cases on some devices
// (see b/233754398)
// - appeared at least in: "OpenGL ES 3.2 v1.r26p0-01eac0"
// - wasn't present in: "OpenGL ES 3.2 v1.r32p1-00pxl1"
// - timer queries sometime crash with an NPE (see b/273759031)
bugs->dont_use_timer_query = true;
}
// Mali seems to have no problem with this (which is good for us)
bugs->allow_read_only_ancillary_feedback_loop = true;
@@ -760,7 +740,6 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
ext->EXT_texture_compression_rgtc = exts.has("GL_EXT_texture_compression_rgtc"sv);
ext->EXT_texture_compression_bptc = exts.has("GL_EXT_texture_compression_bptc"sv);
ext->EXT_texture_cube_map_array = exts.has("GL_EXT_texture_cube_map_array"sv) || exts.has("GL_OES_texture_cube_map_array"sv);
ext->EXT_texture_filter_anisotropic = exts.has("GL_EXT_texture_filter_anisotropic"sv);
ext->GOOGLE_cpp_style_line_directive = exts.has("GL_GOOGLE_cpp_style_line_directive"sv);
ext->KHR_debug = exts.has("GL_KHR_debug"sv);
ext->KHR_parallel_shader_compile = exts.has("GL_KHR_parallel_shader_compile"sv);
@@ -866,12 +845,10 @@ 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());
}
// by construction, defaultFboForThisContext has a value. value_or() avoids a throwing call.
buffer = defaultFboForThisContext.value_or(0);
buffer = defaultFboForThisContext.value();
}
bindFramebufferResolved(target, buffer);
return buffer;
+2 -3
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);
void destroyWithContext(size_t index, std::function<void(OpenGLContext&)> const& closure) noexcept;
// glGet*() values
struct Gets {
@@ -338,7 +338,6 @@ public:
// 'OpenGL error 0x502 (GL_INVALID_OPERATION) in "draw2" at line 4389'
// This coincides with the use of framebuffer fetch (ColorGradingAsSubpass). We disable
// framebuffer fetch in the case of llvmpipe.
// Some Mali drivers also have problems with this (b/445721121)
bool disable_framebuffer_fetch_extension;
} bugs = {};
@@ -498,7 +497,7 @@ public:
} procs{};
void unbindEverything() noexcept;
void synchronizeStateAndCache(size_t index);
void synchronizeStateAndCache(size_t index) noexcept;
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
GLuint getSamplerSlow(SamplerParams sp) const noexcept;
+93 -126
View File
@@ -50,7 +50,6 @@
#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>
@@ -369,9 +368,8 @@ ShaderModel OpenGLDriver::getShaderModel() const noexcept {
return mContext.getShaderModel();
}
utils::FixedCapacityVector<ShaderLanguage> OpenGLDriver::getShaderLanguages(
ShaderLanguage /*preferredLanguage*/) const noexcept {
return { mContext.isES2() ? ShaderLanguage::ESSL1 : ShaderLanguage::ESSL3 };
ShaderLanguage OpenGLDriver::getShaderLanguage() const noexcept {
return mContext.isES2() ? ShaderLanguage::ESSL1 : ShaderLanguage::ESSL3;
}
// ------------------------------------------------------------------------------------------------
@@ -395,7 +393,7 @@ void OpenGLDriver::setPushConstant(ShaderStage const stage, uint8_t const index,
}
#endif
Slice<const std::pair<GLint, ConstantType>> constants;
Slice<std::pair<GLint, ConstantType>> constants;
if (stage == ShaderStage::VERTEX) {
constants = mCurrentPushConstants->vertexConstants;
} else if (stage == ShaderStage::FRAGMENT) {
@@ -662,7 +660,7 @@ void OpenGLDriver::createVertexBufferInfoR(
uint8_t bufferCount,
uint8_t attributeCount,
AttributeArray attributes,
ImmutableCString&& tag) {
CString&& tag) {
DEBUG_MARKER()
construct<GLVertexBufferInfo>(vbih, bufferCount, attributeCount, attributes);
mHandleAllocator.associateTagToHandle(vbih.getId(), std::move(tag));
@@ -672,7 +670,7 @@ void OpenGLDriver::createVertexBufferR(
Handle<HwVertexBuffer> vbh,
uint32_t vertexCount,
Handle<HwVertexBufferInfo> vbih,
ImmutableCString&& tag) {
CString&& tag) {
DEBUG_MARKER()
construct<GLVertexBuffer>(vbh, vertexCount, vbih);
mHandleAllocator.associateTagToHandle(vbh.getId(), std::move(tag));
@@ -683,7 +681,7 @@ void OpenGLDriver::createIndexBufferR(
ElementType const elementType,
uint32_t indexCount,
BufferUsage const usage,
ImmutableCString&& tag) {
CString&& tag) {
DEBUG_MARKER()
auto& gl = mContext;
@@ -699,7 +697,7 @@ void OpenGLDriver::createIndexBufferR(
}
void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage, ImmutableCString&& tag) {
BufferObjectBinding bindingType, BufferUsage usage, CString&& tag) {
DEBUG_MARKER()
assert_invariant(byteCount > 0);
@@ -726,7 +724,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, ImmutableCString&& tag) {
PrimitiveType const pt, CString&& tag) {
DEBUG_MARKER()
auto& gl = mContext;
@@ -762,9 +760,32 @@ void OpenGLDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
mHandleAllocator.associateTagToHandle(rph.getId(), std::move(tag));
}
void OpenGLDriver::createProgramR(Handle<HwProgram> ph, Program&& program, ImmutableCString&& tag) {
void OpenGLDriver::createProgramR(Handle<HwProgram> ph, Program&& program, CString&& 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));
@@ -872,7 +893,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, ImmutableCString&& tag) {
TextureUsage usage, CString&& tag) {
DEBUG_MARKER()
GLenum internalFormat = getInternalFormat(format);
@@ -965,7 +986,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, ImmutableCString&& tag) {
Handle<HwTexture> srch, uint8_t const baseLevel, uint8_t const levelCount, CString&& tag) {
DEBUG_MARKER()
GLTexture const* const src = handle_cast<GLTexture const*>(srch);
@@ -1013,7 +1034,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,
ImmutableCString&& tag) {
CString&& tag) {
DEBUG_MARKER()
GLTexture const* const src = handle_cast<GLTexture const*>(srch);
@@ -1079,7 +1100,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, ImmutableCString&& tag) {
Platform::ExternalImageHandleRef image, CString&& tag) {
DEBUG_MARKER()
usage |= TextureUsage::SAMPLEABLE;
@@ -1131,7 +1152,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,
ImmutableCString&& tag) {
CString&& tag) {
DEBUG_MARKER()
usage |= TextureUsage::SAMPLEABLE;
@@ -1182,13 +1203,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, ImmutableCString&&) {
void* image, uint32_t plane, CString&&) {
// 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, ImmutableCString&& tag) {
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage, CString&& tag) {
DEBUG_MARKER()
auto const& gl = mContext;
@@ -1643,7 +1664,7 @@ void OpenGLDriver::renderBufferStorage(GLuint const rbo, GLenum internalformat,
}
void OpenGLDriver::createDefaultRenderTargetR(
Handle<HwRenderTarget> rth, ImmutableCString&& tag) {
Handle<HwRenderTarget> rth, CString&& tag) {
DEBUG_MARKER()
construct<GLRenderTarget>(rth, 0, 0); // FIXME: we don't know the width/height
@@ -1666,7 +1687,7 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
MRT color,
TargetBufferInfo depth,
TargetBufferInfo stencil,
ImmutableCString&& tag) {
CString&& tag) {
DEBUG_MARKER()
GLRenderTarget* rt = construct<GLRenderTarget>(rth, width, height);
@@ -1782,41 +1803,31 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
mHandleAllocator.associateTagToHandle(rth.getId(), std::move(tag));
}
void OpenGLDriver::createFenceR(Handle<HwFence> fh, ImmutableCString&& tag) {
void OpenGLDriver::createFenceR(Handle<HwFence> fh, CString&& tag) {
DEBUG_MARKER()
mHandleAllocator.associateTagToHandle(fh.getId(), std::move(tag));
GLFence* f = handle_cast<GLFence*>(fh);
assert_invariant(f->state);
bool const platformCanCreateFence = mPlatform.canCreateFence();
if (mContext.isES2() || platformCanCreateFence) {
std::lock_guard const lock(f->state->lock);
if (platformCanCreateFence) {
f->fence = mPlatform.createFence();
f->state->cond.notify_all();
} else {
f->state->status = FenceStatus::ERROR;
}
return;
if (mPlatform.canCreateFence() || mContext.isES2()) {
assert_invariant(mPlatform.canCreateFence());
f->fence = mPlatform.createFence();
}
// This is the case where we need to use OpenGL fences
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
std::weak_ptr<GLFence::State> const weak = f->state;
whenGpuCommandsComplete([weak] {
if (auto const state = weak.lock()) {
std::lock_guard const lock(state->lock);
state->status = FenceStatus::CONDITION_SATISFIED;
state->cond.notify_all();
}
});
else {
std::weak_ptr const weak = f->state;
whenGpuCommandsComplete([weak](){
if (auto const state = weak.lock()) {
std::lock_guard const lock(state->lock);
state->status = FenceStatus::CONDITION_SATISFIED;
state->cond.notify_all();
}
});
}
#endif
mHandleAllocator.associateTagToHandle(fh.getId(), std::move(tag));
}
void OpenGLDriver::createSyncR(Handle<HwSync> sh, ImmutableCString&& tag) {
void OpenGLDriver::createSyncR(Handle<HwSync> sh, CString&& tag) {
DEBUG_MARKER()
GLSyncFence* s = handle_cast<GLSyncFence*>(sh);
@@ -1835,7 +1846,7 @@ void OpenGLDriver::createSyncR(Handle<HwSync> sh, ImmutableCString&& tag) {
}
void OpenGLDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t const flags,
ImmutableCString&& tag) {
CString&& tag) {
DEBUG_MARKER()
GLSwapChain* sc = handle_cast<GLSwapChain*>(sch);
@@ -1857,7 +1868,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, ImmutableCString&& tag) {
uint32_t const width, uint32_t const height, uint64_t const flags, CString&& tag) {
DEBUG_MARKER()
GLSwapChain* sc = handle_cast<GLSwapChain*>(sch);
@@ -1879,7 +1890,7 @@ void OpenGLDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch,
mHandleAllocator.associateTagToHandle(sch.getId(), std::move(tag));
}
void OpenGLDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, ImmutableCString&& tag) {
void OpenGLDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, CString&& tag) {
DEBUG_MARKER()
GLTimerQuery* tq = handle_cast<GLTimerQuery*>(tqh);
mContext.createTimerQuery(tq);
@@ -1887,14 +1898,14 @@ void OpenGLDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, ImmutableCString&
}
void OpenGLDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
DescriptorSetLayout&& info, ImmutableCString&& tag) {
DescriptorSetLayout&& info, CString&& tag) {
DEBUG_MARKER()
construct<GLDescriptorSetLayout>(dslh, std::move(info));
mHandleAllocator.associateTagToHandle(dslh.getId(), std::move(tag));
}
void OpenGLDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
Handle<HwDescriptorSetLayout> dslh, ImmutableCString&& tag) {
Handle<HwDescriptorSetLayout> dslh, CString&& tag) {
DEBUG_MARKER()
GLDescriptorSetLayout const* dsl = handle_cast<GLDescriptorSetLayout*>(dslh);
construct<GLDescriptorSet>(dsh, mContext, dslh, dsl);
@@ -1903,7 +1914,7 @@ void OpenGLDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
void OpenGLDriver::mapBufferR(MemoryMappedBufferHandle mmbh,
BufferObjectHandle boh, size_t offset,
size_t size, MapBufferAccessFlags access, ImmutableCString&& tag) {
size_t size, MapBufferAccessFlags access, CString&& tag) {
DEBUG_MARKER()
construct<GLMemoryMappedBuffer>(mmbh, mContext, mHandleAllocator, boh, offset, size, access);
mHandleAllocator.associateTagToHandle(mmbh.getId(), std::move(tag));
@@ -2173,14 +2184,14 @@ void OpenGLDriver::unmapBuffer(MemoryMappedBufferHandle mmbh) {
// These are called on the application's thread
// ------------------------------------------------------------------------------------------------
Handle<HwStream> OpenGLDriver::createStreamNative(void* nativeStream, ImmutableCString tag) {
Handle<HwStream> OpenGLDriver::createStreamNative(void* nativeStream, CString 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(ImmutableCString tag) {
Handle<HwStream> OpenGLDriver::createStreamAcquired(CString tag) {
auto handle = initHandle<GLStream>();
mHandleAllocator.associateTagToHandle(handle.getId(), std::move(tag));
return handle;
@@ -2287,76 +2298,38 @@ mat3f OpenGLDriver::getStreamTransformMatrix(Handle<HwStream> sh) {
void OpenGLDriver::destroyFence(Handle<HwFence> fh) {
if (fh) {
GLFence const* const f = handle_cast<GLFence*>(fh);
GLFence const* f = handle_cast<GLFence*>(fh);
if (mPlatform.canCreateFence() || mContext.isES2()) {
mPlatform.destroyFence(f->fence);
} else {
// signal waiters it's time to give-up
std::unique_lock const lock(f->state->lock);
f->state->status = FenceStatus::ERROR;
f->state->cond.notify_all();
}
destruct(fh, f);
}
}
FenceStatus OpenGLDriver::getFenceStatus(Handle<HwFence> fh) {
return fenceWait(fh, 0);
}
FenceStatus OpenGLDriver::fenceWait(FenceHandle fh, uint64_t const timeout) {
if (fh) {
// 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();
steady_clock::time_point until = steady_clock::time_point::max();
if (now <= steady_clock::time_point::max() - nanoseconds(timeout)) {
until = now + nanoseconds(timeout);
}
GLFence const* f = handle_cast<GLFence*>(fh);
assert_invariant(f->state);
bool const platformCanCreateFence = mPlatform.canCreateFence();
// immediately acquire a reference on our state, so that things don't go south if
// the HwFence gets destroyed (on the main thread) while we wait.
std::shared_ptr const state{ f->state };
if (mContext.isES2() || platformCanCreateFence) {
if (platformCanCreateFence) {
std::unique_lock lock(state->lock);
if (f->fence == nullptr) {
// we've been called before the fence was created asynchronously,
// so we need to wait for that, before using the real fence.
// By construction, "f" can't be destroyed while we wait, because its
// construction call is in the queue and a destroy call will have to come later.
state->cond.wait_until(lock, until, [f] {
return f->fence != nullptr;
});
if (f->fence == nullptr) {
// the only possible choice here is that we timed out
assert_invariant(state->status == FenceStatus::TIMEOUT_EXPIRED);
return FenceStatus::TIMEOUT_EXPIRED;
}
GLFence* f = handle_cast<GLFence*>(fh);
if (mPlatform.canCreateFence() || mContext.isES2()) {
if (f->fence == nullptr) {
// we can end-up here if:
// - the platform doesn't support h/w fences
if (UTILS_UNLIKELY(!mPlatform.canCreateFence())) {
return FenceStatus::ERROR;
}
lock.unlock();
// here we know that we have the platform fence
assert_invariant(f->fence);
return mPlatform.waitFence(f->fence, timeout);
// - wait() was called before the fence was asynchronously created.
return FenceStatus::TIMEOUT_EXPIRED;
}
// platform doesn't support fences -- nothing we can do.
return FenceStatus::ERROR;
return mPlatform.waitFence(f->fence, 0);
}
// This is the case where we need to use OpenGL fences
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
std::unique_lock lock(state->lock);
state->cond.wait_until(lock, until, [&state] {
return state->status != FenceStatus::TIMEOUT_EXPIRED;
});
return state->status;
else {
assert_invariant(f->state);
std::unique_lock lock(f->state->lock);
f->state->cond.wait_for(lock, std::chrono::nanoseconds(0), [&state = f->state]() {
return state->status != FenceStatus::TIMEOUT_EXPIRED;
});
return f->state->status;
}
#endif
}
return FenceStatus::ERROR;
@@ -2623,8 +2596,6 @@ 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;
}
@@ -2671,10 +2642,6 @@ size_t OpenGLDriver::getMaxArrayTextureLayers() {
return mContext.gets.max_array_texture_layers;
}
size_t OpenGLDriver::getUniformBufferOffsetAlignment() {
return mContext.gets.uniform_buffer_offset_alignment;
}
// ------------------------------------------------------------------------------------------------
// Swap chains
// ------------------------------------------------------------------------------------------------
@@ -3202,7 +3169,7 @@ void OpenGLDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh)
}
UTILS_NOINLINE
void OpenGLDriver::attachStream(GLTexture* t, GLStream* hwStream) {
void OpenGLDriver::attachStream(GLTexture* t, GLStream* hwStream) noexcept {
mTexturesWithStreamsAttached.push_back(t);
switch (hwStream->streamType) {
@@ -3788,16 +3755,16 @@ void OpenGLDriver::readBufferSubData(BufferObjectHandle boh,
}
void OpenGLDriver::runEveryNowAndThen(std::function<bool()> fn) {
void OpenGLDriver::runEveryNowAndThen(std::function<bool()> fn) noexcept {
mEveryNowAndThenOps.push_back(std::move(fn));
}
void OpenGLDriver::executeEveryNowAndThenOps() noexcept { // NOLINT(*-exception-escape)
void OpenGLDriver::executeEveryNowAndThenOps() noexcept {
auto& v = mEveryNowAndThenOps;
auto it = v.begin();
while (it != v.end()) {
if ((*it)()) {
it = v.erase(it); // cannot throw by construction
it = v.erase(it);
} else {
++it;
}
@@ -3805,17 +3772,17 @@ void OpenGLDriver::executeEveryNowAndThenOps() noexcept { // NOLINT(*-exception-
}
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
void OpenGLDriver::whenFrameComplete(const std::function<void()>& fn) {
void OpenGLDriver::whenFrameComplete(const std::function<void()>& fn) noexcept {
mFrameCompleteOps.push_back(fn);
}
void OpenGLDriver::whenGpuCommandsComplete(const std::function<void()>& fn) {
void OpenGLDriver::whenGpuCommandsComplete(const std::function<void()>& fn) noexcept {
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
mGpuCommandCompleteOps.emplace_back(sync, fn);
CHECK_GL_ERROR()
}
void OpenGLDriver::executeGpuCommandsCompleteOps() noexcept { // NOLINT(*-exception-escape)
void OpenGLDriver::executeGpuCommandsCompleteOps() noexcept {
auto& v = mGpuCommandCompleteOps;
auto it = v.begin();
while (it != v.end()) {
@@ -3831,13 +3798,13 @@ void OpenGLDriver::executeGpuCommandsCompleteOps() noexcept { // NOLINT(*-except
// ready
it->second();
glDeleteSync(sync);
it = v.erase(it); // cannot throw by construction
it = v.erase(it);
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); // cannot throw by construction
it = v.erase(it);
break;
}
}
+7 -9
View File
@@ -45,7 +45,6 @@
#include <utils/FixedCapacityVector.h>
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/ImmutableCString.h>
#include <utils/debug.h>
#include <math/vec4.h>
@@ -195,8 +194,8 @@ public:
struct GLFence : public HwFence {
using HwFence::HwFence;
struct State {
utils::Mutex lock; // NOLINT(*-include-cleaner)
utils::Condition cond; // NOLINT(*-include-cleaner)
std::mutex lock;
std::condition_variable cond;
FenceStatus status{ FenceStatus::TIMEOUT_EXPIRED };
};
std::shared_ptr<State> state{ std::make_shared<State>() };
@@ -234,8 +233,7 @@ private:
}
ShaderModel getShaderModel() const noexcept override;
utils::FixedCapacityVector<ShaderLanguage> getShaderLanguages(
ShaderLanguage preferredLanguage) const noexcept override;
ShaderLanguage getShaderLanguage() const noexcept override;
/*
* OpenGLDriver interface
@@ -396,23 +394,23 @@ private:
std::unordered_map<GLuint, BufferObjectStreamDescriptor> mStreamUniformDescriptors;
void attachStream(GLTexture* t, GLStream* stream);
void attachStream(GLTexture* t, GLStream* stream) noexcept;
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);
void whenGpuCommandsComplete(const std::function<void()>& fn) noexcept;
void executeGpuCommandsCompleteOps() noexcept;
std::vector<std::pair<GLsync, std::function<void()>>> mGpuCommandCompleteOps;
void whenFrameComplete(const std::function<void()>& fn);
void whenFrameComplete(const std::function<void()>& fn) noexcept;
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);
void runEveryNowAndThen(std::function<bool()> fn) noexcept;
void executeEveryNowAndThenOps() noexcept;
std::vector<std::function<bool()>> mEveryNowAndThenOps;
+7 -12
View File
@@ -52,8 +52,8 @@ using namespace backend;
struct OpenGLProgram::LazyInitializationData {
Program::DescriptorSetInfo descriptorBindings;
Program::BindingUniformsInfo bindingUniformInfo;
FixedCapacityVector<Program::PushConstant> vertexPushConstants;
FixedCapacityVector<Program::PushConstant> fragmentPushConstants;
utils::FixedCapacityVector<Program::PushConstant> vertexPushConstants;
utils::FixedCapacityVector<Program::PushConstant> fragmentPushConstants;
};
@@ -121,7 +121,7 @@ void OpenGLProgram::initialize(OpenGLDriver& gld) {
* checkProgramStatus() has been successfully called.
*/
void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint program,
LazyInitializationData& lazyInitializationData) {
LazyInitializationData& lazyInitializationData) noexcept {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
// from the pipeline layout we compute a mapping from {set, binding} to {binding}
@@ -141,7 +141,7 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
context.useProgram(program);
UTILS_NOUNROLL
for (descriptor_set_t set = 0; set < MAX_DESCRIPTOR_SET_COUNT; set++) {
for (backend::descriptor_set_t set = 0; set < MAX_DESCRIPTOR_SET_COUNT; set++) {
for (Program::Descriptor const& entry: lazyInitializationData.descriptorBindings[set]) {
switch (entry.type) {
case DescriptorType::UNIFORM_BUFFER:
@@ -214,8 +214,8 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
case DescriptorType::INPUT_ATTACHMENT:
break;
}
CHECK_GL_ERROR()
}
CHECK_GL_ERROR()
}
if (context.isES2()) {
@@ -262,25 +262,20 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
}
void OpenGLProgram::updateUniforms(
uint32_t const index, GLuint const id, void const* buffer,
uint16_t const age, uint32_t const offset) const noexcept {
uint32_t index, GLuint id, void const* buffer, uint16_t age) const noexcept {
assert_invariant(mUniformsRecords);
assert_invariant(buffer);
// only update the uniforms if the UBO has changed since last time we updated
UniformsRecord const& records = mUniformsRecords[index];
if (records.id == id && records.age == age && records.offset == offset) {
if (records.id == id && records.age == age) {
return;
}
records.id = id;
records.age = age;
records.offset = offset;
assert_invariant(records.uniforms.size() == records.locations.size());
// apply the offset to the buffer
buffer = static_cast<char const*>(buffer) + offset;
for (size_t i = 0, c = records.uniforms.size(); i < c; i++) {
Program::Uniform const& u = records.uniforms[i];
GLint const loc = records.locations[i];
+6 -7
View File
@@ -43,8 +43,8 @@ namespace filament::backend {
class OpenGLDriver;
struct PushConstantBundle {
utils::Slice<const std::pair<GLint, ConstantType>> vertexConstants;
utils::Slice<const std::pair<GLint, ConstantType>> fragmentConstants;
utils::Slice<std::pair<GLint, ConstantType>> vertexConstants;
utils::Slice<std::pair<GLint, ConstantType>> fragmentConstants;
};
class OpenGLProgram : public HwProgram {
@@ -88,14 +88,14 @@ public:
}
// For ES2 only
void updateUniforms(uint32_t index, GLuint id, void const* buffer, uint16_t age, uint32_t offset) const noexcept;
void updateUniforms(uint32_t index, GLuint id, void const* buffer, uint16_t age) const noexcept;
void setRec709ColorSpace(bool rec709) const noexcept;
PushConstantBundle getPushConstants() {
auto fragBegin = mPushConstants.begin() + mPushConstantFragmentStageOffset;
return {
.vertexConstants = { mPushConstants.begin(), fragBegin },
.fragmentConstants = { fragBegin, mPushConstants.end() },
.vertexConstants = utils::Slice(mPushConstants.begin(), fragBegin),
.fragmentConstants = utils::Slice(fragBegin, mPushConstants.end()),
};
}
@@ -106,7 +106,7 @@ private:
void initialize(OpenGLDriver& gld);
void initializeProgramState(OpenGLContext& context, GLuint program,
LazyInitializationData& lazyInitializationData);
LazyInitializationData& lazyInitializationData) noexcept;
BindingMap mBindingMap; // 8 bytes + out-of-line 256 bytes
@@ -123,7 +123,6 @@ private:
LocationInfo locations;
mutable GLuint id = 0;
mutable uint16_t age = std::numeric_limits<uint16_t>::max();
mutable uint32_t offset = 0;
};
UniformsRecord const* mUniformsRecords = nullptr;
GLint mRec709Location : 24; // 4 bytes
@@ -51,7 +51,7 @@ class OpenGLDriver;
bool TimerQueryFactory::mGpuTimeSupported = false;
TimerQueryFactoryInterface* TimerQueryFactory::init(
OpenGLPlatform& platform, OpenGLContext& context) {
OpenGLPlatform& platform, OpenGLContext& context) noexcept {
(void)context;
TimerQueryFactoryInterface* impl = nullptr;
@@ -172,12 +172,46 @@ void TimerQueryNativeFactory::endTimeElapsedQuery(OpenGLDriver& driver, GLTimerQ
// ------------------------------------------------------------------------------------------------
TimerQueryFenceFactory::TimerQueryFenceFactory(OpenGLPlatform& platform)
: mPlatform(platform),
mJobQueue("OpenGLTimerQueryFence", utils::AsyncJobQueue::Priority::URGENT_DISPLAY) {
: 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);
});
}
TimerQueryFenceFactory::~TimerQueryFenceFactory() {
mJobQueue.drainAndExit();
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();
}
void TimerQueryFenceFactory::createTimerQuery(GLTimerQuery* tq) {
@@ -202,7 +236,7 @@ void TimerQueryFenceFactory::beginTimeElapsedQuery(GLTimerQuery* tq) {
// on a dummy target for instance, or somehow latch the begin time at the next renderpass
// start.
mJobQueue.push([&platform = mPlatform, fence = mPlatform.createFence(), weak]() {
push([&platform = mPlatform, fence = mPlatform.createFence(), weak]() {
auto state = weak.lock();
if (state) {
platform.waitFence(fence, FENCE_WAIT_FOR_EVER);
@@ -218,7 +252,7 @@ void TimerQueryFenceFactory::endTimeElapsedQuery(OpenGLDriver&, GLTimerQuery* tq
assert_invariant(tq->state);
std::weak_ptr<GLTimerQuery::State> const weak = tq->state;
mJobQueue.push([&platform = mPlatform, fence = mPlatform.createFence(), weak]() {
push([&platform = mPlatform, fence = mPlatform.createFence(), weak]() {
auto state = weak.lock();
if (state) {
platform.waitFence(fence, FENCE_WAIT_FOR_EVER);
+13 -3
View File
@@ -21,7 +21,8 @@
#include "DriverBase.h"
#include <utils/AsyncJobQueue.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include "gl_headers.h"
@@ -64,7 +65,7 @@ class TimerQueryFactory {
static bool mGpuTimeSupported;
public:
static TimerQueryFactoryInterface* init(
OpenGLPlatform& platform, OpenGLContext& context);
OpenGLPlatform& platform, OpenGLContext& context) noexcept;
static bool isGpuTimeSupported() noexcept {
return mGpuTimeSupported;
@@ -107,13 +108,22 @@ 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;
utils::AsyncJobQueue mJobQueue;
std::thread mThread;
mutable utils::Mutex mLock;
mutable utils::Condition mCondition;
Container mQueue;
bool mExitRequested = false;
};
class TimerQueryFallbackFactory final : public TimerQueryFactoryInterface {

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