Compare commits

..
296 changed files with 12720 additions and 35602 deletions
+1 -2
View File
@@ -21,9 +21,8 @@ jobs:
- name: Run update script - name: Run update script
env: env:
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }} GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
run: | run: |
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 test/renderdiff/src/commit_msg.py) 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="${{ steps.get_commit_msg.outputs.hash }}"
if [[ "${GOLDEN_BRANCH}" != "main" ]]; then if [[ "${GOLDEN_BRANCH}" != "main" ]]; then
git config --global user.email "filament.bot@gmail.com" git config --global user.email "filament.bot@gmail.com"
+6 -6
View File
@@ -134,17 +134,18 @@ jobs:
env: env:
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }} COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
run: | run: |
ls ./gltf/Models
TEST_DIR=test/renderdiff TEST_DIR=test/renderdiff
source ${TEST_DIR}/src/preamble.sh source ${TEST_DIR}/src/preamble.sh
set -eux start_
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 ${TEST_DIR}/src/commit_msg.py) GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 ${TEST_DIR}/src/commit_msg.py)
bash ${TEST_DIR}/generate.sh bash ${TEST_DIR}/generate.sh && \
python3 ${TEST_DIR}/src/golden_manager.py \ python3 ${TEST_DIR}/src/golden_manager.py \
--branch=${GOLDEN_BRANCH} \ --branch=${GOLDEN_BRANCH} \
--output=${GOLDEN_OUTPUT_DIR} --output=${GOLDEN_OUTPUT_DIR}
# Note that we need to upload the output even if comparison fails, so we undo `set -eux` # Note that we need to upload the output even if comparison fails, so we undo `set -ex`
set +eux end_
python3 ${TEST_DIR}/src/compare.py \ python3 ${TEST_DIR}/src/compare.py \
--src=${GOLDEN_OUTPUT_DIR} \ --src=${GOLDEN_OUTPUT_DIR} \
@@ -157,7 +158,6 @@ jobs:
cat compare_output.txt >> "$GITHUB_OUTPUT" cat compare_output.txt >> "$GITHUB_OUTPUT"
echo "$DELIMITER" >> "$GITHUB_OUTPUT" echo "$DELIMITER" >> "$GITHUB_OUTPUT"
fi fi
shell: bash
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v4
with: with:
name: presubmit-renderdiff-result name: presubmit-renderdiff-result
+2 -4
View File
@@ -872,9 +872,7 @@ add_subdirectory(${LIBRARIES}/utils)
add_subdirectory(${LIBRARIES}/viewer) add_subdirectory(${LIBRARIES}/viewer)
add_subdirectory(${FILAMENT}/shaders) add_subdirectory(${FILAMENT}/shaders)
add_subdirectory(${EXTERNAL}/abseil/tnt) add_subdirectory(${EXTERNAL}/abseil/tnt)
# Add zstd before basisu to force it to use the external zstd target, add_subdirectory(${EXTERNAL}/basisu/tnt)
# preventing a duplicate symbol conflict with its bundled version.
add_subdirectory(${EXTERNAL}/zstd/tnt)
add_subdirectory(${EXTERNAL}/civetweb/tnt) add_subdirectory(${EXTERNAL}/civetweb/tnt)
add_subdirectory(${EXTERNAL}/imgui/tnt) add_subdirectory(${EXTERNAL}/imgui/tnt)
add_subdirectory(${EXTERNAL}/robin-map/tnt) add_subdirectory(${EXTERNAL}/robin-map/tnt)
@@ -888,7 +886,7 @@ add_subdirectory(${EXTERNAL}/jsmn/tnt)
add_subdirectory(${EXTERNAL}/stb/tnt) add_subdirectory(${EXTERNAL}/stb/tnt)
add_subdirectory(${EXTERNAL}/getopt) add_subdirectory(${EXTERNAL}/getopt)
add_subdirectory(${EXTERNAL}/perfetto/tnt) add_subdirectory(${EXTERNAL}/perfetto/tnt)
add_subdirectory(${EXTERNAL}/basisu/tnt) add_subdirectory(${EXTERNAL}/zstd/tnt)
# Note that this has to be placed after mikktspace in order for combine_static_libs to work. # Note that this has to be placed after mikktspace in order for combine_static_libs to work.
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
} }
dependencies { dependencies {
implementation 'com.google.android.filament:filament-android:1.68.0' implementation 'com.google.android.filament:filament-android:1.67.1'
} }
``` ```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release: iOS projects can use CocoaPods to install the latest release:
```shell ```shell
pod 'Filament', '~> 1.68.0' pod 'Filament', '~> 1.67.1'
``` ```
## Documentation ## Documentation
-3
View File
@@ -7,9 +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 Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md). [NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.68.1
## v1.68.0 ## v1.68.0
- engine: add `View::getLastDynamicResolutionScale()` (b/457753622) - engine: add `View::getLastDynamicResolutionScale()` (b/457753622)
+1 -1
View File
@@ -100,7 +100,7 @@ buildscript {
'kotlin': '2.0.21', 'kotlin': '2.0.21',
'kotlin_coroutines': '1.9.0', 'kotlin_coroutines': '1.9.0',
'buildTools': '35.0.0', 'buildTools': '35.0.0',
'ndk': '29.0.14206865', 'ndk': '27.0.11718014',
'androidx_core': '1.13.1', 'androidx_core': '1.13.1',
'androidx_annotations': '1.9.0' 'androidx_annotations': '1.9.0'
] ]
@@ -25,7 +25,7 @@ using namespace filament;
extern "C" JNIEXPORT jlong JNICALL extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Material_nBuilderBuild(JNIEnv *env, jclass, Java_com_google_android_filament_Material_nBuilderBuild(JNIEnv *env, jclass,
jlong nativeEngine, jobject buffer_, jint size, jint shBandCount, jint shadowQuality, jint uboBatchingMode) { jlong nativeEngine, jobject buffer_, jint size, jint shBandCount, jint shadowQuality) {
Engine* engine = (Engine*) nativeEngine; Engine* engine = (Engine*) nativeEngine;
AutoBuffer buffer(env, buffer_, size); AutoBuffer buffer(env, buffer_, size);
auto builder = Material::Builder(); auto builder = Material::Builder();
@@ -33,7 +33,6 @@ Java_com_google_android_filament_Material_nBuilderBuild(JNIEnv *env, jclass,
builder.sphericalHarmonicsBandCount(shBandCount); builder.sphericalHarmonicsBandCount(shBandCount);
} }
builder.shadowSamplingQuality((Material::Builder::ShadowSamplingQuality)shadowQuality); builder.shadowSamplingQuality((Material::Builder::ShadowSamplingQuality)shadowQuality);
builder.uboBatching((Material::UboBatchingMode)uboBatchingMode);
Material* material = builder Material* material = builder
.package(buffer.getData(), buffer.getSize()) .package(buffer.getData(), buffer.getSize())
.build(*engine); .build(*engine);
@@ -261,20 +261,6 @@ public class Material {
LOW LOW
} }
/**
* Defines whether a material instance should use UBO batching or not.
*/
public enum UboBatchingMode {
/**
* For default, it follows the engine settings.
* If UBO batching is enabled on the engine and the material domain is SURFACE, it
* turns on the UBO batching. Otherwise, it turns off the UBO batching.
*/
DEFAULT,
/** Disable the Ubo Batching for this material */
DISABLED
}
public static class UserVariantFilterBit { public static class UserVariantFilterBit {
/** Directional lighting */ /** Directional lighting */
public static int DIRECTIONAL_LIGHTING = 0x01; public static int DIRECTIONAL_LIGHTING = 0x01;
@@ -386,7 +372,6 @@ public class Material {
private int mSize; private int mSize;
private int mShBandCount = 0; private int mShBandCount = 0;
private ShadowSamplingQuality mShadowSamplingQuality = ShadowSamplingQuality.LOW; private ShadowSamplingQuality mShadowSamplingQuality = ShadowSamplingQuality.LOW;
private UboBatchingMode mUboBatchingMode = UboBatchingMode.DEFAULT;
/** /**
@@ -431,17 +416,6 @@ public class Material {
return this; return this;
} }
/**
* Set the batching mode of the instances created from this material.
* @param uboBatchingMode
* @return Reference to this Builder for chaining calls.
*/
@NonNull
public Builder uboBatching(UboBatchingMode mode) {
mUboBatchingMode = mode;
return this;
}
/** /**
* Creates and returns the Material object. * Creates and returns the Material object.
* *
@@ -454,7 +428,7 @@ public class Material {
@NonNull @NonNull
public Material build(@NonNull Engine engine) { public Material build(@NonNull Engine engine) {
long nativeMaterial = nBuilderBuild(engine.getNativeObject(), long nativeMaterial = nBuilderBuild(engine.getNativeObject(),
mBuffer, mSize, mShBandCount, mShadowSamplingQuality.ordinal(), mUboBatchingMode.ordinal()); mBuffer, mSize, mShBandCount, mShadowSamplingQuality.ordinal());
if (nativeMaterial == 0) throw new IllegalStateException("Couldn't create Material"); if (nativeMaterial == 0) throw new IllegalStateException("Couldn't create Material");
return new Material(nativeMaterial); return new Material(nativeMaterial);
} }
@@ -1120,7 +1094,7 @@ public class Material {
mNativeObject = 0; mNativeObject = 0;
} }
private static native long nBuilderBuild(long nativeEngine, @NonNull Buffer buffer, int size, int shBandCount, int shadowQuality, int uboBatchingMode); private static native long nBuilderBuild(long nativeEngine, @NonNull Buffer buffer, int size, int shBandCount, int shadowQuality);
private static native long nCreateInstance(long nativeMaterial); private static native long nCreateInstance(long nativeMaterial);
private static native long nCreateInstanceWithName(long nativeMaterial, @NonNull String name); private static native long nCreateInstanceWithName(long nativeMaterial, @NonNull String name);
private static native long nGetDefaultInstance(long nativeMaterial); private static native long nGetDefaultInstance(long nativeMaterial);
@@ -16,9 +16,6 @@
package com.google.android.filament.utils package com.google.android.filament.utils
import android.graphics.Bitmap
import android.os.Handler
import android.os.Looper
import android.view.MotionEvent import android.view.MotionEvent
import android.view.Surface import android.view.Surface
import android.view.SurfaceView import android.view.SurfaceView
@@ -29,7 +26,6 @@ import com.google.android.filament.android.UiHelper
import com.google.android.filament.gltfio.* import com.google.android.filament.gltfio.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import java.nio.Buffer import java.nio.Buffer
import java.nio.ByteBuffer
private const val kNearPlane = 0.05f // 5 cm private const val kNearPlane = 0.05f // 5 cm
private const val kFarPlane = 1000.0f // 1 km private const val kFarPlane = 1000.0f // 1 km
@@ -123,8 +119,6 @@ class ModelViewer(
private val target = DoubleArray(3) private val target = DoubleArray(3)
private val upward = DoubleArray(3) private val upward = DoubleArray(3)
private var debugFrameCallback: ((Bitmap) -> Unit)? = null
init { init {
renderer = engine.createRenderer() renderer = engine.createRenderer()
scene = engine.createScene() scene = engine.createScene()
@@ -311,39 +305,10 @@ class ModelViewer(
// Render the scene, unless the renderer wants to skip the frame. // Render the scene, unless the renderer wants to skip the frame.
if (renderer.beginFrame(swapChain!!, frameTimeNanos)) { if (renderer.beginFrame(swapChain!!, frameTimeNanos)) {
renderer.render(view) renderer.render(view)
debugFrameCallback?.let {
val viewport = view.viewport
val bitmap = Bitmap.createBitmap(viewport.width, viewport.height,
Bitmap.Config.ARGB_8888)
val buffer = ByteBuffer.allocateDirect(viewport.width * viewport.height * 4)
val handler = Handler(Looper.getMainLooper())
val pixelBufferDescriptor = Texture.PixelBufferDescriptor(buffer,
Texture.Format.RGBA, Texture.Type.UBYTE, 1, 0, 0, 0, handler) {
buffer.rewind()
bitmap.copyPixelsFromBuffer(buffer)
it(bitmap)
}
renderer.readPixels(viewport.left, viewport.bottom, viewport.width,
viewport.height, pixelBufferDescriptor)
debugFrameCallback = null
}
renderer.endFrame() renderer.endFrame()
} }
} }
/*
* Sets a callback that will be invoked with the next rendered frame as a Bitmap. Note that this
* is a one-time callback.
*
* @param callback callback to be invoked with a rendered frame as [Bitmap]
*/
fun debugGetNextFrameCallback(callback: (Bitmap) -> Unit) {
debugFrameCallback = callback
}
private fun populateScene(asset: FilamentAsset) { private fun populateScene(asset: FilamentAsset) {
val rcm = engine.renderableManager val rcm = engine.renderableManager
var count = 0 var count = 0
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament GROUP=com.google.android.filament
VERSION_NAME=1.68.0 VERSION_NAME=1.67.1
POM_DESCRIPTION=Real-time physically based rendering engine for Android. POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+1 -1
View File
@@ -158,7 +158,7 @@ function print_fgviewer_help {
} }
# Unless explicitly specified, NDK version will be selected as highest available version within same major release chain # Unless explicitly specified, NDK version will be selected as highest available version within same major release chain
FILAMENT_NDK_VERSION=$(cat `dirname $0`/build/common/versions | grep GITHUB_NDK_VERSION | sed s/GITHUB_NDK_VERSION=//g | cut -f 1 -d ".") FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION:-$(cat `dirname $0`/build/common/versions | grep GITHUB_NDK_VERSION | sed s/GITHUB_NDK_VERSION=//g | cut -f 1 -d ".")}
# Internal variables # Internal variables
ISSUE_CLEAN=false ISSUE_CLEAN=false
+2 -2
View File
@@ -18,8 +18,8 @@ if [[ "$GITHUB_WORKFLOW" ]]; then
fi fi
fi fi
# Unless explicitly specified, NDK version will be selected as highest available version within same major release chain # Unless explicitly specified, NDK version will be set to match exactly the required one
FILAMENT_NDK_VERSION=$(cat `dirname $0`/../common/versions | grep GITHUB_NDK_VERSION | sed s/GITHUB_NDK_VERSION=//g) FILAMENT_NDK_VERSION=${GITHUB_NDK_VERSION:-27.0.11718014}
(! grep "${FILAMENT_NDK_VERSION}" `dirname $0`/../../android/build.gradle > /dev/null) && (! grep "${FILAMENT_NDK_VERSION}" `dirname $0`/../../android/build.gradle > /dev/null) &&
echo "Mismatch of NDK versions: want ${FILAMENT_NDK_VERSION} and not found in android/build.gradle" && echo "Mismatch of NDK versions: want ${FILAMENT_NDK_VERSION} and not found in android/build.gradle" &&
+2 -2
View File
@@ -3,7 +3,7 @@ GITHUB_CMAKE_VERSION=3.22.1
GITHUB_NINJA_VERSION=1.10.2 GITHUB_NINJA_VERSION=1.10.2
GITHUB_MESA_VERSION=24.2.1 GITHUB_MESA_VERSION=24.2.1
GITHUB_LLVM_VERSION=16 GITHUB_LLVM_VERSION=16
GITHUB_NDK_VERSION=29.0.14206865 GITHUB_NDK_VERSION=27.0.11718014
GITHUB_EMSDK_VERSION=3.1.60 GITHUB_EMSDK_VERSION=3.1.60
GITHUB_VULKANSDK_VERSION=1.4.321.0 GITHUB_VULKANSDK_VERSION=1.4.321.0
GITHUB_GLTF_SAMPLE_ASSETS_COMMIT=d441dfdb87413ff412c620849a649d61789a470f GITHUB_GLTF_SAMPLE_ASSETS_COMMIT=d441dfdb87413ff412c620849a649d61789a470f
+1 -1
View File
@@ -75,7 +75,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# C_FLAGS += -Wl,-pie # C_FLAGS += -Wl,-pie
# CXX_FLAGS += -lstdc++ # CXX_FLAGS += -lstdc++
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE -march=armv8-a -mtune=cortex-a78" CACHE STRING "Toolchain CFLAGS") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE -mcpu=cortex-a57" CACHE STRING "Toolchain CFLAGS")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS}" CACHE STRING "Toolchain CXXFLAGS") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS}" CACHE STRING "Toolchain CXXFLAGS")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fPIE -pie -static-libstdc++" CACHE STRING "Toolchain LDFLAGS") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fPIE -pie -static-libstdc++" CACHE STRING "Toolchain LDFLAGS")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libstdc++" CACHE STRING "Toolchain LDFLAGS") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libstdc++" CACHE STRING "Toolchain LDFLAGS")
+1 -1
View File
@@ -87,7 +87,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# for hardfp: CFLAGS must have -mhard-float # for hardfp: CFLAGS must have -mhard-float
# LDFLAGS must have -Wl,--no-warn-mismatch # LDFLAGS must have -Wl,--no-warn-mismatch
# #
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mthumb -march=armv7-a -mfloat-abi=softfp -mfpu=neon-vfpv4 -fPIE" CACHE STRING "Toolchain CFLAGS") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mthumb -march=armv7-a -mcpu=cortex-a15 -mfloat-abi=softfp -mfpu=neon-vfpv4 -fPIE" CACHE STRING "Toolchain CFLAGS")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS}" CACHE STRING "Toolchain CXXFLAGS") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS}" CACHE STRING "Toolchain CXXFLAGS")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -march=armv7-a -Wl,--no-warn-mismatch -L${TOOLCHAIN}/arm-linux-androideabi/lib/armv7-a -static-libstdc++ -fPIE -pie" CACHE STRING "Toolchain LDFLAGS") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -march=armv7-a -Wl,--no-warn-mismatch -L${TOOLCHAIN}/arm-linux-androideabi/lib/armv7-a -static-libstdc++ -fPIE -pie" CACHE STRING "Toolchain LDFLAGS")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -march=armv7-a -Wl,--no-warn-mismatch -L${TOOLCHAIN}/arm-linux-androideabi/lib/armv7-a -static-libstdc++" CACHE STRING "Toolchain LDFLAGS") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -march=armv7-a -Wl,--no-warn-mismatch -L${TOOLCHAIN}/arm-linux-androideabi/lib/armv7-a -static-libstdc++" CACHE STRING "Toolchain LDFLAGS")
+2 -2
View File
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
} }
dependencies { dependencies {
implementation 'com.google.android.filament:filament-android:1.68.0' implementation 'com.google.android.filament:filament-android:1.67.1'
} }
</code></pre> </code></pre>
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p> <p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
@@ -196,7 +196,7 @@ dependencies {
</div> </div>
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3> <h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
<p>iOS projects can use CocoaPods to install the latest release:</p> <p>iOS projects can use CocoaPods to install the latest release:</p>
<pre><code class="language-shell">pod 'Filament', '~&gt; 1.68.0' <pre><code class="language-shell">pod 'Filament', '~&gt; 1.67.1'
</code></pre> </code></pre>
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2> <h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
<ul> <ul>
+270 -268
View File
File diff suppressed because one or more lines are too long
+164 -162
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -10,6 +10,6 @@ sortedcontainers==2.4.0
trio==0.31.0 trio==0.31.0
trio-websocket==0.12.2 trio-websocket==0.12.2
typing_extensions==4.15.0 typing_extensions==4.15.0
urllib3==2.6.0 urllib3==2.5.0
websocket-client==1.9.0 websocket-client==1.9.0
wsproto==1.2.0 wsproto==1.2.0
-2
View File
@@ -206,8 +206,6 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/VulkanDriverFactory.h src/vulkan/VulkanDriverFactory.h
src/vulkan/VulkanExternalImageManager.cpp src/vulkan/VulkanExternalImageManager.cpp
src/vulkan/VulkanExternalImageManager.h src/vulkan/VulkanExternalImageManager.h
src/vulkan/VulkanStreamedImageManager.cpp
src/vulkan/VulkanStreamedImageManager.h
src/vulkan/VulkanFboCache.cpp src/vulkan/VulkanFboCache.cpp
src/vulkan/VulkanFboCache.h src/vulkan/VulkanFboCache.h
src/vulkan/VulkanHandles.cpp src/vulkan/VulkanHandles.cpp
@@ -0,0 +1,63 @@
/*
* 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_BUFFEROBJECTSTREAMDESCRIPTOR_H
#define TNT_FILAMENT_BACKEND_BUFFEROBJECTSTREAMDESCRIPTOR_H
#include <backend/Handle.h>
#include <vector>
namespace filament::backend {
/**
* The type of association between a buffer object and a stream.
*/
enum class BufferObjectStreamAssociationType { TRANSFORM_MATRIX };
/**
* A descriptor for a buffer object to stream association.
*/
class UTILS_PUBLIC BufferObjectStreamDescriptor {
public:
//! creates an empty descriptor
BufferObjectStreamDescriptor() noexcept = default;
BufferObjectStreamDescriptor(const BufferObjectStreamDescriptor& rhs) = delete;
BufferObjectStreamDescriptor& operator=(const BufferObjectStreamDescriptor& rhs) = delete;
BufferObjectStreamDescriptor(BufferObjectStreamDescriptor&& rhs) = default;
BufferObjectStreamDescriptor& operator=(BufferObjectStreamDescriptor&& rhs) = default;
// --------------------------------------------------------------------------------------------
struct StreamDataDescriptor {
uint32_t offset;
Handle<HwStream> stream;
BufferObjectStreamAssociationType associationType;
};
std::vector<StreamDataDescriptor> mStreams;
};
} // namespace filament::backend
#if !defined(NDEBUG)
utils::io::ostream& operator<<(utils::io::ostream& out,
const filament::backend::BufferObjectStreamDescriptor& b);
#endif
#endif // TNT_FILAMENT_BACKEND_BUFFEROBJECTSTREAMDESCRIPTOR_H
@@ -1713,8 +1713,6 @@ using FrameTimestamps = Platform::FrameTimestamps;
using CompositorTiming = Platform::CompositorTiming; using CompositorTiming = Platform::CompositorTiming;
using AsynchronousMode = Platform::AsynchronousMode;
} // namespace filament::backend } // namespace filament::backend
template<> struct utils::EnableBitMaskOperators<filament::backend::ShaderStageFlags> template<> struct utils::EnableBitMaskOperators<filament::backend::ShaderStageFlags>
@@ -74,9 +74,6 @@ public:
ExternalImageHandle& operator=(ExternalImageHandle const& rhs) noexcept; ExternalImageHandle& operator=(ExternalImageHandle const& rhs) noexcept;
ExternalImageHandle& operator=(ExternalImageHandle&& rhs) noexcept; ExternalImageHandle& operator=(ExternalImageHandle&& rhs) noexcept;
bool operator==(const ExternalImageHandle& rhs) const noexcept {
return mTarget == rhs.mTarget;
}
explicit operator bool() const noexcept { return mTarget != nullptr; } explicit operator bool() const noexcept { return mTarget != nullptr; }
ExternalImage* UTILS_NULLABLE get() noexcept { return mTarget; } ExternalImage* UTILS_NULLABLE get() noexcept { return mTarget; }
@@ -255,28 +252,6 @@ public:
REALTIME, REALTIME,
}; };
/**
* Defines how asynchronous operations are handled by the engine.
*/
enum class AsynchronousMode : uint8_t {
/**
* Asynchronous operations are disabled. This is the default.
*/
NONE,
/**
* Attempts to use a dedicated worker thread for asynchronous tasks. If threading is not
* supported by the platform, it automatically falls back to using an amortization strategy.
*/
THREAD_PREFERRED,
/**
* Uses an amortization strategy, processing a small number of asynchronous tasks during
* each engine update cycle.
*/
AMORTIZATION,
};
struct DriverConfig { struct DriverConfig {
/** /**
* Size of handle arena in bytes. Setting to 0 indicates default value is to be used. * Size of handle arena in bytes. Setting to 0 indicates default value is to be used.
@@ -346,11 +321,6 @@ public:
* - VulkanPlatform * - VulkanPlatform
*/ */
bool vulkanEnableStagingBufferBypass = false; bool vulkanEnableStagingBufferBypass = false;
/**
* Asynchronous mode for the engine. Defines how asynchronous operations are handled.
*/
AsynchronousMode asynchronousMode = AsynchronousMode::NONE;
}; };
Platform() noexcept; Platform() noexcept;
@@ -30,6 +30,8 @@
#include <math/mat3.h> #include <math/mat3.h>
#include "AndroidNativeWindow.h"
#include <chrono> #include <chrono>
#include <stddef.h> #include <stddef.h>
@@ -22,6 +22,7 @@
#include "private/backend/Driver.h" #include "private/backend/Driver.h"
#include <backend/BufferDescriptor.h> #include <backend/BufferDescriptor.h>
#include <backend/BufferObjectStreamDescriptor.h>
#include <backend/CallbackHandler.h> #include <backend/CallbackHandler.h>
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <backend/Handle.h> #include <backend/Handle.h>
@@ -51,6 +51,7 @@
namespace filament::backend { namespace filament::backend {
class BufferDescriptor; class BufferDescriptor;
class BufferObjectStreamDescriptor;
class CallbackHandler; class CallbackHandler;
class PixelBufferDescriptor; class PixelBufferDescriptor;
class Program; class Program;
@@ -392,6 +392,10 @@ DECL_DRIVER_API_N(updateBufferObject,
backend::BufferDescriptor&&, data, backend::BufferDescriptor&&, data,
uint32_t, byteOffset) uint32_t, byteOffset)
DECL_DRIVER_API_N(registerBufferObjectStreams,
backend::BufferObjectHandle, boh,
backend::BufferObjectStreamDescriptor&&, streams)
DECL_DRIVER_API_N(updateBufferObjectUnsynchronized, DECL_DRIVER_API_N(updateBufferObjectUnsynchronized,
backend::BufferObjectHandle, boh, backend::BufferObjectHandle, boh,
backend::BufferDescriptor&&, data, backend::BufferDescriptor&&, data,
@@ -61,16 +61,6 @@ int NativeWindow::enableFrameTimestamps(ANativeWindow* anw, bool enable) {
return pWindow->perform(anw, ENABLE_FRAME_TIMESTAMPS, enable); return pWindow->perform(anw, ENABLE_FRAME_TIMESTAMPS, enable);
} }
int NativeWindow::frameTimestampsSupportsPresent(ANativeWindow* anw, bool* outSupportsPresent) {
NativeWindow const* pWindow = reinterpret_cast<NativeWindow const*>(anw);
int value = 0;
bool const success = pWindow->perform(anw, FRAME_TIMESTAMPS_SUPPORTS_PRESENT, &value);
if (success) {
*outSupportsPresent = bool(value);
}
return success;
}
int NativeWindow::getCompositorTiming(ANativeWindow* anw, int NativeWindow::getCompositorTiming(ANativeWindow* anw,
int64_t* compositeDeadline, int64_t* compositeInterval, int64_t* compositeDeadline, int64_t* compositeInterval,
int64_t* compositeToPresentLatency) { int64_t* compositeToPresentLatency) {
@@ -32,7 +32,6 @@ struct NativeWindow {
// is valid query enum value // is valid query enum value
enum { enum {
IS_VALID = 17, IS_VALID = 17,
FRAME_TIMESTAMPS_SUPPORTS_PRESENT = 18,
GET_NEXT_FRAME_ID = 24, GET_NEXT_FRAME_ID = 24,
ENABLE_FRAME_TIMESTAMPS = 25, ENABLE_FRAME_TIMESTAMPS = 25,
GET_COMPOSITOR_TIMING = 26, GET_COMPOSITOR_TIMING = 26,
@@ -52,7 +51,6 @@ struct NativeWindow {
static int getNextFrameId(ANativeWindow* anw, uint64_t* frameId); static int getNextFrameId(ANativeWindow* anw, uint64_t* frameId);
static int enableFrameTimestamps(ANativeWindow* anw, bool enable); static int enableFrameTimestamps(ANativeWindow* anw, bool enable);
static int frameTimestampsSupportsPresent(ANativeWindow* anw, bool* outSupportsPresent);
static int getCompositorTiming(ANativeWindow* anw, static int getCompositorTiming(ANativeWindow* anw,
int64_t* compositeDeadline, int64_t* compositeInterval, int64_t* compositeDeadline, int64_t* compositeInterval,
int64_t* compositeToPresentLatency); int64_t* compositeToPresentLatency);
@@ -19,9 +19,6 @@
#include <android/native_window.h> #include <android/native_window.h>
#include <utils/compiler.h>
#include <utils/Logger.h>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <limits> #include <limits>
@@ -39,23 +36,11 @@ bool AndroidSwapChainHelper::setPresentFrameId(
int const status = NativeWindow::getNextFrameId(anw, &sysFrameId); int const status = NativeWindow::getNextFrameId(anw, &sysFrameId);
if (status == 0) { if (status == 0) {
std::lock_guard const lock(mLock); std::lock_guard const lock(mLock);
// frameIds must be strictly monotonic, if that's not the case (i.e. the new frameId is auto const pos = mFrameIdToSystemFrameId.find(frameId);
// less or equal to the last one in the map), we have to clear the map, because the if (pos && *pos != sysFrameId) {
// map's find() assume sorted keys. // we're trying to associate the same frame id to a different frame!
// This case can happen if two different filament::Renderer are used with the same return false;
// ANativeWindow (the Renderer would have different frameIds). This is expected to
// be a rare case.
if (UTILS_UNLIKELY(!mFrameIdToSystemFrameId.empty() &&
frameId <= mFrameIdToSystemFrameId.back().first)) {
// this log is expected to happen very rarely
DLOG(INFO) << "clearing frame history anw=" << anw
<< ", frameId=" << frameId
<< ", previous=" << mFrameIdToSystemFrameId.back().first
<< ", sysFrameId=" << sysFrameId;
// clear the frame history
mFrameIdToSystemFrameId.clear();
} }
// oldest entry is removed // oldest entry is removed
mFrameIdToSystemFrameId.insert(frameId, sysFrameId); mFrameIdToSystemFrameId.insert(frameId, sysFrameId);
return true; return true;
+3 -3
View File
@@ -146,12 +146,12 @@ void Platform::setBlobFunc(InsertBlobFunc&& insertBlob, RetrieveBlobFunc&& retri
bool Platform::hasInsertBlobFunc() const noexcept { bool Platform::hasInsertBlobFunc() const noexcept {
std::lock_guard<decltype(mMutex)> lock(mMutex); std::lock_guard<decltype(mMutex)> lock(mMutex);
return mInsertBlob && bool(*mInsertBlob); return bool(mInsertBlob);
} }
bool Platform::hasRetrieveBlobFunc() const noexcept { bool Platform::hasRetrieveBlobFunc() const noexcept {
std::lock_guard<decltype(mMutex)> lock(mMutex); std::lock_guard<decltype(mMutex)> lock(mMutex);
return mRetrieveBlob && bool(*mRetrieveBlob); return bool(mRetrieveBlob);
} }
void Platform::insertBlob(void const* key, size_t keySize, void const* value, size_t valueSize) { void Platform::insertBlob(void const* key, size_t keySize, void const* value, size_t valueSize) {
@@ -184,7 +184,7 @@ void Platform::setDebugUpdateStatFunc(DebugUpdateStatFunc&& debugUpdateStat) noe
bool Platform::hasDebugUpdateStatFunc() const noexcept { bool Platform::hasDebugUpdateStatFunc() const noexcept {
std::lock_guard<decltype(mMutex)> lock(mMutex); std::lock_guard<decltype(mMutex)> lock(mMutex);
return mDebugUpdateStat && bool(*mDebugUpdateStat); return mDebugUpdateStat != nullptr;
} }
void Platform::debugUpdateStat(const char* key, uint64_t intValue) { void Platform::debugUpdateStat(const char* key, uint64_t intValue) {
+6 -3
View File
@@ -165,7 +165,7 @@ public:
size_t size, bool forceGpuBuffer = false); size_t size, bool forceGpuBuffer = false);
~MetalBuffer(); ~MetalBuffer();
[[nodiscard]] bool wasAllocationSuccessful() const noexcept { return mBuffer; } [[nodiscard]] bool wasAllocationSuccessful() const noexcept { return mBuffer || mCpuBuffer; }
MetalBuffer(const MetalBuffer& rhs) = delete; MetalBuffer(const MetalBuffer& rhs) = delete;
MetalBuffer& operator=(const MetalBuffer& rhs) = delete; MetalBuffer& operator=(const MetalBuffer& rhs) = delete;
@@ -185,12 +185,14 @@ public:
* Denotes that this buffer is used for a draw call ensuring that its allocation remains valid * Denotes that this buffer is used for a draw call ensuring that its allocation remains valid
* until the end of the current frame. * until the end of the current frame.
* *
* @return The MTLBuffer representing the current state of the buffer to bind, it never returns * @return The MTLBuffer representing the current state of the buffer to bind, or nil if there
* nil. * is no device allocation.
* *
*/ */
id<MTLBuffer> getGpuBufferForDraw() noexcept; id<MTLBuffer> getGpuBufferForDraw() noexcept;
void* getCpuBuffer() const noexcept { return mCpuBuffer; }
void setLabel(const utils::ImmutableCString& label) { void setLabel(const utils::ImmutableCString& label) {
#if FILAMENT_METAL_DEBUG_LABELS #if FILAMENT_METAL_DEBUG_LABELS
if (label.empty()) { if (label.empty()) {
@@ -233,6 +235,7 @@ private:
UploadStrategy mUploadStrategy; UploadStrategy mUploadStrategy;
TrackedMetalBuffer mBuffer; TrackedMetalBuffer mBuffer;
size_t mBufferSize = 0; size_t mBufferSize = 0;
void* mCpuBuffer = nullptr;
MetalContext& mContext; MetalContext& mContext;
}; };
+63 -13
View File
@@ -39,30 +39,34 @@ MetalBuffer::MetalBuffer(MetalContext& context, BufferObjectBinding bindingType,
mUploadStrategy = UploadStrategy::POOL; mUploadStrategy = UploadStrategy::POOL;
} }
MTLResourceOptions options = MTLResourceStorageModePrivate; // If the buffer is less than 4K in size and is updated frequently, we don't use an explicit
// buffer. Instead, we use immediate command encoder methods like setVertexBytes:length:atIndex:.
// This won't work for SSBOs, since they are read/write.
// The buffer will be memory mapped for write operations. /*
if (any(usage & BufferUsage::SHARED_WRITE_BIT)) { if (size <= 4 * 1024 && bindingType != BufferObjectBinding::SHADER_STORAGE &&
#if defined(FILAMENT_IOS) || defined(__arm64__) || defined(__aarch64__) usage == BufferUsage::DYNAMIC && !forceGpuBuffer) {
// iOS and Apple Silicon devices use UMA (Unified Memory Architecture), so we use Shared memory. mBuffer = nil;
options = MTLResourceStorageModeShared; mCpuBuffer = malloc(size);
#else return;
// Intel Macs require Managed memory for CPU/GPU synchronization.
options = MTLResourceStorageModeManaged;
#endif
} }
*/
// Otherwise, we allocate a private GPU buffer.
{ {
ScopedAllocationTimer timer("generic"); ScopedAllocationTimer timer("generic");
mBuffer = { [context.device newBufferWithLength:size options:options], mBuffer = { [context.device newBufferWithLength:size options:MTLResourceStorageModePrivate],
TrackedMetalBuffer::Type::GENERIC }; TrackedMetalBuffer::Type::GENERIC };
} }
// mBuffer might fail to be allocated. Clients can check for this by calling // mBuffer might fail to be allocated. Clients can check for this by calling
// wasAllocationSuccessful(). // wasAllocationSuccessful().
} }
MetalBuffer::~MetalBuffer() = default; MetalBuffer::~MetalBuffer() {
if (mCpuBuffer) {
free(mCpuBuffer);
}
}
void MetalBuffer::copyIntoBuffer( void MetalBuffer::copyIntoBuffer(
void* src, size_t size, size_t byteOffset, TagResolver&& getHandleTag) { void* src, size_t size, size_t byteOffset, TagResolver&& getHandleTag) {
@@ -79,6 +83,12 @@ void MetalBuffer::copyIntoBuffer(
FILAMENT_CHECK_PRECONDITION(!(byteOffset & 0x3)) FILAMENT_CHECK_PRECONDITION(!(byteOffset & 0x3))
<< "byteOffset must be a multiple of 4, tag=" << getHandleTag(); << "byteOffset must be a multiple of 4, tag=" << getHandleTag();
// If we have a cpu buffer, we can directly copy into it.
if (mCpuBuffer) {
memcpy(static_cast<uint8_t*>(mCpuBuffer) + byteOffset, src, size);
return;
}
switch (mUploadStrategy) { switch (mUploadStrategy) {
case UploadStrategy::BUMP_ALLOCATOR: case UploadStrategy::BUMP_ALLOCATOR:
uploadWithBumpAllocator(src, size, byteOffset, std::move(getHandleTag)); uploadWithBumpAllocator(src, size, byteOffset, std::move(getHandleTag));
@@ -96,6 +106,11 @@ void MetalBuffer::copyIntoBufferUnsynchronized(
} }
id<MTLBuffer> MetalBuffer::getGpuBufferForDraw() noexcept { id<MTLBuffer> MetalBuffer::getGpuBufferForDraw() noexcept {
// If there's a CPU buffer, then we return nil here, as the CPU-side buffer will be bound
// separately.
if (mCpuBuffer) {
return nil;
}
assert_invariant(mBuffer); assert_invariant(mBuffer);
return mBuffer.get(); return mBuffer.get();
} }
@@ -156,6 +171,41 @@ void MetalBuffer::bindBuffers(id<MTLCommandBuffer> cmdBuffer, id<MTLCommandEncod
offsets:metalOffsets.data() offsets:metalOffsets.data()
withRange:bufferRange]; withRange:bufferRange];
} }
for (size_t b = 0; b < count; b++) {
MetalBuffer* const buffer = buffers[b];
if (!buffer) {
continue;
}
const void* cpuBuffer = buffer->getCpuBuffer();
if (!cpuBuffer) {
continue;
}
const size_t bufferIndex = bufferStart + b;
const size_t offset = offsets[b];
auto* bytes = static_cast<const uint8_t*>(cpuBuffer);
if (stages & Stage::VERTEX) {
[(id<MTLRenderCommandEncoder>) encoder setVertexBytes:(bytes + offset)
length:(buffer->getSize() - offset)
atIndex:bufferIndex];
}
if (stages & Stage::FRAGMENT) {
[(id<MTLRenderCommandEncoder>) encoder setFragmentBytes:(bytes + offset)
length:(buffer->getSize() - offset)
atIndex:bufferIndex];
}
if (stages & Stage::COMPUTE) {
// TODO: using setBytes means the data is read-only, which currently isn't enforced.
// In practice this won't be an issue since MetalBuffer ensures all SSBOs are realized
// through actual id<MTLBuffer> allocations.
[(id<MTLComputeCommandEncoder>) encoder setBytes:(bytes + offset)
length:(buffer->getSize() - offset)
atIndex:bufferIndex];
}
}
} }
void MetalBuffer::uploadWithPoolBuffer( void MetalBuffer::uploadWithPoolBuffer(
-2
View File
@@ -61,8 +61,6 @@ public:
MetalContext* getContext() { return mContext; } MetalContext* getContext() { return mContext; }
using DriverBase::scheduleDestroy;
private: private:
friend class MetalSwapChain; friend class MetalSwapChain;
+17 -15
View File
@@ -1144,12 +1144,8 @@ bool MetalDriver::isRenderTargetFormatSupported(TextureFormat format) {
bool MetalDriver::isFrameBufferFetchSupported() { bool MetalDriver::isFrameBufferFetchSupported() {
// FrameBuffer fetch is achievable via "programmable blending" in Metal, and only supported on // FrameBuffer fetch is achievable via "programmable blending" in Metal, and only supported on
// Apple GPUs with readWriteTextureSupport. // Apple GPUs with readWriteTextureSupport.
// On macOS, framebuffer fetch requires MSL 2.3, which is only available with macOS 11.0. return mContext->highestSupportedGpuFamily.apple >= 1 &&
if (@available(macOS 11.0, *)) { mContext->device.readWriteTextureSupport;
return mContext->highestSupportedGpuFamily.apple >= 1 &&
mContext->device.readWriteTextureSupport;
}
return false;
} }
bool MetalDriver::isFrameBufferFetchMultiSampleSupported() { bool MetalDriver::isFrameBufferFetchMultiSampleSupported() {
@@ -1290,6 +1286,10 @@ void MetalDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&
scheduleDestroy(std::move(data)); scheduleDestroy(std::move(data));
} }
void MetalDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh, BufferObjectStreamDescriptor&& streams) {
// Noop
}
void MetalDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& data, void MetalDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& data,
uint32_t byteOffset) { uint32_t byteOffset) {
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext)) FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
@@ -2275,10 +2275,7 @@ MemoryMappedBufferHandle MetalDriver::mapBufferS() noexcept {
void MetalDriver::mapBufferR(MemoryMappedBufferHandle mmbh, void MetalDriver::mapBufferR(MemoryMappedBufferHandle mmbh,
BufferObjectHandle boh, size_t offset, BufferObjectHandle boh, size_t offset,
size_t size, MapBufferAccessFlags access, utils::ImmutableCString&& tag) { size_t size, MapBufferAccessFlags access, utils::ImmutableCString&& tag) {
assert_invariant(boh); construct_handle<MetalMemoryMappedBuffer>(mmbh, boh, offset, size, access);
MetalBufferObject* bo = mHandleAllocator.handle_cast<MetalBufferObject*>(boh);
assert_invariant(bo);
construct_handle<MetalMemoryMappedBuffer>(mmbh, bo, offset, size, access);
mHandleAllocator.associateTagToHandle(mmbh.getId(), std::move(tag)); mHandleAllocator.associateTagToHandle(mmbh.getId(), std::move(tag));
} }
@@ -2286,16 +2283,21 @@ void MetalDriver::unmapBuffer(MemoryMappedBufferHandle mmbh) {
if (UTILS_UNLIKELY(!mmbh)) { if (UTILS_UNLIKELY(!mmbh)) {
return; return;
} }
auto* mmb = handle_cast<MetalMemoryMappedBuffer>(mmbh);
mmb->unmap();
destruct_handle<MetalMemoryMappedBuffer>(mmbh); destruct_handle<MetalMemoryMappedBuffer>(mmbh);
} }
void MetalDriver::copyToMemoryMappedBuffer(MemoryMappedBufferHandle mmbh, size_t offset, void MetalDriver::copyToMemoryMappedBuffer(MemoryMappedBufferHandle mmbh, size_t offset,
BufferDescriptor&& data) { BufferDescriptor&& data) {
auto* mmb = handle_cast<MetalMemoryMappedBuffer>(mmbh); auto mmb = handle_cast<MetalMemoryMappedBuffer>(mmbh);
mmb->copy(*this, offset, std::move(data));
assert_invariant(any(mmb->access & MapBufferAccessFlags::WRITE_BIT));
assert_invariant(offset + data.size <= mmb->size);
// TODO: this isa zero-effort implementation of copyToMemoryMappedBuffer(), where we just
// call updateBufferObject(). This could be a fallback implementation for when
// shared memory is not available.
// On UMA systems, this should just be a memcpy into the memory-mapped buffer.
updateBufferObject(mmb->boh, std::move(data), mmb->offset + offset);
} }
// explicit instantiation of the Dispatcher // explicit instantiation of the Dispatcher
+7 -15
View File
@@ -569,22 +569,14 @@ struct MetalDescriptorSet : public HwDescriptorSet {
struct MetalMemoryMappedBuffer : public HwMemoryMappedBuffer { struct MetalMemoryMappedBuffer : public HwMemoryMappedBuffer {
MetalMemoryMappedBuffer(BufferObjectHandle boh, size_t const offset,
size_t const size, MapBufferAccessFlags const access)
: boh(boh), access(access), size(size), offset(offset) {
}
BufferObjectHandle boh{};
MapBufferAccessFlags access{}; MapBufferAccessFlags access{};
struct { uint32_t size = 0;
MetalBufferObject* bo; uint32_t offset = 0;
void* vaddr = nullptr;
uint32_t size = 0;
uint32_t offset = 0;
} mtl;
MetalMemoryMappedBuffer(MetalBufferObject* bo, size_t offset, size_t size,
MapBufferAccessFlags access) noexcept;
~MetalMemoryMappedBuffer();
void unmap();
void copy(MetalDriver& mtld, size_t offset, BufferDescriptor&& data) const;
}; };
} // namespace backend } // namespace backend
@@ -1664,44 +1664,5 @@ id<MTLBuffer> MetalDescriptorSet::finalizeAndGetBuffer(MetalDriver* driver, Shad
return buffer.get(); return buffer.get();
} }
MetalMemoryMappedBuffer::MetalMemoryMappedBuffer(MetalBufferObject* bo, size_t offset, size_t size,
MapBufferAccessFlags access) noexcept : access(access) {
MetalBuffer* buffer = bo->getBuffer();
assert_invariant(buffer);
id<MTLBuffer> mtlBuffer = buffer->getGpuBufferForDraw();
assert_invariant(offset + size <= bo->byteCount);
assert_invariant(mtlBuffer.storageMode != MTLStorageModePrivate);
mtl.bo = bo;
mtl.vaddr = static_cast<char*>(mtlBuffer.contents) + offset;
mtl.size = size;
mtl.offset = offset;
}
MetalMemoryMappedBuffer::~MetalMemoryMappedBuffer() = default;
void MetalMemoryMappedBuffer::unmap() {
#if !defined(FILAMENT_IOS) && defined(__x86_64__)
// Managed memory requires didModifyRange to synchronize changes to the GPU. This is specific to Intel Macs.
MetalBuffer* buffer = mtl.bo->getBuffer();
id<MTLBuffer> mtlBuffer = buffer->getGpuBufferForDraw();
if (mtlBuffer && mtlBuffer.storageMode == MTLStorageModeManaged) {
[mtlBuffer didModifyRange:NSMakeRange(mtl.offset, mtl.size)];
}
#endif
// Shared memory on UMA systems is coherent; no explicit synchronization is required.
}
void MetalMemoryMappedBuffer::copy(MetalDriver& mtld, size_t offset, BufferDescriptor&& data) const {
assert_invariant(any(access & MapBufferAccessFlags::WRITE_BIT));
assert_invariant(offset + data.size <= mtl.size);
assert_invariant(mtl.vaddr);
memcpy(static_cast<char*>(mtl.vaddr) + offset, data.buffer, data.size);
mtld.scheduleDestroy(std::move(data));
}
} // namespace backend } // namespace backend
} // namespace filament } // namespace filament
@@ -111,22 +111,9 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
id<MTLLibrary> library = nil; id<MTLLibrary> library = nil;
switch (program.getShaderLanguage()) { switch (program.getShaderLanguage()) {
case ShaderLanguage::MSL: { case ShaderLanguage::MSL: {
// By default, Metal uses the most recent language version.
MTLCompileOptions* options = [MTLCompileOptions new]; MTLCompileOptions* options = [MTLCompileOptions new];
options.fastMathEnabled = YES; options.fastMathEnabled = YES;
// The docs for MTLCompileOptions say that Metal uses the most
// recent language version if it is not specified, but this is
// not always true: it is possible for the environment to be set
// up in a way where an older version is used by default. So we
// explicitly set the minimum version we require.
#if defined(FILAMENT_IOS)
options.languageVersion = MTLLanguageVersion2_0;
#else
if (@available(macOS 11.0, *)) {
options.languageVersion = MTLLanguageVersion2_3;
} else {
options.languageVersion = MTLLanguageVersion2_2;
}
#endif
assert_invariant(source[source.size() - 1] == '\0'); assert_invariant(source[source.size() - 1] == '\0');
// the shader string is null terminated and the length includes the null character // the shader string is null terminated and the length includes the null character
+2
View File
@@ -291,6 +291,8 @@ void NoopDriver::updateBufferObject(Handle<HwBufferObject> ibh, BufferDescriptor
scheduleDestroy(std::move(p)); scheduleDestroy(std::move(p));
} }
void NoopDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh, BufferObjectStreamDescriptor&& streams) { }
void NoopDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> ibh, BufferDescriptor&& p, void NoopDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> ibh, BufferDescriptor&& p,
uint32_t byteOffset) { uint32_t byteOffset) {
scheduleDestroy(std::move(p)); scheduleDestroy(std::move(p));
+1 -1
View File
@@ -735,7 +735,7 @@ void OpenGLContext::bindVertexArray(RenderPrimitive const* p) noexcept {
// - the nameVersion is out of date *and* we're on the protected context, in this case: // - the nameVersion is out of date *and* we're on the protected context, in this case:
// - the name must be stale from a previous use of this context because we always // - the name must be stale from a previous use of this context because we always
// destroy the protected context when we're done with it. // destroy the protected context when we're done with it.
bool const recreateVaoName = vao != &mDefaultVAO && bool const recreateVaoName = p != &mDefaultVAO &&
((vao->vao[contextIndex] == 0) || ((vao->vao[contextIndex] == 0) ||
(vao->nameVersion != state.age && contextIndex == 1)); (vao->nameVersion != state.age && contextIndex == 1));
if (UTILS_UNLIKELY(recreateVaoName)) { if (UTILS_UNLIKELY(recreateVaoName)) {
@@ -39,6 +39,7 @@
#include <backend/Platform.h> #include <backend/Platform.h>
#include <backend/Program.h> #include <backend/Program.h>
#include <backend/TargetBufferInfo.h> #include <backend/TargetBufferInfo.h>
#include <backend/BufferObjectStreamDescriptor.h>
#include "private/backend/CommandStream.h" #include "private/backend/CommandStream.h"
#include "private/backend/Dispatcher.h" #include "private/backend/Dispatcher.h"
@@ -2836,6 +2837,38 @@ void OpenGLDriver::updateIndexBuffer(
CHECK_GL_ERROR() CHECK_GL_ERROR()
} }
void OpenGLDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh, BufferObjectStreamDescriptor&& streams) {
DEBUG_MARKER()
GLBufferObject const* bo = handle_cast<GLBufferObject*>(boh);
mStreamUniformDescriptors[bo->gl.id] = std::move(streams);
}
// specialization for mat3f (which has a different alignment, see std140 layout rules)
static void copyMat3f(void* addr, size_t const offset, const mat3f& v) noexcept {
struct mat43 {
float v[3][4];
};
addr = static_cast<char*>(addr) + offset;
mat43& temp = *static_cast<mat43*>(addr);
temp.v[0][0] = v[0][0];
temp.v[0][1] = v[0][1];
temp.v[0][2] = v[0][2];
temp.v[1][0] = v[1][0];
temp.v[1][1] = v[1][1];
temp.v[1][2] = v[1][2];
temp.v[2][0] = v[2][0];
temp.v[2][1] = v[2][1];
temp.v[2][2] = v[2][2];
// don't store anything in temp.v[][3] because there could be uniforms packed there
}
void OpenGLDriver::updateBufferObject( void OpenGLDriver::updateBufferObject(
Handle<HwBufferObject> boh, BufferDescriptor&& bd, uint32_t const byteOffset) { Handle<HwBufferObject> boh, BufferDescriptor&& bd, uint32_t const byteOffset) {
DEBUG_MARKER() DEBUG_MARKER()
@@ -2849,6 +2882,19 @@ void OpenGLDriver::updateBufferObject(
gl.bindVertexArray(nullptr); gl.bindVertexArray(nullptr);
} }
if (UTILS_UNLIKELY(!mStreamUniformDescriptors.empty())) {
auto const streamDescriptors = mStreamUniformDescriptors.find(bo->gl.id);
if (streamDescriptors != mStreamUniformDescriptors.end()) {
for (auto const& [offset, stream, associationType] : streamDescriptors->second.mStreams) {
if (associationType == BufferObjectStreamAssociationType::TRANSFORM_MATRIX) {
auto transform = getStreamTransformMatrix(stream);
copyMat3f(bd.buffer, offset, transform);
}
}
mStreamUniformDescriptors.erase(streamDescriptors);
}
}
if (UTILS_UNLIKELY(bo->bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) { if (UTILS_UNLIKELY(bo->bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) {
assert_invariant(bo->gl.buffer); assert_invariant(bo->gl.buffer);
memcpy(static_cast<uint8_t*>(bo->gl.buffer) + byteOffset, bd.buffer, bd.size); memcpy(static_cast<uint8_t*>(bo->gl.buffer) + byteOffset, bd.buffer, bd.size);
@@ -36,6 +36,7 @@
#include <backend/Platform.h> #include <backend/Platform.h>
#include <backend/Program.h> #include <backend/Program.h>
#include <backend/TargetBufferInfo.h> #include <backend/TargetBufferInfo.h>
#include <backend/BufferObjectStreamDescriptor.h>
#include "private/backend/Driver.h" #include "private/backend/Driver.h"
#include "private/backend/HandleAllocator.h" #include "private/backend/HandleAllocator.h"
@@ -393,6 +394,8 @@ private:
// the must be accessed from the user thread only // the must be accessed from the user thread only
std::vector<GLStream*> mStreamsWithPendingAcquiredImage; std::vector<GLStream*> mStreamsWithPendingAcquiredImage;
std::unordered_map<GLuint, BufferObjectStreamDescriptor> mStreamUniformDescriptors;
void attachStream(GLTexture* t, GLStream* stream); void attachStream(GLTexture* t, GLStream* stream);
void detachStream(GLTexture* t) noexcept; void detachStream(GLTexture* t) noexcept;
void replaceStream(GLTexture* t, GLStream* stream) noexcept; void replaceStream(GLTexture* t, GLStream* stream) noexcept;
+1 -1
View File
@@ -37,7 +37,7 @@
*/ */
#if defined(__ANDROID__) || defined(FILAMENT_USE_EXTERNAL_GLES3) || defined(__EMSCRIPTEN__) || defined(FILAMENT_SUPPORTS_EGL_ON_LINUX) #if defined(__ANDROID__) || defined(FILAMENT_USE_EXTERNAL_GLES3) || defined(__EMSCRIPTEN__)
#if defined(__EMSCRIPTEN__) #if defined(__EMSCRIPTEN__)
# include <GLES3/gl3.h> # include <GLES3/gl3.h>
@@ -106,8 +106,6 @@ struct PlatformEGLAndroid::SwapChainEGLAndroid : public SwapChainEGL {
void terminate(PlatformEGLAndroid& platform); void terminate(PlatformEGLAndroid& platform);
bool setPresentFrameId(uint64_t frameId) const noexcept; bool setPresentFrameId(uint64_t frameId) const noexcept;
uint64_t getFrameId(uint64_t frameId) const noexcept; uint64_t getFrameId(uint64_t frameId) const noexcept;
bool compositorTimingSupported = false;
bool frameTimestampsSupported = false;
private: private:
AndroidSwapChainHelper mImpl{}; AndroidSwapChainHelper mImpl{};
}; };
@@ -178,30 +176,20 @@ void PlatformEGLAndroid::beginFrame(
int64_t const monotonic_clock_ns, int64_t const monotonic_clock_ns,
int64_t refreshIntervalNs, int64_t refreshIntervalNs,
uint32_t const frameId) noexcept { uint32_t const frameId) noexcept {
// if frameId is 0, it means we're not associated to a particular frame, which is the case // associate the user frameid with the system frame id
// for standalone views. And in this case we skip the performance hint (since we wouldn't get setPresentFrameId(mCurrentDrawSwapChain, frameId);
// the right timing anyway as well as the frame info timing collections)
// associate the user frameid with the system frame id. if (mPerformanceHintSession.isValid()) {
if (frameId && mCurrentDrawSwapChain) {
// mCurrentDrawSwapChain could be null if we're called from renderStandaloneView
setPresentFrameId(mCurrentDrawSwapChain, frameId);
}
if (frameId && mPerformanceHintSession.isValid()) {
if (refreshIntervalNs <= 0) { if (refreshIntervalNs <= 0) {
// we're not provided with a target time, use the display period, if everything fails, // we're not provided with a target time, use the display period, if everything fails,
// assume 16.67ms // assume 16.67ms
refreshIntervalNs = 16'666'667; refreshIntervalNs = 16'666'667;
if (mCurrentDrawSwapChain) { CompositorTiming compositorTiming{};
// mCurrentDrawSwapChain could be null if we're called from renderStandaloneView bool const hasCompositorTiming =
CompositorTiming compositorTiming{}; queryCompositorTiming(mCurrentDrawSwapChain, &compositorTiming);
bool const hasCompositorTiming = if (hasCompositorTiming && compositorTiming.compositeInterval > 0) {
queryCompositorTiming(mCurrentDrawSwapChain, &compositorTiming); refreshIntervalNs = compositorTiming.compositeInterval;
if (hasCompositorTiming && compositorTiming.compositeInterval > 0) {
refreshIntervalNs = compositorTiming.compositeInterval;
}
} }
} }
mStartTimeOfActualWork = clock::time_point(std::chrono::nanoseconds(monotonic_clock_ns)); mStartTimeOfActualWork = clock::time_point(std::chrono::nanoseconds(monotonic_clock_ns));
@@ -240,9 +228,9 @@ Driver* PlatformEGLAndroid::createDriver(void* sharedContext,
"eglGetNativeClientBufferANDROID")); "eglGetNativeClientBufferANDROID"));
if (ext.egl.ANDROID_presentation_time) { if (ext.egl.ANDROID_presentation_time) {
eglPresentationTimeANDROID = eglGetNativeClientBufferANDROID =
PFNEGLPRESENTATIONTIMEANDROIDPROC(eglGetProcAddress( PFNEGLGETNATIVECLIENTBUFFERANDROIDPROC(eglGetProcAddress(
"eglPresentationTimeANDROID")); "eglGetNativeClientBufferANDROID"));
} }
if (ext.egl.ANDROID_get_frame_timestamps) { if (ext.egl.ANDROID_get_frame_timestamps) {
@@ -301,21 +289,11 @@ bool PlatformEGLAndroid::queryCompositorTiming(SwapChain const* swapchain,
outCompositorTiming->frameTime = preferredTimeline.frameTime; outCompositorTiming->frameTime = preferredTimeline.frameTime;
outCompositorTiming->expectedPresentTime = preferredTimeline.expectedPresentTime; outCompositorTiming->expectedPresentTime = preferredTimeline.expectedPresentTime;
outCompositorTiming->frameTimelineDeadline = preferredTimeline.frameTimelineDeadline; outCompositorTiming->frameTimelineDeadline = preferredTimeline.frameTimelineDeadline;
outCompositorTiming->compositeDeadline = CompositorTiming::INVALID;
outCompositorTiming->compositeInterval = CompositorTiming::INVALID;
outCompositorTiming->compositeToPresentLatency = CompositorTiming::INVALID;
// From this point on, we always return "success" because some timings were returned.
if (!static_cast<SwapChainEGLAndroid const *>(swapchain)->compositorTimingSupported) {
// if this surface doesn't support it, don't attempt to query the values.
return true;
}
if (UTILS_LIKELY(ext.egl.ANDROID_get_frame_timestamps)) { if (UTILS_LIKELY(ext.egl.ANDROID_get_frame_timestamps)) {
EGLSurface const sur = static_cast<SwapChainEGL const *>(swapchain)->sur; EGLSurface const sur = static_cast<SwapChainEGL const *>(swapchain)->sur;
if (sur == EGL_NO_SURFACE) { if (sur == EGL_NO_SURFACE) {
return true; return false;
} }
std::array<EGLnsecsANDROID, 3> values; std::array<EGLnsecsANDROID, 3> values;
@@ -326,16 +304,26 @@ bool PlatformEGLAndroid::queryCompositorTiming(SwapChain const* swapchain,
}; };
EGLBoolean const success = eglGetCompositorTimingANDROID(getEglDisplay(), sur, EGLBoolean const success = eglGetCompositorTimingANDROID(getEglDisplay(), sur,
names.size(), names.data(), values.data()); names.size(), names.data(), values.data());
if (UTILS_UNLIKELY(!success)) { if (!success) {
// reset current error to EGL_SUCCESS return false;
eglGetError();
} else {
outCompositorTiming->compositeDeadline = values[0];
outCompositorTiming->compositeInterval = values[1];
outCompositorTiming->compositeToPresentLatency = values[2];
} }
outCompositorTiming->compositeDeadline = values[0];
outCompositorTiming->compositeInterval = values[1];
outCompositorTiming->compositeToPresentLatency = values[2];
return true;
} }
return true;
// fallback to private APIs
auto const anw = static_cast<SwapChainEGL const *>(swapchain)->nativeWindow;
int const status = NativeWindow::getCompositorTiming(anw,
&outCompositorTiming->compositeDeadline,
&outCompositorTiming->compositeInterval,
&outCompositorTiming->compositeToPresentLatency);
if (status == 0) {
return true;
}
return PlatformEGL::queryCompositorTiming(swapchain, outCompositorTiming);
} }
bool PlatformEGLAndroid::setPresentFrameId(SwapChain const* swapchain, bool PlatformEGLAndroid::setPresentFrameId(SwapChain const* swapchain,
@@ -360,10 +348,6 @@ bool PlatformEGLAndroid::queryFrameTimestamps(SwapChain const* swapchain, uint64
return false; return false;
} }
if (!static_cast<SwapChainEGLAndroid const *>(swapchain)->frameTimestampsSupported) {
return false;
}
if (UTILS_LIKELY(ext.egl.ANDROID_get_frame_timestamps)) { if (UTILS_LIKELY(ext.egl.ANDROID_get_frame_timestamps)) {
EGLSurface const sur = sc->sur; EGLSurface const sur = sc->sur;
if (sur == EGL_NO_SURFACE) { if (sur == EGL_NO_SURFACE) {
@@ -384,9 +368,7 @@ bool PlatformEGLAndroid::queryFrameTimestamps(SwapChain const* swapchain, uint64
}; };
EGLBoolean const success = eglGetFrameTimestampsANDROID(getEglDisplay(), sur, hwFrameId, EGLBoolean const success = eglGetFrameTimestampsANDROID(getEglDisplay(), sur, hwFrameId,
names.size(), names.data(), values.data()); names.size(), names.data(), values.data());
if (UTILS_UNLIKELY(!success)) { if (!success) {
// reset current error to EGL_SUCCESS
eglGetError();
return false; return false;
} }
outFrameTimestamps->requestedPresentTime = values[0]; outFrameTimestamps->requestedPresentTime = values[0];
@@ -400,44 +382,28 @@ bool PlatformEGLAndroid::queryFrameTimestamps(SwapChain const* swapchain, uint64
outFrameTimestamps->releaseTime = values[8]; outFrameTimestamps->releaseTime = values[8];
return true; return true;
} }
// fallback to private APIs
auto const anw = sc->nativeWindow;
int const status = NativeWindow::getFrameTimestamps(anw, hwFrameId,
&outFrameTimestamps->requestedPresentTime,
&outFrameTimestamps->acquireTime,
&outFrameTimestamps->latchTime,
&outFrameTimestamps->firstCompositionStartTime,
&outFrameTimestamps->lastCompositionStartTime,
&outFrameTimestamps->gpuCompositionDoneTime,
&outFrameTimestamps->displayPresentTime,
&outFrameTimestamps->dequeueReadyTime,
&outFrameTimestamps->releaseTime);
if (status == 0) {
return true;
}
return PlatformEGL::queryFrameTimestamps(swapchain, frameId, outFrameTimestamps); return PlatformEGL::queryFrameTimestamps(swapchain, frameId, outFrameTimestamps);
} }
Platform::SwapChain* PlatformEGLAndroid::createSwapChain(void* nativeWindow, uint64_t const flags) { Platform::SwapChain* PlatformEGLAndroid::createSwapChain(void* nativeWindow, uint64_t const flags) {
auto* const sc = new(std::nothrow) SwapChainEGLAndroid(*this, nativeWindow, flags); auto* const sc = new(std::nothrow) SwapChainEGLAndroid(*this, nativeWindow, flags);
if (UTILS_LIKELY(ext.egl.ANDROID_get_frame_timestamps)) {
EGLDisplay const dpy = getEglDisplay();
sc->compositorTimingSupported =
eglGetCompositorTimingSupportedANDROID(dpy, sc->sur,
EGL_COMPOSITE_DEADLINE_ANDROID) &&
eglGetCompositorTimingSupportedANDROID(dpy, sc->sur,
EGL_COMPOSITE_INTERVAL_ANDROID) &&
eglGetCompositorTimingSupportedANDROID(dpy, sc->sur,
EGL_COMPOSITE_TO_PRESENT_LATENCY_ANDROID);
sc->frameTimestampsSupported =
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
EGL_REQUESTED_PRESENT_TIME_ANDROID) &&
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
EGL_RENDERING_COMPLETE_TIME_ANDROID) &&
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
EGL_COMPOSITION_LATCH_TIME_ANDROID) &&
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
EGL_FIRST_COMPOSITION_START_TIME_ANDROID) &&
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
EGL_LAST_COMPOSITION_START_TIME_ANDROID) &&
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
EGL_FIRST_COMPOSITION_GPU_FINISHED_TIME_ANDROID) &&
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
EGL_DISPLAY_PRESENT_TIME_ANDROID) &&
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
EGL_DEQUEUE_READY_TIME_ANDROID) &&
eglGetFrameTimestampSupportedANDROID(dpy, sc->sur,
EGL_READS_DONE_TIME_ANDROID);
}
// This is expected to be a low frequency log, only turned on in debug builds
DLOG(INFO) << "anw: " << nativeWindow
<< ", compositorTimingSupported=" << sc->compositorTimingSupported
<< ", frameTimestampsSupported=" << sc->frameTimestampsSupported;
return sc; return sc;
} }
@@ -450,10 +416,6 @@ Platform::SwapChain* PlatformEGLAndroid::createSwapChain(
void PlatformEGLAndroid::destroySwapChain(SwapChain* swapChain) noexcept { void PlatformEGLAndroid::destroySwapChain(SwapChain* swapChain) noexcept {
if (swapChain) { if (swapChain) {
SwapChainEGLAndroid* const sc = static_cast<SwapChainEGLAndroid*>(swapChain); SwapChainEGLAndroid* const sc = static_cast<SwapChainEGLAndroid*>(swapChain);
if (mCurrentDrawSwapChain == sc) {
// don't keep a dangling pointer around
mCurrentDrawSwapChain = nullptr;
}
sc->terminate(*this); sc->terminate(*this);
delete sc; delete sc;
} }
@@ -763,6 +725,8 @@ PlatformEGLAndroid::SwapChainEGLAndroid::SwapChainEGLAndroid(PlatformEGLAndroid
// we ignore the result, it doesn't matter much if it fails // we ignore the result, it doesn't matter much if it fails
eglSurfaceAttrib(platform.getEglDisplay(), sur, EGL_TIMESTAMPS_ANDROID, EGL_TRUE); eglSurfaceAttrib(platform.getEglDisplay(), sur, EGL_TIMESTAMPS_ANDROID, EGL_TRUE);
} }
} else {
NativeWindow::enableFrameTimestamps(EGLNativeWindowType(nativeWindow), true);
} }
} }
@@ -16,7 +16,7 @@
#include <backend/platforms/PlatformEGLHeadless.h> #include <backend/platforms/PlatformEGLHeadless.h>
#include <bluegl/BlueGL.h> #include "opengl/GLUtils.h"
#include <EGL/egl.h> #include <EGL/egl.h>
#include <EGL/eglext.h> #include <EGL/eglext.h>
@@ -35,31 +35,16 @@ PlatformEGLHeadless::PlatformEGLHeadless() noexcept
} }
bool PlatformEGLHeadless::isOpenGL() const noexcept { bool PlatformEGLHeadless::isOpenGL() const noexcept {
#if defined(BACKEND_OPENGL_VERSION_GL) return true;
return true;
#else
return false;
#endif // defined(BACKEND_OPENGL_VERSION_GL)
} }
backend::Driver* PlatformEGLHeadless::createDriver(void* sharedContext, backend::Driver* PlatformEGLHeadless::createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) { const Platform::DriverConfig& driverConfig) {
auto bindApiHelper = [](EGLenum api, const char* errorString) -> bool { EGLBoolean bindAPI = eglBindAPI(EGL_OPENGL_API);
EGLBoolean bindAPI = eglBindAPI(api); if (UTILS_UNLIKELY(!bindAPI)) {
if (UTILS_UNLIKELY(bindAPI == EGL_FALSE || bindAPI == EGL_BAD_PARAMETER)) { LOG(ERROR) << "eglBindAPI EGL_OPENGL_API failed";
logEglError(errorString);
eglReleaseThread();
return false;
};
return true;
};
EGLenum api = isOpenGL() ? EGL_OPENGL_API : EGL_OPENGL_ES_API;
const char* apiString = isOpenGL() ? "eglBindAPI EGL_OPENGL_API" : "eglBindAPI EGL_OPENGL_ES_API";
if (!bindApiHelper(api, apiString)) {
return nullptr; return nullptr;
} }
int bindBlueGL = bluegl::bind(); int bindBlueGL = bluegl::bind();
if (UTILS_UNLIKELY(bindBlueGL != 0)) { if (UTILS_UNLIKELY(bindBlueGL != 0)) {
LOG(ERROR) << "bluegl bind failed"; LOG(ERROR) << "bluegl bind failed";
+5
View File
@@ -15,6 +15,7 @@
*/ */
#include <backend/BufferDescriptor.h> #include <backend/BufferDescriptor.h>
#include <backend/BufferObjectStreamDescriptor.h>
#include <backend/DescriptorSetOffsetArray.h> #include <backend/DescriptorSetOffsetArray.h>
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <backend/PipelineState.h> #include <backend/PipelineState.h>
@@ -433,6 +434,10 @@ io::ostream& operator<<(io::ostream& out, BufferDescriptor const& b) {
<< ", user=" << b.getUser() << " }"; << ", user=" << b.getUser() << " }";
} }
io::ostream& operator<<(io::ostream& out, const BufferObjectStreamDescriptor& b) {
return out << "BufferObjectStreamDescriptor{ streams(" << b.mStreams.size() << ")=... }";
}
io::ostream& operator<<(io::ostream& out, PixelBufferDescriptor const& b) { io::ostream& operator<<(io::ostream& out, PixelBufferDescriptor const& b) {
BufferDescriptor const& base = static_cast<BufferDescriptor const&>(b); BufferDescriptor const& base = static_cast<BufferDescriptor const&>(b);
return out << "PixelBufferDescriptor{ " << base return out << "PixelBufferDescriptor{ " << base
@@ -62,9 +62,8 @@ void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* c
// a memcpy if its available. // a memcpy if its available.
bool const isStaticOrShared = bool const isStaticOrShared =
any(mUsage & (BufferUsage::STATIC | BufferUsage::SHARED_WRITE_BIT)); any(mUsage & (BufferUsage::STATIC | BufferUsage::SHARED_WRITE_BIT));
bool const useMemcpy = bool const useMemcpy = (isUniformAndAvailable || isStaticOrShared) && isMemcopyable &&
((isUniformAndAvailable && mStagingBufferBypassEnabled) || isStaticOrShared) && mStagingBufferBypassEnabled;
isMemcopyable;
if (useMemcpy) { if (useMemcpy) {
char* dest = static_cast<char*>(mBuffer->getGpuBuffer()->allocationInfo.pMappedData) + char* dest = static_cast<char*>(mBuffer->getGpuBuffer()->allocationInfo.pMappedData) +
byteOffset; byteOffset;
@@ -19,7 +19,6 @@
#include "VulkanCommands.h" #include "VulkanCommands.h"
#include "VulkanHandles.h" #include "VulkanHandles.h"
#include "VulkanConstants.h" #include "VulkanConstants.h"
#include "utils/compiler.h"
#include <utils/FixedCapacityVector.h> #include <utils/FixedCapacityVector.h>
#include <utils/Panic.h> #include <utils/Panic.h>
@@ -32,18 +31,6 @@ namespace filament::backend {
namespace { namespace {
using Bitmask = fvkutils::UniformBufferBitmask;
static_assert(sizeof(Bitmask) * 8 == fvkutils::MAX_DESCRIPTOR_SET_BITMASK_BITS);
Bitmask foldBitsInHalf(Bitmask bitset) {
Bitmask outBitset;
bitset.forEachSetBit([&](size_t index) {
constexpr size_t BITMASK_LOWER_BITS_LEN = sizeof(outBitset) * 4;
outBitset.set(index % BITMASK_LOWER_BITS_LEN);
});
return outBitset;
}
using DescriptorSetLayoutArray = VulkanDescriptorSetCache::DescriptorSetLayoutArray; using DescriptorSetLayoutArray = VulkanDescriptorSetCache::DescriptorSetLayoutArray;
using DescriptorCount = VulkanDescriptorSetCache::DescriptorCount; using DescriptorCount = VulkanDescriptorSetCache::DescriptorCount;
@@ -317,7 +304,8 @@ void VulkanDescriptorSetCache::commit(VulkanCommandBuffer* commands,
// This code actually binds the descriptor sets. // This code actually binds the descriptor sets.
auto set = updateSets[index]; auto set = updateSets[index];
VkCommandBuffer const cmdbuffer = commands->buffer(); VkCommandBuffer const cmdbuffer = commands->buffer();
VkDescriptorSet const vkset = set->getVkSet(); VkDescriptorSet vkset = useExternalSamplers[index] ? set->getExternalSamplerVkSet() :
set->getVkSet();
commands->acquire(set); commands->acquire(set);
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index, vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index,
1, &vkset, set->uniqueDynamicUboCount, set->getOffsets()->data()); 1, &vkset, set->uniqueDynamicUboCount, set->getOffsets()->data());
@@ -353,39 +341,17 @@ void VulkanDescriptorSetCache::updateBuffer(fvkmemory::resource_ptr<VulkanDescri
.pBufferInfo = &info, .pBufferInfo = &info,
}; };
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr); vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
if (auto externalSamplerSet = set->getExternalSamplerVkSet();
externalSamplerSet != VK_NULL_HANDLE) {
descriptorWrite.dstSet = externalSamplerSet;
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
}
set->acquire(bufferObject); set->acquire(bufferObject);
} }
void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set, void VulkanDescriptorSetCache::updateSamplerImpl(VkDescriptorSet vkset, uint8_t binding,
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture, fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept {
VkSampler sampler, VkDescriptorSetLayout externalSamplerLayout) noexcept {
// We have to update a bound set for two use cases
// - streaming API (a changing feed of AHardwareBuffer)
// - external samplers - potential changing of dataspace per-frame
if (UTILS_UNLIKELY(set->isBound())) {
auto layout = set->getLayout();
// Build a new descriptor set from the new layout
auto genLayout = externalSamplerLayout != VK_NULL_HANDLE ?
externalSamplerLayout : layout->getVkLayout();
VkDescriptorSet const newSet = getVkSet(layout->count, genLayout);
Bitmask const ubo = layout->bitmask.ubo | layout->bitmask.dynamicUbo;
Bitmask samplers = layout->bitmask.sampler;
samplers.unset(binding);
// Each bitmask denotes a binding index, and separated into two stages - vertex and buffer
// We fold the two stages into just the lower half of the bits to denote a combined set of
// bindings.
Bitmask const copyBindings = foldBitsInHalf(ubo | samplers);
VkDescriptorSet const srcSet = set->getVkSet();
copySet(srcSet, newSet, copyBindings);
set->addNewSet(newSet,
[this, layoutCount = layout->count, genLayout, newSet](VulkanDescriptorSet*) {
this->manualRecycle(layoutCount, genLayout, newSet);
});
}
VkDescriptorSet const vkset = set->getVkSet();
VkImageSubresourceRange range = texture->getPrimaryViewRange(); VkImageSubresourceRange range = texture->getPrimaryViewRange();
VkImageViewType const expectedType = texture->getViewType(); VkImageViewType const expectedType = texture->getViewType();
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) && if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
@@ -410,6 +376,19 @@ void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescr
.pImageInfo = &info, .pImageInfo = &info,
}; };
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr); vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
}
void VulkanDescriptorSetCache::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
VkSampler sampler) noexcept {
updateSamplerImpl(set->getVkSet(), binding, texture, sampler);
set->acquire(texture);
}
void VulkanDescriptorSetCache::updateSamplerForExternalSamplerSet(
fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
fvkmemory::resource_ptr<VulkanTexture> texture) noexcept {
updateSamplerImpl(set->getExternalSamplerVkSet(), binding, texture, VK_NULL_HANDLE);
set->acquire(texture); set->acquire(texture);
} }
@@ -426,7 +405,7 @@ fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetCache::createSet
auto const& count = layout->count; auto const& count = layout->count;
auto const vklayout = layout->getVkLayout(); auto const vklayout = layout->getVkLayout();
auto set = fvkmemory::resource_ptr<VulkanDescriptorSet>::make( auto set = fvkmemory::resource_ptr<VulkanDescriptorSet>::make(
mResourceManager, handle, layout, mResourceManager, handle, layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
[vkSet, count, vklayout, this]( [vkSet, count, vklayout, this](
VulkanDescriptorSet*) { this->manualRecycle(count, vklayout, vkSet); }, VulkanDescriptorSet*) { this->manualRecycle(count, vklayout, vkSet); },
vkSet); vkSet);
@@ -449,22 +428,4 @@ void VulkanDescriptorSetCache::manualRecycle(VulkanDescriptorSetLayout::Count co
void VulkanDescriptorSetCache::gc() { mStashedSets = {}; } void VulkanDescriptorSetCache::gc() { mStashedSets = {}; }
void VulkanDescriptorSetCache::copySet(VkDescriptorSet srcSet, VkDescriptorSet dstSet,
fvkutils::SamplerBitmask bindings) const {
// TODO: fix the size for better memory management
std::vector<VkCopyDescriptorSet> copies;
bindings.forEachSetBit([&](size_t index) {
copies.push_back({
.sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET,
.srcSet = srcSet,
.srcBinding = (uint32_t) index,
.dstSet = dstSet,
.dstBinding = (uint32_t) index,
.descriptorCount = 1,
});
});
vkUpdateDescriptorSets(mDevice, 0, nullptr, copies.size(), copies.data());
}
} // namespace filament::backend } // namespace filament::backend
@@ -55,8 +55,7 @@ public:
VkDeviceSize size) noexcept; VkDeviceSize size) noexcept;
void updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding, void updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler, fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept;
VkDescriptorSetLayout externalSamplerLayout = VK_NULL_HANDLE) noexcept;
void updateSamplerForExternalSamplerSet(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding, void updateSamplerForExternalSamplerSet(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
fvkmemory::resource_ptr<VulkanTexture> texture) noexcept; fvkmemory::resource_ptr<VulkanTexture> texture) noexcept;
@@ -91,8 +90,8 @@ public:
void resetCachedState() noexcept { mLastBoundInfo = {}; } void resetCachedState() noexcept { mLastBoundInfo = {}; }
private: private:
void copySet(VkDescriptorSet srcSet, VkDescriptorSet destSet, void updateSamplerImpl(VkDescriptorSet set, uint8_t binding,
fvkutils::SamplerBitmask copyBindings) const; fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept;
class DescriptorInfinitePool; class DescriptorInfinitePool;
+12 -109
View File
@@ -31,7 +31,6 @@
#include "vulkan/memory/ResourcePointer.h" #include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Conversion.h" #include "vulkan/utils/Conversion.h"
#include "vulkan/utils/Definitions.h" #include "vulkan/utils/Definitions.h"
#include "vulkan/utils/Image.h"
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <backend/platforms/VulkanPlatform.h> #include <backend/platforms/VulkanPlatform.h>
@@ -245,7 +244,6 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext& context,
mQueryManager(mPlatform->getDevice()), mQueryManager(mPlatform->getDevice()),
mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache, mExternalImageManager(platform, &mSamplerCache, &mYcbcrConversionCache, &mDescriptorSetCache,
&mDescriptorSetLayoutCache), &mDescriptorSetLayoutCache),
mStreamedImageManager(&mExternalImageManager),
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported), mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported),
mIsMSAASwapChainSupported(false), // TODO: support MSAA swapchain mIsMSAASwapChainSupported(false), // TODO: support MSAA swapchain
mStereoscopicType(driverConfig.stereoscopicType) { mStereoscopicType(driverConfig.stereoscopicType) {
@@ -422,14 +420,12 @@ void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
int64_t refreshIntervalNs, uint32_t frameId) { int64_t refreshIntervalNs, uint32_t frameId) {
FVK_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME); FVK_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
// if frameId is 0, it means we're not associated to a particular frame, which is the case if (mCurrentSwapChain) { // This should be guaranteed
// for standalone views. And in this case we skip the frame info timing collection.
if (frameId && mCurrentSwapChain) { // This should be guaranteed
mPlatform->setPresentFrameId(mCurrentSwapChain->swapChain, frameId); mPlatform->setPresentFrameId(mCurrentSwapChain->swapChain, frameId);
} }
// Check if any command have finished and reset all its used resources. The resources // Check if any command have finished and reset all its used resources. The resources
// won't be destroyed but their reference count will be decreased if the command is already // wont be destroyed but their reference count will decreased if the command is already
// completed. // completed.
// //
// This will let us check if any VulkanBuffer is currently in flight or not. // This will let us check if any VulkanBuffer is currently in flight or not.
@@ -483,9 +479,6 @@ void VulkanDriver::updateDescriptorSetTexture(
if (UTILS_UNLIKELY(mExternalImageManager.isExternallySampledTexture(texture))) { if (UTILS_UNLIKELY(mExternalImageManager.isExternallySampledTexture(texture))) {
mExternalImageManager.bindExternallySampledTexture(set, binding, texture, params); mExternalImageManager.bindExternallySampledTexture(set, binding, texture, params);
mAppState.hasBoundExternalImages = true; mAppState.hasBoundExternalImages = true;
} else if (bool(texture->getStream())) {
mStreamedImageManager.bindStreamedTexture(set, binding, texture, params);
mAppState.hasBoundExternalImages = true;
} else { } else {
VulkanSamplerCache::Params cacheParams = { VulkanSamplerCache::Params cacheParams = {
.sampler = params, .sampler = params,
@@ -493,7 +486,6 @@ void VulkanDriver::updateDescriptorSetTexture(
VkSampler const vksampler = mSamplerCache.getSampler(cacheParams); VkSampler const vksampler = mSamplerCache.getSampler(cacheParams);
mDescriptorSetCache.updateSampler(set, binding, texture, vksampler); mDescriptorSetCache.updateSampler(set, binding, texture, vksampler);
mExternalImageManager.clearTextureBinding(set, binding); mExternalImageManager.clearTextureBinding(set, binding);
mStreamedImageManager.unbindStreamedTexture(set, binding);
} }
} }
@@ -505,7 +497,10 @@ void VulkanDriver::copyToMemoryMappedBuffer(MemoryMappedBufferHandle mmbh, size_
assert_invariant(any(mmb->access & MapBufferAccessFlags::WRITE_BIT)); assert_invariant(any(mmb->access & MapBufferAccessFlags::WRITE_BIT));
assert_invariant(offset + data.size <= mmb->size); assert_invariant(offset + data.size <= mmb->size);
// On UMA systems, this is a memcpy into the memory-mapped buffer, otherwise use a staging buffer. // TODO: this isa zero-effort implementation of copyToMemoryMappedBuffer(), where we just
// call updateBufferObject(). This could be a fallback implementation for when
// shared memory is not available.
// On UMA systems, this should just be a memcpy into the memory-mapped buffer.
updateBufferObject(mmb->boh, std::move(data), mmb->offset + offset); updateBufferObject(mmb->boh, std::move(data), mmb->offset + offset);
} }
@@ -1113,12 +1108,6 @@ void VulkanDriver::destroySwapChain(Handle<HwSwapChain> sch) {
} }
void VulkanDriver::destroyStream(Handle<HwStream> sh) { void VulkanDriver::destroyStream(Handle<HwStream> sh) {
FVK_SYSTRACE_SCOPE();
if (!sh) {
return;
}
auto stream = resource_ptr<VulkanStream>::cast(&mResourceManager, sh);
stream.dec();
} }
void VulkanDriver::destroySync(Handle<HwSync> sh) { void VulkanDriver::destroySync(Handle<HwSync> sh) {
@@ -1156,38 +1145,18 @@ void VulkanDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
} }
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream, utils::ImmutableCString tag) { Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream, utils::ImmutableCString tag) {
FVK_LOGW << "createStreamNative not supported in Vulkan.";
return {}; return {};
} }
Handle<HwStream> VulkanDriver::createStreamAcquired(utils::ImmutableCString tag) { Handle<HwStream> VulkanDriver::createStreamAcquired(utils::ImmutableCString tag) {
// @TODO This is still not thread-safe. We might have to revisit this question. return {};
FVK_SYSTRACE_SCOPE();
auto handle = mResourceManager.allocHandle<VulkanStream>();
auto stream = resource_ptr<VulkanStream>::make(&mResourceManager, handle);
stream.inc();
return handle;
} }
void VulkanDriver::setAcquiredImage(Handle<HwStream> sh, void* image, const math::mat3f& transform, void VulkanDriver::setAcquiredImage(Handle<HwStream> sh, void* image, const math::mat3f& transform,
CallbackHandler* handler, StreamCallback cb, void* userData) { CallbackHandler* handler, StreamCallback cb, void* userData) {
FVK_SYSTRACE_SCOPE();
auto stream = resource_ptr<VulkanStream>::cast(&mResourceManager, sh);
assert_invariant(stream->streamType == StreamType::ACQUIRED);
assert_invariant(image != nullptr);
// This covers the case where setAcquiredImage is called back to back (no updateStreams in between)
if (stream->previousNeedsRelease()) {
scheduleRelease(stream->takePrevious());
}
stream->acquire({ image, cb, userData, handler});
mStreamsWithPendingAcquiredImage.push_back(stream);
} }
void VulkanDriver::setStreamDimensions(Handle<HwStream> sh, uint32_t width, uint32_t height) { void VulkanDriver::setStreamDimensions(Handle<HwStream> sh, uint32_t width, uint32_t height) {
FVK_SYSTRACE_SCOPE();
auto stream = resource_ptr<VulkanStream>::cast(&mResourceManager, sh);
stream->width = width;
stream->height = height;
} }
int64_t VulkanDriver::getStreamTimestamp(Handle<HwStream> sh) { int64_t VulkanDriver::getStreamTimestamp(Handle<HwStream> sh) {
@@ -1195,70 +1164,6 @@ int64_t VulkanDriver::getStreamTimestamp(Handle<HwStream> sh) {
} }
void VulkanDriver::updateStreams(CommandStream* driver) { void VulkanDriver::updateStreams(CommandStream* driver) {
FVK_SYSTRACE_SCOPE();
if (UTILS_UNLIKELY(!mStreamsWithPendingAcquiredImage.empty())) {
for (auto& stream: mStreamsWithPendingAcquiredImage) {
if (stream->previousNeedsRelease()) {
scheduleRelease(stream->takePrevious());
}
// This executes on the backend thread (updateStreams is synchonous which means it
// executes on the user thread) Note: stream is captured by copy which is fine, this is
// a copy of a resource_ptr<VulkanStream>. We only need it find the associated stream
// inside the mStreamedImageManager texture bindings
driver->queueCommand([this, stream, s = stream.get(),
image = stream->getAcquired().image]() {
auto texture = s->getTexture(image);
if (!texture) {
auto externalImage = fvkutils::createExternalImageFromRaw(mPlatform, image, false);
auto metadata = mPlatform->extractExternalImageMetadata(externalImage);
auto imgData = mPlatform->createVkImageFromExternal(externalImage);
assert_invariant(imgData.internal.valid() || imgData.external.valid());
VkFormat vkformat = metadata.format;
VkImage vkimage = VK_NULL_HANDLE;
VkDeviceMemory memory = VK_NULL_HANDLE;
if (imgData.internal.valid()) {
metadata.externalFormat = 0;
vkimage = imgData.internal.image;
memory = imgData.internal.memory;
} else {
vkformat = VK_FORMAT_UNDEFINED;
vkimage = imgData.external.image;
memory = imgData.external.memory;
}
VkSamplerYcbcrConversion const conversion =
mExternalImageManager.getVkSamplerYcbcrConversion(metadata);
auto newTexture = resource_ptr<VulkanTexture>::construct(&mResourceManager,
mContext, mPlatform->getDevice(), mAllocator, &mResourceManager,
&mCommands, vkimage, memory, vkformat, conversion, metadata.samples,
metadata.width, metadata.height, metadata.layers,
metadata.filamentUsage, mStagePool);
auto& commands = mCommands.get();
// Unlike uploaded textures or swapchains, we need to explicit transition this
// texture into the read layout.
newTexture->transitionLayout(&commands, newTexture->getPrimaryViewRange(),
VulkanLayout::FRAG_READ);
if (imgData.external.valid()) {
mExternalImageManager.addExternallySampledTexture(newTexture,
externalImage);
// Cache the AHB backed image. Acquires the image here.
s->pushImage(image, newTexture);
}
texture = newTexture;
}
mStreamedImageManager.onStreamAcquireImage(texture, stream);
});
}
mStreamsWithPendingAcquiredImage.clear();
}
} }
void VulkanDriver::destroyFence(Handle<HwFence> fh) { void VulkanDriver::destroyFence(Handle<HwFence> fh) {
@@ -1554,13 +1459,17 @@ void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor
scheduleDestroy(std::move(p)); scheduleDestroy(std::move(p));
} }
void VulkanDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh,
BufferObjectStreamDescriptor&& streams) {
// Noop
}
void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& bd, void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& bd,
uint32_t byteOffset) { uint32_t byteOffset) {
VulkanCommandBuffer& commands = mCommands.get(); VulkanCommandBuffer& commands = mCommands.get();
auto bo = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh); auto bo = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh);
commands.acquire(bo); commands.acquire(bo);
bo->loadFromCpu(commands, bd.buffer, byteOffset, bd.size); bo->loadFromCpu(commands, bd.buffer, byteOffset, bd.size);
scheduleDestroy(std::move(bd)); scheduleDestroy(std::move(bd));
@@ -1628,12 +1537,6 @@ TimerQueryResult VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint
} }
void VulkanDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh) { void VulkanDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh) {
FVK_SYSTRACE_SCOPE();
auto t = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
assert_invariant(t);
auto s = resource_ptr<VulkanStream>::cast(&mResourceManager, sh);
assert_invariant(s);
t->setStream(s);
} }
void VulkanDriver::generateMipmaps(Handle<HwTexture> th) { void VulkanDriver::generateMipmaps(Handle<HwTexture> th) {
@@ -34,14 +34,12 @@
#include "vulkan/VulkanDescriptorSetCache.h" #include "vulkan/VulkanDescriptorSetCache.h"
#include "vulkan/VulkanDescriptorSetLayoutCache.h" #include "vulkan/VulkanDescriptorSetLayoutCache.h"
#include "vulkan/VulkanExternalImageManager.h" #include "vulkan/VulkanExternalImageManager.h"
#include "vulkan/VulkanStreamedImageManager.h"
#include "vulkan/VulkanPipelineLayoutCache.h" #include "vulkan/VulkanPipelineLayoutCache.h"
#include "vulkan/memory/ResourceManager.h" #include "vulkan/memory/ResourceManager.h"
#include "vulkan/memory/ResourcePointer.h" #include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Definitions.h" #include "vulkan/utils/Definitions.h"
#include "backend/DriverEnums.h" #include "backend/DriverEnums.h"
#include "backend/BufferObjectStreamDescriptor.h"
#include "DriverBase.h" #include "DriverBase.h"
#include "private/backend/Driver.h" #include "private/backend/Driver.h"
@@ -159,8 +157,6 @@ private:
VulkanDescriptorSetCache mDescriptorSetCache; VulkanDescriptorSetCache mDescriptorSetCache;
VulkanQueryManager mQueryManager; VulkanQueryManager mQueryManager;
VulkanExternalImageManager mExternalImageManager; VulkanExternalImageManager mExternalImageManager;
VulkanStreamedImageManager mStreamedImageManager;
// This maps a VulkanSwapchain to a native swapchain. VulkanSwapchain should have a copy of the // This maps a VulkanSwapchain to a native swapchain. VulkanSwapchain should have a copy of the
// Platform::Swapchain pointer, but queryFrameTimestamps() and queryCompositorTiming() are // Platform::Swapchain pointer, but queryFrameTimestamps() and queryCompositorTiming() are
@@ -206,10 +202,6 @@ private:
bool const mIsSRGBSwapChainSupported; bool const mIsSRGBSwapChainSupported;
bool const mIsMSAASwapChainSupported; bool const mIsMSAASwapChainSupported;
backend::StereoscopicType const mStereoscopicType; backend::StereoscopicType const mStereoscopicType;
// setAcquiredImage is a DECL_DRIVER_API_SYNCHRONOUS_N which means we don't necessarily have the
// data to process it at call time. So we store it and process it during updateStreams.
std::vector<resource_ptr<VulkanStream>> mStreamsWithPendingAcquiredImage;
}; };
} // namespace filament::backend } // namespace filament::backend
@@ -31,6 +31,9 @@ namespace filament::backend {
namespace { namespace {
using Bitmask = fvkutils::UniformBufferBitmask;
static_assert(sizeof(Bitmask) * 8 == fvkutils::MAX_DESCRIPTOR_SET_BITMASK_BITS);
template<typename T> template<typename T>
void erasep(std::vector<T>& v, std::function<bool(T const&)> f) { void erasep(std::vector<T>& v, std::function<bool(T const&)> f) {
auto newEnd = std::remove_if(v.begin(), v.end(), f); auto newEnd = std::remove_if(v.begin(), v.end(), f);
@@ -47,6 +50,31 @@ ImageData& findImage(std::vector<ImageData>& images,
return *itr; return *itr;
} }
void copySet(VkDevice device, VkDescriptorSet srcSet, VkDescriptorSet dstSet, Bitmask bindings) {
// TODO: fix the size for better memory management
std::vector<VkCopyDescriptorSet> copies;
bindings.forEachSetBit([&](size_t index) {
copies.push_back({
.sType = VK_STRUCTURE_TYPE_COPY_DESCRIPTOR_SET,
.srcSet = srcSet,
.srcBinding = (uint32_t) index,
.dstSet = dstSet,
.dstBinding = (uint32_t) index,
.descriptorCount = 1,
});
});
vkUpdateDescriptorSets(device, 0, nullptr, copies.size(), copies.data());
}
Bitmask foldBitsInHalf(Bitmask bitset) {
Bitmask outBitset;
bitset.forEachSetBit([&](size_t index) {
constexpr size_t BITMASK_LOWER_BITS_LEN = sizeof(outBitset) * 4;
outBitset.set(index % BITMASK_LOWER_BITS_LEN);
});
return outBitset;
}
}// namespace }// namespace
VulkanExternalImageManager::VulkanExternalImageManager(VulkanPlatform* platform, VulkanExternalImageManager::VulkanExternalImageManager(VulkanPlatform* platform,
@@ -113,9 +141,6 @@ void VulkanExternalImageManager::updateSetAndLayout(
continue; continue;
} }
auto& imageData = findImage(mImages, bindingInfo.image); auto& imageData = findImage(mImages, bindingInfo.image);
// For non YUV images (some ext images are NOT ext FMT)
// getVkSamplerYcbcrConversion(metadata) will return NULL, and image->conversion will be
// null
updateImage(&imageData); updateImage(&imageData);
auto samplerParams = bindingInfo.samplerParams; auto samplerParams = bindingInfo.samplerParams;
@@ -147,14 +172,38 @@ void VulkanExternalImageManager::updateSetAndLayout(
std::for_each(samplerAndBindings.begin(), samplerAndBindings.end(), std::for_each(samplerAndBindings.begin(), samplerAndBindings.end(),
[&](auto const& b) { outSamplers.push_back(std::get<1>(b)); }); [&](auto const& b) { outSamplers.push_back(std::get<1>(b)); });
VkDescriptorSetLayout const oldLayout = layout->getExternalSamplerVkLayout();
VkDescriptorSetLayout const newLayout = mDescriptorSetLayoutCache->getVkLayout(layout->bitmask, VkDescriptorSetLayout const newLayout = mDescriptorSetLayoutCache->getVkLayout(layout->bitmask,
actualExternalSamplers, outSamplers); actualExternalSamplers, outSamplers);
layout->setExternalSamplerVkLayout(newLayout);
// Need to copy the set
VkDescriptorSet const oldSet = set->getExternalSamplerVkSet();
if (oldLayout != newLayout || oldSet == VK_NULL_HANDLE) {
// Build a new descriptor set from the new layout
VkDescriptorSet const newSet = mDescriptorSetCache->getVkSet(layout->count, newLayout);
auto const ubo = layout->bitmask.ubo | layout->bitmask.dynamicUbo;
auto const samplers = layout->bitmask.sampler & (~actualExternalSamplers);
// Each bitmask denotes a binding index, and separated into two stages - vertex and buffer
// We fold the two stages into just the lower half of the bits to denote a combined set of
// bindings.
Bitmask const copyBindings = foldBitsInHalf(ubo | samplers);
VkDescriptorSet const srcSet = oldSet != VK_NULL_HANDLE ? oldSet : set->getVkSet();
copySet(mPlatform->getDevice(), srcSet, newSet, copyBindings);
set->setExternalSamplerVkSet(newSet,
[&descriptorSetCache = mDescriptorSetCache, layoutCount = layout->count, newLayout,
newSet](VulkanDescriptorSet*) {
descriptorSetCache->manualRecycle(layoutCount, newLayout, newSet);
});
if (oldLayout != newLayout) {
layout->setExternalSamplerVkLayout(newLayout);
}
}
// Update the external samplers in the set // Update the external samplers in the set
for (auto& [binding, sampler, image]: samplerAndBindings) { for (auto& [binding, sampler, image]: samplerAndBindings) {
// We cannot call updateSamplerForExternalSamplerSet because some samplers are non NULL mDescriptorSetCache->updateSamplerForExternalSamplerSet(set, binding, image);
// (RGB) and we cannot do a combined update with a NULL sampler.
mDescriptorSetCache->updateSampler(set, binding, image, sampler, newLayout);
} }
} }
@@ -212,25 +261,13 @@ void VulkanExternalImageManager::bindExternallySampledTexture(
fvkmemory::resource_ptr<VulkanTexture> image, SamplerParams samplerParams) { fvkmemory::resource_ptr<VulkanTexture> image, SamplerParams samplerParams) {
// Should we do duplicate validation here? // Should we do duplicate validation here?
auto& imageData = findImage(mImages, image); auto& imageData = findImage(mImages, image);
auto itr = std::find_if(mSetBindings.begin(), mSetBindings.end(), mSetBindings.push_back({ bindingPoint, imageData.image, set, samplerParams });
[&](SetBindingInfo const& binding) {
return (binding.set == set && binding.binding == bindingPoint);
});
if (itr == mSetBindings.end()) {
mSetBindings.push_back({ bindingPoint, imageData.image, set, samplerParams });
} else {
// override the image data in the binding point
itr->image = image;
itr->samplerParams = samplerParams;
}
} }
void VulkanExternalImageManager::addExternallySampledTexture( void VulkanExternalImageManager::addExternallySampledTexture(
fvkmemory::resource_ptr<VulkanTexture> image, fvkmemory::resource_ptr<VulkanTexture> image,
Platform::ExternalImageHandleRef platformHandleRef) { Platform::ExternalImageHandleRef platformHandleRef) {
// By passing VK_NULL_HANDLE which is already there by default. mImages.push_back({ image, platformHandleRef, false });
// We make it clear that all default images do NOT have a chroma conversion.
mImages.push_back({ image, platformHandleRef, false, VK_NULL_HANDLE });
} }
void VulkanExternalImageManager::removeExternallySampledTexture( void VulkanExternalImageManager::removeExternallySampledTexture(
@@ -31,7 +31,7 @@ class VulkanSamplerCache;
class VulkanDescriptorSetLayoutCache; class VulkanDescriptorSetLayoutCache;
class VulkanDescriptorSetCache; class VulkanDescriptorSetCache;
// Manages the logic of external images and their quirks wrt Vulkan. // Manages the logic of external images and their quirks wrt Vulikan.
class VulkanExternalImageManager { class VulkanExternalImageManager {
public: public:
+2 -62
View File
@@ -40,11 +40,6 @@ namespace filament::backend {
namespace { namespace {
// We don't have a good estimate for this magic number. This is used to remove empty
// slots from a descriptor set (a VulkanDescriptorset is backed by multiple
// VkDescriptorSet.
size_t DESCRIPTOR_SET_GC_LIMIT = 10;
inline VulkanBufferBinding getBufferObjectBinding(BufferObjectBinding bindingType) noexcept { inline VulkanBufferBinding getBufferObjectBinding(BufferObjectBinding bindingType) noexcept {
switch (bindingType) { switch (bindingType) {
case BufferObjectBinding::VERTEX: case BufferObjectBinding::VERTEX:
@@ -197,70 +192,15 @@ VulkanDescriptorSetLayout::Bitmask VulkanDescriptorSetLayout::Bitmask::fromLayou
return fromBackendLayout(layout); return fromBackendLayout(layout);
} }
VulkanDescriptorSet::~VulkanDescriptorSet() { // This method will store an age associated with this command buffer into the VulkanBuffer, which
for (auto setBundle: mSets) { // will allow us to determine whether a barrier is necessary or not.
if (setBundle.onRecycleFn) {
setBundle.onRecycleFn(this);
}
}
}
VulkanDescriptorSet::VulkanDescriptorSet(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout,
OnRecycle&& onRecycleFn, VkDescriptorSet vkSet)
: dynamicUboMask(layout->bitmask.dynamicUbo),
uniqueDynamicUboCount(layout->count.dynamicUbo),
mLayout(layout),
mCurrentSetIndex(0) {
addNewSet(vkSet, std::move(onRecycleFn));
}
void VulkanDescriptorSet::referencedBy(VulkanCommandBuffer& commands) { void VulkanDescriptorSet::referencedBy(VulkanCommandBuffer& commands) {
// This will store an age associated with this command buffer into the VulkanBuffer, which
// will allow us to determine whether a barrier is necessary or not.
mUboMask.forEachSetBit([this, &commands](size_t index) { mUboMask.forEachSetBit([this, &commands](size_t index) {
auto& res = mResources[index]; auto& res = mResources[index];
fvkmemory::resource_ptr<VulkanBufferObject> bo = fvkmemory::resource_ptr<VulkanBufferObject> bo =
fvkmemory::resource_ptr<VulkanBufferObject>::cast((VulkanBufferObject*) res.get()); fvkmemory::resource_ptr<VulkanBufferObject>::cast((VulkanBufferObject*) res.get());
bo->referencedBy(commands); bo->referencedBy(commands);
}); });
mSets[mCurrentSetIndex].fenceStatus = commands.getFenceStatus();
}
void VulkanDescriptorSet::gc() {
size_t empty = 0;
for (auto& setBundle: mSets) {
assert_invariant(setBundle.onRecycleFn);
if (setBundle.vkSet == VK_NULL_HANDLE) {
empty++;
continue;
}
if (setBundle.fenceStatus && setBundle.fenceStatus->getStatus() == VK_SUCCESS) {
setBundle.onRecycleFn(this);
setBundle.vkSet = VK_NULL_HANDLE;
setBundle.fenceStatus = {};
empty++;
}
}
if (empty > DESCRIPTOR_SET_GC_LIMIT) {
std::vector<InternalVkSet> retainedSets;
for (auto& setBundle: mSets) {
if (setBundle.vkSet != VK_NULL_HANDLE) {
retainedSets.push_back({
setBundle.vkSet,
std::move(setBundle.onRecycleFn),
std::move(setBundle.fenceStatus)
});
}
}
std::swap(mSets, retainedSets);
}
}
void VulkanDescriptorSet::addNewSet(VkDescriptorSet vkSet, OnRecycle&& onRecycleFn) {
gc();
mCurrentSetIndex = mSets.size();
mSets.push_back({ vkSet, std::move(onRecycleFn) });
} }
PushConstantDescription::PushConstantDescription(backend::Program const& program) { PushConstantDescription::PushConstantDescription(backend::Program const& program) {
+33 -28
View File
@@ -28,7 +28,6 @@
#include "VulkanTexture.h" #include "VulkanTexture.h"
#include "vulkan/VulkanCommands.h" #include "vulkan/VulkanCommands.h"
#include "vulkan/memory/Resource.h" #include "vulkan/memory/Resource.h"
#include "vulkan/memory/ResourcePointer.h"
#include "vulkan/utils/Definitions.h" #include "vulkan/utils/Definitions.h"
#include "vulkan/utils/StaticVector.h" #include "vulkan/utils/StaticVector.h"
@@ -58,8 +57,6 @@ inline uint8_t collapsedCount(Bitmask const& mask) {
} // anonymous namespace } // anonymous namespace
class VulkanDescriptorSetCache;
struct VulkanBufferObject; struct VulkanBufferObject;
struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Resource { struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Resource {
@@ -162,14 +159,38 @@ public:
using OnRecycle = std::function<void(VulkanDescriptorSet*)>; using OnRecycle = std::function<void(VulkanDescriptorSet*)>;
VulkanDescriptorSet( VulkanDescriptorSet(
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout, fvkutils::UniformBufferBitmask const& dynamicUboMask,
OnRecycle&& onRecycleFn, VkDescriptorSet vkSet); uint8_t uniqueDynamicUboCount,
OnRecycle&& onRecycleFn, VkDescriptorSet vkSet)
: dynamicUboMask(dynamicUboMask),
uniqueDynamicUboCount(uniqueDynamicUboCount),
mVkSet(vkSet),
mOnRecycleFn(std::move(onRecycleFn)) {}
// NOLINTNEXTLINE(bugprone-exception-escape) // NOLINTNEXTLINE(bugprone-exception-escape)
~VulkanDescriptorSet(); ~VulkanDescriptorSet() {
if (mOnRecycleFn) {
mOnRecycleFn(this);
}
if (mOnRecycleExternalSamplerFn) {
mOnRecycleExternalSamplerFn(this);
}
}
VkDescriptorSet getVkSet() const noexcept { VkDescriptorSet getVkSet() const noexcept {
return mSets[mCurrentSetIndex].vkSet; return mVkSet;
}
VkDescriptorSet getExternalSamplerVkSet() const noexcept {
return mExternalSamplerVkSet;
}
void setExternalSamplerVkSet(VkDescriptorSet vkset, OnRecycle onRecycle) {
mExternalSamplerVkSet = vkset;
if (mOnRecycleExternalSamplerFn) {
mOnRecycleExternalSamplerFn(this);
}
mOnRecycleExternalSamplerFn = onRecycle;
} }
void setOffsets(backend::DescriptorSetOffsetArray&& offsets) noexcept { void setOffsets(backend::DescriptorSetOffsetArray&& offsets) noexcept {
@@ -190,33 +211,17 @@ public:
void referencedBy(VulkanCommandBuffer& commands); void referencedBy(VulkanCommandBuffer& commands);
bool isBound() const { fvkutils::UniformBufferBitmask const dynamicUboMask;
return bool(mSets[mCurrentSetIndex].fenceStatus);
}
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> getLayout() const { return mLayout; }
fvkutils::UniformBufferBitmask const& dynamicUboMask;
uint8_t const uniqueDynamicUboCount; uint8_t const uniqueDynamicUboCount;
private: private:
friend class VulkanDescriptorSetCache; VkDescriptorSet const mVkSet;
VkDescriptorSet mExternalSamplerVkSet = VK_NULL_HANDLE;
void addNewSet(VkDescriptorSet vkSet, OnRecycle&& onRecycleFn);
void gc();
struct InternalVkSet {
VkDescriptorSet vkSet = {};
OnRecycle onRecycleFn;
std::shared_ptr<VulkanCmdFence> fenceStatus;
};
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> mLayout;
backend::DescriptorSetOffsetArray mOffsets; backend::DescriptorSetOffsetArray mOffsets;
std::vector<fvkmemory::resource_ptr<fvkmemory::Resource>> mResources; std::vector<fvkmemory::resource_ptr<fvkmemory::Resource>> mResources;
uint8_t mCurrentSetIndex; OnRecycle mOnRecycleFn;
std::vector<InternalVkSet> mSets; OnRecycle mOnRecycleExternalSamplerFn;
fvkutils::UniformBufferBitmask mUboMask; fvkutils::UniformBufferBitmask mUboMask;
}; };
@@ -191,12 +191,9 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
.alphaToCoverageEnable = raster.alphaToCoverageEnable, .alphaToCoverageEnable = raster.alphaToCoverageEnable,
.alphaToOneEnable = VK_FALSE, .alphaToOneEnable = VK_FALSE,
}; };
bool const enableDepthTest =
raster.depthCompareOp != SamplerCompareFunc::A ||
raster.depthWriteEnable;
VkPipelineDepthStencilStateCreateInfo vkDs = { VkPipelineDepthStencilStateCreateInfo vkDs = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = enableDepthTest ? VK_TRUE : VK_FALSE, .depthTestEnable = VK_TRUE,
.depthWriteEnable = raster.depthWriteEnable, .depthWriteEnable = raster.depthWriteEnable,
.depthCompareOp = fvkutils::getCompareOp(raster.depthCompareOp), .depthCompareOp = fvkutils::getCompareOp(raster.depthCompareOp),
.depthBoundsTestEnable = VK_FALSE, .depthBoundsTestEnable = VK_FALSE,
@@ -1,60 +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 "VulkanStreamedImageManager.h"
#include "VulkanDescriptorSetCache.h"
#include "VulkanExternalImageManager.h"
#include "VulkanDescriptorSetLayoutCache.h"
#include "VulkanSamplerCache.h"
namespace filament::backend {
VulkanStreamedImageManager::VulkanStreamedImageManager(VulkanExternalImageManager* manager)
: mExternalImageManager(manager) {}
VulkanStreamedImageManager::~VulkanStreamedImageManager() = default;
void VulkanStreamedImageManager::terminate() { mStreamedTexturesBindings.clear(); }
void VulkanStreamedImageManager::bindStreamedTexture(
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t bindingPoint, fvkmemory::resource_ptr<VulkanTexture> image,
SamplerParams samplerParams) {
mStreamedTexturesBindings.push_back({ bindingPoint, image, set, samplerParams });
}
void VulkanStreamedImageManager::unbindStreamedTexture(
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t bindingPoint) {
auto iter = std::remove_if(mStreamedTexturesBindings.begin(), mStreamedTexturesBindings.end(),
[&](StreamedTextureBinding& binding) {
return ((binding.set == set) && (binding.binding == bindingPoint));
});
mStreamedTexturesBindings.erase(iter, mStreamedTexturesBindings.end());
}
void VulkanStreamedImageManager::onStreamAcquireImage(fvkmemory::resource_ptr<VulkanTexture> image,
fvkmemory::resource_ptr<VulkanStream> stream) {
for (StreamedTextureBinding const& data: mStreamedTexturesBindings) {
// Find the right stream
if (data.image->getStream() == stream) {
mExternalImageManager->bindExternallySampledTexture(data.set, data.binding, image,
data.samplerParams);
}
}
}
} // namespace filament::backend
@@ -1,64 +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_CACHING_VULKANSTREAMEDIMAGEMANAGER_H
#define TNT_FILAMENT_BACKEND_CACHING_VULKANSTREAMEDIMAGEMANAGER_H
#include "VulkanHandles.h"
#include <backend/DriverEnums.h>
#include <array>
#include <vector>
namespace filament::backend {
class VulkanExternalImageManager;
class VulkanDescriptorSetCache;
class VulkanSamplerCache;
// Manages the logic of streamed images.
class VulkanStreamedImageManager {
public:
VulkanStreamedImageManager(
VulkanExternalImageManager* manager);
~VulkanStreamedImageManager();
void terminate();
public:
void bindStreamedTexture(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t bindingPoint,
fvkmemory::resource_ptr<VulkanTexture> image, SamplerParams samplerParams);
void unbindStreamedTexture(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t bindingPoint);
void onStreamAcquireImage(fvkmemory::resource_ptr<VulkanTexture> image,
fvkmemory::resource_ptr<VulkanStream> stream);
private:
struct StreamedTextureBinding {
uint8_t binding = 0;
fvkmemory::resource_ptr<VulkanTexture> image;
fvkmemory::resource_ptr<VulkanDescriptorSet> set;
SamplerParams samplerParams;
};
// keep track of all the stream bindings
std::vector<StreamedTextureBinding> mStreamedTexturesBindings;
VulkanExternalImageManager* mExternalImageManager;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_CACHING_VULKANSTREAMEDIMAGEMANAGER_H
@@ -19,7 +19,6 @@
#include "VulkanCommands.h" #include "VulkanCommands.h"
#include "VulkanTexture.h" #include "VulkanTexture.h"
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h> #include <utils/FixedCapacityVector.h>
#include <utils/Panic.h> #include <utils/Panic.h>
@@ -106,11 +105,6 @@ void VulkanSwapChain::update() {
} }
void VulkanSwapChain::present(DriverBase& driver) { void VulkanSwapChain::present(DriverBase& driver) {
// The last acquire failed, so just skip presenting.
if (!mAcquired) {
return;
}
if (!mHeadless && mTransitionSwapChainImageLayoutForPresent) { if (!mHeadless && mTransitionSwapChainImageLayoutForPresent) {
VulkanCommandBuffer& commands = mCommands->get(); VulkanCommandBuffer& commands = mCommands->get();
VkImageSubresourceRange const subresources{ VkImageSubresourceRange const subresources{
@@ -167,15 +161,7 @@ void VulkanSwapChain::acquire(bool& resized) {
VulkanPlatform::ImageSyncData imageSyncData; VulkanPlatform::ImageSyncData imageSyncData;
VkResult const result = mPlatform->acquire(swapChain, &imageSyncData); VkResult const result = mPlatform->acquire(swapChain, &imageSyncData);
if (result != VK_SUCCESS) {
// We just don't set mAcquired here so the next present will just skip.
FVK_LOGD << "Failed to acquire next image in the swapchain result=" << (int) result;
return;
}
mCurrentSwapIndex = imageSyncData.imageIndex; mCurrentSwapIndex = imageSyncData.imageIndex;
assert_invariant(mCurrentSwapIndex < mFinishedDrawing.size());
mFinishedDrawing[mCurrentSwapIndex] = {}; mFinishedDrawing[mCurrentSwapIndex] = {};
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR) FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
<< "Cannot acquire in swapchain. error=" << static_cast<int32_t>(result); << "Cannot acquire in swapchain. error=" << static_cast<int32_t>(result);
@@ -36,43 +36,6 @@ namespace filament::backend {
struct VulkanTexture; struct VulkanTexture;
struct VulkanStream : public HwStream, fvkmemory::Resource {
//-- These methods are only called from the frontend
void acquire(const AcquiredImage& image) {
user_thread.mPrevious = user_thread.mAcquired;
user_thread.mAcquired = image;
}
bool previousNeedsRelease() const { return (user_thread.mPrevious.image != nullptr); }
// this function will null the previous once the caller takes it.
// It ensures we don't schedule for release twice.
AcquiredImage takePrevious() {
AcquiredImage previous = user_thread.mPrevious;
user_thread.mPrevious = {};
return previous;
}
const AcquiredImage& getAcquired() const { return user_thread.mAcquired; }
//-- These methods are only called from the backend thread
fvkmemory::resource_ptr<VulkanTexture> getTexture(void* ahb) {
if (auto itr = mTextures.find(ahb); itr != mTextures.end()) {
return itr->second;
}
return {};
}
void pushImage(void* ahb, fvkmemory::resource_ptr<VulkanTexture> tex) { mTextures[ahb] = tex; }
private:
// These are only called from the frontend
struct {
AcquiredImage mAcquired;
AcquiredImage mPrevious;
} user_thread;
// #TODO b/442937292
std::unordered_map<void*, fvkmemory::resource_ptr<VulkanTexture>> mTextures;
};
struct VulkanTextureState : public fvkmemory::Resource { struct VulkanTextureState : public fvkmemory::Resource {
VulkanTextureState(VulkanStagePool& stagePool, VulkanCommands* commands, VmaAllocator allocator, VulkanTextureState(VulkanStagePool& stagePool, VulkanCommands* commands, VmaAllocator allocator,
VkDevice device, VkImage image, VkDeviceMemory deviceMemory, VkFormat format, VkDevice device, VkImage image, VkDeviceMemory deviceMemory, VkFormat format,
@@ -111,8 +74,6 @@ private:
// The texture with the sidecar owns the sidecar. // The texture with the sidecar owns the sidecar.
fvkmemory::resource_ptr<VulkanTexture> mSidecarMSAA; fvkmemory::resource_ptr<VulkanTexture> mSidecarMSAA;
// The stream this texture is associated with.
fvkmemory::resource_ptr<VulkanStream> mStream;
VkImage const mTextureImage; VkImage const mTextureImage;
VkDeviceMemory const mTextureImageMemory; VkDeviceMemory const mTextureImageMemory;
@@ -144,7 +105,6 @@ private:
using ImageViewHash = utils::hash::MurmurHashFn<ImageViewKey>; using ImageViewHash = utils::hash::MurmurHashFn<ImageViewKey>;
std::unordered_map<ImageViewKey, VkImageView, ImageViewHash> mCachedImageViews; std::unordered_map<ImageViewKey, VkImageView, ImageViewHash> mCachedImageViews;
friend struct VulkanTexture; friend struct VulkanTexture;
}; };
@@ -222,14 +182,6 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
return mState->mSidecarMSAA; return mState->mSidecarMSAA;
} }
void setStream(fvkmemory::resource_ptr<VulkanStream> stream) {
mState->mStream = stream;
}
fvkmemory::resource_ptr<VulkanStream> getStream() const {
return mState->mStream;
}
bool isTransientAttachment() const { bool isTransientAttachment() const {
return mState->mUsage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT; return mState->mUsage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
} }
@@ -41,7 +41,6 @@ template ResourceType getTypeEnum<VulkanBuffer>() noexcept;
template ResourceType getTypeEnum<VulkanSync>() noexcept; template ResourceType getTypeEnum<VulkanSync>() noexcept;
template ResourceType getTypeEnum<VulkanMemoryMappedBuffer>() noexcept; template ResourceType getTypeEnum<VulkanMemoryMappedBuffer>() noexcept;
template ResourceType getTypeEnum<VulkanSemaphore>() noexcept; template ResourceType getTypeEnum<VulkanSemaphore>() noexcept;
template ResourceType getTypeEnum<VulkanStream>() noexcept;
template<typename D> template<typename D>
ResourceType getTypeEnum() noexcept { ResourceType getTypeEnum() noexcept {
@@ -105,9 +104,6 @@ ResourceType getTypeEnum() noexcept {
if constexpr (std::is_same_v<D, VulkanSemaphore>) { if constexpr (std::is_same_v<D, VulkanSemaphore>) {
return ResourceType::SEMAPHORE; return ResourceType::SEMAPHORE;
} }
if constexpr (std::is_same_v<D, VulkanStream>) {
return ResourceType::STREAM;
}
return ResourceType::UNDEFINED_TYPE; return ResourceType::UNDEFINED_TYPE;
} }
@@ -153,8 +149,6 @@ std::string_view getTypeStr(ResourceType type) {
return "VulkanMemoryMappedBuffer"; return "VulkanMemoryMappedBuffer";
case ResourceType::SEMAPHORE: case ResourceType::SEMAPHORE:
return "Semaphore"; return "Semaphore";
case ResourceType::STREAM:
return "VulkanStream";
case ResourceType::UNDEFINED_TYPE: case ResourceType::UNDEFINED_TYPE:
return ""; return "";
} }
@@ -55,8 +55,7 @@ enum class ResourceType : uint8_t {
SYNC = 17, SYNC = 17,
MEMORY_MAPPED_BUFFER = 18, MEMORY_MAPPED_BUFFER = 18,
SEMAPHORE = 19, SEMAPHORE = 19,
STREAM = 20, UNDEFINED_TYPE = 20, // Must be the last enum because we use it for iterating over the enums.
UNDEFINED_TYPE = 21, // Must be the last enum because we use it for iterating over the enums.
}; };
template<typename D> template<typename D>
@@ -123,9 +123,6 @@ void ResourceManager::destroyWithType(ResourceType type, HandleId id) {
case ResourceType::SEMAPHORE: case ResourceType::SEMAPHORE:
destruct<VulkanSemaphore>(Handle<VulkanSemaphore>(id)); destruct<VulkanSemaphore>(Handle<VulkanSemaphore>(id));
break; break;
case ResourceType::STREAM:
destruct<VulkanStream>(Handle<VulkanStream>(id));
break;
case ResourceType::UNDEFINED_TYPE: case ResourceType::UNDEFINED_TYPE:
break; break;
} }
@@ -541,15 +541,9 @@ bool VulkanPlatformAndroid::queryCompositorTiming(SwapChain const* swapchain,
outCompositorTiming->frameTime = preferredTimeline.frameTime; outCompositorTiming->frameTime = preferredTimeline.frameTime;
outCompositorTiming->expectedPresentTime = preferredTimeline.expectedPresentTime; outCompositorTiming->expectedPresentTime = preferredTimeline.expectedPresentTime;
outCompositorTiming->frameTimelineDeadline = preferredTimeline.frameTimelineDeadline; outCompositorTiming->frameTimelineDeadline = preferredTimeline.frameTimelineDeadline;
outCompositorTiming->compositeDeadline = CompositorTiming::INVALID;
outCompositorTiming->compositeInterval = CompositorTiming::INVALID;
outCompositorTiming->compositeToPresentLatency = CompositorTiming::INVALID;
// From this point on, we always return "success" because some timings were returned.
auto vulkanSwapchain = static_cast<VulkanPlatformSwapChainBase const *>(swapchain); auto vulkanSwapchain = static_cast<VulkanPlatformSwapChainBase const *>(swapchain);
vulkanSwapchain->queryCompositorTiming(outCompositorTiming); return vulkanSwapchain->queryCompositorTiming(outCompositorTiming);
return true;
} }
bool VulkanPlatformAndroid::setPresentFrameId(SwapChain const* swapchain, uint64_t frameId) noexcept { bool VulkanPlatformAndroid::setPresentFrameId(SwapChain const* swapchain, uint64_t frameId) noexcept {
@@ -361,15 +361,13 @@ bool VulkanPlatformSurfaceSwapChain::queryCompositorTiming(
CompositorTiming* outCompositorTiming) const { CompositorTiming* outCompositorTiming) const {
#ifdef __ANDROID__ #ifdef __ANDROID__
// fallback to private APIs // fallback to private APIs
if (UTILS_VERY_LIKELY(mNativeWindow)) { int const status = NativeWindow::getCompositorTiming(
int const status = NativeWindow::getCompositorTiming( static_cast<ANativeWindow*>(mNativeWindow),
static_cast<ANativeWindow*>(mNativeWindow), &outCompositorTiming->compositeDeadline,
&outCompositorTiming->compositeDeadline, &outCompositorTiming->compositeInterval,
&outCompositorTiming->compositeInterval, &outCompositorTiming->compositeToPresentLatency);
&outCompositorTiming->compositeToPresentLatency); if (status == 0) {
if (status == 0) { return true;
return true;
}
} }
#endif #endif
return VulkanPlatformSwapChainBase::queryCompositorTiming(outCompositorTiming); return VulkanPlatformSwapChainBase::queryCompositorTiming(outCompositorTiming);
@@ -18,12 +18,6 @@
#include "vulkan/VulkanTexture.h" #include "vulkan/VulkanTexture.h"
#if defined(__ANDROID__)
#include "backend/platforms/VulkanPlatformAndroid.h"
#include <android/hardware_buffer.h>
#include <android/native_window.h>
#endif
#include <utils/Panic.h> #include <utils/Panic.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/debug.h> #include <utils/debug.h>
@@ -240,19 +234,6 @@ uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask) {
return mostSignificantBit((sampleCount - 1) & mask); return mostSignificantBit((sampleCount - 1) & mask);
} }
filament::backend::Platform::ExternalImageHandle createExternalImageFromRaw(
filament::backend::VulkanPlatform* platform, void* image, bool sRGB) {
#if defined(__ANDROID__)
if (__builtin_available(android 26, *)) {
return static_cast<filament::backend::VulkanPlatformAndroid*>(platform)
->createExternalImage(reinterpret_cast<AHardwareBuffer const*>(image), sRGB);
}
#endif
return {};
}
} // namespace filament::backend::fvkutils } // namespace filament::backend::fvkutils
bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b) { bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b) {
@@ -18,7 +18,6 @@
#define TNT_FILAMENT_BACKEND_VULKAN_UTILS_IMAGE_H #define TNT_FILAMENT_BACKEND_VULKAN_UTILS_IMAGE_H
#include <backend/DriverEnums.h> #include <backend/DriverEnums.h>
#include <backend/platforms/VulkanPlatform.h>
#include <utils/Log.h> #include <utils/Log.h>
@@ -107,9 +106,6 @@ VkImageAspectFlags getImageAspect(VkFormat format);
uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask); uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask);
Platform::ExternalImageHandle createExternalImageFromRaw(filament::backend::VulkanPlatform* platform,
void* image,
bool sRGB);
} // namespace fvkutils } // namespace fvkutils
} // namespace filament::backend } // namespace filament::backend
@@ -30,6 +30,7 @@
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <iostream>
namespace filament::backend { namespace filament::backend {
@@ -80,33 +81,54 @@ void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor const& bufferDescriptor,
// This may have some performance implications. That should be investigated later. // This may have some performance implications. That should be investigated later.
assert_invariant(mBuffer.GetUsage() & wgpu::BufferUsage::CopyDst); assert_invariant(mBuffer.GetUsage() & wgpu::BufferUsage::CopyDst);
// Calculate some alignment related sizes // // Calculate some alignment related sizes
const size_t remainder = bufferDescriptor.size % FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS; const size_t remainder = bufferDescriptor.size % FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS;
const size_t mainBulk = bufferDescriptor.size - remainder; const size_t mainBulk = bufferDescriptor.size - remainder;
const size_t stagingBufferSize = const size_t stagingBufferSize =
remainder == 0 ? bufferDescriptor.size : mainBulk + FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS; remainder == 0 ? bufferDescriptor.size : mainBulk + FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS;
wgpu::Buffer stagingBuffer = webGPUStagePool->acquireBuffer(stagingBufferSize, Stage stage = webGPUStagePool->acquireBuffer(stagingBufferSize);
webGPUQueueManager->getLatestSubmissionState());
void* mappedRange = stagingBuffer.GetMappedRange(); std::string mappedRangeIsNull = stage.mappedRange
assert_invariant(mappedRange); ? "no"
: "yes";
std::cout << "Run Yu: got mapped range on the staging buffer with size "
<< stage.buffer.GetSize() << " and it is null? " << mappedRangeIsNull << std::endl;
memcpy(stage.mappedRange, bufferDescriptor.buffer, bufferDescriptor.size);
memcpy(mappedRange, bufferDescriptor.buffer, bufferDescriptor.size); stage.buffer.Unmap();
// Make sure the padded memory is set to 0 to have deterministic behaviors
if (remainder != 0) {
uint8_t* paddingStart = static_cast<uint8_t*>(mappedRange) + bufferDescriptor.size;
memset(paddingStart, 0, FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS - remainder);
}
stagingBuffer.Unmap();
std::cout << "Run Yu: about to issue copy command with actual staging buffer of size "
<< stage.buffer.GetSize() << ", and computed size of " << stagingBufferSize
<< ". The mBuffer size is " << mBuffer.GetSize() << std::endl;
// Copy the staging buffer contents to the destination buffer. // Copy the staging buffer contents to the destination buffer.
webGPUQueueManager->getCommandEncoder().CopyBufferToBuffer(stagingBuffer, 0, mBuffer, webGPUQueueManager->getCommandEncoder().CopyBufferToBuffer(stage.buffer, 0, mBuffer,
byteOffset, byteOffset,
remainder == 0 ? bufferDescriptor.size remainder == 0 ? bufferDescriptor.size
: mainBulk + FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS); : mainBulk + FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS);
webGPUQueueManager->flush();
struct UserData final {
wgpu::Buffer stagingBuffer;
WebGPUStagePool* webGPUStagePool;
};
auto userData = std::make_unique<UserData>(
UserData{ .stagingBuffer = stage.buffer, .webGPUStagePool = webGPUStagePool });
stage.buffer.MapAsync(wgpu::MapMode::Write, 0, stagingBufferSize,
wgpu::CallbackMode::AllowSpontaneous,
[data = std::move(userData)](wgpu::MapAsyncStatus status, const char* message) {
if (UTILS_LIKELY(status == wgpu::MapAsyncStatus::Success)) {
std::cout << "Run Yu: successfully mapped a buffer with size "
<< data->stagingBuffer.GetSize() << std::endl;
void* mappedRange = data->stagingBuffer.GetMappedRange();
if (!mappedRange) {
std::cout << "Run Yu: MAPPED RANGE IS NULL RIGHT AWAY!!\n";
}
data->webGPUStagePool->addBufferToPool(data->stagingBuffer, mappedRange);
} else {
std::cout << "Run Yu: MAPPING UNSUCCESSFUL!!\n";
}
});
} }
} // namespace filament::backend } // namespace filament::backend
+5 -3
View File
@@ -178,9 +178,6 @@ void WebGPUDriver::endFrame(const uint32_t /* frameId */) {
for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) { for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) {
mCurrentDescriptorSets[i] = {}; mCurrentDescriptorSets[i] = {};
} }
// Garbage collection (if necessary)
mStagePool.gc();
} }
// If a command encoder is in flight then the encoder is finished and submitted to the GPU queue. // If a command encoder is in flight then the encoder is finished and submitted to the GPU queue.
@@ -655,6 +652,11 @@ void WebGPUDriver::setAcquiredImage(Handle<HwStream> sh, void* image, const math
//todo //todo
} }
void WebGPUDriver::registerBufferObjectStreams(Handle<HwBufferObject> bufferObjectHandle,
BufferObjectStreamDescriptor&& streams) {
//todo
}
void WebGPUDriver::setStreamDimensions(Handle<HwStream> sh, uint32_t width, uint32_t height) { void WebGPUDriver::setStreamDimensions(Handle<HwStream> sh, uint32_t width, uint32_t height) {
//todo //todo
} }
@@ -64,7 +64,6 @@ wgpu::CommandEncoder WebGPUQueueManager::getCommandEncoder() {
}; };
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor); mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
ASSERT_POSTCONDITION(mCommandEncoder, "Failed to create command encoder."); ASSERT_POSTCONDITION(mCommandEncoder, "Failed to create command encoder.");
mLatestSubmissionState = std::make_shared<WebGPUSubmissionState>();
} }
return mCommandEncoder; return mCommandEncoder;
} }
@@ -95,6 +94,8 @@ void WebGPUQueueManager::submit() {
return; return;
} }
mLatestSubmissionState = std::make_shared<WebGPUSubmissionState>();
wgpu::CommandBufferDescriptor commandBufferDescriptor{ wgpu::CommandBufferDescriptor commandBufferDescriptor{
.label = "Filament Command Buffer", .label = "Filament Command Buffer",
}; };
+36 -42
View File
@@ -17,7 +17,8 @@
#include "WebGPUStagePool.h" #include "WebGPUStagePool.h"
#include "WebGPUConstants.h" #include "WebGPUConstants.h"
#include "WebGPUQueueManager.h"
#include <iostream>
namespace filament::backend { namespace filament::backend {
@@ -25,62 +26,55 @@ WebGPUStagePool::WebGPUStagePool(wgpu::Device const& device) : mDevice(device) {
WebGPUStagePool::~WebGPUStagePool() = default; WebGPUStagePool::~WebGPUStagePool() = default;
wgpu::Buffer WebGPUStagePool::acquireBuffer(size_t requiredSize, Stage WebGPUStagePool::acquireBuffer(size_t requiredSize) {
std::shared_ptr<WebGPUSubmissionState> submissionState) { std::cout << "Run Yu: required size in acquireBuffer: " << requiredSize << std::endl;
wgpu::Buffer buffer; std::cout << "Run Yu: the pool size is " << mBuffers.size() << std::endl;
{ {
std::lock_guard<std::mutex> lock(mMutex); std::lock_guard<std::mutex> lock(mMutex);
auto iter = mBuffers.lower_bound(requiredSize); auto iter = mBuffers.lower_bound(requiredSize);
if (iter != mBuffers.end()) { if (iter != mBuffers.end()) {
buffer = iter->second; const Stage& fromPool = iter->second;
std::cout << "Run Yu: found buffer in the pool with size " << fromPool.buffer.GetSize()
<< std::endl;
if (fromPool.buffer.GetMapState() != wgpu::BufferMapState::Mapped) {
std::cout << "Run Yu: buffer from pool is not mapped!!" << std::endl;
}
Stage result{ .buffer = fromPool.buffer, .mappedRange = fromPool.mappedRange };
mBuffers.erase(iter); mBuffers.erase(iter);
return result;
} }
} }
if (!buffer.Get()) { wgpu::Buffer newBuffer = createNewBuffer(requiredSize);
buffer = createNewBuffer(requiredSize); return { .buffer = newBuffer, .mappedRange = newBuffer.GetMappedRange() };
}
mInProgress.push_back({submissionState, buffer});
return buffer;
} }
void WebGPUStagePool::recycleBuffer(wgpu::Buffer buffer) { void WebGPUStagePool::addBufferToPool(wgpu::Buffer buffer, void* mappedRange) {
struct UserData final { std::lock_guard<std::mutex> lock(mMutex);
wgpu::Buffer buffer; std::cout << "Run Yu: adding buffer to the pool with size " << buffer.GetSize() << std::endl;
WebGPUStagePool* webGPUStagePool; Stage stage {.buffer = buffer, .mappedRange = mappedRange};
}; mBuffers.emplace(buffer.GetSize(), stage);
auto userData = std::cout << "Run Yu: added buffer to the pool with size " << buffer.GetSize() << std::endl;
std::make_unique<UserData>(UserData{ .buffer = buffer, .webGPUStagePool = this });
buffer.MapAsync(wgpu::MapMode::Write, 0, buffer.GetSize(), wgpu::CallbackMode::AllowSpontaneous,
[data = std::move(userData)](wgpu::MapAsyncStatus status, const char* message) {
if (UTILS_LIKELY(status == wgpu::MapAsyncStatus::Success)) {
if (!data->webGPUStagePool) {
return;
}
std::lock_guard<std::mutex> lock(data->webGPUStagePool->mMutex);
data->webGPUStagePool->mBuffers.insert(
{ data->buffer.GetSize(), data->buffer });
} else {
FWGPU_LOGE << "Failed to MapAsync when recycling staging buffer: " << message;
}
});
}
void WebGPUStagePool::gc() { bool allMapped = true;
// We found that MapAsync would sometimes lead to GetMappedRange returning nullptr if the for (const auto& pair : mBuffers) {
// command using that staging buffer has not finished executing, so here we only recycle those auto state = pair.second.buffer.GetMapState();
// buffers that are not still being used by any command if (state != wgpu::BufferMapState::Mapped) {
std::vector<std::pair<std::shared_ptr<WebGPUSubmissionState>, wgpu::Buffer>> stillInProgress; allMapped = false;
for (auto& [st, buffer]: mInProgress) { std::cout << "Run Yu: the buffer with size " << pair.second.buffer.GetSize()
if (st->getStatus() == FenceStatus::CONDITION_SATISFIED) { << " is not mapped but somehow was added to the pool, its state is "
recycleBuffer(buffer); << static_cast<int>(state) << std::endl;
} else {
stillInProgress.push_back({st, buffer});
} }
} }
std::swap(mInProgress, stillInProgress); if (!allMapped) {
std::cout << "Run Yu: found buffers that are not mapped\n";
} else {
std::cout << "Run Yu: all buffers are mapped\n";
}
} }
wgpu::Buffer WebGPUStagePool::createNewBuffer(size_t bufferSize) { wgpu::Buffer WebGPUStagePool::createNewBuffer(size_t bufferSize) {
std::cout << "Run Yu: creating new buffer with size " << bufferSize << std::endl;
wgpu::BufferDescriptor descriptor{ wgpu::BufferDescriptor descriptor{
.label = "Filament WebGPU Staging Buffer", .label = "Filament WebGPU Staging Buffer",
.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc, .usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc,
+9 -10
View File
@@ -24,27 +24,26 @@
namespace filament::backend { namespace filament::backend {
struct WebGPUSubmissionState; struct Stage {
wgpu::Buffer buffer;
void* mappedRange;
};
class WebGPUStagePool { class WebGPUStagePool {
public: public:
WebGPUStagePool(wgpu::Device const& device); WebGPUStagePool(wgpu::Device const& device);
~WebGPUStagePool(); ~WebGPUStagePool();
wgpu::Buffer acquireBuffer(size_t requiredSize, Stage acquireBuffer(size_t requiredSize);
std::shared_ptr<WebGPUSubmissionState> submissionState); void addBufferToPool(wgpu::Buffer buffer, void* mappedRange);
void recycleBuffer(wgpu::Buffer buffer);
void gc();
private: private:
wgpu::Buffer createNewBuffer(size_t bufferSize); wgpu::Buffer createNewBuffer(size_t bufferSize);
std::multimap<uint32_t, wgpu::Buffer> mBuffers; std::multimap<uint32_t, Stage> mBuffers;
std::vector<std::pair<std::shared_ptr<WebGPUSubmissionState>, wgpu::Buffer>> mInProgress; mutable std::mutex mMutex;
std::mutex mMutex;
wgpu::Device mDevice; wgpu::Device mDevice;
}; };
} // namespace filament::backend }
#endif // TNT_FILAMENT_BACKEND_WEBGPUSTAGEPOOL_H #endif // TNT_FILAMENT_BACKEND_WEBGPUSTAGEPOOL_H
-14
View File
@@ -192,7 +192,6 @@ public:
using StereoscopicType = backend::StereoscopicType; using StereoscopicType = backend::StereoscopicType;
using Driver = backend::Driver; using Driver = backend::Driver;
using GpuContextPriority = backend::Platform::GpuContextPriority; using GpuContextPriority = backend::Platform::GpuContextPriority;
using AsynchronousMode = backend::AsynchronousMode;
/** /**
* Config is used to define the memory footprint used by the engine, such as the * Config is used to define the memory footprint used by the engine, such as the
@@ -429,11 +428,6 @@ public:
* initial memory usage. * initial memory usage.
*/ */
uint32_t sharedUboInitialSizeInBytes = 256 * 64; uint32_t sharedUboInitialSizeInBytes = 256 * 64;
/**
* Asynchronous mode for the engine. Defines how asynchronous operations are handled.
*/
AsynchronousMode asynchronousMode = AsynchronousMode::NONE;
}; };
@@ -748,14 +742,6 @@ public:
*/ */
bool isStereoSupported(StereoscopicType stereoscopicType) const noexcept; bool isStereoSupported(StereoscopicType stereoscopicType) const noexcept;
/**
* Checks if the engine is set up for asynchronous operation. If it returns true, the
* asynchronous versions of the APIs are available for use.
*
* @return true if the engine supports asynchronous operation.
*/
bool isAsynchronousOperationSupported() const noexcept;
/** /**
* Retrieves the configuration settings of this Engine. * Retrieves the configuration settings of this Engine.
* *
-21
View File
@@ -69,20 +69,6 @@ public:
using ShaderModel = backend::ShaderModel; using ShaderModel = backend::ShaderModel;
using SubpassType = backend::SubpassType; using SubpassType = backend::SubpassType;
/**
* Defines whether a material instance should use UBO batching or not.
*/
enum class UboBatchingMode {
/**
* For default, it follows the engine settings.
* If UBO batching is enabled on the engine and the material domain is SURFACE, it
* turns on the UBO batching. Otherwise, it turns off the UBO batching.
*/
DEFAULT,
//! Disable the Ubo Batching for this material
DISABLED,
};
/** /**
* Holds information about a material parameter. * Holds information about a material parameter.
*/ */
@@ -179,13 +165,6 @@ public:
*/ */
Builder& shadowSamplingQuality(ShadowSamplingQuality quality) noexcept; Builder& shadowSamplingQuality(ShadowSamplingQuality quality) noexcept;
/**
* Set the batching mode of the instances created from this material.
* @param uboBatchingMode
* @return
*/
Builder& uboBatching(UboBatchingMode uboBatchingMode) noexcept;
/** /**
* Creates the Material object and returns a pointer to it. * Creates the Material object and returns a pointer to it.
* *
-6
View File
@@ -113,10 +113,8 @@ public:
/** /**
* Retrieve a history of frame timing information. The maximum frame history size is * Retrieve a history of frame timing information. The maximum frame history size is
* given by getMaxFrameHistorySize(). * given by getMaxFrameHistorySize().
* All or part of the history can be lost when using a different SwapChain in beginFrame().
* @param historySize requested history size. The returned vector could be smaller. * @param historySize requested history size. The returned vector could be smaller.
* @return A vector of FrameInfo. * @return A vector of FrameInfo.
* @see beginFrame()
*/ */
utils::FixedCapacityVector<FrameInfo> getFrameInfoHistory( utils::FixedCapacityVector<FrameInfo> getFrameInfoHistory(
size_t historySize = 1) const noexcept; size_t historySize = 1) const noexcept;
@@ -328,8 +326,6 @@ public:
* or 0 if unknown. This value should be the timestamp of * or 0 if unknown. This value should be the timestamp of
* the last h/w vsync. It is expressed in the * the last h/w vsync. It is expressed in the
* std::chrono::steady_clock time base. * std::chrono::steady_clock time base.
* On Android this should be the frame time received from
* a Choreographer.
* @param swapChain A pointer to the SwapChain instance to use. * @param swapChain A pointer to the SwapChain instance to use.
* *
* @return * @return
@@ -341,8 +337,6 @@ public:
* *
* @note * @note
* All calls to render() must happen *after* beginFrame(). * All calls to render() must happen *after* beginFrame().
* It is recommended to use the same swapChain for every call to beginFrame, failing to do
* so can result is losing all or part of the FrameInfo history.
* *
* @see * @see
* endFrame() * endFrame()
-4
View File
@@ -455,10 +455,6 @@ bool Engine::isStereoSupported(StereoscopicType) const noexcept {
return downcast(this)->isStereoSupported(); return downcast(this)->isStereoSupported();
} }
bool Engine::isAsynchronousOperationSupported() const noexcept {
return downcast(this)->isAsynchronousOperationSupported();
}
size_t Engine::getMaxStereoscopicEyes() noexcept { size_t Engine::getMaxStereoscopicEyes() noexcept {
return FEngine::getMaxStereoscopicEyes(); return FEngine::getMaxStereoscopicEyes();
} }
+4 -25
View File
@@ -101,24 +101,11 @@ void FrameInfoManager::beginFrame(FSwapChain* swapChain, DriverApi& driver,
auto& history = mFrameTimeHistory; auto& history = mFrameTimeHistory;
// don't exceed the capacity, drop the oldest entry // don't exceed the capacity, drop the oldest entry
if (UTILS_LIKELY(history.size() == history.capacity())) { if (UTILS_LIKELY(history.size() == history.capacity())) {
FrameInfoImpl& frameInfo = history.back(); if (!mDisableGpuFrameComplete) {
if (frameInfo.ready.load(std::memory_order_relaxed)) { assert_invariant(history.back().fence);
if (!mDisableGpuFrameComplete) { driver.destroyFence(std::move(history.back().fence));
assert_invariant(frameInfo.fence);
driver.destroyFence(std::move(frameInfo.fence));
}
history.pop_back();
} else {
// This is a big problem, we ran out of space in the circular queue and that entry
// hasn't been processed yet. Because the code below keeps a reference to the
// front element of the queue, we can't pop/push. Our only option is to not record
// a new entry for this frame, which will create a false skipped frame in the
// data.
LOG(WARNING) << "FrameInfo's circular queue is full, but the oldest item hasn't "
"been processed yet. Skipping this frame, id = " << frameId;
mLastBeginFrameSkipped = true;
return;
} }
history.pop_back();
} }
// create a new entry // create a new entry
@@ -206,14 +193,6 @@ void FrameInfoManager::beginFrame(FSwapChain* swapChain, DriverApi& driver,
} }
void FrameInfoManager::endFrame(DriverApi& driver) noexcept { void FrameInfoManager::endFrame(DriverApi& driver) noexcept {
if (mLastBeginFrameSkipped) {
// if we had to skip the last beginFrame(), endFrame() needs to be skipped too
// because history.front() now references the wrong frame.
// It is guaranteed that if beginFrame() is called, endFrame() will be called too.
mLastBeginFrameSkipped = false;
return;
}
auto& front = mFrameTimeHistory.front(); auto& front = mFrameTimeHistory.front();
front.endFrame = std::chrono::steady_clock::now(); front.endFrame = std::chrono::steady_clock::now();
+27 -3
View File
@@ -95,8 +95,33 @@ struct FrameInfoImpl : public details::FrameInfo {
assert_invariant(!fence); assert_invariant(!fence);
} }
FrameInfoImpl(FrameInfoImpl& rhs) noexcept = delete; FrameInfoImpl(FrameInfoImpl&& rhs) noexcept :
FrameInfoImpl& operator=(FrameInfoImpl& rhs) noexcept = delete; details::FrameInfo(rhs),
frameId(rhs.frameId),
beginFrame(rhs.beginFrame),
endFrame(rhs.endFrame),
backendBeginFrame(rhs.backendBeginFrame),
backendEndFrame(rhs.backendEndFrame),
gpuFrameComplete(rhs.gpuFrameComplete),
vsync(rhs.vsync),
fence(rhs.fence),
ready(rhs.ready.load())
{
}
FrameInfoImpl& operator=(FrameInfoImpl&& rhs) noexcept {
details::FrameInfo::operator=(rhs);
frameId = rhs.frameId;
beginFrame = rhs.beginFrame;
endFrame = rhs.endFrame;
backendBeginFrame = rhs.backendBeginFrame;
backendEndFrame = rhs.backendEndFrame;
gpuFrameComplete = rhs.gpuFrameComplete;
vsync = rhs.vsync;
fence = rhs.fence;
ready.store(rhs.ready.load());
return *this;
}
}; };
template<typename T, size_t CAPACITY> template<typename T, size_t CAPACITY>
@@ -315,7 +340,6 @@ private:
FrameHistoryQueue mFrameTimeHistory{}; FrameHistoryQueue mFrameTimeHistory{};
utils::AsyncJobQueue mJobQueue; utils::AsyncJobQueue mJobQueue;
FSwapChain* mLastSeenSwapChain = nullptr; FSwapChain* mLastSeenSwapChain = nullptr;
bool mLastBeginFrameSkipped = false;
bool const mHasTimerQueries = false; bool const mHasTimerQueries = false;
bool const mDisableGpuFrameComplete = false; bool const mDisableGpuFrameComplete = false;
}; };
+6 -13
View File
@@ -98,7 +98,7 @@ using namespace filaflat;
namespace { namespace {
backend::Platform::DriverConfig getDriverConfig(FEngine* instance) { backend::Platform::DriverConfig getDriverConfig(FEngine* instance) {
Platform::DriverConfig driverConfig { return {
.handleArenaSize = instance->getRequestedDriverHandleArenaSize(), .handleArenaSize = instance->getRequestedDriverHandleArenaSize(),
.metalUploadBufferSizeBytes = instance->getConfig().metalUploadBufferSizeBytes, .metalUploadBufferSizeBytes = instance->getConfig().metalUploadBufferSizeBytes,
.disableParallelShaderCompile = instance->features.backend.disable_parallel_shader_compile, .disableParallelShaderCompile = instance->features.backend.disable_parallel_shader_compile,
@@ -116,11 +116,7 @@ backend::Platform::DriverConfig getDriverConfig(FEngine* instance) {
.gpuContextPriority = instance->getConfig().gpuContextPriority, .gpuContextPriority = instance->getConfig().gpuContextPriority,
.vulkanEnableStagingBufferBypass = .vulkanEnableStagingBufferBypass =
instance->features.backend.vulkan.enable_staging_buffer_bypass, instance->features.backend.vulkan.enable_staging_buffer_bypass,
.asynchronousMode = instance->features.backend.enable_asynchronous_operation ?
instance->getConfig().asynchronousMode : AsynchronousMode::NONE,
}; };
return driverConfig;
} }
} // anonymous } // anonymous
@@ -731,6 +727,7 @@ void FEngine::prepare() {
// post-process materials instances must be commited explicitly because their // post-process materials instances must be commited explicitly because their
// parameters are typically not set at this point in time. // parameters are typically not set at this point in time.
if (item->getMaterial()->getMaterialDomain() == MaterialDomain::SURFACE) { if (item->getMaterial()->getMaterialDomain() == MaterialDomain::SURFACE) {
item->commitStreamUniformAssociations(driver);
item->commit(driver, uboManager); item->commit(driver, uboManager);
} }
}); });
@@ -994,9 +991,10 @@ FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material,
return p; return p;
} }
FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material, FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material, const char* name,
const char* name) noexcept { UboBatchingMode batchingMode) noexcept {
FMaterialInstance* p = mHeapAllocator.make<FMaterialInstance>(*this, material, name); FMaterialInstance* p =
mHeapAllocator.make<FMaterialInstance>(*this, material, name, batchingMode);
if (UTILS_LIKELY(p)) { if (UTILS_LIKELY(p)) {
auto pos = mMaterialInstances.emplace(material, "MaterialInstance"); auto pos = mMaterialInstances.emplace(material, "MaterialInstance");
pos.first->second.insert(p); pos.first->second.insert(p);
@@ -1505,11 +1503,6 @@ Engine::FeatureLevel FEngine::setActiveFeatureLevel(FeatureLevel featureLevel) {
return (mActiveFeatureLevel = std::max(mActiveFeatureLevel, featureLevel)); return (mActiveFeatureLevel = std::max(mActiveFeatureLevel, featureLevel));
} }
bool FEngine::isAsynchronousOperationSupported() const noexcept {
return features.backend.enable_asynchronous_operation &&
mConfig.asynchronousMode != AsynchronousMode::NONE;
}
#if defined(__EMSCRIPTEN__) #if defined(__EMSCRIPTEN__)
void FEngine::resetBackendState() noexcept { void FEngine::resetBackendState() noexcept {
getDriverApi().resetState(); getDriverApi().resetState();
+13 -8
View File
@@ -216,8 +216,6 @@ public:
return getDriver().isStereoSupported(); return getDriver().isStereoSupported();
} }
bool isAsynchronousOperationSupported() const noexcept;
static size_t getMaxStereoscopicEyes() noexcept { static size_t getMaxStereoscopicEyes() noexcept {
return CONFIG_MAX_STEREOSCOPIC_EYES; return CONFIG_MAX_STEREOSCOPIC_EYES;
} }
@@ -347,10 +345,21 @@ public:
FRenderer* createRenderer() noexcept; FRenderer* createRenderer() noexcept;
// Defines whether a material instance should use UBO batching or not.
enum class UboBatchingMode {
// For default, it follows the engine settings.
// If UBO batching is enabled on the engine and the material domain is not SURFACE, it
// turns on the UBO batching. Otherwise, it turns off the UBO batching.
DEFAULT,
NO_UBO_BATCHING,
UBO_BATCHING
};
FMaterialInstance* createMaterialInstance(const FMaterial* material, FMaterialInstance* createMaterialInstance(const FMaterial* material,
const FMaterialInstance* other, const char* name) noexcept; const FMaterialInstance* other, const char* name) noexcept;
FMaterialInstance* createMaterialInstance(const FMaterial* material, const char* name) noexcept; FMaterialInstance* createMaterialInstance(const FMaterial* material, const char* name,
UboBatchingMode batchingMode) noexcept;
FScene* createScene() noexcept; FScene* createScene() noexcept;
FView* createView() noexcept; FView* createView() noexcept;
@@ -771,7 +780,7 @@ public:
bool assert_texture_can_generate_mipmap = CORRECTNESS_ASSERTION_DEFAULT; bool assert_texture_can_generate_mipmap = CORRECTNESS_ASSERTION_DEFAULT;
} debug; } debug;
struct { struct {
bool disable_gpu_frame_complete_metric = true; bool disable_gpu_frame_complete_metric = false;
} frame_info; } frame_info;
} engine; } engine;
struct { struct {
@@ -788,7 +797,6 @@ public:
bool disable_amortized_shader_compile = true; bool disable_amortized_shader_compile = true;
bool disable_handle_use_after_free_check = false; bool disable_handle_use_after_free_check = false;
bool disable_heap_handle_tags = true; // FIXME: this should be false bool disable_heap_handle_tags = true; // FIXME: this should be false
bool enable_asynchronous_operation = false;
} backend; } backend;
struct { struct {
bool check_crc32_after_loading = false; bool check_crc32_after_loading = false;
@@ -809,9 +817,6 @@ public:
{ "backend.disable_heap_handle_tags", { "backend.disable_heap_handle_tags",
"Disable Handle<> tags for heap-allocated handles.", "Disable Handle<> tags for heap-allocated handles.",
&features.backend.disable_heap_handle_tags, true }, &features.backend.disable_heap_handle_tags, true },
{ "backend.enable_asynchronous_operation",
"Enable asynchronous operation for resource management.",
&features.backend.enable_asynchronous_operation, true },
{ "backend.opengl.assert_native_window_is_valid", { "backend.opengl.assert_native_window_is_valid",
"Asserts that the ANativeWindow is valid when rendering starts.", "Asserts that the ANativeWindow is valid when rendering starts.",
&features.backend.opengl.assert_native_window_is_valid, true }, &features.backend.opengl.assert_native_window_is_valid, true },
+3 -22
View File
@@ -70,19 +70,10 @@
namespace filament { namespace filament {
namespace {
using namespace backend; using namespace backend;
using namespace filaflat; using namespace filaflat;
using namespace utils; using namespace utils;
using UboBatchingMode = Material::UboBatchingMode; using UboBatchingMode = FEngine::UboBatchingMode;
bool shouldEnableBatching(FEngine& engine, UboBatchingMode batchingMode, MaterialDomain domain) {
return batchingMode != UboBatchingMode::DISABLED && engine.isUboBatchingEnabled() &&
domain == MaterialDomain::SURFACE;
}
} // anonymous namespace
struct Material::BuilderDetails { struct Material::BuilderDetails {
const void* mPayload = nullptr; const void* mPayload = nullptr;
@@ -90,7 +81,6 @@ struct Material::BuilderDetails {
bool mDefaultMaterial = false; bool mDefaultMaterial = false;
int32_t mShBandsCount = 3; int32_t mShBandsCount = 3;
Builder::ShadowSamplingQuality mShadowSamplingQuality = Builder::ShadowSamplingQuality::LOW; Builder::ShadowSamplingQuality mShadowSamplingQuality = Builder::ShadowSamplingQuality::LOW;
UboBatchingMode mUboBatchingMode = UboBatchingMode::DEFAULT;
std::unordered_map< std::unordered_map<
CString, CString,
std::variant<int32_t, float, bool>, std::variant<int32_t, float, bool>,
@@ -125,11 +115,6 @@ Material::Builder& Material::Builder::shadowSamplingQuality(ShadowSamplingQualit
return *this; return *this;
} }
Material::Builder& Material::Builder::uboBatching(UboBatchingMode const mode) noexcept {
mImpl->mUboBatchingMode = mode;
return *this;
}
template<typename T, typename> template<typename T, typename>
Material::Builder& Material::Builder::constant(const char* name, size_t nameLength, T value) { Material::Builder& Material::Builder::constant(const char* name, size_t nameLength, T value) {
FILAMENT_CHECK_PRECONDITION(name != nullptr) << "name cannot be null"; FILAMENT_CHECK_PRECONDITION(name != nullptr) << "name cannot be null";
@@ -163,12 +148,8 @@ Material* Material::Builder::build(Engine& engine) const {
FMaterial::FMaterial(FEngine& engine, const Builder& builder, MaterialDefinition const& definition) FMaterial::FMaterial(FEngine& engine, const Builder& builder, MaterialDefinition const& definition)
: mDefinition(definition), : mDefinition(definition),
mIsDefaultMaterial(builder->mDefaultMaterial), mIsDefaultMaterial(builder->mDefaultMaterial),
mUseUboBatching(shouldEnableBatching(engine, builder->mUboBatchingMode,
definition.materialDomain)),
mEngine(engine), mEngine(engine),
mMaterialId(engine.getMaterialId()) { mMaterialId(engine.getMaterialId()) {
FILAMENT_CHECK_PRECONDITION(!mUseUboBatching || engine.isUboBatchingEnabled())
<< "UBO batching is not enabled.";
mSpecializationConstants = processSpecializationConstants(builder); mSpecializationConstants = processSpecializationConstants(builder);
precacheDepthVariants(engine); precacheDepthVariants(engine);
@@ -301,14 +282,14 @@ FMaterialInstance* FMaterial::createInstance(const char* name) const noexcept {
return FMaterialInstance::duplicate(mDefaultMaterialInstance, name); return FMaterialInstance::duplicate(mDefaultMaterialInstance, name);
} else { } else {
// but if we don't, just create an instance with all the default parameters // but if we don't, just create an instance with all the default parameters
return mEngine.createMaterialInstance(this, name); return mEngine.createMaterialInstance(this, name, UboBatchingMode::DEFAULT);
} }
} }
FMaterialInstance* FMaterial::getDefaultInstance() noexcept { FMaterialInstance* FMaterial::getDefaultInstance() noexcept {
if (UTILS_UNLIKELY(!mDefaultMaterialInstance)) { if (UTILS_UNLIKELY(!mDefaultMaterialInstance)) {
mDefaultMaterialInstance = mDefaultMaterialInstance =
mEngine.createMaterialInstance(this, mDefinition.name.c_str()); mEngine.createMaterialInstance(this, mDefinition.name.c_str(), UboBatchingMode::DEFAULT);
mDefaultMaterialInstance->setDefaultInstance(true); mDefaultMaterialInstance->setDefaultInstance(true);
} }
return mDefaultMaterialInstance; return mDefaultMaterialInstance;
-6
View File
@@ -248,10 +248,6 @@ public:
return mDefinition.perViewLayoutIndex; return mDefinition.perViewLayoutIndex;
} }
bool useUboBatching() const noexcept {
return mUseUboBatching;
}
#if FILAMENT_ENABLE_MATDBG #if FILAMENT_ENABLE_MATDBG
void applyPendingEdits() noexcept; void applyPendingEdits() noexcept;
@@ -314,8 +310,6 @@ private:
bool mIsDefaultMaterial = false; bool mIsDefaultMaterial = false;
bool mUseUboBatching = false;
// reserve some space to construct the default material instance // reserve some space to construct the default material instance
mutable FMaterialInstance* mDefaultMaterialInstance = nullptr; mutable FMaterialInstance* mDefaultMaterialInstance = nullptr;
+47 -7
View File
@@ -58,8 +58,18 @@ using namespace utils;
namespace filament { namespace filament {
using namespace backend; using namespace backend;
using UboBatchingMode = FEngine::UboBatchingMode;
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name) noexcept bool shouldEnableBatching(FEngine& engine, UboBatchingMode batchingMode, MaterialDomain domain) {
if (batchingMode != UboBatchingMode::DEFAULT) {
return batchingMode == UboBatchingMode::UBO_BATCHING;
}
return engine.isUboBatchingEnabled() && domain == MaterialDomain::SURFACE;
}
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name,
UboBatchingMode batchingMode) noexcept
: mMaterial(material), : mMaterial(material),
mDescriptorSet("MaterialInstance", material->getDescriptorSetLayout()), mDescriptorSet("MaterialInstance", material->getDescriptorSetLayout()),
mCulling(CullingMode::BACK), mCulling(CullingMode::BACK),
@@ -70,9 +80,12 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
mHasScissor(false), mHasScissor(false),
mIsDoubleSided(false), mIsDoubleSided(false),
mIsDefaultInstance(false), mIsDefaultInstance(false),
mUseUboBatching(material->useUboBatching()), mUseUboBatching(
shouldEnableBatching(engine, batchingMode, material->getMaterialDomain())),
mTransparencyMode(TransparencyMode::DEFAULT), mTransparencyMode(TransparencyMode::DEFAULT),
mName(name ? CString(name) : material->getName()) { mName(name ? CString(name) : material->getName()) {
FILAMENT_CHECK_PRECONDITION(!mUseUboBatching || engine.isUboBatchingEnabled())
<< "UBO batching is not enabled.";
FEngine::DriverApi& driver = engine.getDriverApi(); FEngine::DriverApi& driver = engine.getDriverApi();
// even if the material doesn't have any parameters, we allocate a small UBO because it's // even if the material doesn't have any parameters, we allocate a small UBO because it's
@@ -147,6 +160,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
mUseUboBatching(other->mUseUboBatching), mUseUboBatching(other->mUseUboBatching),
mScissorRect(other->mScissorRect), mScissorRect(other->mScissorRect),
mName(name ? CString(name) : other->mName) { mName(name ? CString(name) : other->mName) {
assert_invariant(!mUseUboBatching || engine.isUboBatchingEnabled());
FEngine::DriverApi& driver = engine.getDriverApi(); FEngine::DriverApi& driver = engine.getDriverApi();
FMaterial const* const material = other->getMaterial(); FMaterial const* const material = other->getMaterial();
@@ -209,6 +223,31 @@ void FMaterialInstance::terminate(FEngine& engine) {
} }
} }
void FMaterialInstance::commitStreamUniformAssociations(FEngine::DriverApi& driver) {
mHasStreamUniformAssociations = false;
if (!mTextureParameters.empty()) {
BufferObjectStreamDescriptor descriptor;
for (auto const& [binding, p]: mTextureParameters) {
ssize_t offset = mMaterial->getUniformInterfaceBlock().getTransformFieldOffset(binding);
if (offset >= 0) {
if (auto stream = p.texture->getStream()) {
mHasStreamUniformAssociations = true;
descriptor.mStreams.push_back({ uint32_t(offset), stream->getHandle(),
BufferObjectStreamAssociationType::TRANSFORM_MATRIX });
}
}
}
if (descriptor.mStreams.size() > 0) {
// UBO batching is incompatible with stream uniform associations because streams require a
// dedicated UBO handle, not a sub-allocation. Assert that any instance here uses its own UBO.
assert_invariant(!mUseUboBatching);
auto const* ubHandle = std::get_if<Handle<HwBufferObject>>(&mUboData);
assert_invariant(ubHandle);
driver.registerBufferObjectStreams(*ubHandle, std::move(descriptor));
}
}
}
void FMaterialInstance::commit(FEngine& engine) const { void FMaterialInstance::commit(FEngine& engine) const {
if (UTILS_LIKELY(mMaterial->getMaterialDomain() != MaterialDomain::SURFACE)) { if (UTILS_LIKELY(mMaterial->getMaterialDomain() != MaterialDomain::SURFACE)) {
commit(engine.getDriverApi(), engine.getUboManager()); commit(engine.getDriverApi(), engine.getUboManager());
@@ -216,9 +255,10 @@ void FMaterialInstance::commit(FEngine& engine) const {
} }
void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManager) const { void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManager) const {
if (mUniforms.isDirty()) { if (mUniforms.isDirty() || mHasStreamUniformAssociations) {
mUniforms.clean(); mUniforms.clean();
if (isUsingUboBatching()) { if (mUseUboBatching) {
assert_invariant(uboManager != nullptr);
if (!BufferAllocator::isValid(getAllocationId())) { if (!BufferAllocator::isValid(getAllocationId())) {
// The allocation hasn't happened yet, return. // The allocation hasn't happened yet, return.
return; return;
@@ -249,7 +289,7 @@ void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManage
// TODO: eventually we should remove this in RELEASE builds // TODO: eventually we should remove this in RELEASE builds
fixMissingSamplers(); fixMissingSamplers();
if (isUsingUboBatching() && !BufferAllocator::isValid(getAllocationId())) { if (mUseUboBatching && !BufferAllocator::isValid(getAllocationId())) {
return; return;
} }
@@ -399,7 +439,7 @@ void FMaterialInstance::use(FEngine::DriverApi& driver, Variant variant) const {
return; return;
} }
if (isUsingUboBatching() && !BufferAllocator::isValid(getAllocationId())) { if (mUseUboBatching && !BufferAllocator::isValid(getAllocationId())) {
return; return;
} }
@@ -436,7 +476,7 @@ void FMaterialInstance::assignUboAllocation(
const Handle<HwBufferObject>& ubHandle, const Handle<HwBufferObject>& ubHandle,
BufferAllocator::AllocationId id, BufferAllocator::AllocationId id,
BufferAllocator::allocation_size_t offset) { BufferAllocator::allocation_size_t offset) {
assert_invariant(isUsingUboBatching()); assert_invariant(mUseUboBatching);
mUboData = id; mUboData = id;
mUboOffset = offset; mUboOffset = offset;
+5 -1
View File
@@ -56,7 +56,8 @@ class FTexture;
class FMaterialInstance : public MaterialInstance { class FMaterialInstance : public MaterialInstance {
public: public:
FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name) noexcept; FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name,
FEngine::UboBatchingMode batchingMode) noexcept;
FMaterialInstance(FEngine& engine, FMaterialInstance const* other, const char* name); FMaterialInstance(FEngine& engine, FMaterialInstance const* other, const char* name);
FMaterialInstance(const FMaterialInstance& rhs) = delete; FMaterialInstance(const FMaterialInstance& rhs) = delete;
FMaterialInstance& operator=(const FMaterialInstance& rhs) = delete; FMaterialInstance& operator=(const FMaterialInstance& rhs) = delete;
@@ -66,6 +67,8 @@ public:
~FMaterialInstance() noexcept; ~FMaterialInstance() noexcept;
void terminate(FEngine& engine); void terminate(FEngine& engine);
void commitStreamUniformAssociations(FEngine::DriverApi& driver);
void commit(FEngine& engine) const; void commit(FEngine& engine) const;
@@ -285,6 +288,7 @@ private:
tsl::robin_map<backend::descriptor_binding_t, TextureParameter> mTextureParameters; tsl::robin_map<backend::descriptor_binding_t, TextureParameter> mTextureParameters;
mutable DescriptorSet mDescriptorSet; mutable DescriptorSet mDescriptorSet;
UniformBuffer mUniforms; UniformBuffer mUniforms;
bool mHasStreamUniformAssociations = false;
backend::PolygonOffset mPolygonOffset{}; backend::PolygonOffset mPolygonOffset{};
backend::StencilState mStencilState{}; backend::StencilState mStencilState{};
+20 -8
View File
@@ -292,11 +292,16 @@ void FRenderer::skipFrame(uint64_t vsyncSteadyClockTimeNano) {
// do this before engine.flush() // do this before engine.flush()
mResourceAllocator->gc(true); mResourceAllocator->gc(true);
// Run the component managers' GC in parallel
// WARNING: while doing this we can't access any component manager
auto& js = engine.getJobSystem();
auto *job = js.runAndRetain(jobs::createJob(js, nullptr, &FEngine::gc, &engine)); // gc all managers
engine.flush(); // flush command stream engine.flush(); // flush command stream
// Run the component managers' GC // make sure we're done with the gcs
auto& js = engine.getJobSystem(); js.waitAndRelease(job);
js.runAndWait(jobs::createJob(js, nullptr, &FEngine::gc, &engine)); // gc all managers
} }
bool FRenderer::shouldRenderFrame() const noexcept { bool FRenderer::shouldRenderFrame() const noexcept {
@@ -463,11 +468,16 @@ void FRenderer::endFrame() {
// do this before engine.flush() // do this before engine.flush()
mResourceAllocator->gc(); mResourceAllocator->gc();
// Run the component managers' GC in parallel
// WARNING: while doing this we can't access any component manager
auto& js = engine.getJobSystem();
auto *job = js.runAndRetain(jobs::createJob(js, nullptr, &FEngine::gc, &engine)); // gc all managers
engine.flush(); // flush command stream engine.flush(); // flush command stream
// Run the component managers' GC // make sure we're done with the gcs
auto& js = engine.getJobSystem(); js.waitAndRelease(job);
js.runAndWait(jobs::createJob(js, nullptr, &FEngine::gc, &engine)); // gc all managers
} }
void FRenderer::readPixels( void FRenderer::readPixels(
@@ -561,6 +571,7 @@ void FRenderer::renderStandaloneView(FView const* view) {
if (UTILS_LIKELY(view->getScene())) { if (UTILS_LIKELY(view->getScene())) {
mPreviousRenderTargets.clear(); mPreviousRenderTargets.clear();
mFrameId++;
// ask the engine to do what it needs to (e.g. updates light buffer, materials...) // ask the engine to do what it needs to (e.g. updates light buffer, materials...)
FEngine& engine = mEngine; FEngine& engine = mEngine;
@@ -570,7 +581,8 @@ void FRenderer::renderStandaloneView(FView const* view) {
driver.beginFrame( driver.beginFrame(
steady_clock::now().time_since_epoch().count(), steady_clock::now().time_since_epoch().count(),
mDisplayInfo.refreshRate == 0.0 ? 0 : int64_t( mDisplayInfo.refreshRate == 0.0 ? 0 : int64_t(
1'000'000'000.0 / mDisplayInfo.refreshRate), 0); 1'000'000'000.0 / mDisplayInfo.refreshRate),
mFrameId);
// because we don't have a "present" call, we use flush so the driver can submit // because we don't have a "present" call, we use flush so the driver can submit
// the command buffer; we do this before driver.endFrame() to mimic what would // the command buffer; we do this before driver.endFrame() to mimic what would
@@ -579,7 +591,7 @@ void FRenderer::renderStandaloneView(FView const* view) {
engine.submitFrame(); engine.submitFrame();
driver.endFrame(0); driver.endFrame(mFrameId);
// engine.flush() has already been called by renderInternal(), but we need an extra one // engine.flush() has already been called by renderInternal(), but we need an extra one
// for endFrame() above. This operation in actually not too heavy, it just kicks the // for endFrame() above. This operation in actually not too heavy, it just kicks the
+1 -1
View File
@@ -209,7 +209,7 @@ private:
backend::Handle<backend::HwRenderTarget> mRenderTargetHandle; backend::Handle<backend::HwRenderTarget> mRenderTargetHandle;
FSwapChain* mSwapChain = nullptr; FSwapChain* mSwapChain = nullptr;
size_t mCommandsHighWatermark = 0; size_t mCommandsHighWatermark = 0;
uint32_t mFrameId = 1; // id 0 is reserved for standalone views uint32_t mFrameId = 0;
FrameInfoManager mFrameInfoManager; FrameInfoManager mFrameInfoManager;
backend::TextureFormat mHdrTranslucent; backend::TextureFormat mHdrTranslucent;
backend::TextureFormat mHdrQualityMedium; backend::TextureFormat mHdrQualityMedium;
+4 -5
View File
@@ -39,7 +39,7 @@ using allocation_size_t = BufferAllocator::allocation_size_t;
// FenceManager // FenceManager
// ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------
void UboManager::FenceManager::track(DriverApi& driver, AllocationIdContainer&& allocationIds) { void UboManager::FenceManager::track(DriverApi& driver, std::unordered_set<AllocationId>&& allocationIds) {
if (allocationIds.empty()) { if (allocationIds.empty()) {
return; return;
} }
@@ -159,16 +159,15 @@ void UboManager::finishBeginFrame(DriverApi& driver) {
} }
void UboManager::endFrame(DriverApi& driver) { void UboManager::endFrame(DriverApi& driver) {
auto allocationIds = std::unordered_set<AllocationId> allocationIds;
FenceManager::AllocationIdContainer::with_capacity(mManagedInstances.size()); for (const auto* mi : mManagedInstances) {
for (const auto* mi: mManagedInstances) {
const AllocationId id = mi->getAllocationId(); const AllocationId id = mi->getAllocationId();
if (UTILS_UNLIKELY(!BufferAllocator::isValid(id))) { if (UTILS_UNLIKELY(!BufferAllocator::isValid(id))) {
continue; continue;
} }
mAllocator.acquireGpu(id); mAllocator.acquireGpu(id);
allocationIds.push_back(id); allocationIds.insert(id);
} }
mFenceManager.track(driver, std::move(allocationIds)); mFenceManager.track(driver, std::move(allocationIds));
+2 -3
View File
@@ -55,7 +55,6 @@ public:
class FenceManager { class FenceManager {
public: public:
using AllocationId = BufferAllocator::AllocationId; using AllocationId = BufferAllocator::AllocationId;
using AllocationIdContainer = utils::FixedCapacityVector<AllocationId>;
FenceManager() = default; FenceManager() = default;
~FenceManager() = default; ~FenceManager() = default;
@@ -66,7 +65,7 @@ public:
// Creates a new fence to track a set of allocation IDs for the current frame. // Creates a new fence to track a set of allocation IDs for the current frame.
// This marks the beginning of GPU's usage of these resources. // This marks the beginning of GPU's usage of these resources.
void track(backend::DriverApi& driver, AllocationIdContainer&& allocationIds); void track(backend::DriverApi& driver, std::unordered_set<AllocationId>&& allocationIds);
// Checks all tracked fences and invokes a callback for resources associated with // Checks all tracked fences and invokes a callback for resources associated with
@@ -81,7 +80,7 @@ public:
private: private:
// Not ideal, but we need to know which slots to decrement gpuUseCount for each frame. // Not ideal, but we need to know which slots to decrement gpuUseCount for each frame.
using FenceAndAllocations = using FenceAndAllocations =
std::pair<backend::Handle<backend::HwFence>, AllocationIdContainer>; std::pair<backend::Handle<backend::HwFence>, std::unordered_set<AllocationId>>;
std::vector<FenceAndAllocations> mFenceAllocationList; std::vector<FenceAndAllocations> mFenceAllocationList;
}; };
+1 -15
View File
@@ -13,25 +13,11 @@ set(GENERATION_ROOT ${CMAKE_CURRENT_BINARY_DIR})
set(RESOURCE_DIR "${GENERATION_ROOT}/resources") set(RESOURCE_DIR "${GENERATION_ROOT}/resources")
file(MAKE_DIRECTORY ${RESOURCE_DIR}) file(MAKE_DIRECTORY ${RESOURCE_DIR})
set(MATERIAL_DIR "${GENERATION_ROOT}/material")
file(MAKE_DIRECTORY ${MATERIAL_DIR})
set(SANDBOX_TRANSFORM_NAME_MAT ${CMAKE_CURRENT_SOURCE_DIR}/sandboxTransformName.mat)
set(TEST_MATERIAL_TRANSFORM_NAME_FILAMAT ${MATERIAL_DIR}/test_material_transformname.filamat)
add_custom_command(
OUTPUT ${TEST_MATERIAL_TRANSFORM_NAME_FILAMAT}
COMMAND matc ${MATC_BASE_FLAGS} -o ${TEST_MATERIAL_TRANSFORM_NAME_FILAMAT} ${SANDBOX_TRANSFORM_NAME_MAT}
MAIN_DEPENDENCY ${SANDBOX_TRANSFORM_NAME_MAT}
DEPENDS matc
COMMENT "Compiling material ${SANDBOX_TRANSFORM_NAME_MAT}"
)
get_resgen_vars(${RESOURCE_DIR} filament_test_resources) get_resgen_vars(${RESOURCE_DIR} filament_test_resources)
set(RESOURCE_BINS set(RESOURCE_BINS
${CMAKE_CURRENT_SOURCE_DIR}/test_material.filamat ${CMAKE_CURRENT_SOURCE_DIR}/test_material.filamat
${TEST_MATERIAL_TRANSFORM_NAME_FILAMAT} ${CMAKE_CURRENT_SOURCE_DIR}/test_material_transformname.filamat
) )
add_custom_command( add_custom_command(
Binary file not shown.
Binary file not shown.
+1
View File
@@ -11,6 +11,7 @@ OUTPUT_DIR="filament/test/"
./build.sh -p desktop release matc ./build.sh -p desktop release matc
MATC=out/cmake-release/tools/matc/matc MATC=out/cmake-release/tools/matc/matc
${MATC} --platform all --api all -o ${OUTPUT_DIR}/test_material.filamat samples/materials/sandboxLit.mat ${MATC} --platform all --api all -o ${OUTPUT_DIR}/test_material.filamat samples/materials/sandboxLit.mat
${MATC} --platform all --api all -o ${OUTPUT_DIR}/test_material_transformname.filamat samples/materials/sandboxTransformName.mat
set +x set +x
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec| Pod::Spec.new do |spec|
spec.name = "Filament" spec.name = "Filament"
spec.version = "1.68.0" spec.version = "1.67.1"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament" spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC." spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0" spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.68.0/filament-v1.68.0-ios.tgz" } spec.source = { :http => "https://github.com/google/filament/releases/download/v1.67.1/filament-v1.67.1-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = { spec.pod_target_xcconfig = {
@@ -28,7 +28,7 @@
namespace filament { namespace filament {
// update this when a new version of filament wouldn't work with older materials // update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 68; static constexpr size_t MATERIAL_VERSION = 67;
/** /**
* Supported shading models * Supported shading models
@@ -170,6 +170,10 @@ public:
// negative value if name doesn't exist or Panic if exceptions are enabled // negative value if name doesn't exist or Panic if exceptions are enabled
ssize_t getFieldOffset(std::string_view name, size_t index) const; ssize_t getFieldOffset(std::string_view name, size_t index) const;
// returns offset in bytes of the transform matrix for the given external texture binding
// returns -1 if the field doesn't exist
ssize_t getTransformFieldOffset(uint8_t binding) const;
FieldInfo const* getFieldInfo(std::string_view name) const; FieldInfo const* getFieldInfo(std::string_view name) const;
bool hasField(std::string_view name) const noexcept { bool hasField(std::string_view name) const noexcept {
@@ -197,6 +201,7 @@ private:
utils::CString mName; utils::CString mName;
utils::FixedCapacityVector<FieldInfo> mFieldInfoList; utils::FixedCapacityVector<FieldInfo> mFieldInfoList;
std::unordered_map<std::string_view , uint32_t> mInfoMap; std::unordered_map<std::string_view , uint32_t> mInfoMap;
std::unordered_map<uint8_t, uint32_t> mTransformOffsetMap;
uint32_t mSize = 0; // size in bytes rounded to multiple of 4 uint32_t mSize = 0; // size in bytes rounded to multiple of 4
Alignment mAlignment = Alignment::std140; Alignment mAlignment = Alignment::std140;
Target mTarget = Target::UNIFORM; Target mTarget = Target::UNIFORM;
@@ -122,6 +122,8 @@ BufferInterfaceBlock::BufferInterfaceBlock(Builder const& builder) noexcept
auto& uniformsInfoList = mFieldInfoList; auto& uniformsInfoList = mFieldInfoList;
auto& transformOffsetMap = mTransformOffsetMap;
uint32_t i = 0; uint32_t i = 0;
uint16_t offset = 0; uint16_t offset = 0;
for (auto const& e : builder.mEntries) { for (auto const& e : builder.mEntries) {
@@ -148,6 +150,11 @@ BufferInterfaceBlock::BufferInterfaceBlock(Builder const& builder) noexcept
// record this uniform info // record this uniform info
infoMap[{ info.name.data(), info.name.size() }] = i; infoMap[{ info.name.data(), info.name.size() }] = i;
// record the transform offset if this is an external sampler
if (e.associatedSampler != 0) {
transformOffsetMap[e.associatedSampler] = offset;
}
// advance offset to next slot // advance offset to next slot
offset += stride * std::max(1u, e.size); offset += stride * std::max(1u, e.size);
++i; ++i;
@@ -163,6 +170,14 @@ ssize_t BufferInterfaceBlock::getFieldOffset(std::string_view name, size_t index
return (ssize_t)info->getBufferOffset(index); return (ssize_t)info->getBufferOffset(index);
} }
ssize_t BufferInterfaceBlock::getTransformFieldOffset(uint8_t binding) const {
auto pos = mTransformOffsetMap.find(binding);
if (pos == mTransformOffsetMap.end()) {
return -1;
}
return (ssize_t)pos->second * sizeof(uint32_t);
}
BufferInterfaceBlock::FieldInfo const* BufferInterfaceBlock::getFieldInfo( BufferInterfaceBlock::FieldInfo const* BufferInterfaceBlock::getFieldInfo(
std::string_view name) const { std::string_view name) const {
auto pos = mInfoMap.find(name); auto pos = mInfoMap.find(name);
+3 -5
View File
@@ -17,6 +17,9 @@
#ifndef FILAGUI_IMGUIHELPER_H_ #ifndef FILAGUI_IMGUIHELPER_H_
#define FILAGUI_IMGUIHELPER_H_ #define FILAGUI_IMGUIHELPER_H_
#include <vector>
#include <functional>
#include <filament/Engine.h> #include <filament/Engine.h>
#include <filament/IndexBuffer.h> #include <filament/IndexBuffer.h>
#include <filament/Material.h> #include <filament/Material.h>
@@ -29,10 +32,6 @@
#include <utils/Entity.h> #include <utils/Entity.h>
#include <utils/Path.h> #include <utils/Path.h>
#include <functional>
#include <vector>
#include <unordered_set>
struct ImDrawData; struct ImDrawData;
struct ImGuiIO; struct ImGuiIO;
struct ImGuiContext; struct ImGuiContext;
@@ -101,7 +100,6 @@ public:
filament::TextureSampler mSampler; filament::TextureSampler mSampler;
bool mFlipVertical = false; bool mFlipVertical = false;
utils::Path mSettingsPath; utils::Path mSettingsPath;
std::unordered_set<filament::Texture*> mImGuiTextures;
}; };
} // namespace filagui } // namespace filagui
+1 -49
View File
@@ -148,12 +148,6 @@ ImGuiHelper::~ImGuiHelper() {
mEngine->destroy(ib); mEngine->destroy(ib);
} }
// These are textures that have been created by imgui but were not explicitly freed before
// shutdown.
for (auto itextures: mImGuiTextures) {
mEngine->destroy(itextures);
}
EntityManager& em = utils::EntityManager::get(); EntityManager& em = utils::EntityManager::get();
em.destroy(mRenderable); em.destroy(mRenderable);
em.destroy(mCameraEntity); em.destroy(mCameraEntity);
@@ -195,48 +189,6 @@ void ImGuiHelper::render(float timeStepInSeconds, Callback imguiCommands) {
void ImGuiHelper::processImGuiCommands(ImDrawData* commands, const ImGuiIO& io) { void ImGuiHelper::processImGuiCommands(ImDrawData* commands, const ImGuiIO& io) {
ImGui::SetCurrentContext(mImGuiContext); ImGui::SetCurrentContext(mImGuiContext);
if (commands->Textures != nullptr) {
for (ImTextureData* tex : *commands->Textures) {
if (tex->Status == ImTextureStatus_OK) {
continue;
} else if (tex->Status == ImTextureStatus_WantCreate) {
IM_ASSERT(tex->TexID == 0 && tex->BackendUserData == nullptr);
Texture* ftex = Texture::Builder()
.width(tex->Width)
.height(tex->Height)
.levels(1)
.format(Texture::InternalFormat::RGBA8)
.sampler(Texture::Sampler::SAMPLER_2D)
.build(*mEngine);
IM_ASSERT(tex->Format == ImTextureFormat_RGBA32);
const int size = tex->Width * tex->Height * 4;
Texture::PixelBufferDescriptor pb(tex->GetPixels(), size,
Texture::Format::RGBA,
Texture::Type::UBYTE);
ftex->setImage(*mEngine, 0, std::move(pb));
tex->SetTexID((ImTextureID)ftex);
tex->SetStatus(ImTextureStatus_OK);
mImGuiTextures.insert(ftex);
} else if (tex->Status == ImTextureStatus_WantUpdates) {
const int size = tex->Width * tex->Height * 4;
Texture::PixelBufferDescriptor pb(tex->GetPixels(), size,
Texture::Format::RGBA,
Texture::Type::UBYTE);
filament::Texture* ftex = (filament::Texture*)tex->TexID;
ftex->setImage(*mEngine, 0, std::move(pb));
tex->SetStatus(ImTextureStatus_OK);
} else if (tex->Status == ImTextureStatus_WantDestroy &&
tex->UnusedFrames > 0) {
filament::Texture* ftex = (filament::Texture*)tex->TexID;
mEngine->destroy(ftex);
tex->SetTexID(ImTextureID_Invalid);
tex->SetStatus(ImTextureStatus_Destroyed);
mImGuiTextures.erase(ftex);
}
}
}
mHasSynced = false; mHasSynced = false;
auto& rcm = mEngine->getRenderableManager(); auto& rcm = mEngine->getRenderableManager();
@@ -276,7 +228,7 @@ void ImGuiHelper::processImGuiCommands(ImDrawData* commands, const ImGuiIO& io)
if (pcmd.UserCallback) { if (pcmd.UserCallback) {
pcmd.UserCallback(cmds, &pcmd); pcmd.UserCallback(cmds, &pcmd);
} else { } else {
auto texture = (Texture const*)pcmd.GetTexID(); auto texture = (Texture const*)pcmd.TextureId;
MaterialInstance* materialInstance; MaterialInstance* materialInstance;
#ifdef __ANDROID__ #ifdef __ANDROID__
if (texture && texture->getTarget() == Texture::Sampler::SAMPLER_EXTERNAL) { if (texture && texture->getTarget() == Texture::Sampler::SAMPLER_EXTERNAL) {

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