Compare commits
28
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4436ffaaab | ||
|
|
c98038b2d7 | ||
|
|
849c4bb8c5 | ||
|
|
ee64322d76 | ||
|
|
79d65a768a | ||
|
|
37610dc8f3 | ||
|
|
2e31dc20e4 | ||
|
|
ae3d98fb47 | ||
|
|
a938a790fa | ||
|
|
71d556dddf | ||
|
|
d61227b2fd | ||
|
|
612a6b00e4 | ||
|
|
b3353b518c | ||
|
|
0b44761d43 | ||
|
|
c7979e4023 | ||
|
|
e00904e100 | ||
|
|
96e2366569 | ||
|
|
e76624862d | ||
|
|
c5c83a61d3 | ||
|
|
ccf0d6205f | ||
|
|
f482c1d702 | ||
|
|
a4c056e822 | ||
|
|
f5071b2aae | ||
|
|
0fa13ee43d | ||
|
|
1d2b8a08fa | ||
|
|
0c06744450 | ||
|
|
4ada29fbc3 | ||
|
|
76182231dd |
@@ -21,8 +21,9 @@ jobs:
|
||||
- name: Run update script
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.FILAMENTBOT_TOKEN }}
|
||||
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
|
||||
run: |
|
||||
GOLDEN_BRANCH=$(echo "${{ steps.get_commit_msg.outputs.msg }}" | python3 test/renderdiff/src/commit_msg.py)
|
||||
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 test/renderdiff/src/commit_msg.py)
|
||||
COMMIT_HASH="${{ steps.get_commit_msg.outputs.hash }}"
|
||||
if [[ "${GOLDEN_BRANCH}" != "main" ]]; then
|
||||
git config --global user.email "filament.bot@gmail.com"
|
||||
|
||||
@@ -134,18 +134,17 @@ jobs:
|
||||
env:
|
||||
COMMIT_MESSAGE: ${{ steps.get_commit_msg.outputs.msg }}
|
||||
run: |
|
||||
ls ./gltf/Models
|
||||
TEST_DIR=test/renderdiff
|
||||
source ${TEST_DIR}/src/preamble.sh
|
||||
start_
|
||||
set -eux
|
||||
GOLDEN_BRANCH=$(echo "${COMMIT_MESSAGE}" | python3 ${TEST_DIR}/src/commit_msg.py)
|
||||
bash ${TEST_DIR}/generate.sh && \
|
||||
python3 ${TEST_DIR}/src/golden_manager.py \
|
||||
bash ${TEST_DIR}/generate.sh
|
||||
python3 ${TEST_DIR}/src/golden_manager.py \
|
||||
--branch=${GOLDEN_BRANCH} \
|
||||
--output=${GOLDEN_OUTPUT_DIR}
|
||||
|
||||
# Note that we need to upload the output even if comparison fails, so we undo `set -ex`
|
||||
end_
|
||||
# Note that we need to upload the output even if comparison fails, so we undo `set -eux`
|
||||
set +eux
|
||||
|
||||
python3 ${TEST_DIR}/src/compare.py \
|
||||
--src=${GOLDEN_OUTPUT_DIR} \
|
||||
@@ -158,6 +157,7 @@ jobs:
|
||||
cat compare_output.txt >> "$GITHUB_OUTPUT"
|
||||
echo "$DELIMITER" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
shell: bash
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: presubmit-renderdiff-result
|
||||
|
||||
+4
-2
@@ -872,7 +872,9 @@ add_subdirectory(${LIBRARIES}/utils)
|
||||
add_subdirectory(${LIBRARIES}/viewer)
|
||||
add_subdirectory(${FILAMENT}/shaders)
|
||||
add_subdirectory(${EXTERNAL}/abseil/tnt)
|
||||
add_subdirectory(${EXTERNAL}/basisu/tnt)
|
||||
# Add zstd before basisu to force it to use the external zstd target,
|
||||
# preventing a duplicate symbol conflict with its bundled version.
|
||||
add_subdirectory(${EXTERNAL}/zstd/tnt)
|
||||
add_subdirectory(${EXTERNAL}/civetweb/tnt)
|
||||
add_subdirectory(${EXTERNAL}/imgui/tnt)
|
||||
add_subdirectory(${EXTERNAL}/robin-map/tnt)
|
||||
@@ -886,7 +888,7 @@ add_subdirectory(${EXTERNAL}/jsmn/tnt)
|
||||
add_subdirectory(${EXTERNAL}/stb/tnt)
|
||||
add_subdirectory(${EXTERNAL}/getopt)
|
||||
add_subdirectory(${EXTERNAL}/perfetto/tnt)
|
||||
add_subdirectory(${EXTERNAL}/zstd/tnt)
|
||||
add_subdirectory(${EXTERNAL}/basisu/tnt)
|
||||
|
||||
|
||||
# Note that this has to be placed after mikktspace in order for combine_static_libs to work.
|
||||
|
||||
@@ -100,7 +100,7 @@ buildscript {
|
||||
'kotlin': '2.0.21',
|
||||
'kotlin_coroutines': '1.9.0',
|
||||
'buildTools': '35.0.0',
|
||||
'ndk': '27.0.11718014',
|
||||
'ndk': '29.0.14206865',
|
||||
'androidx_core': '1.13.1',
|
||||
'androidx_annotations': '1.9.0'
|
||||
]
|
||||
|
||||
@@ -25,7 +25,7 @@ using namespace filament;
|
||||
|
||||
extern "C" JNIEXPORT jlong JNICALL
|
||||
Java_com_google_android_filament_Material_nBuilderBuild(JNIEnv *env, jclass,
|
||||
jlong nativeEngine, jobject buffer_, jint size, jint shBandCount, jint shadowQuality) {
|
||||
jlong nativeEngine, jobject buffer_, jint size, jint shBandCount, jint shadowQuality, jint uboBatchingMode) {
|
||||
Engine* engine = (Engine*) nativeEngine;
|
||||
AutoBuffer buffer(env, buffer_, size);
|
||||
auto builder = Material::Builder();
|
||||
@@ -33,6 +33,7 @@ Java_com_google_android_filament_Material_nBuilderBuild(JNIEnv *env, jclass,
|
||||
builder.sphericalHarmonicsBandCount(shBandCount);
|
||||
}
|
||||
builder.shadowSamplingQuality((Material::Builder::ShadowSamplingQuality)shadowQuality);
|
||||
builder.uboBatching((Material::UboBatchingMode)uboBatchingMode);
|
||||
Material* material = builder
|
||||
.package(buffer.getData(), buffer.getSize())
|
||||
.build(*engine);
|
||||
|
||||
@@ -261,6 +261,20 @@ public class Material {
|
||||
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 {
|
||||
/** Directional lighting */
|
||||
public static int DIRECTIONAL_LIGHTING = 0x01;
|
||||
@@ -372,6 +386,7 @@ public class Material {
|
||||
private int mSize;
|
||||
private int mShBandCount = 0;
|
||||
private ShadowSamplingQuality mShadowSamplingQuality = ShadowSamplingQuality.LOW;
|
||||
private UboBatchingMode mUboBatchingMode = UboBatchingMode.DEFAULT;
|
||||
|
||||
|
||||
/**
|
||||
@@ -416,6 +431,17 @@ public class Material {
|
||||
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.
|
||||
*
|
||||
@@ -428,7 +454,7 @@ public class Material {
|
||||
@NonNull
|
||||
public Material build(@NonNull Engine engine) {
|
||||
long nativeMaterial = nBuilderBuild(engine.getNativeObject(),
|
||||
mBuffer, mSize, mShBandCount, mShadowSamplingQuality.ordinal());
|
||||
mBuffer, mSize, mShBandCount, mShadowSamplingQuality.ordinal(), mUboBatchingMode.ordinal());
|
||||
if (nativeMaterial == 0) throw new IllegalStateException("Couldn't create Material");
|
||||
return new Material(nativeMaterial);
|
||||
}
|
||||
@@ -1094,7 +1120,7 @@ public class Material {
|
||||
mNativeObject = 0;
|
||||
}
|
||||
|
||||
private static native long nBuilderBuild(long nativeEngine, @NonNull Buffer buffer, int size, int shBandCount, int shadowQuality);
|
||||
private static native long nBuilderBuild(long nativeEngine, @NonNull Buffer buffer, int size, int shBandCount, int shadowQuality, int uboBatchingMode);
|
||||
private static native long nCreateInstance(long nativeMaterial);
|
||||
private static native long nCreateInstanceWithName(long nativeMaterial, @NonNull String name);
|
||||
private static native long nGetDefaultInstance(long nativeMaterial);
|
||||
|
||||
@@ -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
|
||||
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 ".")}
|
||||
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
|
||||
ISSUE_CLEAN=false
|
||||
|
||||
@@ -18,8 +18,8 @@ if [[ "$GITHUB_WORKFLOW" ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Unless explicitly specified, NDK version will be set to match exactly the required one
|
||||
FILAMENT_NDK_VERSION=${GITHUB_NDK_VERSION:-27.0.11718014}
|
||||
# Unless explicitly specified, NDK version will be selected as highest available version within same major release chain
|
||||
FILAMENT_NDK_VERSION=$(cat `dirname $0`/../common/versions | grep GITHUB_NDK_VERSION | sed s/GITHUB_NDK_VERSION=//g)
|
||||
|
||||
(! 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" &&
|
||||
|
||||
@@ -3,7 +3,7 @@ GITHUB_CMAKE_VERSION=3.22.1
|
||||
GITHUB_NINJA_VERSION=1.10.2
|
||||
GITHUB_MESA_VERSION=24.2.1
|
||||
GITHUB_LLVM_VERSION=16
|
||||
GITHUB_NDK_VERSION=27.0.11718014
|
||||
GITHUB_NDK_VERSION=29.0.14206865
|
||||
GITHUB_EMSDK_VERSION=3.1.60
|
||||
GITHUB_VULKANSDK_VERSION=1.4.321.0
|
||||
GITHUB_GLTF_SAMPLE_ASSETS_COMMIT=d441dfdb87413ff412c620849a649d61789a470f
|
||||
GITHUB_GLTF_SAMPLE_ASSETS_COMMIT=d441dfdb87413ff412c620849a649d61789a470f
|
||||
|
||||
@@ -75,7 +75,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
# C_FLAGS += -Wl,-pie
|
||||
# CXX_FLAGS += -lstdc++
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE -mcpu=cortex-a57" CACHE STRING "Toolchain CFLAGS")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIE -march=armv8-a -mtune=cortex-a78" CACHE STRING "Toolchain CFLAGS")
|
||||
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_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -static-libstdc++" CACHE STRING "Toolchain LDFLAGS")
|
||||
|
||||
@@ -87,7 +87,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||
# for hardfp: CFLAGS must have -mhard-float
|
||||
# LDFLAGS must have -Wl,--no-warn-mismatch
|
||||
#
|
||||
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_C_FLAGS "${CMAKE_C_FLAGS} -mthumb -march=armv7-a -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_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")
|
||||
|
||||
+2
-2
@@ -181,7 +181,7 @@ important for <code>matc</code> (material compiler).</p>
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.67.1'
|
||||
implementation 'com.google.android.filament:filament-android:1.68.0'
|
||||
}
|
||||
</code></pre>
|
||||
<p>Here are all the libraries available in the group <code>com.google.android.filament</code>:</p>
|
||||
@@ -196,7 +196,7 @@ dependencies {
|
||||
</div>
|
||||
<h3 id="ios"><a class="header" href="#ios">iOS</a></h3>
|
||||
<p>iOS projects can use CocoaPods to install the latest release:</p>
|
||||
<pre><code class="language-shell">pod 'Filament', '~> 1.67.1'
|
||||
<pre><code class="language-shell">pod 'Filament', '~> 1.68.0'
|
||||
</code></pre>
|
||||
<h2 id="documentation"><a class="header" href="#documentation">Documentation</a></h2>
|
||||
<ul>
|
||||
|
||||
+268
-270
File diff suppressed because one or more lines are too long
+162
-164
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -10,6 +10,6 @@ sortedcontainers==2.4.0
|
||||
trio==0.31.0
|
||||
trio-websocket==0.12.2
|
||||
typing_extensions==4.15.0
|
||||
urllib3==2.5.0
|
||||
urllib3==2.6.0
|
||||
websocket-client==1.9.0
|
||||
wsproto==1.2.0
|
||||
|
||||
@@ -318,6 +318,8 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
src/webgpu/WebGPURenderPrimitive.h
|
||||
src/webgpu/WebGPURenderTarget.cpp
|
||||
src/webgpu/WebGPURenderTarget.h
|
||||
src/webgpu/WebGPUStagePool.cpp
|
||||
src/webgpu/WebGPUStagePool.h
|
||||
src/webgpu/WebGPUStrings.h
|
||||
src/webgpu/WebGPUSwapChain.cpp
|
||||
src/webgpu/WebGPUSwapChain.h
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#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,6 +1713,8 @@ using FrameTimestamps = Platform::FrameTimestamps;
|
||||
|
||||
using CompositorTiming = Platform::CompositorTiming;
|
||||
|
||||
using AsynchronousMode = Platform::AsynchronousMode;
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
template<> struct utils::EnableBitMaskOperators<filament::backend::ShaderStageFlags>
|
||||
|
||||
@@ -255,6 +255,28 @@ public:
|
||||
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 {
|
||||
/**
|
||||
* Size of handle arena in bytes. Setting to 0 indicates default value is to be used.
|
||||
@@ -324,6 +346,11 @@ public:
|
||||
* - VulkanPlatform
|
||||
*/
|
||||
bool vulkanEnableStagingBufferBypass = false;
|
||||
|
||||
/**
|
||||
* Asynchronous mode for the engine. Defines how asynchronous operations are handled.
|
||||
*/
|
||||
AsynchronousMode asynchronousMode = AsynchronousMode::NONE;
|
||||
};
|
||||
|
||||
Platform() noexcept;
|
||||
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "private/backend/Driver.h"
|
||||
|
||||
#include <backend/BufferDescriptor.h>
|
||||
#include <backend/BufferObjectStreamDescriptor.h>
|
||||
#include <backend/CallbackHandler.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
@@ -51,7 +51,6 @@
|
||||
namespace filament::backend {
|
||||
|
||||
class BufferDescriptor;
|
||||
class BufferObjectStreamDescriptor;
|
||||
class CallbackHandler;
|
||||
class PixelBufferDescriptor;
|
||||
class Program;
|
||||
|
||||
@@ -392,10 +392,6 @@ DECL_DRIVER_API_N(updateBufferObject,
|
||||
backend::BufferDescriptor&&, data,
|
||||
uint32_t, byteOffset)
|
||||
|
||||
DECL_DRIVER_API_N(registerBufferObjectStreams,
|
||||
backend::BufferObjectHandle, boh,
|
||||
backend::BufferObjectStreamDescriptor&&, streams)
|
||||
|
||||
DECL_DRIVER_API_N(updateBufferObjectUnsynchronized,
|
||||
backend::BufferObjectHandle, boh,
|
||||
backend::BufferDescriptor&&, data,
|
||||
|
||||
@@ -1144,8 +1144,12 @@ bool MetalDriver::isRenderTargetFormatSupported(TextureFormat format) {
|
||||
bool MetalDriver::isFrameBufferFetchSupported() {
|
||||
// FrameBuffer fetch is achievable via "programmable blending" in Metal, and only supported on
|
||||
// Apple GPUs with readWriteTextureSupport.
|
||||
return mContext->highestSupportedGpuFamily.apple >= 1 &&
|
||||
mContext->device.readWriteTextureSupport;
|
||||
// On macOS, framebuffer fetch requires MSL 2.3, which is only available with macOS 11.0.
|
||||
if (@available(macOS 11.0, *)) {
|
||||
return mContext->highestSupportedGpuFamily.apple >= 1 &&
|
||||
mContext->device.readWriteTextureSupport;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MetalDriver::isFrameBufferFetchMultiSampleSupported() {
|
||||
@@ -1286,10 +1290,6 @@ void MetalDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&
|
||||
scheduleDestroy(std::move(data));
|
||||
}
|
||||
|
||||
void MetalDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh, BufferObjectStreamDescriptor&& streams) {
|
||||
// Noop
|
||||
}
|
||||
|
||||
void MetalDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& data,
|
||||
uint32_t byteOffset) {
|
||||
FILAMENT_CHECK_PRECONDITION(!isInRenderPass(mContext))
|
||||
|
||||
@@ -111,9 +111,22 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
|
||||
id<MTLLibrary> library = nil;
|
||||
switch (program.getShaderLanguage()) {
|
||||
case ShaderLanguage::MSL: {
|
||||
// By default, Metal uses the most recent language version.
|
||||
MTLCompileOptions* options = [MTLCompileOptions new];
|
||||
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');
|
||||
// the shader string is null terminated and the length includes the null character
|
||||
|
||||
@@ -291,8 +291,6 @@ void NoopDriver::updateBufferObject(Handle<HwBufferObject> ibh, BufferDescriptor
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void NoopDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh, BufferObjectStreamDescriptor&& streams) { }
|
||||
|
||||
void NoopDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> ibh, BufferDescriptor&& p,
|
||||
uint32_t byteOffset) {
|
||||
scheduleDestroy(std::move(p));
|
||||
|
||||
@@ -39,7 +39,6 @@
|
||||
#include <backend/Platform.h>
|
||||
#include <backend/Program.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
#include <backend/BufferObjectStreamDescriptor.h>
|
||||
|
||||
#include "private/backend/CommandStream.h"
|
||||
#include "private/backend/Dispatcher.h"
|
||||
@@ -2837,38 +2836,6 @@ void OpenGLDriver::updateIndexBuffer(
|
||||
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(
|
||||
Handle<HwBufferObject> boh, BufferDescriptor&& bd, uint32_t const byteOffset) {
|
||||
DEBUG_MARKER()
|
||||
@@ -2882,19 +2849,6 @@ void OpenGLDriver::updateBufferObject(
|
||||
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())) {
|
||||
assert_invariant(bo->gl.buffer);
|
||||
memcpy(static_cast<uint8_t*>(bo->gl.buffer) + byteOffset, bd.buffer, bd.size);
|
||||
|
||||
@@ -36,7 +36,6 @@
|
||||
#include <backend/Platform.h>
|
||||
#include <backend/Program.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
#include <backend/BufferObjectStreamDescriptor.h>
|
||||
|
||||
#include "private/backend/Driver.h"
|
||||
#include "private/backend/HandleAllocator.h"
|
||||
@@ -394,8 +393,6 @@ private:
|
||||
// the must be accessed from the user thread only
|
||||
std::vector<GLStream*> mStreamsWithPendingAcquiredImage;
|
||||
|
||||
std::unordered_map<GLuint, BufferObjectStreamDescriptor> mStreamUniformDescriptors;
|
||||
|
||||
void attachStream(GLTexture* t, GLStream* stream);
|
||||
void detachStream(GLTexture* t) noexcept;
|
||||
void replaceStream(GLTexture* t, GLStream* stream) noexcept;
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
*/
|
||||
|
||||
|
||||
#if defined(__ANDROID__) || defined(FILAMENT_USE_EXTERNAL_GLES3) || defined(__EMSCRIPTEN__)
|
||||
#if defined(__ANDROID__) || defined(FILAMENT_USE_EXTERNAL_GLES3) || defined(__EMSCRIPTEN__) || defined(FILAMENT_SUPPORTS_EGL_ON_LINUX)
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
# include <GLES3/gl3.h>
|
||||
|
||||
@@ -178,20 +178,30 @@ void PlatformEGLAndroid::beginFrame(
|
||||
int64_t const monotonic_clock_ns,
|
||||
int64_t refreshIntervalNs,
|
||||
uint32_t const frameId) noexcept {
|
||||
// associate the user frameid with the system frame id
|
||||
setPresentFrameId(mCurrentDrawSwapChain, frameId);
|
||||
// if frameId is 0, it means we're not associated to a particular frame, which is the case
|
||||
// for standalone views. And in this case we skip the performance hint (since we wouldn't get
|
||||
// the right timing anyway as well as the frame info timing collections)
|
||||
|
||||
if (mPerformanceHintSession.isValid()) {
|
||||
// associate the user frameid with the system frame id.
|
||||
if (frameId && mCurrentDrawSwapChain) {
|
||||
// mCurrentDrawSwapChain could be null if we're called from renderStandaloneView
|
||||
setPresentFrameId(mCurrentDrawSwapChain, frameId);
|
||||
}
|
||||
|
||||
if (frameId && mPerformanceHintSession.isValid()) {
|
||||
if (refreshIntervalNs <= 0) {
|
||||
// we're not provided with a target time, use the display period, if everything fails,
|
||||
// assume 16.67ms
|
||||
refreshIntervalNs = 16'666'667;
|
||||
|
||||
CompositorTiming compositorTiming{};
|
||||
bool const hasCompositorTiming =
|
||||
queryCompositorTiming(mCurrentDrawSwapChain, &compositorTiming);
|
||||
if (hasCompositorTiming && compositorTiming.compositeInterval > 0) {
|
||||
refreshIntervalNs = compositorTiming.compositeInterval;
|
||||
if (mCurrentDrawSwapChain) {
|
||||
// mCurrentDrawSwapChain could be null if we're called from renderStandaloneView
|
||||
CompositorTiming compositorTiming{};
|
||||
bool const hasCompositorTiming =
|
||||
queryCompositorTiming(mCurrentDrawSwapChain, &compositorTiming);
|
||||
if (hasCompositorTiming && compositorTiming.compositeInterval > 0) {
|
||||
refreshIntervalNs = compositorTiming.compositeInterval;
|
||||
}
|
||||
}
|
||||
}
|
||||
mStartTimeOfActualWork = clock::time_point(std::chrono::nanoseconds(monotonic_clock_ns));
|
||||
@@ -440,6 +450,10 @@ Platform::SwapChain* PlatformEGLAndroid::createSwapChain(
|
||||
void PlatformEGLAndroid::destroySwapChain(SwapChain* swapChain) noexcept {
|
||||
if (swapChain) {
|
||||
SwapChainEGLAndroid* const sc = static_cast<SwapChainEGLAndroid*>(swapChain);
|
||||
if (mCurrentDrawSwapChain == sc) {
|
||||
// don't keep a dangling pointer around
|
||||
mCurrentDrawSwapChain = nullptr;
|
||||
}
|
||||
sc->terminate(*this);
|
||||
delete sc;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include <backend/platforms/PlatformEGLHeadless.h>
|
||||
|
||||
#include "opengl/GLUtils.h"
|
||||
#include <bluegl/BlueGL.h>
|
||||
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
@@ -35,16 +35,31 @@ PlatformEGLHeadless::PlatformEGLHeadless() noexcept
|
||||
}
|
||||
|
||||
bool PlatformEGLHeadless::isOpenGL() const noexcept {
|
||||
return true;
|
||||
#if defined(BACKEND_OPENGL_VERSION_GL)
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // defined(BACKEND_OPENGL_VERSION_GL)
|
||||
}
|
||||
|
||||
backend::Driver* PlatformEGLHeadless::createDriver(void* sharedContext,
|
||||
const Platform::DriverConfig& driverConfig) {
|
||||
EGLBoolean bindAPI = eglBindAPI(EGL_OPENGL_API);
|
||||
if (UTILS_UNLIKELY(!bindAPI)) {
|
||||
LOG(ERROR) << "eglBindAPI EGL_OPENGL_API failed";
|
||||
auto bindApiHelper = [](EGLenum api, const char* errorString) -> bool {
|
||||
EGLBoolean bindAPI = eglBindAPI(api);
|
||||
if (UTILS_UNLIKELY(bindAPI == EGL_FALSE || bindAPI == EGL_BAD_PARAMETER)) {
|
||||
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;
|
||||
}
|
||||
|
||||
int bindBlueGL = bluegl::bind();
|
||||
if (UTILS_UNLIKELY(bindBlueGL != 0)) {
|
||||
LOG(ERROR) << "bluegl bind failed";
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
#include <backend/BufferDescriptor.h>
|
||||
#include <backend/BufferObjectStreamDescriptor.h>
|
||||
#include <backend/DescriptorSetOffsetArray.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/PipelineState.h>
|
||||
@@ -434,10 +433,6 @@ io::ostream& operator<<(io::ostream& out, BufferDescriptor const& b) {
|
||||
<< ", 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) {
|
||||
BufferDescriptor const& base = static_cast<BufferDescriptor const&>(b);
|
||||
return out << "PixelBufferDescriptor{ " << base
|
||||
|
||||
@@ -62,8 +62,9 @@ void VulkanBufferProxy::loadFromCpu(VulkanCommandBuffer& commands, const void* c
|
||||
// a memcpy if its available.
|
||||
bool const isStaticOrShared =
|
||||
any(mUsage & (BufferUsage::STATIC | BufferUsage::SHARED_WRITE_BIT));
|
||||
bool const useMemcpy = (isUniformAndAvailable || isStaticOrShared) && isMemcopyable &&
|
||||
mStagingBufferBypassEnabled;
|
||||
bool const useMemcpy =
|
||||
((isUniformAndAvailable && mStagingBufferBypassEnabled) || isStaticOrShared) &&
|
||||
isMemcopyable;
|
||||
if (useMemcpy) {
|
||||
char* dest = static_cast<char*>(mBuffer->getGpuBuffer()->allocationInfo.pMappedData) +
|
||||
byteOffset;
|
||||
|
||||
@@ -150,31 +150,6 @@ static CallbackHandler::Callback syncCallbackWrapper = [](void* userData) {
|
||||
cbData->cb(cbData->sync, cbData->userData);
|
||||
};
|
||||
|
||||
// This needs to be discussed because Vulkan has different default Matrix formats
|
||||
// right now this is following GL.
|
||||
static void copyMat3f(void* addr, size_t const offset, const math::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
|
||||
}
|
||||
|
||||
}// anonymous namespace
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
@@ -447,12 +422,14 @@ void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
int64_t refreshIntervalNs, uint32_t frameId) {
|
||||
FVK_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
|
||||
|
||||
if (mCurrentSwapChain) { // This should be guaranteed
|
||||
// if frameId is 0, it means we're not associated to a particular frame, which is the case
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Check if any command have finished and reset all its used resources. The resources
|
||||
// wont be destroyed but their reference count will decreased if the command is already
|
||||
// won't be destroyed but their reference count will be decreased if the command is already
|
||||
// completed.
|
||||
//
|
||||
// This will let us check if any VulkanBuffer is currently in flight or not.
|
||||
@@ -528,10 +505,7 @@ void VulkanDriver::copyToMemoryMappedBuffer(MemoryMappedBufferHandle mmbh, size_
|
||||
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.
|
||||
// On UMA systems, this is a memcpy into the memory-mapped buffer, otherwise use a staging buffer.
|
||||
updateBufferObject(mmb->boh, std::move(data), mmb->offset + offset);
|
||||
}
|
||||
|
||||
@@ -1182,7 +1156,7 @@ void VulkanDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
|
||||
}
|
||||
|
||||
Handle<HwStream> VulkanDriver::createStreamNative(void* nativeStream, utils::ImmutableCString tag) {
|
||||
FILAMENT_CHECK_PRECONDITION(false) << "createStreamNative not supported in Vulkan.";
|
||||
FVK_LOGW << "createStreamNative not supported in Vulkan.";
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -1206,7 +1180,6 @@ void VulkanDriver::setAcquiredImage(Handle<HwStream> sh, void* image, const math
|
||||
scheduleRelease(stream->takePrevious());
|
||||
}
|
||||
stream->acquire({ image, cb, userData, handler});
|
||||
stream->setFrontEndTransform(transform);
|
||||
mStreamsWithPendingAcquiredImage.push_back(stream);
|
||||
}
|
||||
|
||||
@@ -1234,10 +1207,8 @@ void VulkanDriver::updateStreams(CommandStream* driver) {
|
||||
// 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,
|
||||
transform = stream->getFrontEndTransform()]() {
|
||||
image = stream->getAcquired().image]() {
|
||||
auto texture = s->getTexture(image);
|
||||
s->setBackendTransform(transform);
|
||||
if (!texture) {
|
||||
auto externalImage = fvkutils::createExternalImageFromRaw(mPlatform, image, false);
|
||||
auto metadata = mPlatform->extractExternalImageMetadata(externalImage);
|
||||
@@ -1583,13 +1554,6 @@ void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void VulkanDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh,
|
||||
BufferObjectStreamDescriptor&& streams) {
|
||||
|
||||
auto bo = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh);
|
||||
mStreamUniformDescriptors[bo.get()] = std::move(streams);
|
||||
}
|
||||
|
||||
void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescriptor&& bd,
|
||||
uint32_t byteOffset) {
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
@@ -1597,33 +1561,6 @@ void VulkanDriver::updateBufferObject(Handle<HwBufferObject> boh, BufferDescript
|
||||
auto bo = resource_ptr<VulkanBufferObject>::cast(&mResourceManager, boh);
|
||||
commands.acquire(bo);
|
||||
|
||||
if (UTILS_UNLIKELY(!mStreamUniformDescriptors.empty())) {
|
||||
auto streamDescriptors = mStreamUniformDescriptors.find(bo.get());
|
||||
if (streamDescriptors != mStreamUniformDescriptors.end()) {
|
||||
for (auto const& [offset, streamHandle, associationType]:
|
||||
streamDescriptors->second.mStreams) {
|
||||
if (associationType == BufferObjectStreamAssociationType::TRANSFORM_MATRIX) {
|
||||
math::mat3f transform;
|
||||
if (streamHandle) {
|
||||
auto stream =
|
||||
resource_ptr<VulkanStream>::cast(&mResourceManager, streamHandle);
|
||||
if (stream->streamType == StreamType::NATIVE) {
|
||||
FILAMENT_CHECK_PRECONDITION(false)
|
||||
<< "Native Stream not supported in Vulkan.";
|
||||
} else {
|
||||
// Backend call since getStreamTransformMatrix is called from async
|
||||
// updateBufferObject.
|
||||
transform = stream->getBackEndTransform();
|
||||
}
|
||||
}
|
||||
|
||||
copyMat3f(bd.buffer, offset, transform);
|
||||
}
|
||||
}
|
||||
mStreamUniformDescriptors.erase(streamDescriptors);
|
||||
}
|
||||
}
|
||||
|
||||
bo->loadFromCpu(commands, bd.buffer, byteOffset, bd.size);
|
||||
|
||||
scheduleDestroy(std::move(bd));
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
#include "vulkan/utils/Definitions.h"
|
||||
|
||||
#include "backend/DriverEnums.h"
|
||||
#include "backend/BufferObjectStreamDescriptor.h"
|
||||
|
||||
#include "DriverBase.h"
|
||||
#include "private/backend/Driver.h"
|
||||
@@ -160,10 +161,6 @@ private:
|
||||
VulkanExternalImageManager mExternalImageManager;
|
||||
VulkanStreamedImageManager mStreamedImageManager;
|
||||
|
||||
// Stream transforms
|
||||
std::unordered_map<VulkanBufferObject*, BufferObjectStreamDescriptor> mStreamUniformDescriptors;
|
||||
math::mat3f getStreamTransformMatrix(Handle<HwStream> sh);
|
||||
|
||||
|
||||
// This maps a VulkanSwapchain to a native swapchain. VulkanSwapchain should have a copy of the
|
||||
// Platform::Swapchain pointer, but queryFrameTimestamps() and queryCompositorTiming() are
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanTexture.h"
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
@@ -105,6 +106,11 @@ void VulkanSwapChain::update() {
|
||||
}
|
||||
|
||||
void VulkanSwapChain::present(DriverBase& driver) {
|
||||
// The last acquire failed, so just skip presenting.
|
||||
if (!mAcquired) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mHeadless && mTransitionSwapChainImageLayoutForPresent) {
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VkImageSubresourceRange const subresources{
|
||||
@@ -161,7 +167,15 @@ void VulkanSwapChain::acquire(bool& resized) {
|
||||
|
||||
VulkanPlatform::ImageSyncData 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;
|
||||
assert_invariant(mCurrentSwapIndex < mFinishedDrawing.size());
|
||||
mFinishedDrawing[mCurrentSwapIndex] = {};
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
|
||||
<< "Cannot acquire in swapchain. error=" << static_cast<int32_t>(result);
|
||||
|
||||
@@ -52,12 +52,8 @@ struct VulkanStream : public HwStream, fvkmemory::Resource {
|
||||
return previous;
|
||||
}
|
||||
const AcquiredImage& getAcquired() const { return user_thread.mAcquired; }
|
||||
void setFrontEndTransform(const math::mat3f& transform) { user_thread.mTransform = transform; }
|
||||
const math::mat3f& getFrontEndTransform() const { return user_thread.mTransform; }
|
||||
|
||||
//-- These methods are only called from the backend thread
|
||||
void setBackendTransform(const math::mat3f& transform) { mTransform = transform; }
|
||||
const math::mat3f& getBackEndTransform() const { return mTransform; }
|
||||
fvkmemory::resource_ptr<VulkanTexture> getTexture(void* ahb) {
|
||||
if (auto itr = mTextures.find(ahb); itr != mTextures.end()) {
|
||||
return itr->second;
|
||||
@@ -71,11 +67,9 @@ private:
|
||||
struct {
|
||||
AcquiredImage mAcquired;
|
||||
AcquiredImage mPrevious;
|
||||
math::mat3f mTransform;
|
||||
} user_thread;
|
||||
|
||||
// #TODO b/442937292
|
||||
math::mat3f mTransform;
|
||||
std::unordered_map<void*, fvkmemory::resource_ptr<VulkanTexture>> mTextures;
|
||||
};
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include "WebGPUConstants.h"
|
||||
#include "WebGPUQueueManager.h"
|
||||
#include "WebGPUStagePool.h"
|
||||
|
||||
#include "DriverBase.h"
|
||||
#include <backend/BufferDescriptor.h>
|
||||
@@ -65,7 +66,7 @@ WebGPUBufferBase::WebGPUBufferBase(wgpu::Device const& device, const wgpu::Buffe
|
||||
// of 4 by padding with zeros.
|
||||
void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor const& bufferDescriptor,
|
||||
const uint32_t byteOffset, wgpu::Device const& device,
|
||||
WebGPUQueueManager* const webGPUQueueManager) {
|
||||
WebGPUQueueManager* const webGPUQueueManager, WebGPUStagePool* const webGPUStagePool) {
|
||||
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.buffer)
|
||||
<< "updateGPUBuffer called with a null buffer";
|
||||
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.size + byteOffset <= mBuffer.GetSize())
|
||||
@@ -85,15 +86,12 @@ void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor const& bufferDescriptor,
|
||||
const size_t stagingBufferSize =
|
||||
remainder == 0 ? bufferDescriptor.size : mainBulk + FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS;
|
||||
|
||||
// create a staging buffer
|
||||
wgpu::BufferDescriptor descriptor{
|
||||
.label = "Filament WebGPU Staging Buffer",
|
||||
.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc,
|
||||
.size = stagingBufferSize,
|
||||
.mappedAtCreation = true };
|
||||
wgpu::Buffer stagingBuffer = device.CreateBuffer(&descriptor);
|
||||
wgpu::Buffer stagingBuffer = webGPUStagePool->acquireBuffer(stagingBufferSize,
|
||||
webGPUQueueManager->getLatestSubmissionState());
|
||||
|
||||
void* mappedRange = stagingBuffer.GetMappedRange();
|
||||
assert_invariant(mappedRange);
|
||||
|
||||
memcpy(mappedRange, bufferDescriptor.buffer, bufferDescriptor.size);
|
||||
|
||||
// Make sure the padded memory is set to 0 to have deterministic behaviors
|
||||
@@ -106,7 +104,9 @@ void WebGPUBufferBase::updateGPUBuffer(BufferDescriptor const& bufferDescriptor,
|
||||
|
||||
// Copy the staging buffer contents to the destination buffer.
|
||||
webGPUQueueManager->getCommandEncoder().CopyBufferToBuffer(stagingBuffer, 0, mBuffer,
|
||||
byteOffset, stagingBufferSize);
|
||||
byteOffset,
|
||||
remainder == 0 ? bufferDescriptor.size
|
||||
: mainBulk + FILAMENT_WEBGPU_BUFFER_SIZE_MODULUS);
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace filament::backend {
|
||||
|
||||
class BufferDescriptor;
|
||||
class WebGPUQueueManager;
|
||||
class WebGPUStagePool;
|
||||
|
||||
/**
|
||||
* A base class for WebGPU buffer objects, providing common functionality for creating and
|
||||
@@ -40,7 +41,7 @@ public:
|
||||
* ensures the calls happen in the expected sequence.
|
||||
*/
|
||||
void updateGPUBuffer(BufferDescriptor const&, uint32_t byteOffset, wgpu::Device const& device,
|
||||
WebGPUQueueManager* const webGPUQueueManager);
|
||||
WebGPUQueueManager* const webGPUQueueManager, WebGPUStagePool* const webGPUStagePool);
|
||||
|
||||
[[nodiscard]] wgpu::Buffer const& getBuffer() const { return mBuffer; }
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform,
|
||||
mAdapter{ mPlatform.requestAdapter(nullptr) },
|
||||
mDevice{ mPlatform.requestDevice(mAdapter) },
|
||||
mQueueManager{ mDevice },
|
||||
mStagePool{ mDevice },
|
||||
mPipelineLayoutCache{ mDevice },
|
||||
mPipelineCache{ mDevice },
|
||||
mRenderPassMipmapGenerator{ mDevice, &mQueueManager },
|
||||
@@ -177,6 +178,9 @@ void WebGPUDriver::endFrame(const uint32_t /* frameId */) {
|
||||
for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; 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.
|
||||
@@ -651,11 +655,6 @@ void WebGPUDriver::setAcquiredImage(Handle<HwStream> sh, void* image, const math
|
||||
//todo
|
||||
}
|
||||
|
||||
void WebGPUDriver::registerBufferObjectStreams(Handle<HwBufferObject> bufferObjectHandle,
|
||||
BufferObjectStreamDescriptor&& streams) {
|
||||
//todo
|
||||
}
|
||||
|
||||
void WebGPUDriver::setStreamDimensions(Handle<HwStream> sh, uint32_t width, uint32_t height) {
|
||||
//todo
|
||||
}
|
||||
@@ -856,7 +855,7 @@ void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> indexBufferHandle,
|
||||
// draw calls are made.
|
||||
flush();
|
||||
handleCast<WebGPUIndexBuffer>(indexBufferHandle)
|
||||
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager);
|
||||
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager, &mStagePool);
|
||||
scheduleDestroy(std::move(bufferDescriptor));
|
||||
}
|
||||
|
||||
@@ -867,14 +866,14 @@ void WebGPUDriver::updateBufferObject(Handle<HwBufferObject> bufferObjectHandle,
|
||||
// draw calls are made.
|
||||
flush();
|
||||
handleCast<WebGPUBufferObject>(bufferObjectHandle)
|
||||
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager);
|
||||
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager, &mStagePool);
|
||||
scheduleDestroy(std::move(bufferDescriptor));
|
||||
}
|
||||
|
||||
void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> bufferObjectHandle,
|
||||
BufferDescriptor&& bufferDescriptor, const uint32_t byteOffset) {
|
||||
handleCast<WebGPUBufferObject>(bufferObjectHandle)
|
||||
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager);
|
||||
->updateGPUBuffer(bufferDescriptor, byteOffset, mDevice, &mQueueManager, &mStagePool);
|
||||
scheduleDestroy(std::move(bufferDescriptor));
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "webgpu/WebGPUPipelineLayoutCache.h"
|
||||
#include "webgpu/WebGPURenderPassMipmapGenerator.h"
|
||||
#include "webgpu/WebGPUQueueManager.h"
|
||||
#include "webgpu/WebGPUStagePool.h"
|
||||
#include "webgpu/utils/AsyncTaskCounter.h"
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
@@ -81,6 +82,7 @@ private:
|
||||
wgpu::Device mDevice = nullptr;
|
||||
wgpu::Limits mDeviceLimits = {};
|
||||
WebGPUQueueManager mQueueManager;
|
||||
WebGPUStagePool mStagePool;
|
||||
void* mNativeWindow = nullptr;
|
||||
WebGPUSwapChain* mSwapChain = nullptr;
|
||||
uint64_t mNextFakeHandle = 1;
|
||||
|
||||
@@ -64,6 +64,7 @@ wgpu::CommandEncoder WebGPUQueueManager::getCommandEncoder() {
|
||||
};
|
||||
mCommandEncoder = mDevice.CreateCommandEncoder(&commandEncoderDescriptor);
|
||||
ASSERT_POSTCONDITION(mCommandEncoder, "Failed to create command encoder.");
|
||||
mLatestSubmissionState = std::make_shared<WebGPUSubmissionState>();
|
||||
}
|
||||
return mCommandEncoder;
|
||||
}
|
||||
@@ -94,8 +95,6 @@ void WebGPUQueueManager::submit() {
|
||||
return;
|
||||
}
|
||||
|
||||
mLatestSubmissionState = std::make_shared<WebGPUSubmissionState>();
|
||||
|
||||
wgpu::CommandBufferDescriptor commandBufferDescriptor{
|
||||
.label = "Filament Command Buffer",
|
||||
};
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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 "WebGPUStagePool.h"
|
||||
|
||||
#include "WebGPUConstants.h"
|
||||
#include "WebGPUQueueManager.h"
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
WebGPUStagePool::WebGPUStagePool(wgpu::Device const& device) : mDevice(device) {}
|
||||
|
||||
WebGPUStagePool::~WebGPUStagePool() = default;
|
||||
|
||||
wgpu::Buffer WebGPUStagePool::acquireBuffer(size_t requiredSize,
|
||||
std::shared_ptr<WebGPUSubmissionState> submissionState) {
|
||||
wgpu::Buffer buffer;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mMutex);
|
||||
auto iter = mBuffers.lower_bound(requiredSize);
|
||||
if (iter != mBuffers.end()) {
|
||||
buffer = iter->second;
|
||||
mBuffers.erase(iter);
|
||||
}
|
||||
}
|
||||
if (!buffer.Get()) {
|
||||
buffer = createNewBuffer(requiredSize);
|
||||
}
|
||||
mInProgress.push_back({submissionState, buffer});
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void WebGPUStagePool::recycleBuffer(wgpu::Buffer buffer) {
|
||||
struct UserData final {
|
||||
wgpu::Buffer buffer;
|
||||
WebGPUStagePool* webGPUStagePool;
|
||||
};
|
||||
auto userData =
|
||||
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() {
|
||||
// We found that MapAsync would sometimes lead to GetMappedRange returning nullptr if the
|
||||
// command using that staging buffer has not finished executing, so here we only recycle those
|
||||
// buffers that are not still being used by any command
|
||||
std::vector<std::pair<std::shared_ptr<WebGPUSubmissionState>, wgpu::Buffer>> stillInProgress;
|
||||
for (auto& [st, buffer]: mInProgress) {
|
||||
if (st->getStatus() == FenceStatus::CONDITION_SATISFIED) {
|
||||
recycleBuffer(buffer);
|
||||
} else {
|
||||
stillInProgress.push_back({st, buffer});
|
||||
}
|
||||
}
|
||||
std::swap(mInProgress, stillInProgress);
|
||||
}
|
||||
|
||||
wgpu::Buffer WebGPUStagePool::createNewBuffer(size_t bufferSize) {
|
||||
wgpu::BufferDescriptor descriptor{
|
||||
.label = "Filament WebGPU Staging Buffer",
|
||||
.usage = wgpu::BufferUsage::MapWrite | wgpu::BufferUsage::CopySrc,
|
||||
.size = bufferSize,
|
||||
.mappedAtCreation = true };
|
||||
return mDevice.CreateBuffer(&descriptor);
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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_WEBGPUSTAGEPOOL_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUSTAGEPOOL_H
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
struct WebGPUSubmissionState;
|
||||
|
||||
class WebGPUStagePool {
|
||||
public:
|
||||
WebGPUStagePool(wgpu::Device const& device);
|
||||
~WebGPUStagePool();
|
||||
|
||||
wgpu::Buffer acquireBuffer(size_t requiredSize,
|
||||
std::shared_ptr<WebGPUSubmissionState> submissionState);
|
||||
void recycleBuffer(wgpu::Buffer buffer);
|
||||
void gc();
|
||||
|
||||
private:
|
||||
wgpu::Buffer createNewBuffer(size_t bufferSize);
|
||||
std::multimap<uint32_t, wgpu::Buffer> mBuffers;
|
||||
std::vector<std::pair<std::shared_ptr<WebGPUSubmissionState>, wgpu::Buffer>> mInProgress;
|
||||
std::mutex mMutex;
|
||||
|
||||
wgpu::Device mDevice;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_WEBGPUSTAGEPOOL_H
|
||||
@@ -192,6 +192,7 @@ public:
|
||||
using StereoscopicType = backend::StereoscopicType;
|
||||
using Driver = backend::Driver;
|
||||
using GpuContextPriority = backend::Platform::GpuContextPriority;
|
||||
using AsynchronousMode = backend::AsynchronousMode;
|
||||
|
||||
/**
|
||||
* Config is used to define the memory footprint used by the engine, such as the
|
||||
@@ -428,6 +429,11 @@ public:
|
||||
* initial memory usage.
|
||||
*/
|
||||
uint32_t sharedUboInitialSizeInBytes = 256 * 64;
|
||||
|
||||
/**
|
||||
* Asynchronous mode for the engine. Defines how asynchronous operations are handled.
|
||||
*/
|
||||
AsynchronousMode asynchronousMode = AsynchronousMode::NONE;
|
||||
};
|
||||
|
||||
|
||||
@@ -742,6 +748,14 @@ public:
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -69,6 +69,20 @@ public:
|
||||
using ShaderModel = backend::ShaderModel;
|
||||
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.
|
||||
*/
|
||||
@@ -165,6 +179,13 @@ public:
|
||||
*/
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include <math/mat4.h>
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <math/vec2.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
@@ -34,6 +36,10 @@ using namespace math;
|
||||
void Camera::setProjection(double const fovInDegrees, double const aspect,
|
||||
double const near, double const far, Fov const direction) {
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(near > 0 && far > near)
|
||||
<< "Camera preconditions not met in setProjection(): near <= 0 or far <= near, near="
|
||||
<< near << ", far=" << far;
|
||||
|
||||
setCustomProjection(
|
||||
projection(direction, fovInDegrees, aspect, near),
|
||||
projection(direction, fovInDegrees, aspect, near, far),
|
||||
@@ -43,6 +49,10 @@ void Camera::setProjection(double const fovInDegrees, double const aspect,
|
||||
void Camera::setLensProjection(double const focalLengthInMillimeters,
|
||||
double const aspect, double const near, double const far) {
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(near > 0 && far > near)
|
||||
<< "Camera preconditions not met in setLensProjection(): near <= 0 or far <= near, near="
|
||||
<< near << ", far=" << far;
|
||||
|
||||
setCustomProjection(
|
||||
projection(focalLengthInMillimeters, aspect, near),
|
||||
projection(focalLengthInMillimeters, aspect, near, far),
|
||||
|
||||
@@ -455,6 +455,10 @@ bool Engine::isStereoSupported(StereoscopicType) const noexcept {
|
||||
return downcast(this)->isStereoSupported();
|
||||
}
|
||||
|
||||
bool Engine::isAsynchronousOperationSupported() const noexcept {
|
||||
return downcast(this)->isAsynchronousOperationSupported();
|
||||
}
|
||||
|
||||
size_t Engine::getMaxStereoscopicEyes() noexcept {
|
||||
return FEngine::getMaxStereoscopicEyes();
|
||||
}
|
||||
|
||||
@@ -101,11 +101,24 @@ void FrameInfoManager::beginFrame(FSwapChain* swapChain, DriverApi& driver,
|
||||
auto& history = mFrameTimeHistory;
|
||||
// don't exceed the capacity, drop the oldest entry
|
||||
if (UTILS_LIKELY(history.size() == history.capacity())) {
|
||||
if (!mDisableGpuFrameComplete) {
|
||||
assert_invariant(history.back().fence);
|
||||
driver.destroyFence(std::move(history.back().fence));
|
||||
FrameInfoImpl& frameInfo = history.back();
|
||||
if (frameInfo.ready.load(std::memory_order_relaxed)) {
|
||||
if (!mDisableGpuFrameComplete) {
|
||||
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
|
||||
@@ -193,6 +206,14 @@ void FrameInfoManager::beginFrame(FSwapChain* swapChain, DriverApi& driver,
|
||||
}
|
||||
|
||||
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();
|
||||
front.endFrame = std::chrono::steady_clock::now();
|
||||
|
||||
|
||||
@@ -95,33 +95,8 @@ struct FrameInfoImpl : public details::FrameInfo {
|
||||
assert_invariant(!fence);
|
||||
}
|
||||
|
||||
FrameInfoImpl(FrameInfoImpl&& rhs) noexcept :
|
||||
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;
|
||||
}
|
||||
FrameInfoImpl(FrameInfoImpl& rhs) noexcept = delete;
|
||||
FrameInfoImpl& operator=(FrameInfoImpl& rhs) noexcept = delete;
|
||||
};
|
||||
|
||||
template<typename T, size_t CAPACITY>
|
||||
@@ -340,6 +315,7 @@ private:
|
||||
FrameHistoryQueue mFrameTimeHistory{};
|
||||
utils::AsyncJobQueue mJobQueue;
|
||||
FSwapChain* mLastSeenSwapChain = nullptr;
|
||||
bool mLastBeginFrameSkipped = false;
|
||||
bool const mHasTimerQueries = false;
|
||||
bool const mDisableGpuFrameComplete = false;
|
||||
};
|
||||
|
||||
@@ -95,13 +95,8 @@ mat4 FCamera::projection(double const focalLengthInMillimeters,
|
||||
void UTILS_NOINLINE FCamera::setCustomProjection(mat4 const& projection,
|
||||
mat4 const& projectionForCulling, double const near, double const far) noexcept {
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(near > 0)
|
||||
<< "Camera preconditions not met in setCustomProjection(): near <= 0, near=" << near;
|
||||
auto const& featureFlags = mEngine.features.engine.debug;
|
||||
FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION(far > near,
|
||||
featureFlags.assert_camera_projection_near_far)
|
||||
<< "Camera preconditions not met in setCustomProjection(): far <= near, near="
|
||||
<< near << ", far=" << far;
|
||||
FILAMENT_CHECK_PRECONDITION(near != far)
|
||||
<< "Camera preconditions not met in setCustomProjection(): near = far = " << near;
|
||||
|
||||
for (auto& eyeProjection: mEyeProjection) {
|
||||
eyeProjection = projection;
|
||||
@@ -115,13 +110,8 @@ void UTILS_NOINLINE FCamera::setCustomEyeProjection(mat4 const* projection, size
|
||||
mat4 const& projectionForCulling, double const near, double const far) {
|
||||
const Engine::Config& config = mEngine.getConfig();
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(near > 0)
|
||||
<< "Camera preconditions not met in setCustomEyeProjection(): near <= 0, near=" << near;
|
||||
auto const& featureFlags = mEngine.features.engine.debug;
|
||||
FILAMENT_FLAG_GUARDED_CHECK_PRECONDITION(far > near,
|
||||
featureFlags.assert_camera_projection_near_far)
|
||||
<< "Camera preconditions not met in setCustomEyeProjection(): far <= near, near="
|
||||
<< near << ", far=" << far;
|
||||
FILAMENT_CHECK_PRECONDITION(near != far)
|
||||
<< "Camera preconditions not met in setCustomEyeProjection(): near = far = " << near;
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(count >= config.stereoscopicEyeCount)
|
||||
<< "All eye projections must be supplied together, count must be >= "
|
||||
|
||||
@@ -98,7 +98,7 @@ using namespace filaflat;
|
||||
namespace {
|
||||
|
||||
backend::Platform::DriverConfig getDriverConfig(FEngine* instance) {
|
||||
return {
|
||||
Platform::DriverConfig driverConfig {
|
||||
.handleArenaSize = instance->getRequestedDriverHandleArenaSize(),
|
||||
.metalUploadBufferSizeBytes = instance->getConfig().metalUploadBufferSizeBytes,
|
||||
.disableParallelShaderCompile = instance->features.backend.disable_parallel_shader_compile,
|
||||
@@ -116,7 +116,11 @@ backend::Platform::DriverConfig getDriverConfig(FEngine* instance) {
|
||||
.gpuContextPriority = instance->getConfig().gpuContextPriority,
|
||||
.vulkanEnableStagingBufferBypass =
|
||||
instance->features.backend.vulkan.enable_staging_buffer_bypass,
|
||||
.asynchronousMode = instance->features.backend.enable_asynchronous_operation ?
|
||||
instance->getConfig().asynchronousMode : AsynchronousMode::NONE,
|
||||
};
|
||||
|
||||
return driverConfig;
|
||||
}
|
||||
|
||||
} // anonymous
|
||||
@@ -727,7 +731,6 @@ void FEngine::prepare() {
|
||||
// post-process materials instances must be commited explicitly because their
|
||||
// parameters are typically not set at this point in time.
|
||||
if (item->getMaterial()->getMaterialDomain() == MaterialDomain::SURFACE) {
|
||||
item->commitStreamUniformAssociations(driver);
|
||||
item->commit(driver, uboManager);
|
||||
}
|
||||
});
|
||||
@@ -991,10 +994,9 @@ FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material,
|
||||
return p;
|
||||
}
|
||||
|
||||
FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material, const char* name,
|
||||
UboBatchingMode batchingMode) noexcept {
|
||||
FMaterialInstance* p =
|
||||
mHeapAllocator.make<FMaterialInstance>(*this, material, name, batchingMode);
|
||||
FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material,
|
||||
const char* name) noexcept {
|
||||
FMaterialInstance* p = mHeapAllocator.make<FMaterialInstance>(*this, material, name);
|
||||
if (UTILS_LIKELY(p)) {
|
||||
auto pos = mMaterialInstances.emplace(material, "MaterialInstance");
|
||||
pos.first->second.insert(p);
|
||||
@@ -1503,6 +1505,11 @@ Engine::FeatureLevel FEngine::setActiveFeatureLevel(FeatureLevel 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__)
|
||||
void FEngine::resetBackendState() noexcept {
|
||||
getDriverApi().resetState();
|
||||
|
||||
@@ -216,6 +216,8 @@ public:
|
||||
return getDriver().isStereoSupported();
|
||||
}
|
||||
|
||||
bool isAsynchronousOperationSupported() const noexcept;
|
||||
|
||||
static size_t getMaxStereoscopicEyes() noexcept {
|
||||
return CONFIG_MAX_STEREOSCOPIC_EYES;
|
||||
}
|
||||
@@ -345,21 +347,10 @@ public:
|
||||
|
||||
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,
|
||||
const FMaterialInstance* other, const char* name) noexcept;
|
||||
|
||||
FMaterialInstance* createMaterialInstance(const FMaterial* material, const char* name,
|
||||
UboBatchingMode batchingMode) noexcept;
|
||||
FMaterialInstance* createMaterialInstance(const FMaterial* material, const char* name) noexcept;
|
||||
|
||||
FScene* createScene() noexcept;
|
||||
FView* createView() noexcept;
|
||||
@@ -778,7 +769,6 @@ public:
|
||||
bool assert_material_instance_texture_descriptor_set_compatible =
|
||||
CORRECTNESS_ASSERTION_DEFAULT;
|
||||
bool assert_texture_can_generate_mipmap = CORRECTNESS_ASSERTION_DEFAULT;
|
||||
bool assert_camera_projection_near_far = CORRECTNESS_ASSERTION_DEFAULT;
|
||||
} debug;
|
||||
struct {
|
||||
bool disable_gpu_frame_complete_metric = true;
|
||||
@@ -798,6 +788,7 @@ public:
|
||||
bool disable_amortized_shader_compile = true;
|
||||
bool disable_handle_use_after_free_check = false;
|
||||
bool disable_heap_handle_tags = true; // FIXME: this should be false
|
||||
bool enable_asynchronous_operation = false;
|
||||
} backend;
|
||||
struct {
|
||||
bool check_crc32_after_loading = false;
|
||||
@@ -818,6 +809,9 @@ public:
|
||||
{ "backend.disable_heap_handle_tags",
|
||||
"Disable Handle<> tags for heap-allocated handles.",
|
||||
&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",
|
||||
"Asserts that the ANativeWindow is valid when rendering starts.",
|
||||
&features.backend.opengl.assert_native_window_is_valid, true },
|
||||
@@ -848,9 +842,6 @@ public:
|
||||
{ "engine.debug.assert_texture_can_generate_mipmap",
|
||||
"Assert if a texture has the correct usage set for generating mipmaps.",
|
||||
&features.engine.debug.assert_texture_can_generate_mipmap, false },
|
||||
{ "engine.debug.assert_camera_projection_near_far",
|
||||
"Assert that the near plane is smaller than the far plane.",
|
||||
&features.engine.debug.assert_camera_projection_near_far, false },
|
||||
{ "material.check_crc32_after_loading",
|
||||
"Verify the checksum of package data when a material is loaded.",
|
||||
&features.material.check_crc32_after_loading, false },
|
||||
|
||||
@@ -70,10 +70,19 @@
|
||||
|
||||
namespace filament {
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace backend;
|
||||
using namespace filaflat;
|
||||
using namespace utils;
|
||||
using UboBatchingMode = FEngine::UboBatchingMode;
|
||||
using UboBatchingMode = Material::UboBatchingMode;
|
||||
|
||||
bool shouldEnableBatching(FEngine& engine, UboBatchingMode batchingMode, MaterialDomain domain) {
|
||||
return batchingMode != UboBatchingMode::DISABLED && engine.isUboBatchingEnabled() &&
|
||||
domain == MaterialDomain::SURFACE;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
struct Material::BuilderDetails {
|
||||
const void* mPayload = nullptr;
|
||||
@@ -81,6 +90,7 @@ struct Material::BuilderDetails {
|
||||
bool mDefaultMaterial = false;
|
||||
int32_t mShBandsCount = 3;
|
||||
Builder::ShadowSamplingQuality mShadowSamplingQuality = Builder::ShadowSamplingQuality::LOW;
|
||||
UboBatchingMode mUboBatchingMode = UboBatchingMode::DEFAULT;
|
||||
std::unordered_map<
|
||||
CString,
|
||||
std::variant<int32_t, float, bool>,
|
||||
@@ -115,6 +125,11 @@ Material::Builder& Material::Builder::shadowSamplingQuality(ShadowSamplingQualit
|
||||
return *this;
|
||||
}
|
||||
|
||||
Material::Builder& Material::Builder::uboBatching(UboBatchingMode const mode) noexcept {
|
||||
mImpl->mUboBatchingMode = mode;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T, typename>
|
||||
Material::Builder& Material::Builder::constant(const char* name, size_t nameLength, T value) {
|
||||
FILAMENT_CHECK_PRECONDITION(name != nullptr) << "name cannot be null";
|
||||
@@ -148,8 +163,12 @@ Material* Material::Builder::build(Engine& engine) const {
|
||||
FMaterial::FMaterial(FEngine& engine, const Builder& builder, MaterialDefinition const& definition)
|
||||
: mDefinition(definition),
|
||||
mIsDefaultMaterial(builder->mDefaultMaterial),
|
||||
mUseUboBatching(shouldEnableBatching(engine, builder->mUboBatchingMode,
|
||||
definition.materialDomain)),
|
||||
mEngine(engine),
|
||||
mMaterialId(engine.getMaterialId()) {
|
||||
FILAMENT_CHECK_PRECONDITION(!mUseUboBatching || engine.isUboBatchingEnabled())
|
||||
<< "UBO batching is not enabled.";
|
||||
mSpecializationConstants = processSpecializationConstants(builder);
|
||||
precacheDepthVariants(engine);
|
||||
|
||||
@@ -282,14 +301,14 @@ FMaterialInstance* FMaterial::createInstance(const char* name) const noexcept {
|
||||
return FMaterialInstance::duplicate(mDefaultMaterialInstance, name);
|
||||
} else {
|
||||
// but if we don't, just create an instance with all the default parameters
|
||||
return mEngine.createMaterialInstance(this, name, UboBatchingMode::DEFAULT);
|
||||
return mEngine.createMaterialInstance(this, name);
|
||||
}
|
||||
}
|
||||
|
||||
FMaterialInstance* FMaterial::getDefaultInstance() noexcept {
|
||||
if (UTILS_UNLIKELY(!mDefaultMaterialInstance)) {
|
||||
mDefaultMaterialInstance =
|
||||
mEngine.createMaterialInstance(this, mDefinition.name.c_str(), UboBatchingMode::DEFAULT);
|
||||
mEngine.createMaterialInstance(this, mDefinition.name.c_str());
|
||||
mDefaultMaterialInstance->setDefaultInstance(true);
|
||||
}
|
||||
return mDefaultMaterialInstance;
|
||||
|
||||
@@ -248,6 +248,10 @@ public:
|
||||
return mDefinition.perViewLayoutIndex;
|
||||
}
|
||||
|
||||
bool useUboBatching() const noexcept {
|
||||
return mUseUboBatching;
|
||||
}
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
void applyPendingEdits() noexcept;
|
||||
|
||||
@@ -310,6 +314,8 @@ private:
|
||||
|
||||
bool mIsDefaultMaterial = false;
|
||||
|
||||
bool mUseUboBatching = false;
|
||||
|
||||
// reserve some space to construct the default material instance
|
||||
mutable FMaterialInstance* mDefaultMaterialInstance = nullptr;
|
||||
|
||||
|
||||
@@ -58,18 +58,8 @@ using namespace utils;
|
||||
namespace filament {
|
||||
|
||||
using namespace backend;
|
||||
using UboBatchingMode = FEngine::UboBatchingMode;
|
||||
|
||||
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
|
||||
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name) noexcept
|
||||
: mMaterial(material),
|
||||
mDescriptorSet("MaterialInstance", material->getDescriptorSetLayout()),
|
||||
mCulling(CullingMode::BACK),
|
||||
@@ -80,12 +70,9 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
|
||||
mHasScissor(false),
|
||||
mIsDoubleSided(false),
|
||||
mIsDefaultInstance(false),
|
||||
mUseUboBatching(
|
||||
shouldEnableBatching(engine, batchingMode, material->getMaterialDomain())),
|
||||
mUseUboBatching(material->useUboBatching()),
|
||||
mTransparencyMode(TransparencyMode::DEFAULT),
|
||||
mName(name ? CString(name) : material->getName()) {
|
||||
FILAMENT_CHECK_PRECONDITION(!mUseUboBatching || engine.isUboBatchingEnabled())
|
||||
<< "UBO batching is not enabled.";
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
// even if the material doesn't have any parameters, we allocate a small UBO because it's
|
||||
@@ -160,7 +147,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
|
||||
mUseUboBatching(other->mUseUboBatching),
|
||||
mScissorRect(other->mScissorRect),
|
||||
mName(name ? CString(name) : other->mName) {
|
||||
assert_invariant(!mUseUboBatching || engine.isUboBatchingEnabled());
|
||||
FEngine::DriverApi& driver = engine.getDriverApi();
|
||||
FMaterial const* const material = other->getMaterial();
|
||||
|
||||
@@ -223,31 +209,6 @@ 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 {
|
||||
if (UTILS_LIKELY(mMaterial->getMaterialDomain() != MaterialDomain::SURFACE)) {
|
||||
commit(engine.getDriverApi(), engine.getUboManager());
|
||||
@@ -255,10 +216,9 @@ void FMaterialInstance::commit(FEngine& engine) const {
|
||||
}
|
||||
|
||||
void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManager) const {
|
||||
if (mUniforms.isDirty() || mHasStreamUniformAssociations) {
|
||||
if (mUniforms.isDirty()) {
|
||||
mUniforms.clean();
|
||||
if (mUseUboBatching) {
|
||||
assert_invariant(uboManager != nullptr);
|
||||
if (isUsingUboBatching()) {
|
||||
if (!BufferAllocator::isValid(getAllocationId())) {
|
||||
// The allocation hasn't happened yet, return.
|
||||
return;
|
||||
@@ -289,7 +249,7 @@ void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManage
|
||||
// TODO: eventually we should remove this in RELEASE builds
|
||||
fixMissingSamplers();
|
||||
|
||||
if (mUseUboBatching && !BufferAllocator::isValid(getAllocationId())) {
|
||||
if (isUsingUboBatching() && !BufferAllocator::isValid(getAllocationId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -439,7 +399,7 @@ void FMaterialInstance::use(FEngine::DriverApi& driver, Variant variant) const {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mUseUboBatching && !BufferAllocator::isValid(getAllocationId())) {
|
||||
if (isUsingUboBatching() && !BufferAllocator::isValid(getAllocationId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -476,7 +436,7 @@ void FMaterialInstance::assignUboAllocation(
|
||||
const Handle<HwBufferObject>& ubHandle,
|
||||
BufferAllocator::AllocationId id,
|
||||
BufferAllocator::allocation_size_t offset) {
|
||||
assert_invariant(mUseUboBatching);
|
||||
assert_invariant(isUsingUboBatching());
|
||||
|
||||
mUboData = id;
|
||||
mUboOffset = offset;
|
||||
|
||||
@@ -56,8 +56,7 @@ class FTexture;
|
||||
|
||||
class FMaterialInstance : public MaterialInstance {
|
||||
public:
|
||||
FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name,
|
||||
FEngine::UboBatchingMode batchingMode) noexcept;
|
||||
FMaterialInstance(FEngine& engine, FMaterial const* material, const char* name) noexcept;
|
||||
FMaterialInstance(FEngine& engine, FMaterialInstance const* other, const char* name);
|
||||
FMaterialInstance(const FMaterialInstance& rhs) = delete;
|
||||
FMaterialInstance& operator=(const FMaterialInstance& rhs) = delete;
|
||||
@@ -67,8 +66,6 @@ public:
|
||||
~FMaterialInstance() noexcept;
|
||||
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
void commitStreamUniformAssociations(FEngine::DriverApi& driver);
|
||||
|
||||
void commit(FEngine& engine) const;
|
||||
|
||||
@@ -288,7 +285,6 @@ private:
|
||||
tsl::robin_map<backend::descriptor_binding_t, TextureParameter> mTextureParameters;
|
||||
mutable DescriptorSet mDescriptorSet;
|
||||
UniformBuffer mUniforms;
|
||||
bool mHasStreamUniformAssociations = false;
|
||||
|
||||
backend::PolygonOffset mPolygonOffset{};
|
||||
backend::StencilState mStencilState{};
|
||||
|
||||
@@ -292,16 +292,11 @@ void FRenderer::skipFrame(uint64_t vsyncSteadyClockTimeNano) {
|
||||
// do this before engine.flush()
|
||||
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
|
||||
|
||||
// make sure we're done with the gcs
|
||||
js.waitAndRelease(job);
|
||||
// Run the component managers' GC
|
||||
auto& js = engine.getJobSystem();
|
||||
js.runAndWait(jobs::createJob(js, nullptr, &FEngine::gc, &engine)); // gc all managers
|
||||
}
|
||||
|
||||
bool FRenderer::shouldRenderFrame() const noexcept {
|
||||
@@ -468,16 +463,11 @@ void FRenderer::endFrame() {
|
||||
// do this before engine.flush()
|
||||
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
|
||||
|
||||
// make sure we're done with the gcs
|
||||
js.waitAndRelease(job);
|
||||
// Run the component managers' GC
|
||||
auto& js = engine.getJobSystem();
|
||||
js.runAndWait(jobs::createJob(js, nullptr, &FEngine::gc, &engine)); // gc all managers
|
||||
}
|
||||
|
||||
void FRenderer::readPixels(
|
||||
@@ -571,7 +561,6 @@ void FRenderer::renderStandaloneView(FView const* view) {
|
||||
|
||||
if (UTILS_LIKELY(view->getScene())) {
|
||||
mPreviousRenderTargets.clear();
|
||||
mFrameId++;
|
||||
|
||||
// ask the engine to do what it needs to (e.g. updates light buffer, materials...)
|
||||
FEngine& engine = mEngine;
|
||||
@@ -581,8 +570,7 @@ void FRenderer::renderStandaloneView(FView const* view) {
|
||||
driver.beginFrame(
|
||||
steady_clock::now().time_since_epoch().count(),
|
||||
mDisplayInfo.refreshRate == 0.0 ? 0 : int64_t(
|
||||
1'000'000'000.0 / mDisplayInfo.refreshRate),
|
||||
mFrameId);
|
||||
1'000'000'000.0 / mDisplayInfo.refreshRate), 0);
|
||||
|
||||
// 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
|
||||
@@ -591,7 +579,7 @@ void FRenderer::renderStandaloneView(FView const* view) {
|
||||
|
||||
engine.submitFrame();
|
||||
|
||||
driver.endFrame(mFrameId);
|
||||
driver.endFrame(0);
|
||||
|
||||
// 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
|
||||
|
||||
@@ -209,7 +209,7 @@ private:
|
||||
backend::Handle<backend::HwRenderTarget> mRenderTargetHandle;
|
||||
FSwapChain* mSwapChain = nullptr;
|
||||
size_t mCommandsHighWatermark = 0;
|
||||
uint32_t mFrameId = 0;
|
||||
uint32_t mFrameId = 1; // id 0 is reserved for standalone views
|
||||
FrameInfoManager mFrameInfoManager;
|
||||
backend::TextureFormat mHdrTranslucent;
|
||||
backend::TextureFormat mHdrQualityMedium;
|
||||
|
||||
@@ -13,11 +13,25 @@ set(GENERATION_ROOT ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(RESOURCE_DIR "${GENERATION_ROOT}/resources")
|
||||
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)
|
||||
|
||||
set(RESOURCE_BINS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_material.filamat
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_material_transformname.filamat
|
||||
${TEST_MATERIAL_TRANSFORM_NAME_FILAMAT}
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -11,7 +11,6 @@ OUTPUT_DIR="filament/test/"
|
||||
./build.sh -p desktop release 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_transformname.filamat samples/materials/sandboxTransformName.mat
|
||||
|
||||
set +x
|
||||
|
||||
|
||||
@@ -170,10 +170,6 @@ public:
|
||||
// negative value if name doesn't exist or Panic if exceptions are enabled
|
||||
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;
|
||||
|
||||
bool hasField(std::string_view name) const noexcept {
|
||||
@@ -201,7 +197,6 @@ private:
|
||||
utils::CString mName;
|
||||
utils::FixedCapacityVector<FieldInfo> mFieldInfoList;
|
||||
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
|
||||
Alignment mAlignment = Alignment::std140;
|
||||
Target mTarget = Target::UNIFORM;
|
||||
|
||||
@@ -122,8 +122,6 @@ BufferInterfaceBlock::BufferInterfaceBlock(Builder const& builder) noexcept
|
||||
|
||||
auto& uniformsInfoList = mFieldInfoList;
|
||||
|
||||
auto& transformOffsetMap = mTransformOffsetMap;
|
||||
|
||||
uint32_t i = 0;
|
||||
uint16_t offset = 0;
|
||||
for (auto const& e : builder.mEntries) {
|
||||
@@ -150,11 +148,6 @@ BufferInterfaceBlock::BufferInterfaceBlock(Builder const& builder) noexcept
|
||||
// record this uniform info
|
||||
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
|
||||
offset += stride * std::max(1u, e.size);
|
||||
++i;
|
||||
@@ -170,14 +163,6 @@ ssize_t BufferInterfaceBlock::getFieldOffset(std::string_view name, size_t 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(
|
||||
std::string_view name) const {
|
||||
auto pos = mInfoMap.find(name);
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
#ifndef FILAGUI_IMGUIHELPER_H_
|
||||
#define FILAGUI_IMGUIHELPER_H_
|
||||
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/IndexBuffer.h>
|
||||
#include <filament/Material.h>
|
||||
@@ -32,6 +29,10 @@
|
||||
#include <utils/Entity.h>
|
||||
#include <utils/Path.h>
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
struct ImDrawData;
|
||||
struct ImGuiIO;
|
||||
struct ImGuiContext;
|
||||
@@ -100,6 +101,7 @@ public:
|
||||
filament::TextureSampler mSampler;
|
||||
bool mFlipVertical = false;
|
||||
utils::Path mSettingsPath;
|
||||
std::unordered_set<filament::Texture*> mImGuiTextures;
|
||||
};
|
||||
|
||||
} // namespace filagui
|
||||
|
||||
@@ -148,6 +148,12 @@ ImGuiHelper::~ImGuiHelper() {
|
||||
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();
|
||||
em.destroy(mRenderable);
|
||||
em.destroy(mCameraEntity);
|
||||
@@ -189,7 +195,6 @@ void ImGuiHelper::render(float timeStepInSeconds, Callback imguiCommands) {
|
||||
|
||||
void ImGuiHelper::processImGuiCommands(ImDrawData* commands, const ImGuiIO& io) {
|
||||
ImGui::SetCurrentContext(mImGuiContext);
|
||||
|
||||
if (commands->Textures != nullptr) {
|
||||
for (ImTextureData* tex : *commands->Textures) {
|
||||
if (tex->Status == ImTextureStatus_OK) {
|
||||
@@ -203,7 +208,6 @@ void ImGuiHelper::processImGuiCommands(ImDrawData* commands, const ImGuiIO& io)
|
||||
.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,
|
||||
@@ -213,6 +217,7 @@ void ImGuiHelper::processImGuiCommands(ImDrawData* commands, const ImGuiIO& io)
|
||||
|
||||
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,
|
||||
@@ -222,11 +227,13 @@ void ImGuiHelper::processImGuiCommands(ImDrawData* commands, const ImGuiIO& io)
|
||||
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);
|
||||
tex->UnusedFrames > 0) {
|
||||
|
||||
filament::Texture* ftex = (filament::Texture*)tex->TexID;
|
||||
mEngine->destroy(ftex);
|
||||
tex->SetTexID(ImTextureID_Invalid);
|
||||
tex->SetStatus(ImTextureStatus_Destroyed);
|
||||
mImGuiTextures.erase(ftex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,19 +38,18 @@ void* getNativeWindow(SDL_Window* sdlWindow) {
|
||||
SDL_GetWindowSize(sdlWindow, &width, &height);
|
||||
|
||||
// Static is used here to allocate the struct pointer for the lifetime of the program.
|
||||
// Without static the valid struct quickyly goes out of scope, and ends with seemingly
|
||||
// random segfaults.
|
||||
// Without static the valid struct quickly goes out of scope, and ends with seemingly
|
||||
// random segfaults. We must update the values on each call.
|
||||
static struct {
|
||||
struct wl_display *display;
|
||||
struct wl_surface *surface;
|
||||
uint32_t width;
|
||||
uint32_t height;
|
||||
} wayland {
|
||||
wmi.info.wl.display,
|
||||
wmi.info.wl.surface,
|
||||
static_cast<uint32_t>(width),
|
||||
static_cast<uint32_t>(height)
|
||||
};
|
||||
} wayland;
|
||||
wayland.display = wmi.info.wl.display;
|
||||
wayland.surface = wmi.info.wl.surface;
|
||||
wayland.width = static_cast<uint32_t>(width);
|
||||
wayland.height = static_cast<uint32_t>(height);
|
||||
return (void *) &wayland;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -18,25 +18,125 @@
|
||||
|
||||
#include <benchmark/benchmark.h>
|
||||
|
||||
#include <utils/memalign.h>
|
||||
|
||||
static void BM_memcpy(benchmark::State& state) {
|
||||
char* src = new char[state.range(0)];
|
||||
char* dst = new char[state.range(0)];
|
||||
memset(src, 'x', (size_t)state.range(0));
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
{
|
||||
PerformanceCounters pc(state);
|
||||
for (auto _ : state) {
|
||||
memcpy(dst, src, (size_t)state.range(0));
|
||||
benchmark::DoNotOptimize(dst);
|
||||
benchmark::DoNotOptimize(src);
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
}
|
||||
state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(state.range(0)));
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
delete[] src;
|
||||
delete[] dst;
|
||||
#ifdef __linux__
|
||||
#include <sched.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
// Returns true if successful, false if failed
|
||||
static bool pinThreadToCore(int core_id) {
|
||||
#ifdef __linux__
|
||||
cpu_set_t cpuset;
|
||||
CPU_ZERO(&cpuset);
|
||||
CPU_SET(core_id, &cpuset);
|
||||
|
||||
// 0 = current thread
|
||||
int const result = sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
|
||||
return (result == 0);
|
||||
#else
|
||||
(void)core_id;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
BENCHMARK(BM_memcpy)->Range(8, 8192<<10)->Threads(1)->Threads(8);
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
static constexpr size_t ALIGNMENT = 16 << 10;
|
||||
|
||||
template<class T, size_t Align>
|
||||
struct AlignedAllocator {
|
||||
using value_type = T;
|
||||
|
||||
template<class U>
|
||||
struct rebind {
|
||||
using other = AlignedAllocator<U, Align>;
|
||||
};
|
||||
|
||||
AlignedAllocator() noexcept = default;
|
||||
|
||||
template<class U>
|
||||
explicit AlignedAllocator(const AlignedAllocator<U, Align>&) noexcept {}
|
||||
|
||||
T* allocate(size_t const n) {
|
||||
size_t const bytes = n * sizeof(T);
|
||||
// std::aligned_alloc requires size to be a multiple of alignment.
|
||||
size_t const aligned_bytes = ((bytes + Align - 1) / Align) * Align;
|
||||
void* ptr = utils::aligned_alloc(aligned_bytes, Align);
|
||||
return static_cast<T*>(ptr);
|
||||
}
|
||||
|
||||
void deallocate(T* p, size_t) noexcept {
|
||||
utils::aligned_free(p);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using PageAlignedVector = std::vector<T, AlignedAllocator<T, ALIGNMENT>>;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
static void BM_memcpy(benchmark::State& state) {
|
||||
// pinThreadToCore(7);
|
||||
int64_t const size = state.range(0);
|
||||
PageAlignedVector<char> src(size);
|
||||
PageAlignedVector<char> dst(size);
|
||||
|
||||
// make all these pages resident
|
||||
memset(src.data(), 0, size);
|
||||
memset(dst.data(), 0, size);
|
||||
benchmark::ClobberMemory();
|
||||
|
||||
{
|
||||
// PerformanceCounters const pc(state);
|
||||
for (auto _: state) {
|
||||
memcpy(dst.data(), src.data(), size);
|
||||
benchmark::DoNotOptimize(dst);
|
||||
benchmark::DoNotOptimize(src);
|
||||
}
|
||||
}
|
||||
state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(size));
|
||||
}
|
||||
|
||||
static void BM_memset(benchmark::State& state) {
|
||||
//pinThreadToCore(7);
|
||||
int64_t const size = state.range(0);
|
||||
PageAlignedVector<char> src(size);
|
||||
|
||||
// make all these pages resident
|
||||
memset(src.data(), 0, size);
|
||||
benchmark::ClobberMemory();
|
||||
|
||||
{
|
||||
// PerformanceCounters const pc(state);
|
||||
for (auto _: state) {
|
||||
memset(src.data(), 0, size);
|
||||
benchmark::DoNotOptimize(src);
|
||||
}
|
||||
}
|
||||
state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(size));
|
||||
}
|
||||
|
||||
BENCHMARK(BM_memcpy)
|
||||
->DenseRange( 4<<10, 128<<10, 8<<10)
|
||||
->DenseRange(128<<10, 4<<20, 128<<10)
|
||||
->DenseRange( 4<<20, 16<<20, 1<<20)
|
||||
->DenseRange( 16<<20, 32<<20, 2<<20)
|
||||
;
|
||||
|
||||
BENCHMARK(BM_memset)
|
||||
->DenseRange( 4<<10, 128<<10, 8<<10)
|
||||
->DenseRange(128<<10, 4<<20, 128<<10)
|
||||
->DenseRange( 4<<20, 16<<20, 1<<20)
|
||||
->DenseRange( 16<<20, 32<<20, 2<<20)
|
||||
;
|
||||
|
||||
@@ -37,7 +37,6 @@ set(MATERIAL_SRCS
|
||||
materials/sandboxLitSolidRefractionSsr.mat
|
||||
materials/sandboxSpecGloss.mat
|
||||
materials/sandboxSubsurface.mat
|
||||
materials/sandboxTransformName.mat
|
||||
materials/sandboxUnlit.mat
|
||||
materials/texturedLit.mat
|
||||
)
|
||||
|
||||
@@ -42,7 +42,8 @@ def _render_single_model(gltf_viewer, test_json_path, named_output_dir,
|
||||
env |= {
|
||||
'VK_ICD_FILENAMES': vk_icd,
|
||||
'VK_DRIVER_FILES': vk_icd,
|
||||
'VK_LOADER_DEBUG': 'all',
|
||||
# Uncomment when there's a problem finding the vk driver
|
||||
# 'VK_LOADER_DEBUG': 'all',
|
||||
}
|
||||
|
||||
out_name = f'{test_name}.{backend}.{model}'
|
||||
|
||||
@@ -8,6 +8,6 @@ itsdangerous==2.2.0
|
||||
Jinja2==3.1.6
|
||||
MarkupSafe==3.0.2
|
||||
requests==2.32.5
|
||||
urllib3==2.5.0
|
||||
urllib3==2.6.0
|
||||
waitress==3.0.2
|
||||
Werkzeug==3.1.4
|
||||
|
||||
+11
-6
@@ -2,8 +2,6 @@ project(basisu)
|
||||
|
||||
cmake_minimum_required(VERSION 3.19)
|
||||
|
||||
set(ZSTD zstd-basisu)
|
||||
|
||||
set(TRANSCODER_SRC
|
||||
../transcoder/basisu_transcoder.cpp
|
||||
)
|
||||
@@ -52,21 +50,28 @@ else()
|
||||
set (BASIS_CONFIG ${BASIS_CONFIG} BASISD_SUPPORT_DXT5A=0 BASISD_SUPPORT_DXT1=0)
|
||||
endif()
|
||||
|
||||
add_library(${ZSTD} ../zstd/zstd.c)
|
||||
# If an external zstd target exists, use it.
|
||||
if(NOT TARGET zstd)
|
||||
add_library(zstd-basisu ../zstd/zstd.c)
|
||||
target_include_directories(zstd-basisu PUBLIC ../zstd)
|
||||
set_target_properties(zstd-basisu PROPERTIES FOLDER ThirdParty)
|
||||
set(ZSTD_TARGET zstd-basisu)
|
||||
else()
|
||||
set(ZSTD_TARGET zstd)
|
||||
endif()
|
||||
|
||||
add_library(basis_encoder ${ENCODER_SRC})
|
||||
add_library(basis_transcoder ${TRANSCODER_SRC})
|
||||
|
||||
target_link_libraries(basis_transcoder PRIVATE ${ZSTD})
|
||||
target_link_libraries(basis_transcoder PRIVATE ${ZSTD_TARGET})
|
||||
target_link_libraries(basis_encoder basis_transcoder)
|
||||
|
||||
target_include_directories(${ZSTD} PUBLIC ../zstd)
|
||||
target_include_directories(basis_encoder PUBLIC ../encoder)
|
||||
target_include_directories(basis_transcoder PUBLIC ../transcoder)
|
||||
|
||||
target_compile_definitions(basis_encoder PRIVATE ${BASIS_CONFIG})
|
||||
target_compile_definitions(basis_transcoder PRIVATE ${BASIS_CONFIG})
|
||||
|
||||
set_target_properties(${ZSTD} PROPERTIES FOLDER ThirdParty)
|
||||
set_target_properties(basis_encoder PROPERTIES FOLDER ThirdParty)
|
||||
set_target_properties(basis_transcoder PROPERTIES FOLDER ThirdParty)
|
||||
|
||||
|
||||
@@ -125,10 +125,12 @@ Filament.loadClassExtensions = function() {
|
||||
|
||||
/// createMaterial ::method::
|
||||
/// package ::argument:: asset string, or Uint8Array, or [Buffer] with filamat contents
|
||||
/// options ::argument:: optional dictionary with `uboBatching` key.
|
||||
/// ::retval:: an instance of [createMaterial]
|
||||
Filament.Engine.prototype.createMaterial = function(buffer) {
|
||||
Filament.Engine.prototype.createMaterial = function(buffer, options) {
|
||||
buffer = getBufferDescriptor(buffer);
|
||||
const result = this._createMaterial(buffer);
|
||||
const uboBatching = (options && options.uboBatching) || Filament.Material$UboBatchingMode.DEFAULT; // The default is DEFAULT
|
||||
const result = this._createMaterial(buffer, uboBatching);
|
||||
buffer.delete();
|
||||
return result;
|
||||
};
|
||||
|
||||
Vendored
+5
-1
@@ -375,6 +375,10 @@ export class Material {
|
||||
public getName(): string;
|
||||
}
|
||||
|
||||
export enum Material$UboBatchingMode {
|
||||
DISABLED,
|
||||
DEFAULT,
|
||||
}
|
||||
export class Frustum {
|
||||
constructor(pv: mat4);
|
||||
public setProjection(pv: mat4): void;
|
||||
@@ -542,7 +546,7 @@ export class Engine {
|
||||
public static destroy(engine: Engine): void;
|
||||
public execute(): void;
|
||||
public createCamera(entity: Entity): Camera;
|
||||
public createMaterial(urlOrBuffer: BufferReference): Material;
|
||||
public createMaterial(urlOrBuffer: BufferReference, options?: { uboBatching?: Material$UboBatchingMode }): Material;
|
||||
public createRenderer(): Renderer;
|
||||
public createScene(): Scene;
|
||||
public createSwapChain(): SwapChain;
|
||||
|
||||
@@ -495,8 +495,11 @@ class_<Engine>("Engine")
|
||||
(Engine* engine, utils::Entity camera) { engine->destroyCameraComponent(camera); },
|
||||
allow_raw_pointers())
|
||||
|
||||
.function("_createMaterial", EMBIND_LAMBDA(Material*, (Engine* engine, BufferDescriptor mbd), {
|
||||
return Material::Builder().package(mbd.bd->buffer, mbd.bd->size).build(*engine);
|
||||
.function("_createMaterial", EMBIND_LAMBDA(Material*, (Engine* engine, BufferDescriptor mbd, Material::UboBatchingMode uboBatching), {
|
||||
return Material::Builder()
|
||||
.package(mbd.bd->buffer, mbd.bd->size)
|
||||
.uboBatching(uboBatching)
|
||||
.build(*engine);
|
||||
}), allow_raw_pointers())
|
||||
/// destroyMaterial ::method::
|
||||
/// material ::argument:: an instance of [Material]
|
||||
@@ -1360,6 +1363,10 @@ class_<Material>("Material")
|
||||
return transformName ? std::string(transformName) : std::string();
|
||||
}), allow_raw_pointers());
|
||||
|
||||
enum_<Material::UboBatchingMode>("Material$UboBatchingMode")
|
||||
.value("DISABLED", Material::UboBatchingMode::DISABLED)
|
||||
.value("DEFAULT", Material::UboBatchingMode::DEFAULT);
|
||||
|
||||
class_<MaterialInstance>("MaterialInstance")
|
||||
.function("getName", EMBIND_LAMBDA(std::string, (MaterialInstance* self), {
|
||||
return std::string(self->getName());
|
||||
|
||||
Reference in New Issue
Block a user