Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d49eb5e111 | ||
|
|
8812a35cbd | ||
|
|
35d7b819e1 | ||
|
|
9b2959d3a4 |
+5
-10
@@ -474,12 +474,12 @@ if (NOT MSVC)
|
||||
endif()
|
||||
|
||||
if (ANDROID OR WASM)
|
||||
# On Android and WebGL RELEASE builds, we omit unwind info to save space.
|
||||
# On Android and WASM RELEASE builds, we omit unwind info to save space.
|
||||
# (We keep unwind info on iOS to allow readable stack traces in crash reports.)
|
||||
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-unwind-tables -fno-asynchronous-unwind-tables")
|
||||
endif()
|
||||
|
||||
# With WebGL, we disable RTTI because we pass emscripten::val back and forth
|
||||
# With WASM, we disable RTTI because we pass emscripten::val back and forth
|
||||
# between C++ and JavaScript in order to efficiently access typed arrays, which are unbound.
|
||||
# NOTE: This is not documented in emscripten so we should consider a different approach.
|
||||
if (WASM)
|
||||
@@ -603,7 +603,7 @@ if (FILAMENT_SUPPORTS_WEBP_TEXTURES)
|
||||
add_definitions(-DFILAMENT_SUPPORTS_WEBP_TEXTURES)
|
||||
endif()
|
||||
|
||||
# Build with Metal support on non-WebGL Apple platforms.
|
||||
# Build with Metal support on non-WASM Apple platforms.
|
||||
if (APPLE AND NOT WASM)
|
||||
option(FILAMENT_SUPPORTS_METAL "Include the Metal backend" ON)
|
||||
else()
|
||||
@@ -691,11 +691,6 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
set(MATC_API_FLAGS ${MATC_API_FLAGS} -a webgpu --variant-filter=stereo)
|
||||
endif()
|
||||
|
||||
# Disable ESSL 1.0 code generation.
|
||||
if (NOT FILAMENT_ENABLE_FEATURE_LEVEL_0)
|
||||
set(MATC_API_FLAGS ${MATC_API_FLAGS} -1)
|
||||
endif()
|
||||
|
||||
# Enable debug info (preserves names in SPIR-V)
|
||||
if (FILAMENT_ENABLE_MATDBG)
|
||||
set(MATC_OPT_FLAGS ${MATC_OPT_FLAGS} -d)
|
||||
@@ -974,7 +969,7 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
||||
add_subdirectory(${EXTERNAL}/spirv-headers)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
if (FILAMENT_SUPPORTS_WEBGPU AND NOT WASM)
|
||||
add_subdirectory(${EXTERNAL}/dawn/tnt/)
|
||||
endif()
|
||||
|
||||
@@ -1026,7 +1021,7 @@ if (IS_HOST_PLATFORM)
|
||||
add_subdirectory(${TOOLS}/specgen)
|
||||
endif()
|
||||
|
||||
# Generate exported executables for cross-compiled builds (Android, WebGL, and iOS)
|
||||
# Generate exported executables for cross-compiled builds (Android, WASM, and iOS)
|
||||
if ((NOT CMAKE_CROSSCOMPILING AND NOT FILAMENT_IMPORT_PREBUILT_EXECUTABLES) OR FILAMENT_EXPORT_PREBUILT_EXECUTABLES)
|
||||
export(TARGETS matc cmgen filamesh mipgen resgen uberz glslminifier FILE ${IMPORT_EXECUTABLES})
|
||||
endif()
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
#include <filament/Texture.h>
|
||||
#include <filament/TextureSampler.h>
|
||||
|
||||
#include "common/CallbackUtils.h"
|
||||
|
||||
#include <math/mat3.h>
|
||||
#include <math/mat4.h>
|
||||
#include <math/vec2.h>
|
||||
@@ -258,69 +256,6 @@ Java_com_google_android_filament_MaterialInstance_nSetFloatParameterArray(JNIEnv
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nGetConstantBool(JNIEnv *env, jclass,
|
||||
jlong nativeMaterialInstance, jstring name_) {
|
||||
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
|
||||
const char *name = env->GetStringUTFChars(name_, 0);
|
||||
jboolean result = instance->getConstant<bool>(name);
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jfloat JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nGetConstantFloat(JNIEnv *env, jclass,
|
||||
jlong nativeMaterialInstance, jstring name_) {
|
||||
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
|
||||
const char *name = env->GetStringUTFChars(name_, 0);
|
||||
jfloat result = instance->getConstant<float>(name);
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jint JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nGetConstantInt(JNIEnv *env, jclass,
|
||||
jlong nativeMaterialInstance, jstring name_) {
|
||||
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
|
||||
const char *name = env->GetStringUTFChars(name_, 0);
|
||||
jint result = instance->getConstant<int32_t>(name);
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
return result;
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nSetConstantBool(JNIEnv *env, jclass,
|
||||
jlong nativeMaterialInstance, jstring name_, jboolean x) {
|
||||
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
|
||||
const char *name = env->GetStringUTFChars(name_, 0);
|
||||
instance->setConstant<bool>(name, x);
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nSetConstantFloat(JNIEnv *env, jclass,
|
||||
jlong nativeMaterialInstance, jstring name_, jfloat x) {
|
||||
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
|
||||
const char *name = env->GetStringUTFChars(name_, 0);
|
||||
instance->setConstant<float>(name, x);
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nSetConstantInt(JNIEnv *env, jclass,
|
||||
jlong nativeMaterialInstance, jstring name_, jint x) {
|
||||
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
|
||||
const char *name = env->GetStringUTFChars(name_, 0);
|
||||
instance->setConstant<int32_t>(name, x);
|
||||
env->ReleaseStringUTFChars(name_, name);
|
||||
}
|
||||
|
||||
// defined in TextureSampler.cpp
|
||||
namespace filament::JniUtils {
|
||||
TextureSampler from_long(jlong params) noexcept;
|
||||
@@ -657,17 +592,3 @@ Java_com_google_android_filament_MaterialInstance_nGetTransparencyMode(JNIEnv*,
|
||||
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
|
||||
return (jint) instance->getTransparencyMode();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_MaterialInstance_nCompile(JNIEnv *env, jclass clazz,
|
||||
jlong nativeMaterialInstance, jint priority, jint variants, jobject handler, jobject runnable) {
|
||||
MaterialInstance* materialInstance = (MaterialInstance*) nativeMaterialInstance;
|
||||
JniCallback* jniCallback = JniCallback::make(env, handler, runnable);
|
||||
materialInstance->compile(
|
||||
(MaterialInstance::CompilerPriorityQueue) priority,
|
||||
(UserVariantFilterBit) variants,
|
||||
jniCallback->getHandler(), [jniCallback](MaterialInstance*){
|
||||
JniCallback::postToJavaAndDestroy(jniCallback);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -523,19 +523,11 @@ public class Material {
|
||||
* for stereoscopic rendering. If an application is not planning to render in stereo, this bit
|
||||
* should be turned off to avoid unnecessary material compilations.
|
||||
*</p>
|
||||
*<p>
|
||||
* Note that it is possible to override specialization constants on a per-MaterialInstance basis
|
||||
* (see {@link MaterialInstance#setConstant}). In that case, the programs compiled by a call to
|
||||
* Material::compile() may not be reusable by that MaterialInstance. It's better to call
|
||||
* MaterialInstance::compile() in cases where you intend to override specialization constants.
|
||||
*</p>
|
||||
* @param priority Which priority queue to use, LOW or HIGH.
|
||||
* @param variants Variants to include to the compile command.
|
||||
* @param handler An {@link java.util.concurrent.Executor Executor}. On Android this can also be a {@link android.os.Handler Handler}.
|
||||
* @param callback callback called on the main thread when the compilation is done on
|
||||
* by backend.
|
||||
*
|
||||
* @see MaterialInstance#compile
|
||||
*/
|
||||
public void compile(@NonNull CompilerPriorityQueue priority,
|
||||
int variants,
|
||||
|
||||
-98
@@ -18,7 +18,6 @@ package com.google.android.filament;
|
||||
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.Size;
|
||||
|
||||
import com.google.android.filament.proguard.UsedByNative;
|
||||
@@ -143,28 +142,6 @@ public class MaterialInstance {
|
||||
return mMaterial;
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously ensures that a subset of this MaterialInstance's variants are compiled.
|
||||
*
|
||||
* <p>This function behaves identically to {@link Material#compile}, but takes into account
|
||||
* the specific constants overridden by {@link #setConstant}.</p>
|
||||
*
|
||||
* @param priority Priority of the compile command.
|
||||
* @param variants Variants to include to the compile command.
|
||||
* @param handler An {@link java.util.concurrent.Executor Executor}. On Android this can also be a {@link android.os.Handler Handler}.
|
||||
* @param callback callback called on the main thread when the compilation is done on
|
||||
* by backend.
|
||||
*
|
||||
* @see Material#compile
|
||||
* @see #setConstant
|
||||
*/
|
||||
public void compile(@NonNull Material.CompilerPriorityQueue priority,
|
||||
int variants,
|
||||
@Nullable Object handler,
|
||||
@Nullable Runnable callback) {
|
||||
nCompile(getNativeObject(), priority.ordinal(), variants, handler, callback);
|
||||
}
|
||||
|
||||
/** @return the name associated with this instance */
|
||||
@NonNull
|
||||
public String getName() {
|
||||
@@ -443,69 +420,6 @@ public class MaterialInstance {
|
||||
nSetParameterFloat4(getNativeObject(), name, color[0], color[1], color[2], color[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides a specialization constant of this material instance.
|
||||
*
|
||||
* @param name The name of the constant as defined in the material.
|
||||
* @param value The value of the constant.
|
||||
* @see Material.Builder#constant
|
||||
*/
|
||||
public void setConstant(@NonNull String name, boolean value) {
|
||||
nSetConstantBool(getNativeObject(), name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides a specialization constant of this material instance.
|
||||
*
|
||||
* @param name The name of the constant as defined in the material.
|
||||
* @param value The value of the constant.
|
||||
* @see Material.Builder#constant
|
||||
*/
|
||||
public void setConstant(@NonNull String name, float value) {
|
||||
nSetConstantFloat(getNativeObject(), name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides a specialization constant of this material instance.
|
||||
*
|
||||
* @param name The name of the constant as defined in the material.
|
||||
* @param value The value of the constant.
|
||||
* @see Material.Builder#constant
|
||||
*/
|
||||
public void setConstant(@NonNull String name, int value) {
|
||||
nSetConstantInt(getNativeObject(), name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a specialization constant by name.
|
||||
*
|
||||
* @param name The name of the constant as defined in the material.
|
||||
* @return The value of the constant.
|
||||
*/
|
||||
public boolean getConstantBoolean(@NonNull String name) {
|
||||
return nGetConstantBool(getNativeObject(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a specialization constant by name.
|
||||
*
|
||||
* @param name The name of the constant as defined in the material.
|
||||
* @return The value of the constant.
|
||||
*/
|
||||
public float getConstantFloat(@NonNull String name) {
|
||||
return nGetConstantFloat(getNativeObject(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a specialization constant by name.
|
||||
*
|
||||
* @param name The name of the constant as defined in the material.
|
||||
* @return The value of the constant.
|
||||
*/
|
||||
public int getConstantInt(@NonNull String name) {
|
||||
return nGetConstantInt(getNativeObject(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set-up a custom scissor rectangle; by default it is disabled.
|
||||
*
|
||||
@@ -1041,17 +955,6 @@ public class MaterialInstance {
|
||||
@NonNull String name, int element, @NonNull @Size(min = 1) float[] v,
|
||||
@IntRange(from = 0) int offset, @IntRange(from = 1) int count);
|
||||
|
||||
private static native boolean nGetConstantBool(long nativeMaterialInstance, @NonNull String name);
|
||||
private static native float nGetConstantFloat(long nativeMaterialInstance, @NonNull String name);
|
||||
private static native int nGetConstantInt(long nativeMaterialInstance, @NonNull String name);
|
||||
|
||||
private static native void nSetConstantBool(long nativeMaterialInstance,
|
||||
@NonNull String name, boolean x);
|
||||
private static native void nSetConstantFloat(long nativeMaterialInstance,
|
||||
@NonNull String name, float x);
|
||||
private static native void nSetConstantInt(long nativeMaterialInstance,
|
||||
@NonNull String name, int x);
|
||||
|
||||
private static native void nSetParameterTexture(long nativeMaterialInstance,
|
||||
@NonNull String name, long nativeTexture, long sampler);
|
||||
|
||||
@@ -1115,5 +1018,4 @@ public class MaterialInstance {
|
||||
private static native int nGetDepthFunc(long nativeMaterialInstance);
|
||||
private static native void nSetTransparencyMode(long nativeMaterialInstance, int mode);
|
||||
private static native int nGetTransparencyMode(long nativeMaterialInstance);
|
||||
private static native void nCompile(long nativeMaterialInstance, int priority, int variants, Object handler, Runnable callback);
|
||||
}
|
||||
|
||||
@@ -368,6 +368,11 @@ function build_wasm_with_target {
|
||||
ISSUE_CMAKE_ALWAYS=true
|
||||
fi
|
||||
|
||||
if [[ "${WEBGPU_OPTION}" == *"-DFILAMENT_SUPPORTS_WEBGPU=ON"* ]]; then
|
||||
WEBGPU_OPTION="${WEBGPU_OPTION} -DCMAKE_CXX_FLAGS=\"--use-port=emdawnwebgpu\""
|
||||
WEBGPU_OPTION="${WEBGPU_OPTION} -DCMAKE_C_FLAGS=\"--use-port=emdawnwebgpu\""
|
||||
fi
|
||||
|
||||
if [[ ! -d "CMakeFiles" ]] || [[ "${ISSUE_CMAKE_ALWAYS}" == "true" ]]; then
|
||||
# Apply the emscripten environment within a subshell.
|
||||
(
|
||||
@@ -1027,7 +1032,8 @@ while getopts ":hacCfgimp:q:uvWslwedtk:bVx:S:X:Py:E" opt; do
|
||||
echo "Consider using -c after changing this option to clear the Gradle cache."
|
||||
;;
|
||||
W)
|
||||
WEBGPU_OPTION="-DFILAMENT_SUPPORTS_WEBGPU=ON"
|
||||
WEBGPU_OPTION='-DFILAMENT_SUPPORTS_WEBGPU=ON'
|
||||
|
||||
WEBGPU_ANDROID_GRADLE_OPTION="-Pcom.google.android.filament.include-webgpu"
|
||||
echo "Enable support for WebGPU(Experimental) in the core Filament library."
|
||||
;;
|
||||
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -358,6 +358,11 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
include/backend/platforms/WebGPUPlatformAndroid.h
|
||||
src/webgpu/platform/WebGPUPlatformAndroid.cpp
|
||||
)
|
||||
elseif (WASM)
|
||||
list(APPEND SRCS
|
||||
include/backend/platforms/WebGPUPlatformWasm.h
|
||||
src/webgpu/platform/WebGPUPlatformWasm.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if (TNT_DEV)
|
||||
@@ -431,7 +436,7 @@ if (FILAMENT_USE_ABSEIL_LOGGING)
|
||||
target_link_libraries(${TARGET} PRIVATE absl::log)
|
||||
endif()
|
||||
|
||||
# Android, iOS, and WebGL do not use bluegl.
|
||||
# Android, iOS, and WASM do not use bluegl.
|
||||
if(FILAMENT_SUPPORTS_OPENGL AND NOT IOS AND NOT ANDROID AND NOT WASM)
|
||||
target_link_libraries(${TARGET} PRIVATE bluegl)
|
||||
endif()
|
||||
@@ -441,7 +446,7 @@ if (FILAMENT_SUPPORTS_VULKAN)
|
||||
target_link_libraries(${TARGET} PRIVATE SPIRV-Headers)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
if (FILAMENT_SUPPORTS_WEBGPU AND NOT WASM)
|
||||
target_link_libraries(${TARGET} PRIVATE webgpu_dawn dawncpp_headers)
|
||||
endif()
|
||||
|
||||
@@ -559,7 +564,7 @@ install(TARGETS ${TARGET} ${INSTALL_TYPE} DESTINATION lib/${DIST_DIR})
|
||||
install(TARGETS ${INSTALL_TYPE} DESTINATION lib/${DIST_DIR})
|
||||
install(DIRECTORY ${PUBLIC_HDR_DIR}/backend DESTINATION include)
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
if (FILAMENT_SUPPORTS_WEBGPU AND NOT WASM)
|
||||
install(TARGETS webgpu_dawn ${INSTALL_TYPE} DESTINATION lib/${DIST_DIR})
|
||||
endif()
|
||||
|
||||
|
||||
@@ -31,6 +31,11 @@
|
||||
#endif
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
// We need to polyfill some Dawn extensions that are not in Emscripten.
|
||||
#include "WebGPUWasmPolyfill.h"
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
@@ -61,9 +66,9 @@ public:
|
||||
// either returns a valid surface or panics
|
||||
[[nodiscard]] virtual wgpu::Surface createSurface(void* nativeWindow, uint64_t flags) = 0;
|
||||
// either returns a valid adapter or panics
|
||||
[[nodiscard]] wgpu::Adapter requestAdapter(wgpu::Surface const& surface);
|
||||
[[nodiscard]] virtual wgpu::Adapter requestAdapter(wgpu::Surface const& surface);
|
||||
// either returns a valid device or panics
|
||||
[[nodiscard]] wgpu::Device requestDevice(wgpu::Adapter const& adapter);
|
||||
[[nodiscard]] virtual wgpu::Device requestDevice(wgpu::Adapter const& adapter);
|
||||
|
||||
struct Configuration {
|
||||
wgpu::BackendType forceBackendType = wgpu::BackendType::Undefined;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2026 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_PLATFORMS_WEBGPUPLATFORMWASM_H
|
||||
#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMWASM_H
|
||||
|
||||
#include <backend/platforms/WebGPUPlatform.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
class WebGPUPlatformWasm : public WebGPUPlatform {
|
||||
public:
|
||||
wgpu::Extent2D getSurfaceExtent(void* nativeWindow) const override;
|
||||
wgpu::Surface createSurface(void* nativeWindow, uint64_t /*flags*/) override;
|
||||
|
||||
wgpu::Adapter requestAdapter(wgpu::Surface const& surface) override;
|
||||
wgpu::Device requestDevice(wgpu::Adapter const& adapter) override;
|
||||
|
||||
static wgpu::Device sDevice;
|
||||
|
||||
protected:
|
||||
std::vector<wgpu::RequestAdapterOptions> getAdapterOptions() override;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORMWASM_H
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2026 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_WEBGPU_WASM_POLYFILL_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPU_WASM_POLYFILL_H
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace wgpu {
|
||||
|
||||
struct Extent2D {
|
||||
uint32_t width = 0;
|
||||
uint32_t height = 0;
|
||||
operator Extent3D() const { return {width, height, 1}; }
|
||||
};
|
||||
|
||||
struct Origin2D {
|
||||
uint32_t x = 0;
|
||||
uint32_t y = 0;
|
||||
operator Origin3D() const { return {x, y, 0}; }
|
||||
};
|
||||
|
||||
// b/508270158
|
||||
enum class ComponentSwizzle : uint32_t {
|
||||
Undefined = 0,
|
||||
Zero = 1,
|
||||
One = 2,
|
||||
R = 3,
|
||||
G = 4,
|
||||
B = 5,
|
||||
A = 6,
|
||||
};
|
||||
|
||||
struct TextureComponentSwizzle {
|
||||
ComponentSwizzle r = ComponentSwizzle::Undefined;
|
||||
ComponentSwizzle g = ComponentSwizzle::Undefined;
|
||||
ComponentSwizzle b = ComponentSwizzle::Undefined;
|
||||
ComponentSwizzle a = ComponentSwizzle::Undefined;
|
||||
};
|
||||
|
||||
struct DawnTogglesDescriptor : public ChainedStruct {
|
||||
uint32_t enabledToggleCount = 0;
|
||||
const char* const* enabledToggles = nullptr;
|
||||
};
|
||||
|
||||
struct DawnAdapterPropertiesPowerPreference : public ChainedStructOut {
|
||||
PowerPreference powerPreference = PowerPreference::Undefined;
|
||||
};
|
||||
|
||||
} // namespace wgpu
|
||||
|
||||
#endif // __EMSCRIPTEN__
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_WEBGPU_WASM_POLYFILL_H
|
||||
@@ -30,6 +30,8 @@
|
||||
#include "backend/platforms/WebGPUPlatformLinux.h"
|
||||
#elif defined(WIN32)
|
||||
#include "backend/platforms/WebGPUPlatformWindows.h"
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
#include "backend/platforms/WebGPUPlatformWasm.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -152,6 +154,8 @@ Platform* PlatformFactory::create(Backend* backend) noexcept {
|
||||
return new WebGPUPlatformLinux();
|
||||
#elif defined(WIN32)
|
||||
return new WebGPUPlatformWindows();
|
||||
#elif defined(__EMSCRIPTEN__)
|
||||
return new WebGPUPlatformWasm();
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
@@ -209,3 +213,4 @@ void PlatformFactory::destroy(Platform** platform) noexcept {
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <backend/platforms/WebGPUWasmPolyfill.h>
|
||||
#endif
|
||||
|
||||
#include <tsl/robin_map.h>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
|
||||
@@ -64,6 +64,22 @@
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
|
||||
// https://issues.chromium.org/issues/507581790
|
||||
// Manual polyfill pending upstream fix.
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <emscripten/em_js.h>
|
||||
|
||||
EM_JS(void, wgpuRenderPassEncoderSetImmediates, (WGPURenderPassEncoder passEncoder,
|
||||
uint32_t offset, void const * data, size_t size), {
|
||||
const encoder = WebGPU.Internals.jsObjects[passEncoder];
|
||||
if (encoder && encoder.setImmediates) {
|
||||
const buffer = HEAPU8.slice(data, data + size);
|
||||
encoder.setImmediates(offset, buffer);
|
||||
}
|
||||
})
|
||||
#endif
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
@@ -147,8 +163,10 @@ void WebGPUDriver::terminate() {
|
||||
}
|
||||
|
||||
void WebGPUDriver::tick(int) {
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
mDevice.Tick();
|
||||
mAdapter.GetInstance().ProcessEvents();
|
||||
#endif
|
||||
}
|
||||
|
||||
void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
@@ -198,7 +216,9 @@ void WebGPUDriver::finish(int /* dummy */) {
|
||||
// processed. Note that blocking with mReadPixelMapsCounter.waitForAllToFinish will only
|
||||
// deadlock since we could not advance the counter.
|
||||
while (!mReadPixelMapsCounter.isIdle()) {
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
mAdapter.GetInstance().ProcessEvents();
|
||||
#endif
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
}
|
||||
@@ -782,7 +802,7 @@ FenceStatus WebGPUDriver::fenceWait(FenceHandle fenceHandle, uint64_t const time
|
||||
if (!fence) {
|
||||
return FenceStatus::ERROR;
|
||||
}
|
||||
|
||||
|
||||
using namespace std::chrono;
|
||||
auto now = steady_clock::now();
|
||||
steady_clock::time_point until = steady_clock::time_point::max();
|
||||
@@ -800,15 +820,15 @@ FenceStatus WebGPUDriver::fenceWait(FenceHandle fenceHandle, uint64_t const time
|
||||
state = fence->getState();
|
||||
return bool(state);
|
||||
}, until);
|
||||
|
||||
|
||||
if (status == FenceStatus::ERROR) {
|
||||
return FenceStatus::ERROR;
|
||||
}
|
||||
|
||||
|
||||
if (status == FenceStatus::TIMEOUT_EXPIRED) {
|
||||
return FenceStatus::TIMEOUT_EXPIRED;
|
||||
}
|
||||
|
||||
|
||||
auto duration_ns = static_cast<uint64_t>(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(steady_clock::now() - now)
|
||||
.count());
|
||||
@@ -837,7 +857,11 @@ bool WebGPUDriver::isTextureFormatSupported(const TextureFormat format) {
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isTextureSwizzleSupported() {
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
return false;
|
||||
#else
|
||||
return mDevice.HasFeature(wgpu::FeatureName::TextureComponentSwizzle);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isTextureFormatMipmappable(const TextureFormat format) {
|
||||
@@ -859,7 +883,7 @@ bool WebGPUDriver::isTextureFormatFilterable(TextureFormat format) {
|
||||
if (isFp32ColorFormat(format)) {
|
||||
return mDevice.HasFeature(wgpu::FeatureName::Float32Filterable);
|
||||
}
|
||||
if (isUnsignedIntFormat(format) || isSignedIntFormat(format) ||
|
||||
if (isUnsignedIntFormat(format) || isSignedIntFormat(format) ||
|
||||
isDepthFormat(format) || isStencilFormat(format)) {
|
||||
return false;
|
||||
}
|
||||
@@ -1324,7 +1348,8 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> renderTargetHandle,
|
||||
const uint8_t mipLevel = colorInfos[i].level;
|
||||
const uint32_t arrayLayer = colorInfos[i].layer;
|
||||
customColorViews[customColorViewCount] =
|
||||
colorTexture->makeAttachmentTextureView(mipLevel, arrayLayer, renderTarget->getLayerCount());
|
||||
colorTexture->makeAttachmentTextureView(mipLevel, arrayLayer,
|
||||
renderTarget->getLayerCount());
|
||||
if (msaaSidecarsRequired) {
|
||||
const wgpu::TextureView msaaSidecarView{
|
||||
colorTexture->makeMsaaSidecarTextureViewIfTextureSidecarExists(
|
||||
@@ -1483,7 +1508,12 @@ void WebGPUDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
} else if (std::holds_alternative<bool>(value)) {
|
||||
data = std::get<bool>(value) ? 1 : 0;
|
||||
}
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
wgpuRenderPassEncoderSetImmediates(mRenderPassEncoder.Get(), index * sizeof(uint32_t), &data,
|
||||
sizeof(uint32_t));
|
||||
#else
|
||||
mRenderPassEncoder.SetImmediates(index * sizeof(uint32_t), &data, sizeof(uint32_t));
|
||||
#endif
|
||||
}
|
||||
|
||||
void WebGPUDriver::insertEventMarker(char const* string) {
|
||||
|
||||
@@ -168,8 +168,10 @@ private:
|
||||
wgpu::TextureFormat::Undefined //
|
||||
}; // 32 : 328
|
||||
};
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
static_assert(sizeof(RenderPipelineKey) == 360,
|
||||
"RenderPipelineKey must not have implicit padding.");
|
||||
#endif
|
||||
static_assert(std::is_trivially_copyable<RenderPipelineKey>::value,
|
||||
"RenderPipelineKey must be a trivially copyable POD for fast hashing.");
|
||||
|
||||
|
||||
@@ -28,6 +28,33 @@
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
// https://issues.chromium.org/issues/507581790
|
||||
// Manual polyfill pending upstream fix.
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <emscripten/em_js.h>
|
||||
|
||||
EM_JS(WGPUPipelineLayout, filamentCreatePipelineLayoutWithImmediateData,
|
||||
(WGPUDevice devicePtr, WGPUPipelineLayoutDescriptor const* descriptor), {
|
||||
const bglCount = HEAPU32[(descriptor >> 2) + 3];
|
||||
const bglPtr = HEAPU32[(descriptor >> 2) + 4];
|
||||
const bgls = [];
|
||||
for (let i = 0; i < bglCount; ++i) {
|
||||
bgls.push(WebGPU.getJsObject(HEAPU32[(bglPtr >> 2) + i]));
|
||||
}
|
||||
const desc = {
|
||||
"label": WebGPU.makeStringFromOptionalStringView(descriptor + 4),
|
||||
"bindGroupLayouts": bgls,
|
||||
"immediateSize": HEAPU32[(descriptor >> 2) + 5],
|
||||
};
|
||||
|
||||
const device = WebGPU.getJsObject(devicePtr);
|
||||
const ptr = _emwgpuCreatePipelineLayout(0);
|
||||
WebGPU.Internals.jsObjectInsert(ptr, device.createPipelineLayout(desc));
|
||||
return ptr;
|
||||
})
|
||||
#endif
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
WebGPUPipelineLayoutCache::WebGPUPipelineLayoutCache(wgpu::Device const& device)
|
||||
@@ -72,13 +99,28 @@ wgpu::PipelineLayout WebGPUPipelineLayoutCache::createPipelineLayout(
|
||||
wgpu::Limits supportedLimits{};
|
||||
mDevice.GetLimits(&supportedLimits);
|
||||
|
||||
uint32_t immediateSize = supportedLimits.maxImmediateSize;
|
||||
if (immediateSize == 0) {
|
||||
// Fallback: WebGPU implementations that support 'chromium-experimental-immediate-data'
|
||||
// might not expose 'maxImmediateSize' via JS limits yet, so we assume 64.
|
||||
immediateSize = 64;
|
||||
}
|
||||
|
||||
const wgpu::PipelineLayoutDescriptor descriptor{
|
||||
.label = wgpu::StringView(request.label.c_str_safe()),
|
||||
.bindGroupLayoutCount = request.bindGroupLayoutCount,
|
||||
.bindGroupLayouts = request.bindGroupLayouts.data(),
|
||||
.immediateSize = supportedLimits.maxImmediateSize,
|
||||
.immediateSize = immediateSize,
|
||||
};
|
||||
const wgpu::PipelineLayout layout{ mDevice.CreatePipelineLayout(&descriptor) };
|
||||
|
||||
const wgpu::PipelineLayout layout =
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
wgpu::PipelineLayout::Acquire(
|
||||
filamentCreatePipelineLayoutWithImmediateData(mDevice.Get(),
|
||||
reinterpret_cast<WGPUPipelineLayoutDescriptor const*>(&descriptor)));
|
||||
#else
|
||||
mDevice.CreatePipelineLayout(&descriptor);
|
||||
#endif
|
||||
FILAMENT_CHECK_POSTCONDITION(layout)
|
||||
<< "Failed to create pipeline layout " << descriptor.label << ".";
|
||||
return layout;
|
||||
|
||||
@@ -76,8 +76,10 @@ private:
|
||||
uint8_t bindGroupLayoutCount{ 0 }; // 1 :32
|
||||
uint8_t padding[7]{ 0 }; // 7 :33
|
||||
};
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
static_assert(sizeof(PipelineLayoutKey) == 40,
|
||||
"PipelineLayoutKey must not have implicit padding.");
|
||||
#endif
|
||||
static_assert(std::is_trivially_copyable<PipelineLayoutKey>::value,
|
||||
"PipelineLayoutKey must be a trivially copyable POD for fast hashing.");
|
||||
|
||||
|
||||
@@ -43,101 +43,6 @@ namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* Given the source code of a [WGSL] WebGPU shader in text (string view to it) and the constants
|
||||
* to be overwritten now (at runtime), returns updated [WGSL] shader code text, where the constants
|
||||
* have been replaced.
|
||||
* @param shaderLabel Something to call this shader for troubleshooting, error messaging, etc.
|
||||
* @param shaderSource The original WGSL WebGPU shader code as text to be processed. This is a
|
||||
* view to that text and this function does not change it; it is immutable.
|
||||
* Instead, the processed version of this source is returned by the function.
|
||||
* @param specConstants The constants to replace in the shader code, indexed by constant id.
|
||||
* @return Processed version of the WGSL WebGPU shader code provided, where the constants have
|
||||
* been replaced with the values provided by the specConstants parameter.
|
||||
*/
|
||||
[[nodiscard]] std::string replaceSpecConstants(std::string_view shaderLabel,
|
||||
std::string_view shaderSource,
|
||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specConstants) {
|
||||
// this function is not expected to be called at all if no spec constants are to be replaced
|
||||
assert_invariant(!specConstants.empty());
|
||||
static constexpr std::string_view specConstantPrefix = "FILAMENT_SPEC_CONST_";
|
||||
static constexpr size_t specConstantPrefixSize = specConstantPrefix.size();
|
||||
const char* const sourceData = shaderSource.data();
|
||||
std::stringstream processedShaderSource{};
|
||||
size_t pos = 0;
|
||||
while (pos < shaderSource.size()) {
|
||||
const size_t posOfNextSpecConstant = shaderSource.find(specConstantPrefix, pos);
|
||||
if (posOfNextSpecConstant == std::string::npos) {
|
||||
// no more spec constants, so just stream the rest of the source code string
|
||||
processedShaderSource << std::string_view(sourceData + pos, shaderSource.size() - pos);
|
||||
break;
|
||||
}
|
||||
const size_t posOfId = posOfNextSpecConstant + specConstantPrefixSize;
|
||||
const size_t posAfterId = shaderSource.find('_', posOfId);
|
||||
FILAMENT_CHECK_POSTCONDITION(posAfterId != std::string::npos)
|
||||
<< "malformed " << shaderLabel << ". Found spec constant prefix '"
|
||||
<< specConstantPrefix << "' without an id or '_' after it.";
|
||||
const std::string_view idStr =
|
||||
std::string_view(sourceData + posOfId, posAfterId - posOfId);
|
||||
const size_t posEndOfStatement = shaderSource.find(';', posAfterId);
|
||||
FILAMENT_CHECK_POSTCONDITION(posEndOfStatement != std::string::npos)
|
||||
<< "malformed " << shaderLabel << ". Found spec constant assignment with id "
|
||||
<< idStr << " without a terminating ';' character?";
|
||||
// this is a view into part of the statement, from after the id to the ';'
|
||||
const std::string_view statementSegment =
|
||||
std::string_view(sourceData + posAfterId, posEndOfStatement - posAfterId);
|
||||
size_t posOfEqual = statementSegment.find('=');
|
||||
if (posOfEqual == std::string::npos) {
|
||||
// Not an assignment, so we don't need to replace it.
|
||||
processedShaderSource << std::string_view(sourceData + pos,
|
||||
posEndOfStatement + 1 - pos);
|
||||
pos = posEndOfStatement + 1;
|
||||
continue;
|
||||
}
|
||||
posOfEqual += posAfterId; // position in original source overall, not just the segment
|
||||
int constantId = 0;
|
||||
char* endPtr;
|
||||
errno = 0; // Clear errno before the call
|
||||
|
||||
long tempConstantId = std::strtol(idStr.data(), &endPtr, 10);
|
||||
|
||||
// Check for conversion errors
|
||||
if (endPtr == idStr.data() || *endPtr != '\0' || errno == ERANGE) {
|
||||
// Parsing failed, or conversion was out of range for 'long'
|
||||
// or the string contained non-numeric characters after the number.
|
||||
PANIC_POSTCONDITION("Invalid spec constant id '%s' in %s (not a valid integer or out "
|
||||
"of range for 'long'?)",
|
||||
idStr.data(), shaderLabel.data());
|
||||
} else {
|
||||
// Check if the parsed long value fits into an int
|
||||
if (tempConstantId > std::numeric_limits<int>::max() ||
|
||||
tempConstantId < std::numeric_limits<int>::min()) {
|
||||
PANIC_POSTCONDITION(
|
||||
"Invalid spec constant id '%s' in %s (value out of range for 'int')",
|
||||
idStr.data(), shaderLabel.data());
|
||||
} else {
|
||||
constantId = static_cast<int>(tempConstantId);
|
||||
}
|
||||
}
|
||||
const std::variant<int32_t, float, bool> newValue = specConstants[constantId];
|
||||
// stream up to the equal sign...
|
||||
processedShaderSource << std::string_view(sourceData + pos, posOfEqual + 1 - pos);
|
||||
// stream the new value...
|
||||
if (auto* v = std::get_if<int32_t>(&newValue)) {
|
||||
processedShaderSource << " " << *v << "i";
|
||||
} else if (auto* f = std::get_if<float>(&newValue)) {
|
||||
processedShaderSource << " " << *f << "f";
|
||||
} else if (auto* b = std::get_if<bool>(&newValue)) {
|
||||
processedShaderSource << " " << ((*b) ? "true" : "false");
|
||||
}
|
||||
// end the statement...
|
||||
processedShaderSource << ";";
|
||||
// and skip to after the end of the statement in the original source and continue...
|
||||
pos = posEndOfStatement + 1;
|
||||
}
|
||||
return processedShaderSource.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WebGPU shader module for a given "program" "stage", accounting for override constants.
|
||||
* Effectively, this function is responsible for preprocessing the shader source and compiling it.
|
||||
@@ -145,14 +50,12 @@ namespace {
|
||||
* a shader module
|
||||
* @param program The "program" to compile/create the shader, which includes the shader source
|
||||
* @param stage The stage (e.g. vertex, fragment, etc.) to create the shader module
|
||||
* @param specConstants Override constants to apply when creating/compiling the shader module.
|
||||
* @return the proper WebGPU shader module compiled/created from the input parameters. This might
|
||||
* wrap a null handle if the shader is not present (if the shader source is empty), such as
|
||||
* a missing fragment or compute shader.
|
||||
*/
|
||||
[[nodiscard]] wgpu::ShaderModule createShaderModule(wgpu::Device const& device,
|
||||
Program const& program, const ShaderStage stage,
|
||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specConstants) {
|
||||
Program const& program, const ShaderStage stage) {
|
||||
const char* const programName = program.getName().c_str_safe();
|
||||
std::array<utils::FixedCapacityVector<uint8_t>, Program::SHADER_TYPE_COUNT> const&
|
||||
shaderSource = program.getShadersSource();
|
||||
@@ -164,18 +67,18 @@ namespace {
|
||||
std::stringstream labelStream;
|
||||
labelStream << programName << " " << filamentShaderStageToString(stage) << " shader";
|
||||
const auto label = labelStream.str();
|
||||
const std::string processedShaderSource =
|
||||
specConstants.empty()
|
||||
? reinterpret_cast<const char*>(sourceBytes.data())
|
||||
: replaceSpecConstants(label, reinterpret_cast<const char*>(sourceBytes.data()),
|
||||
specConstants);
|
||||
|
||||
wgpu::ShaderSourceWGSL wgslDescriptor{};
|
||||
wgslDescriptor.code = wgpu::StringView(processedShaderSource);
|
||||
wgslDescriptor.code = wgpu::StringView(reinterpret_cast<const char*>(sourceBytes.data()));
|
||||
const wgpu::ShaderModuleDescriptor descriptor{
|
||||
.nextInChain = &wgslDescriptor,
|
||||
.label = label.data()
|
||||
};
|
||||
const wgpu::ShaderModule shaderModule = device.CreateShaderModule(&descriptor);
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
// TODO: We don't really need to wait for compilation info in production. It's helpful only
|
||||
// for debugging.
|
||||
const wgpu::Instance instance = device.GetAdapter().GetInstance();
|
||||
|
||||
// Synchronously compile the shader module.
|
||||
@@ -244,6 +147,7 @@ namespace {
|
||||
PANIC_POSTCONDITION("Timed out creating/compiling shader %s", descriptor.label.data);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
FILAMENT_CHECK_POSTCONDITION(shaderModule) << "Failed to create " << descriptor.label;
|
||||
return shaderModule;
|
||||
}
|
||||
@@ -251,14 +155,11 @@ namespace {
|
||||
}// namespace
|
||||
|
||||
WebGPUProgram::WebGPUProgram(wgpu::Device const& device, Program const& program)
|
||||
: HwProgram{ program.getName() } {
|
||||
utils::FixedCapacityVector<Program::SpecializationConstant> const& specConstants =
|
||||
program.getSpecializationConstants();
|
||||
: HwProgram{ program.getName() } {
|
||||
// TODO: Consider creating/compiling these shaders in parallel.
|
||||
vertexShaderModule = createShaderModule(device, program, ShaderStage::VERTEX, specConstants);
|
||||
fragmentShaderModule =
|
||||
createShaderModule(device, program, ShaderStage::FRAGMENT, specConstants);
|
||||
computeShaderModule = createShaderModule(device, program, ShaderStage::COMPUTE, specConstants);
|
||||
vertexShaderModule = createShaderModule(device, program, ShaderStage::VERTEX);
|
||||
fragmentShaderModule = createShaderModule(device, program, ShaderStage::FRAGMENT);
|
||||
computeShaderModule = createShaderModule(device, program, ShaderStage::COMPUTE);
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
@@ -81,7 +81,9 @@ void WebGPUQueueManager::finish() {
|
||||
// This is similar to draining a work queue. We currently have no other way to force the "last"
|
||||
// callback to be called.
|
||||
while (mLatestSubmissionState->getStatus() == FenceStatus::TIMEOUT_EXPIRED) {
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
mDevice.GetAdapter().GetInstance().ProcessEvents();
|
||||
#endif
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -392,6 +392,9 @@ WebGPURenderPassMipmapGenerator::getScalarSampleTypeFrom(const wgpu::TextureForm
|
||||
case wgpu::TextureFormat::R16Snorm:
|
||||
case wgpu::TextureFormat::RG16Snorm:
|
||||
case wgpu::TextureFormat::RGBA16Snorm:
|
||||
|
||||
// Formats not available on WASM
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
case wgpu::TextureFormat::R8BG8Biplanar420Unorm:
|
||||
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm:
|
||||
case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm:
|
||||
@@ -400,6 +403,7 @@ WebGPURenderPassMipmapGenerator::getScalarSampleTypeFrom(const wgpu::TextureForm
|
||||
case wgpu::TextureFormat::R10X6BG10X6Biplanar422Unorm:
|
||||
case wgpu::TextureFormat::R10X6BG10X6Biplanar444Unorm:
|
||||
case wgpu::TextureFormat::OpaqueYCbCrAndroid:
|
||||
#endif
|
||||
return ScalarSampleType::F32;
|
||||
case wgpu::TextureFormat::Depth16Unorm:
|
||||
case wgpu::TextureFormat::Depth24Plus:
|
||||
|
||||
@@ -67,10 +67,12 @@ template<typename WebGPUPrintable>
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
[[nodiscard]] static inline std::string_view powerPreferenceToString(
|
||||
const wgpu::DawnAdapterPropertiesPowerPreference powerPreference) {
|
||||
return powerPreferenceToString(powerPreference.powerPreference);
|
||||
}
|
||||
#endif
|
||||
|
||||
[[nodiscard]] constexpr std::string_view backendTypeToString(const wgpu::BackendType backendType) {
|
||||
switch (backendType) {
|
||||
@@ -231,6 +233,7 @@ template<typename WebGPUPrintable>
|
||||
case wgpu::TextureFormat::R16Snorm: return "R16Snorm";
|
||||
case wgpu::TextureFormat::RG16Snorm: return "RG16Snorm";
|
||||
case wgpu::TextureFormat::RGBA16Snorm: return "RGBA16Snorm";
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
case wgpu::TextureFormat::R8BG8Biplanar420Unorm: return "R8BG8Biplanar420Unorm";
|
||||
case wgpu::TextureFormat::R10X6BG10X6Biplanar420Unorm: return "R10X6BG10X6Biplanar420Unorm";
|
||||
case wgpu::TextureFormat::R8BG8A8Triplanar420Unorm: return "R8BG8A8Triplanar420Unorm";
|
||||
@@ -239,6 +242,7 @@ template<typename WebGPUPrintable>
|
||||
case wgpu::TextureFormat::R10X6BG10X6Biplanar422Unorm: return "R10X6BG10X6Biplanar422Unorm";
|
||||
case wgpu::TextureFormat::R10X6BG10X6Biplanar444Unorm: return "R10X6BG10X6Biplanar444Unorm";
|
||||
case wgpu::TextureFormat::OpaqueYCbCrAndroid: return "OpaqueYCbCrAndroid";
|
||||
#endif
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -387,9 +387,12 @@ wgpu::Texture WebGPUSwapChain::getCurrentTexture() {
|
||||
}
|
||||
|
||||
void WebGPUSwapChain::present(DriverBase& driver) {
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
if (!isHeadless()) {
|
||||
mSurface.Present();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mFrameScheduled.callback) {
|
||||
driver.scheduleCallback(mFrameScheduled.handler,
|
||||
[callback = mFrameScheduled.callback]() {
|
||||
|
||||
@@ -17,11 +17,15 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_WEBGPUSWAPCHAIN_H
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUSWAPCHAIN_H
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include "DriverBase.h"
|
||||
#include <backend/Platform.h>
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <backend/platforms/WebGPUWasmPolyfill.h>
|
||||
#endif
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
|
||||
@@ -41,6 +41,14 @@ namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
bool supportsTransientAttachment(wgpu::Device const& device) {
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
return device.HasFeature(wgpu::FeatureName::TransientAttachments);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr wgpu::StringView getUserTextureLabel(const SamplerType target) {
|
||||
// TODO will be helpful to get more useful info than this
|
||||
switch (target) {
|
||||
@@ -209,9 +217,9 @@ WebGPUTexture::WebGPUTexture(const SamplerType samplerType, const uint8_t levels
|
||||
mWebGPUUsage{ fToWGPUTextureUsage(usage, samples,
|
||||
mMipmapGenerationStrategy == MipmapGenerationStrategy::SPD_COMPUTE_PASS,
|
||||
mMipmapGenerationStrategy == MipmapGenerationStrategy::RENDER_PASS,
|
||||
device.HasFeature(wgpu::FeatureName::TransientAttachments)) },
|
||||
supportsTransientAttachment(device)) },
|
||||
mViewUsage{ fToWGPUTextureUsage(usage, samples, false, false,
|
||||
device.HasFeature(wgpu::FeatureName::TransientAttachments)) },
|
||||
supportsTransientAttachment(device)) },
|
||||
mDimension{ toWebGPUTextureViewDimension(samplerType) },
|
||||
mBlockWidth{ filament::backend::getBlockWidth(format) },
|
||||
mBlockHeight{ filament::backend::getBlockHeight(format) },
|
||||
@@ -291,10 +299,7 @@ WebGPUTexture::WebGPUTexture(WebGPUTexture const* src, const uint8_t baseLevel,
|
||||
mDefaultTextureView = makeTextureView(mDefaultMipLevel, levelCount, 0,
|
||||
mDefaultBaseArrayLayer, src->getViewDimension());
|
||||
} else {
|
||||
wgpu::TextureComponentSwizzleDescriptor swizzleDesc{};
|
||||
swizzleDesc.swizzle = mSwizzle;
|
||||
const wgpu::TextureViewDescriptor viewDesc{
|
||||
.nextInChain = &swizzleDesc,
|
||||
wgpu::TextureViewDescriptor viewDesc{
|
||||
.label = "swizzled_texture_view",
|
||||
.format = mTexture.GetFormat(),
|
||||
.dimension = src->getViewDimension(),
|
||||
@@ -303,6 +308,12 @@ WebGPUTexture::WebGPUTexture(WebGPUTexture const* src, const uint8_t baseLevel,
|
||||
.baseArrayLayer = 0,
|
||||
.arrayLayerCount = mDefaultBaseArrayLayer,
|
||||
};
|
||||
// b/508270158
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
wgpu::TextureComponentSwizzleDescriptor swizzleDesc{};
|
||||
swizzleDesc.swizzle = mSwizzle;
|
||||
viewDesc.nextInChain = &swizzleDesc,
|
||||
#endif
|
||||
mDefaultTextureView = mTexture.CreateView(&viewDesc);
|
||||
FILAMENT_CHECK_POSTCONDITION(mDefaultTextureView)
|
||||
<< "Failed to create swizzled Texture view";
|
||||
@@ -327,15 +338,18 @@ WebGPUTexture::WebGPUTexture(const WebGPUTexture* src,
|
||||
mDefaultBaseArrayLayer{ 0 },
|
||||
mMsaaSidecarTexture{src->mMsaaSidecarTexture},
|
||||
mSwizzle{ composeSwizzle(src->getSwizzle(), nextSwizzle) } {
|
||||
wgpu::TextureComponentSwizzleDescriptor swizzleDesc{};
|
||||
swizzleDesc.swizzle = mSwizzle;
|
||||
|
||||
const wgpu::TextureViewDescriptor viewDesc{
|
||||
.nextInChain = &swizzleDesc,
|
||||
wgpu::TextureViewDescriptor viewDesc{
|
||||
.label = "swizzled_texture_view",
|
||||
.format = mTexture.GetFormat(),
|
||||
.dimension = src->getViewDimension(),
|
||||
};
|
||||
// b/508270158
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
wgpu::TextureComponentSwizzleDescriptor swizzleDesc{};
|
||||
swizzleDesc.swizzle = mSwizzle;
|
||||
viewDesc.nextInChain = &swizzleDesc,
|
||||
#endif
|
||||
mDefaultTextureView = mTexture.CreateView(&viewDesc);
|
||||
FILAMENT_CHECK_POSTCONDITION(mDefaultTextureView) << "Failed to create swizzled Texture view";
|
||||
}
|
||||
|
||||
@@ -20,6 +20,9 @@
|
||||
#include "DriverBase.h"
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <backend/platforms/WebGPUWasmPolyfill.h>
|
||||
#endif
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
@@ -21,6 +21,10 @@
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <backend/platforms/WebGPUWasmPolyfill.h>
|
||||
#endif
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <string_view>
|
||||
@@ -597,7 +601,7 @@ namespace filament::backend {
|
||||
}
|
||||
|
||||
wgpu::TextureUsage transientAttachmentNeeded{ wgpu::TextureUsage::None };
|
||||
const bool useTransientAttachment {
|
||||
bool const useTransientAttachment =
|
||||
deviceSupportsTransientAttachments &&
|
||||
// Usage consists of attachment flags only.
|
||||
none(fUsage & ~TextureUsage::ALL_ATTACHMENTS) &&
|
||||
@@ -608,9 +612,12 @@ namespace filament::backend {
|
||||
// restriction.
|
||||
// Note that the custom shader does not resolve stencil. We do need to move to vk 1.2
|
||||
// and above to be able to support stencil resolve (along with depth).
|
||||
!(any(fUsage & TextureUsage::DEPTH_ATTACHMENT) && samples > 1)};
|
||||
!(any(fUsage & TextureUsage::DEPTH_ATTACHMENT) && samples > 1);
|
||||
|
||||
if (useTransientAttachment) {
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
transientAttachmentNeeded |= wgpu::TextureUsage::TransientAttachment;
|
||||
#endif
|
||||
}
|
||||
|
||||
// A texture that is a blit destination or render attachment will often need to be
|
||||
|
||||
@@ -225,42 +225,73 @@ struct AttributeInfo final {
|
||||
* associated with a vertex buffer, and the slot each one belongs
|
||||
*/
|
||||
void createBufferLayoutsBindingSlotsAndAttributeInfos(AttributeArray const& attributes,
|
||||
const uint8_t attributeCount, const uint32_t deviceMaxVertexBuffers,
|
||||
const uint32_t deviceMaxVertexBuffers,
|
||||
std::vector<WebGPUVertexBufferInfo::WebGPUSlotBindingInfo>& outWebGPUSlotBindingInfos,
|
||||
std::vector<wgpu::VertexBufferLayout>& outVertexBufferLayouts,
|
||||
std::array<AttributeInfo, MAX_VERTEX_ATTRIBUTE_COUNT>& outAttributeInfos) {
|
||||
std::array<AttributeInfo, MAX_VERTEX_ATTRIBUTE_COUNT>& outAttributeInfos,
|
||||
uint8_t& outActualAttributeCount) {
|
||||
uint8_t currentWebGPUSlotIndex = 0;
|
||||
uint8_t currentAttributeIndex = 0;
|
||||
outVertexBufferLayouts.reserve(MAX_VERTEX_BUFFER_COUNT);
|
||||
outWebGPUSlotBindingInfos.reserve(outVertexBufferLayouts.capacity());
|
||||
for (uint32_t attributeIndex = 0; attributeIndex < attributes.size(); ++attributeIndex) {
|
||||
const auto& attribute = attributes[attributeIndex];
|
||||
if (attribute.buffer == Attribute::BUFFER_UNUSED) {
|
||||
// We ignore "unused" attributes, ones not associated with a buffer. If a shader
|
||||
// references such an attribute we have a bug one way or another. Either the API/CPU
|
||||
// will produce an error (best case scenario), where an attribute is referenced in the
|
||||
// shader but not provided by the backend API (CPU-side), or the shader would be getting
|
||||
// junk/undefined data from the GPU, since we do not have a valid buffer of data to
|
||||
// provide to the shader/GPU.
|
||||
continue;
|
||||
|
||||
// Find a valid attribute to use as a dummy for unused slots (typically POSITION at index 0)
|
||||
Attribute dummyAttribute{};
|
||||
bool hasDummy = false;
|
||||
for (uint32_t i = 0; i < attributes.size(); ++i) {
|
||||
if (attributes[i].buffer != Attribute::BUFFER_UNUSED) {
|
||||
dummyAttribute = attributes[i];
|
||||
hasDummy = true;
|
||||
break;
|
||||
}
|
||||
const bool isInteger = attribute.flags & Attribute::FLAG_INTEGER_TARGET;
|
||||
const bool isNormalized = attribute.flags & Attribute::FLAG_NORMALIZED;
|
||||
const wgpu::VertexFormat vertexFormat =
|
||||
getVertexFormat(attribute.type, isNormalized, isInteger);
|
||||
}
|
||||
|
||||
/*
|
||||
* WebGPU Strict Validation Workaround:
|
||||
*
|
||||
* 1. Completeness: WebGPU requires ALL shader-declared attributes to exist in the Pipeline's
|
||||
* VertexState, even if unused by a specific mesh variant (e.g., BONE_WEIGHTS).
|
||||
* 2. Stride Boundary: Offset + Format_Size <= arrayStride.
|
||||
*
|
||||
* If we omit an unused attribute, WebGPU crashes (violates 1).
|
||||
* If we pad it as Float32x4 (16 bytes) into Slot 0 (e.g., Position, stride 12), WebGPU crashes
|
||||
* (violates 2):
|
||||
*
|
||||
* [ Slot 0 Stride: 12 bytes ]
|
||||
* |-------POSITION--------|
|
||||
* |------ BONE_WEIGHTS (Float32x4 = 16b) -----| <-- OVERFLOWS STRIDE!
|
||||
*
|
||||
* FIX: We alias unused attributes into Slot 0 as a 4-byte format (Float32 or Uint32).
|
||||
*
|
||||
* [ Slot 0 Stride: 12 bytes ]
|
||||
* |-------POSITION--------|
|
||||
* |-BONE-| <-- (Float32 = 4b) Safely fits inside the 12-byte stride!
|
||||
*
|
||||
* WebGPU safely promotes the 4-byte Float32 into a WGSL vec4<f32> as (x, 0.0, 0.0, 1.0).
|
||||
*/
|
||||
for (uint32_t attributeIndex = 0; attributeIndex < attributes.size(); ++attributeIndex) {
|
||||
auto attribute = attributes[attributeIndex];
|
||||
wgpu::VertexFormat vertexFormat;
|
||||
|
||||
if (attribute.buffer == Attribute::BUFFER_UNUSED) {
|
||||
if (!hasDummy) {
|
||||
continue;
|
||||
}
|
||||
// HACK: Re-use the dummy buffer for disabled attributes to satisfy WebGPU validation.
|
||||
// Filament's shaders expect vec4 or uvec4. We provide a dummy format.
|
||||
const bool isInteger = attribute.flags & Attribute::FLAG_INTEGER_TARGET;
|
||||
vertexFormat = isInteger ? wgpu::VertexFormat::Uint8x4 : wgpu::VertexFormat::Unorm8x4;
|
||||
attribute = dummyAttribute;
|
||||
} else {
|
||||
const bool isInteger = attribute.flags & Attribute::FLAG_INTEGER_TARGET;
|
||||
const bool isNormalized = attribute.flags & Attribute::FLAG_NORMALIZED;
|
||||
vertexFormat = getVertexFormat(attribute.type, isNormalized, isInteger);
|
||||
}
|
||||
|
||||
uint8_t existingSlot = INVALID_SLOT_INDEX;
|
||||
for (uint32_t slot = 0; slot < currentWebGPUSlotIndex; slot++) {
|
||||
WebGPUVertexBufferInfo::WebGPUSlotBindingInfo const& info =
|
||||
outWebGPUSlotBindingInfos[slot];
|
||||
// We consider attributes to be in the same slot/layout only if they belong to the
|
||||
// same buffer and are interleaved; they cannot belong to separate partitions in the
|
||||
// same buffer, for example.
|
||||
// For the attributes to be interleaved, the stride must match (among other things).
|
||||
// The attribute offset being less than the slot's/layout's buffer offset indicates
|
||||
// that it is in a separate partition before this slot/layout, thus not part of it.
|
||||
// The difference from the attribute's offset and this slot's/layout's buffer offset
|
||||
// being greater than the stride indicates it is in a separate partition after this
|
||||
// slot/layout, thus not part of it.
|
||||
if (info.sourceBufferIndex == attribute.buffer && info.stride == attribute.stride &&
|
||||
attribute.offset >= info.bufferOffset &&
|
||||
((attribute.offset - info.bufferOffset) < attribute.stride)) {
|
||||
@@ -269,26 +300,18 @@ void createBufferLayoutsBindingSlotsAndAttributeInfos(AttributeArray const& attr
|
||||
}
|
||||
}
|
||||
if (existingSlot == INVALID_SLOT_INDEX) {
|
||||
// New combination, use a new WebGPU slot
|
||||
FILAMENT_CHECK_PRECONDITION(currentWebGPUSlotIndex < MAX_VERTEX_BUFFER_COUNT &&
|
||||
currentWebGPUSlotIndex < deviceMaxVertexBuffers)
|
||||
<< "Number of vertex buffer layouts must not exceed MAX_VERTEX_BUFFER_COUNT ("
|
||||
<< MAX_VERTEX_BUFFER_COUNT << ") or the device limit ("
|
||||
<< deviceMaxVertexBuffers << ")";
|
||||
existingSlot = currentWebGPUSlotIndex++;
|
||||
outWebGPUSlotBindingInfos.push_back({
|
||||
.sourceBufferIndex = attribute.buffer,
|
||||
outWebGPUSlotBindingInfos.push_back({ .sourceBufferIndex = attribute.buffer,
|
||||
.bufferOffset = attribute.offset,
|
||||
.stride = attribute.stride });
|
||||
// we need to have a vertex buffer layout for each slot
|
||||
outVertexBufferLayouts.push_back({
|
||||
.stepMode = wgpu::VertexStepMode::Vertex,
|
||||
.arrayStride = attribute.stride
|
||||
// we do not know attributeCount or attributes at this time. We get those
|
||||
// in a subsequent pass over the attributeInfos we collect in this loop.
|
||||
});
|
||||
outVertexBufferLayouts.push_back(
|
||||
{ .stepMode = wgpu::VertexStepMode::Vertex, .arrayStride = attribute.stride });
|
||||
}
|
||||
// we don't use a designated initializer here because certain compilers can't handle it
|
||||
outAttributeInfos[currentAttributeIndex++] = AttributeInfo(
|
||||
existingSlot,
|
||||
{
|
||||
@@ -297,9 +320,8 @@ void createBufferLayoutsBindingSlotsAndAttributeInfos(AttributeArray const& attr
|
||||
attribute.offset - outWebGPUSlotBindingInfos[existingSlot].bufferOffset,
|
||||
.shaderLocation = attributeIndex });
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(currentAttributeIndex == attributeCount)
|
||||
<< "Using " << currentAttributeIndex << " attributes, but " << attributeCount
|
||||
<< " where indicated.";
|
||||
|
||||
outActualAttributeCount = currentAttributeIndex;
|
||||
outVertexBufferLayouts.shrink_to_fit();
|
||||
outWebGPUSlotBindingInfos.shrink_to_fit();
|
||||
}
|
||||
@@ -316,21 +338,21 @@ WebGPUVertexBufferInfo::WebGPUVertexBufferInfo(const uint8_t bufferCount,
|
||||
<< ") exceeds Filament's MAX_VERTEX_ATTRIBUTE_COUNT limit ("
|
||||
<< MAX_VERTEX_ATTRIBUTE_COUNT << ") and/or the device's limit ("
|
||||
<< deviceLimits.maxVertexAttributes << ")";
|
||||
mVertexAttributes.reserve(attributeCount);
|
||||
mVertexAttributes.reserve(MAX_VERTEX_ATTRIBUTE_COUNT); // Reserve max as we might add dummies
|
||||
if (attributeCount == 0) {
|
||||
mVertexBufferLayouts.resize(0);
|
||||
mWebGPUSlotBindingInfos.resize(0);
|
||||
return; // should not be possible, but being defensive. nothing to do otherwise
|
||||
}
|
||||
std::array<AttributeInfo, MAX_VERTEX_ATTRIBUTE_COUNT> attributeInfos{};
|
||||
createBufferLayoutsBindingSlotsAndAttributeInfos(attributes, attributeCount,
|
||||
deviceLimits.maxVertexBuffers, mWebGPUSlotBindingInfos, mVertexBufferLayouts,
|
||||
attributeInfos);
|
||||
uint8_t actualAttributeCount = 0;
|
||||
createBufferLayoutsBindingSlotsAndAttributeInfos(attributes, deviceLimits.maxVertexBuffers,
|
||||
mWebGPUSlotBindingInfos, mVertexBufferLayouts, attributeInfos, actualAttributeCount);
|
||||
// sort attribute infos by increasing slot (by increasing offset within each slot).
|
||||
// We do this to ensure that attributes for the same slot/layout are contiguous in
|
||||
// the vector, so the vertex buffer layout associated with these contiguous attributes
|
||||
// can directly reference them in the mVertexAttributes vector below.
|
||||
std::sort(attributeInfos.data(), attributeInfos.data() + attributeCount,
|
||||
std::sort(attributeInfos.data(), attributeInfos.data() + actualAttributeCount,
|
||||
[](AttributeInfo const& first, AttributeInfo const& second) {
|
||||
if (first.slot < second.slot) {
|
||||
return true;
|
||||
@@ -344,7 +366,7 @@ WebGPUVertexBufferInfo::WebGPUVertexBufferInfo(const uint8_t bufferCount,
|
||||
});
|
||||
// populate mVertexAttributes and update mVertexBufferLayouts to reference the correct
|
||||
// attributes in it (which will be contiguous in memory as ensured by the sorting above)...
|
||||
for (uint32_t attributeIndex = 0; attributeIndex < attributeCount; ++attributeIndex) {
|
||||
for (uint32_t attributeIndex = 0; attributeIndex < actualAttributeCount; ++attributeIndex) {
|
||||
AttributeInfo const& info = attributeInfos[attributeIndex];
|
||||
mVertexAttributes.push_back(info.attribute);
|
||||
if (mVertexBufferLayouts[info.slot].attributeCount == 0) {
|
||||
|
||||
@@ -57,20 +57,29 @@ namespace {
|
||||
constexpr uint32_t MAX_MIPMAP_STORAGE_TEXTURES_PER_STAGE = 12u;
|
||||
|
||||
constexpr std::array REQUIRED_FEATURES = {
|
||||
wgpu::FeatureName::TransientAttachments,
|
||||
// Qualcomm 500 and 600 GPUs do not support this so it is not part of core webgpu spec. To
|
||||
// support such devices, we will either need Filament to not attempt this, or find another
|
||||
// workaround. https://github.com/gpuweb/gpuweb/issues/2648
|
||||
wgpu::FeatureName::RG11B10UfloatRenderable,
|
||||
// necessary for blit conversions of formats like RGBA32Float...
|
||||
wgpu::FeatureName::Float32Filterable,
|
||||
|
||||
|
||||
// Unsupported on WASM
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
wgpu::FeatureName::TransientAttachments,
|
||||
#endif
|
||||
};
|
||||
|
||||
constexpr std::array OPTIONAL_FEATURES = {
|
||||
wgpu::FeatureName::CoreFeaturesAndLimits,
|
||||
wgpu::FeatureName::DepthClipControl,
|
||||
wgpu::FeatureName::Depth32FloatStencil8,
|
||||
|
||||
// Unsupported on WASM
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
wgpu::FeatureName::TextureComponentSwizzle,
|
||||
#endif
|
||||
};
|
||||
|
||||
enum class LimitToValidate : uint8_t {
|
||||
@@ -266,12 +275,21 @@ void printInstanceDetails(wgpu::Instance const& instance) {
|
||||
#endif
|
||||
dawnTogglesDescriptor.enabledToggleCount = toggles.size();
|
||||
dawnTogglesDescriptor.enabledToggles = toggles.data();
|
||||
const wgpu::InstanceFeatureName features[] = {wgpu::InstanceFeatureName::TimedWaitAny};
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
constexpr std::array<wgpu::InstanceFeatureName, 0> features;
|
||||
#else
|
||||
constexpr std::array features = {
|
||||
wgpu::InstanceFeatureName::TimedWaitAny
|
||||
};
|
||||
#endif
|
||||
|
||||
wgpu::InstanceDescriptor instanceDescriptor{
|
||||
.nextInChain = &dawnTogglesDescriptor,
|
||||
.requiredFeatures = features,
|
||||
.requiredFeatureCount = features.size(),
|
||||
.requiredFeatures = features.data(),
|
||||
};
|
||||
instanceDescriptor.requiredFeatureCount = 1;
|
||||
|
||||
wgpu::Instance instance = wgpu::CreateInstance(&instanceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(instance != nullptr) << "Unable to create WebGPU instance.";
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
@@ -351,6 +369,8 @@ struct AdapterDetails final {
|
||||
: info(std::move(info)),
|
||||
powerPreference(powerPreference),
|
||||
adapter(std::move(adapter)) {}
|
||||
AdapterDetails(AdapterDetails&& other) noexcept = default;
|
||||
|
||||
AdapterDetails& operator=(AdapterDetails&& other) noexcept {
|
||||
adapter = std::exchange(other.adapter, nullptr);
|
||||
info = std::exchange(other.info, {});
|
||||
@@ -373,8 +393,10 @@ struct AdapterDetails final {
|
||||
|
||||
[[nodiscard]] std::string toString(AdapterDetails const& details) {
|
||||
std::stringstream out;
|
||||
out << adapterInfoToString(details.info)
|
||||
<< " power preference " << powerPreferenceToString(details.powerPreference);
|
||||
out << adapterInfoToString(details.info);
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
out << " power preference " << powerPreferenceToString(details.powerPreference);
|
||||
#endif
|
||||
return out.str();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2026 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 <backend/platforms/WebGPUPlatformWasm.h>
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
#include <emscripten/html5.h>
|
||||
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
extern "C" WGPUDevice emscripten_webgpu_get_device(void);
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
wgpu::Device WebGPUPlatformWasm::sDevice = nullptr;
|
||||
|
||||
std::vector<wgpu::RequestAdapterOptions> WebGPUPlatformWasm::getAdapterOptions() {
|
||||
std::vector<wgpu::RequestAdapterOptions> requests;
|
||||
requests.reserve(2);
|
||||
requests.emplace_back(wgpu::RequestAdapterOptions{
|
||||
.powerPreference = wgpu::PowerPreference::HighPerformance,
|
||||
.forceFallbackAdapter = false,
|
||||
.backendType = wgpu::BackendType::WebGPU,
|
||||
});
|
||||
requests.emplace_back(wgpu::RequestAdapterOptions{
|
||||
.powerPreference = wgpu::PowerPreference::LowPower,
|
||||
.forceFallbackAdapter = false,
|
||||
.backendType = wgpu::BackendType::WebGPU,
|
||||
});
|
||||
return requests;
|
||||
}
|
||||
|
||||
wgpu::Adapter WebGPUPlatformWasm::requestAdapter(wgpu::Surface const& surface) { return nullptr; }
|
||||
|
||||
wgpu::Device WebGPUPlatformWasm::requestDevice(wgpu::Adapter const& adapter) {
|
||||
if (sDevice == nullptr) {
|
||||
WGPUDevice device = emscripten_webgpu_get_device();
|
||||
FILAMENT_CHECK_POSTCONDITION(device != nullptr)
|
||||
<< "WebGPU device not initialized. Call Filament.initWebGPU() first.";
|
||||
sDevice = wgpu::Device::Acquire(device);
|
||||
}
|
||||
return sDevice;
|
||||
}
|
||||
|
||||
wgpu::Extent2D WebGPUPlatformWasm::getSurfaceExtent(void* nativeWindow) const {
|
||||
const char* selector = static_cast<const char*>(nativeWindow);
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
emscripten_get_canvas_element_size(selector, &width, &height);
|
||||
return wgpu::Extent2D{
|
||||
.width = static_cast<uint32_t>(width),
|
||||
.height = static_cast<uint32_t>(height),
|
||||
};
|
||||
}
|
||||
|
||||
wgpu::Surface WebGPUPlatformWasm::createSurface(void* nativeWindow, uint64_t /*flags*/) {
|
||||
wgpu::EmscriptenSurfaceSourceCanvasHTMLSelector canvasSource{};
|
||||
canvasSource.selector = static_cast<const char*>(nativeWindow);
|
||||
const wgpu::SurfaceDescriptor surfaceDescriptor{
|
||||
.nextInChain = &canvasSource,
|
||||
.label = "wasm_surface",
|
||||
};
|
||||
wgpu::Surface surface = mInstance.CreateSurface(&surfaceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(surface.Get() != nullptr)
|
||||
<< "Unable to create Wasm-backed surface.";
|
||||
return surface;
|
||||
}
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -139,8 +139,10 @@ public:
|
||||
Builder& package(const void* UTILS_NONNULL payload, size_t size);
|
||||
|
||||
template<typename T>
|
||||
using is_supported_constant_parameter_t =
|
||||
MaterialInstance::is_supported_constant_parameter_t<T>;
|
||||
using is_supported_constant_parameter_t = std::enable_if_t<
|
||||
std::is_same_v<int32_t, T> ||
|
||||
std::is_same_v<float, T> ||
|
||||
std::is_same_v<bool, T>>;
|
||||
|
||||
/**
|
||||
* Specialize a constant parameter specified in the material definition with a concrete
|
||||
@@ -241,18 +243,11 @@ public:
|
||||
* for stereoscopic rendering. If an application is not planning to render in stereo, this bit
|
||||
* should be turned off to avoid unnecessary material compilations.
|
||||
*
|
||||
* Note that it is possible to override specialization constants on a per-MaterialInstance basis
|
||||
* (@see MaterialInstance::setConstant). In that case, the programs compiled by a call to
|
||||
* Material::compile() may not be reusable by that MaterialInstance. It's better to call
|
||||
* MaterialInstance::compile() in cases where you intend to override specialization constants.
|
||||
*
|
||||
* @param priority Which priority queue to use, LOW or HIGH.
|
||||
* @param variants Variants to include to the compile command.
|
||||
* @param handler Handler to dispatch the callback or nullptr for the default handler
|
||||
* @param callback callback called on the main thread when the compilation is done on
|
||||
* by backend.
|
||||
*
|
||||
* @see Material::compile
|
||||
*/
|
||||
void compile(CompilerPriorityQueue priority,
|
||||
UserVariantFilterMask variants,
|
||||
|
||||
@@ -94,12 +94,6 @@ public:
|
||||
std::is_same_v<math::mat3f, T>
|
||||
>;
|
||||
|
||||
template<typename T>
|
||||
using is_supported_constant_parameter_t = std::enable_if_t<
|
||||
std::is_same_v<int32_t, T> ||
|
||||
std::is_same_v<float, T> ||
|
||||
std::is_same_v<bool, T>>;
|
||||
|
||||
/**
|
||||
* Creates a new MaterialInstance using another MaterialInstance as a template for initialization.
|
||||
* The new MaterialInstance is an instance of the same Material of the template instance and
|
||||
@@ -282,91 +276,6 @@ public:
|
||||
return getParameter<T>(name, strlen(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides a specialization constant of this material instance.
|
||||
*
|
||||
* @tparam T The type of the constant. Must be int32_t, float, or bool.
|
||||
* @param name The name of the constant as defined in the material. Cannot be nullptr.
|
||||
* @param nameLength Length in `char` of the name parameter.
|
||||
* @param value The value of the constant.
|
||||
*
|
||||
* @see Material::Builder::constant
|
||||
*/
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
void setConstant(const char* UTILS_NONNULL name, size_t nameLength, T value);
|
||||
|
||||
/** inline helper to provide the name as a null-terminated string literal */
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
void setConstant(StringLiteral const name, T value) {
|
||||
setConstant<T>(name.data, name.size, value);
|
||||
}
|
||||
|
||||
/** inline helper to provide the name as a null-terminated C string */
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
void setConstant(const char* UTILS_NONNULL name, T value) {
|
||||
setConstant<T>(name, strlen(name), value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a specialization constant by name.
|
||||
*
|
||||
* @tparam T The type of the constant. Must be int32_t, float, or bool.
|
||||
* @param name The name of the constant as defined in the material. Cannot be nullptr.
|
||||
* @param nameLength Length in `char` of the name parameter.
|
||||
* @return The value of the constant.
|
||||
*/
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
T getConstant(const char* UTILS_NONNULL name, size_t nameLength) const;
|
||||
|
||||
/** inline helper to provide the name as a null-terminated C string */
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
T getConstant(StringLiteral const name) const {
|
||||
return getConstant<T>(name.data, name.size);
|
||||
}
|
||||
|
||||
/** inline helper to provide the name as a null-terminated C string */
|
||||
template<typename T, typename = is_supported_constant_parameter_t<T>>
|
||||
T getConstant(const char* UTILS_NONNULL name) const {
|
||||
return getConstant<T>(name, strlen(name));
|
||||
}
|
||||
|
||||
using CompilerPriorityQueue = backend::CompilerPriorityQueue;
|
||||
|
||||
/**
|
||||
* Asynchronously ensures that a subset of this MaterialInstance's variants are compiled.
|
||||
*
|
||||
* This function behaves identically to Material::compile(), but takes into account the
|
||||
* specific constants overridden by setConstant().
|
||||
*
|
||||
* @param priority Which priority queue to use, LOW or HIGH.
|
||||
* @param variants Variants to include to the compile command.
|
||||
* @param handler Handler to dispatch the callback or nullptr for the default handler
|
||||
* @param callback callback called on the main thread when the compilation is done on
|
||||
* by backend.
|
||||
*
|
||||
* @see Material::compile
|
||||
* @see setConstant
|
||||
*/
|
||||
void compile(CompilerPriorityQueue priority,
|
||||
UserVariantFilterMask variants,
|
||||
backend::CallbackHandler* UTILS_NULLABLE handler = nullptr,
|
||||
utils::Invocable<void(MaterialInstance* UTILS_NONNULL)>&& callback = {}) noexcept;
|
||||
|
||||
inline void compile(CompilerPriorityQueue priority,
|
||||
UserVariantFilterBit variants,
|
||||
backend::CallbackHandler* UTILS_NULLABLE handler = nullptr,
|
||||
utils::Invocable<void(MaterialInstance* UTILS_NONNULL)>&& callback = {}) noexcept {
|
||||
compile(priority, UserVariantFilterMask(variants), handler,
|
||||
std::forward<utils::Invocable<void(MaterialInstance* UTILS_NONNULL)>>(callback));
|
||||
}
|
||||
|
||||
inline void compile(CompilerPriorityQueue priority,
|
||||
backend::CallbackHandler* UTILS_NULLABLE handler = nullptr,
|
||||
utils::Invocable<void(MaterialInstance* UTILS_NONNULL)>&& callback = {}) noexcept {
|
||||
compile(priority, UserVariantFilterBit::ALL, handler,
|
||||
std::forward<utils::Invocable<void(MaterialInstance* UTILS_NONNULL)>>(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set-up a custom scissor rectangle; by default it is disabled.
|
||||
*
|
||||
|
||||
@@ -182,11 +182,15 @@ Program::SpecializationConstant LocalProgramCache::getConstantImpl(
|
||||
std::string_view name) const noexcept {
|
||||
assert_invariant(mMaterial != nullptr);
|
||||
|
||||
auto const& constants = mMaterial->getDefinition().specializationConstantsNameToIndex;
|
||||
auto it = constants.find(name);
|
||||
FILAMENT_CHECK_PRECONDITION(it != constants.end()) << "Constant " << name << " does not exist";
|
||||
MaterialDefinition const& definition = mMaterial->getDefinition();
|
||||
auto it = definition.specializationConstantsNameToIndex.find(name);
|
||||
if (it != definition.specializationConstantsNameToIndex.cend()) {
|
||||
return getConstantImpl(it->second + CONFIG_MAX_RESERVED_SPEC_CONSTANTS);
|
||||
}
|
||||
|
||||
return getConstantImpl(it->second + CONFIG_MAX_RESERVED_SPEC_CONSTANTS);
|
||||
CString name_cstring(name);
|
||||
PANIC_PRECONDITION("No such constant exists: %s", name_cstring.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
void LocalProgramCache::setConstants(
|
||||
@@ -236,13 +240,6 @@ void LocalProgramCache::setConstants(
|
||||
}
|
||||
}
|
||||
|
||||
void LocalProgramCache::setConstants(
|
||||
FixedCapacityVector<Program::SpecializationConstant> constants) noexcept {
|
||||
assert_invariant(mMaterial != nullptr);
|
||||
|
||||
setConstantsImpl(std::move(constants));
|
||||
}
|
||||
|
||||
void LocalProgramCache::setConstantsImpl(
|
||||
FixedCapacityVector<Program::SpecializationConstant> constants) noexcept {
|
||||
FEngine& engine = mMaterial->getEngine();
|
||||
|
||||
@@ -119,14 +119,7 @@ public:
|
||||
std::pair<std::string_view, backend::Program::SpecializationConstant>>
|
||||
constants) noexcept;
|
||||
|
||||
// Set constants list directly.
|
||||
void setConstants(utils::FixedCapacityVector<backend::Program::SpecializationConstant>
|
||||
constants) noexcept;
|
||||
|
||||
private:
|
||||
// Apply any pending specialization constants. Invalidates programs as necessary.
|
||||
void flushConstants() const;
|
||||
|
||||
backend::Handle<backend::HwProgram> prepareProgramSlow(backend::DriverApi& driver,
|
||||
Variant const variant,
|
||||
backend::CompilerPriorityQueue const priorityQueue) const noexcept;
|
||||
|
||||
@@ -233,26 +233,6 @@ template UTILS_PUBLIC mat3f MaterialInstance::getParameter<mat3f> (const ch
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename T, typename>
|
||||
void MaterialInstance::setConstant(const char* name, size_t nameLength, T value) {
|
||||
downcast(this)->setConstantImpl(std::string_view{name, nameLength}, value);
|
||||
}
|
||||
|
||||
template UTILS_PUBLIC void MaterialInstance::setConstant<int32_t>(const char* name, size_t nameLength, int32_t value);
|
||||
template UTILS_PUBLIC void MaterialInstance::setConstant<float>(const char* name, size_t nameLength, float value);
|
||||
template UTILS_PUBLIC void MaterialInstance::setConstant<bool>(const char* name, size_t nameLength, bool value);
|
||||
|
||||
template<typename T, typename>
|
||||
T MaterialInstance::getConstant(const char* name, size_t nameLength) const {
|
||||
return downcast(this)->getConstantImpl<T>(std::string_view{name, nameLength});
|
||||
}
|
||||
|
||||
template UTILS_PUBLIC int32_t MaterialInstance::getConstant<int32_t>(const char* name, size_t nameLength) const;
|
||||
template UTILS_PUBLIC float MaterialInstance::getConstant<float>(const char* name, size_t nameLength) const;
|
||||
template UTILS_PUBLIC bool MaterialInstance::getConstant<bool>(const char* name, size_t nameLength) const;
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
Material const* MaterialInstance::getMaterial() const noexcept {
|
||||
return downcast(this)->getMaterial();
|
||||
}
|
||||
@@ -423,10 +403,4 @@ void MaterialInstance::commit(Engine& engine) const {
|
||||
downcast(this)->commit(downcast(engine));
|
||||
}
|
||||
|
||||
void MaterialInstance::compile(CompilerPriorityQueue const priority,
|
||||
UserVariantFilterMask const variants, CallbackHandler* handler,
|
||||
utils::Invocable<void(MaterialInstance*)>&& callback) noexcept {
|
||||
downcast(this)->compile(priority, variants, handler, std::move(callback));
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -186,10 +186,12 @@ void PostProcessManager::PostProcessMaterial::loadMaterial(FEngine& engine) cons
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
FMaterial* PostProcessManager::PostProcessMaterial::getMaterial(FEngine& engine) const noexcept {
|
||||
FMaterial* PostProcessManager::PostProcessMaterial::getMaterial(FEngine& engine,
|
||||
DriverApi& driver, Variant::type_t const variant) const noexcept {
|
||||
if (UTILS_UNLIKELY(mSize)) {
|
||||
loadMaterial(engine);
|
||||
}
|
||||
mMaterial->prepareProgram(driver, Variant{ variant }, CompilerPriorityQueue::CRITICAL);
|
||||
return mMaterial;
|
||||
}
|
||||
|
||||
@@ -247,20 +249,6 @@ void PostProcessManager::bindPerRenderableDescriptorSet(DriverApi& driver) const
|
||||
{ { 0, 0 }, driver });
|
||||
}
|
||||
|
||||
FMaterialInstance* PostProcessManager::getMaterialInstance(backend::DriverApi& driver,
|
||||
FMaterial const* ma, Variant::type_t variant) const {
|
||||
FMaterialInstance* mi = mMaterialInstanceManager.getMaterialInstance(ma);
|
||||
mi->prepareProgram(driver, Variant{ variant }, backend::CompilerPriorityQueue::CRITICAL);
|
||||
return mi;
|
||||
}
|
||||
|
||||
FMaterialInstance* PostProcessManager::getMaterialInstanceWithTag(backend::DriverApi& driver,
|
||||
FMaterial const* ma, uint32_t tag, Variant::type_t variant) const {
|
||||
FMaterialInstance* mi = mMaterialInstanceManager.getMaterialInstance(ma, tag);
|
||||
mi->prepareProgram(driver, Variant { variant }, backend::CompilerPriorityQueue::CRITICAL);
|
||||
return mi;
|
||||
}
|
||||
|
||||
UboManager* PostProcessManager::getUboManager() const noexcept {
|
||||
return mEngine.getUboManager();
|
||||
}
|
||||
@@ -480,10 +468,9 @@ void PostProcessManager::unbindAllDescriptorSets(DriverApi& driver) noexcept {
|
||||
|
||||
UTILS_NOINLINE
|
||||
PipelineState PostProcessManager::getPipelineState(
|
||||
FMaterialInstance const* const mi, Variant::type_t const variant) const noexcept {
|
||||
FMaterial const* const ma = mi->getMaterial();
|
||||
FMaterial const* const ma, Variant::type_t const variant) const noexcept {
|
||||
return {
|
||||
.program = mi->getProgram(Variant{ variant }),
|
||||
.program = ma->getProgram(Variant{ variant }),
|
||||
.vertexBufferInfo = mFullScreenQuadVbih,
|
||||
.pipelineLayout = {
|
||||
.setLayout = {
|
||||
@@ -491,7 +478,7 @@ PipelineState PostProcessManager::getPipelineState(
|
||||
mPerRenderableDslh,
|
||||
ma->getDescriptorSetLayout().getHandle()
|
||||
}},
|
||||
.rasterState = mi->getRasterState()
|
||||
.rasterState = ma->getRasterState()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -531,7 +518,8 @@ void PostProcessManager::commitAndRenderFullScreenQuad(DriverApi& driver,
|
||||
PostProcessVariant const variant) const noexcept {
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->use(driver);
|
||||
PipelineState const pipeline = getPipelineState(mi, variant);
|
||||
FMaterial const* const ma = mi->getMaterial();
|
||||
PipelineState const pipeline = getPipelineState(ma, variant);
|
||||
|
||||
assert_invariant(
|
||||
((out.params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH)
|
||||
@@ -648,12 +636,12 @@ PostProcessManager::StructurePassOutput PostProcessManager::structure(FrameGraph
|
||||
|
||||
auto in = resources.getTexture(data.depth);
|
||||
auto& material = getPostProcessMaterial("mipmapDepth");
|
||||
FMaterial const* const ma = material.getMaterial(mEngine);
|
||||
FMaterialInstance* const mi = getMaterialInstance(driver, ma);
|
||||
FMaterial const* const ma = material.getMaterial(mEngine, driver);
|
||||
FMaterialInstance* const mi = getMaterialInstance(ma);
|
||||
// Only the depth texture is changing in the material instance (no UBO updates),
|
||||
// we do not move getMaterialInstance() inside the loop.
|
||||
|
||||
auto pipeline = getPipelineState(mi);
|
||||
auto pipeline = getPipelineState(ma);
|
||||
|
||||
// The first mip already exists, so we process n-1 lods
|
||||
for (size_t level = 0; level < levelCount - 1; level++) {
|
||||
@@ -1013,13 +1001,14 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
#endif
|
||||
auto& material = getPostProcessMaterial(materialName);
|
||||
|
||||
FMaterial* ma = material.getMaterial(mEngine);
|
||||
FMaterial* ma = material.getMaterial(mEngine, driver);
|
||||
ma->getPrograms().setConstants({
|
||||
{ "useVisibilityBitmasks", options.gtao.useVisibilityBitmasks },
|
||||
{ "linearThickness", options.gtao.linearThickness },
|
||||
});
|
||||
|
||||
FMaterialInstance* const mi = getMaterialInstance(driver, ma);
|
||||
ma = material.getMaterial(mEngine, driver);
|
||||
FMaterialInstance* const mi = getMaterialInstance(ma);
|
||||
|
||||
// Set AO type specific material parameters
|
||||
switch (aoType) {
|
||||
@@ -1095,7 +1084,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->use(driver);
|
||||
|
||||
auto pipeline = getPipelineState(mi);
|
||||
auto pipeline = getPipelineState(ma);
|
||||
pipeline.rasterState.depthFunc = RasterState::DepthFunc::L;
|
||||
assert_invariant(ssao.params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH);
|
||||
renderFullScreenQuad(ssao, pipeline, driver);
|
||||
@@ -1205,8 +1194,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::bilateralBlurPass(FrameGraph
|
||||
auto& material = config.bentNormals ?
|
||||
getPostProcessMaterial("bilateralBlurBentNormals") :
|
||||
getPostProcessMaterial("bilateralBlur");
|
||||
FMaterial const* const ma = material.getMaterial(mEngine);
|
||||
FMaterialInstance* const mi = getMaterialInstance(driver, ma);
|
||||
FMaterial const* const ma = material.getMaterial(mEngine, driver);
|
||||
FMaterialInstance* const mi = getMaterialInstance(ma);
|
||||
mi->setParameter("ssao", ssao, { /* only reads level 0 */ });
|
||||
mi->setParameter("axis", axis / float2{desc.width, desc.height});
|
||||
mi->setParameter("kernel", kGaussianSamples, kGaussianCount);
|
||||
@@ -1216,7 +1205,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::bilateralBlurPass(FrameGraph
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->use(driver);
|
||||
|
||||
auto pipeline = getPipelineState(mi);
|
||||
auto pipeline = getPipelineState(ma);
|
||||
pipeline.rasterState.depthFunc = RasterState::DepthFunc::L;
|
||||
renderFullScreenQuad(blurred, pipeline, driver);
|
||||
unbindAllDescriptorSets(driver);
|
||||
@@ -1375,7 +1364,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::gaussianBlurPass(FrameGraph&
|
||||
"separableGaussianBlur4L"sv : "separableGaussianBlur4"sv; break;
|
||||
}
|
||||
auto const& separableGaussianBlur = getPostProcessMaterial(materialName);
|
||||
auto ma = separableGaussianBlur.getMaterial(mEngine);
|
||||
auto ma = separableGaussianBlur.getMaterial(mEngine, driver);
|
||||
|
||||
const size_t kernelStorageSize = ma->reflect("kernel")->size;
|
||||
float2 kernel[64];
|
||||
@@ -1898,10 +1887,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
|
||||
auto inOutCoc = resources.getTexture(data.inOutCoc);
|
||||
|
||||
auto const& material = getPostProcessMaterial("dofMipmap");
|
||||
FMaterial const* const ma = material.getMaterial(mEngine);
|
||||
FMaterialInstance* const mi = getMaterialInstance(driver, ma);
|
||||
FMaterial const* const ma = material.getMaterial(mEngine, driver);
|
||||
|
||||
auto const pipeline = getPipelineState(mi, variant);
|
||||
auto const pipeline = getPipelineState(ma, variant);
|
||||
|
||||
for (size_t level = 0 ; level < mipmapCount - 1u ; level++) {
|
||||
const float w = FTexture::valueForLevel(level, desc.width);
|
||||
@@ -1909,8 +1897,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
|
||||
auto const& out = resources.getRenderPassInfo(data.rp[level]);
|
||||
auto inColor = driver.createTextureView(inOutColor, level, 1);
|
||||
auto inCoc = driver.createTextureView(inOutCoc, level, 1);
|
||||
// FIXME: is this necessary?
|
||||
FMaterialInstance* const mi = getMaterialInstance(driver, ma);
|
||||
FMaterialInstance* const mi = getMaterialInstance(ma);
|
||||
|
||||
mi->setParameter("color", inColor, SamplerParams{
|
||||
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
|
||||
@@ -2439,9 +2426,9 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
|
||||
auto const& outDesc = resources.getDescriptor(data.out);
|
||||
|
||||
auto const& material = getPostProcessMaterial("bloomUpsample");
|
||||
FMaterial const* const ma = material.getMaterial(mEngine);
|
||||
FMaterial const* const ma = material.getMaterial(mEngine, driver);
|
||||
|
||||
auto pipeline = getPipelineState(getMaterialInstance(driver, ma));
|
||||
auto pipeline = getPipelineState(ma);
|
||||
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
pipeline.rasterState.blendFunctionDstRGB = BlendFunction::ONE;
|
||||
|
||||
@@ -2449,7 +2436,7 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
|
||||
// Note that we wouldn't want to use the same instance for each pass since that
|
||||
// would imply using the same UBOs, which implies synchronization across the
|
||||
// passes.
|
||||
FMaterialInstance* mi = getMaterialInstance(driver, ma);
|
||||
FMaterialInstance* mi = getMaterialInstance(ma);
|
||||
auto hwDstRT = resources.getRenderPassInfo(data.outRT[i - 1]);
|
||||
hwDstRT.params.flags.discardStart = TargetBufferFlags::NONE; // b/c we'll blend
|
||||
hwDstRT.params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
@@ -2582,12 +2569,11 @@ void PostProcessManager::colorGradingSubpass(DriverApi& driver,
|
||||
PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE;
|
||||
|
||||
auto const& material = getPostProcessMaterial("colorGradingAsSubpass");
|
||||
FMaterial const* const ma = material.getMaterial(mEngine);
|
||||
FMaterial const* const ma = material.getMaterial(mEngine, driver, variant);
|
||||
// the UBO has been set and committed in colorGradingPrepareSubpass()
|
||||
FMaterialInstance const* mi =
|
||||
getMaterialInstanceWithTag(driver, ma, colorGradingConfig.translucent, variant);
|
||||
FMaterialInstance const* mi = mMaterialInstanceManager.getMaterialInstance(ma, colorGradingConfig.translucent);
|
||||
mi->use(driver);
|
||||
auto const pipeline = getPipelineState(mi, variant);
|
||||
auto const pipeline = getPipelineState(ma, variant);
|
||||
driver.nextSubpass();
|
||||
driver.scissor(mi->getScissor());
|
||||
driver.draw(pipeline, mFullScreenQuadRph, 0, 3, 1);
|
||||
@@ -2595,8 +2581,8 @@ void PostProcessManager::colorGradingSubpass(DriverApi& driver,
|
||||
|
||||
void PostProcessManager::customResolvePrepareSubpass(DriverApi& driver, CustomResolveOp const op) noexcept {
|
||||
auto const& material = getPostProcessMaterial("customResolveAsSubpass");
|
||||
auto const ma = material.getMaterial(mEngine);
|
||||
auto* const mi = getMaterialInstance(driver, ma, 0);
|
||||
auto const ma = material.getMaterial(mEngine, driver, PostProcessVariant::OPAQUE);
|
||||
auto* const mi = mMaterialInstanceManager.getMaterialInstance(ma, 0);
|
||||
mi->setParameter("direction", op == CustomResolveOp::COMPRESS ? 1.0f : -1.0f),
|
||||
mi->commit(driver, getUboManager());
|
||||
}
|
||||
@@ -2606,12 +2592,12 @@ void PostProcessManager::customResolveSubpass(DriverApi& driver) noexcept {
|
||||
bindPerRenderableDescriptorSet(driver);
|
||||
|
||||
auto const& material = getPostProcessMaterial("customResolveAsSubpass");
|
||||
FMaterial const* const ma = material.getMaterial(mEngine);
|
||||
FMaterial const* const ma = material.getMaterial(mEngine, driver);
|
||||
// the UBO has been set and committed in customResolvePrepareSubpass()
|
||||
FMaterialInstance const* mi = getMaterialInstance(driver, ma, 0);
|
||||
FMaterialInstance const* mi = mMaterialInstanceManager.getMaterialInstance(ma, 0);
|
||||
mi->use(driver);
|
||||
|
||||
auto const pipeline = getPipelineState(mi);
|
||||
auto const pipeline = getPipelineState(ma);
|
||||
driver.nextSubpass();
|
||||
driver.scissor(mi->getScissor());
|
||||
driver.draw(pipeline, mFullScreenQuadRph, 0, 3, 1);
|
||||
@@ -2647,8 +2633,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::customResolveUncompressPass(
|
||||
void PostProcessManager::clearAncillaryBuffersPrepare(DriverApi& driver,
|
||||
Variant::type_t variant) noexcept {
|
||||
auto const& material = getPostProcessMaterial("clearDepth");
|
||||
auto const ma = material.getMaterial(mEngine);
|
||||
auto const mi = getMaterialInstanceWithTag(driver, ma, 0, variant);
|
||||
auto const ma = material.getMaterial(mEngine, driver, variant);
|
||||
auto const mi = mMaterialInstanceManager.getMaterialInstance(ma, 0);
|
||||
mi->commit(driver, getUboManager());
|
||||
}
|
||||
|
||||
@@ -2663,13 +2649,13 @@ void PostProcessManager::clearAncillaryBuffers(DriverApi& driver,
|
||||
bindPerRenderableDescriptorSet(driver);
|
||||
|
||||
auto const& material = getPostProcessMaterial("clearDepth");
|
||||
FMaterial const* const ma = material.getMaterial(mEngine);
|
||||
FMaterial const* const ma = material.getMaterial(mEngine, driver, variant);
|
||||
|
||||
// the UBO has been set and committed in clearAncillaryBuffersPrepare()
|
||||
FMaterialInstance const* const mi = getMaterialInstanceWithTag(driver, ma, 0, variant);
|
||||
FMaterialInstance const* const mi = mMaterialInstanceManager.getMaterialInstance(ma, 0);
|
||||
mi->use(driver);
|
||||
|
||||
auto pipeline = getPipelineState(mi, variant);
|
||||
auto pipeline = getPipelineState(ma, variant);
|
||||
pipeline.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||
|
||||
driver.scissor(mi->getScissor());
|
||||
@@ -2679,7 +2665,7 @@ void PostProcessManager::clearAncillaryBuffers(DriverApi& driver,
|
||||
void PostProcessManager::fogPrepare(DriverApi& driver) noexcept {
|
||||
// ensures the material is loaded and material instance created
|
||||
auto const& material = getPostProcessMaterial("fog");
|
||||
FMaterial const* const ma = material.getMaterial(mEngine);
|
||||
FMaterial const* const ma = material.getMaterial(mEngine, driver, PostProcessVariant::OPAQUE);
|
||||
FMaterialInstance const* mi = ma->getDefaultInstance();
|
||||
mi->commit(driver, getUboManager());
|
||||
}
|
||||
@@ -2690,11 +2676,11 @@ void PostProcessManager::fog(DriverApi& driver) noexcept {
|
||||
bindPerRenderableDescriptorSet(driver);
|
||||
|
||||
auto const& material = getPostProcessMaterial("fog");
|
||||
FMaterial const* const ma = material.getMaterial(mEngine);
|
||||
FMaterial const* const ma = material.getMaterial(mEngine, driver);
|
||||
FMaterialInstance const* mi = ma->getDefaultInstance();
|
||||
mi->use(driver);
|
||||
|
||||
auto pipeline = getPipelineState(mi, Variant::NO_VARIANT);
|
||||
auto pipeline = getPipelineState(ma, Variant::NO_VARIANT);
|
||||
driver.scissor(mi->getScissor());
|
||||
driver.draw(pipeline, mFullScreenQuadRph, 0, 3, 1);
|
||||
}
|
||||
@@ -2951,7 +2937,7 @@ void PostProcessManager::TaaJitterCamera(
|
||||
void PostProcessManager::configureTemporalAntiAliasingMaterial(backend::DriverApi& driver,
|
||||
TemporalAntiAliasingOptions const& taaOptions) noexcept {
|
||||
|
||||
FMaterial* const ma = getPostProcessMaterial("taa").getMaterial(mEngine);
|
||||
FMaterial* const ma = getPostProcessMaterial("taa").getMaterial(mEngine, driver);
|
||||
ma->getPrograms().setConstants({
|
||||
{ "upscaling", taaOptions.upscaling > 1.0f },
|
||||
{ "historyReprojection", taaOptions.historyReprojection },
|
||||
@@ -2970,7 +2956,7 @@ FMaterialInstance* PostProcessManager::configureColorGradingMaterial(backend::Dr
|
||||
PostProcessMaterial const& material, FColorGrading const* colorGrading,
|
||||
ColorGradingConfig const& colorGradingConfig, VignetteOptions const& vignetteOptions,
|
||||
uint32_t const width, uint32_t const height) noexcept {
|
||||
FMaterial* ma = material.getMaterial(mEngine);
|
||||
FMaterial* ma = material.getMaterial(mEngine, driver);
|
||||
ma->getPrograms().setConstants({
|
||||
{ "isOneDimensional", colorGrading->isOneDimensional() },
|
||||
{ "isLDR", colorGrading->isLDR() },
|
||||
@@ -2979,9 +2965,8 @@ FMaterialInstance* PostProcessManager::configureColorGradingMaterial(backend::Dr
|
||||
PostProcessVariant const variant = colorGradingConfig.translucent
|
||||
? PostProcessVariant::TRANSLUCENT
|
||||
: PostProcessVariant::OPAQUE;
|
||||
ma = material.getMaterial(mEngine);
|
||||
FMaterialInstance* mi =
|
||||
getMaterialInstanceWithTag(driver, ma, colorGradingConfig.translucent, variant);
|
||||
ma = material.getMaterial(mEngine, driver, variant);
|
||||
FMaterialInstance* mi = mMaterialInstanceManager.getMaterialInstance(ma, colorGradingConfig.translucent);
|
||||
|
||||
const SamplerParams params = SamplerParams{
|
||||
.filterMag = SamplerMagFilter::LINEAR,
|
||||
@@ -3093,9 +3078,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
|
||||
PostProcessVariant const variant = colorGradingConfig.translucent ?
|
||||
PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE;
|
||||
|
||||
FMaterial const* const ma = material.getMaterial(mEngine);
|
||||
FMaterial const* const ma = material.getMaterial(mEngine, driver, variant);
|
||||
|
||||
FMaterialInstance* mi = getMaterialInstance(driver, ma);
|
||||
FMaterialInstance* mi = getMaterialInstance(ma);
|
||||
mi->setParameter("color", color, SamplerParams{}); // nearest
|
||||
mi->setParameter("depth", depth, SamplerParams{}); // nearest
|
||||
mi->setParameter("history", history, SamplerParams{
|
||||
@@ -3135,7 +3120,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
|
||||
if (colorGradingConfig.asSubpass) {
|
||||
out.params.subpassMask = 1;
|
||||
}
|
||||
auto const pipeline = getPipelineState(mi, variant);
|
||||
auto const pipeline = getPipelineState(ma, variant);
|
||||
|
||||
driver.beginRenderPass(out.target, out.params);
|
||||
driver.draw(pipeline, mFullScreenQuadRph, 0, 3, 1);
|
||||
@@ -3217,7 +3202,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::rcas(
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->use(driver);
|
||||
|
||||
auto pipeline = getPipelineState(mi, variant);
|
||||
auto pipeline = getPipelineState(material.getMaterial(mEngine, driver), variant);
|
||||
if (mode == RcasMode::BLENDED) {
|
||||
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
pipeline.rasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
|
||||
@@ -3310,7 +3295,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleBilinear(FrameGraph&
|
||||
|
||||
auto out = resources.getRenderPassInfo();
|
||||
|
||||
auto pipeline = getPipelineState(mi);
|
||||
auto pipeline = getPipelineState(material.getMaterial(mEngine, driver));
|
||||
if (blended) {
|
||||
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
pipeline.rasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
|
||||
@@ -3524,15 +3509,15 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscaleFSR1(FrameGraph& fg,
|
||||
auto out = resources.getRenderPassInfo();
|
||||
|
||||
if (UTILS_UNLIKELY(twoPassesEASU)) {
|
||||
auto pipeline0 = getPipelineState(getMaterialInstance(mEngine, driver, *splitEasuMaterial));
|
||||
auto pipeline1 = getPipelineState(getMaterialInstance(mEngine, driver, *easuMaterial));
|
||||
auto pipeline0 = getPipelineState(splitEasuMaterial->getMaterial(mEngine, driver));
|
||||
auto pipeline1 = getPipelineState(easuMaterial->getMaterial(mEngine, driver));
|
||||
pipeline1.rasterState.depthFunc = SamplerCompareFunc::NE;
|
||||
driver.beginRenderPass(out.target, out.params);
|
||||
driver.draw(pipeline0, mFullScreenQuadRph, 0, 3, 1);
|
||||
driver.draw(pipeline1, mFullScreenQuadRph, 0, 3, 1);
|
||||
driver.endRenderPass();
|
||||
} else {
|
||||
auto pipeline = getPipelineState(getMaterialInstance(mEngine, driver, *easuMaterial));
|
||||
auto pipeline = getPipelineState(easuMaterial->getMaterial(mEngine, driver));
|
||||
renderFullScreenQuad(out, pipeline, driver);
|
||||
}
|
||||
unbindAllDescriptorSets(driver);
|
||||
@@ -3584,8 +3569,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool co
|
||||
|
||||
PostProcessMaterial const& material =
|
||||
getPostProcessMaterial(layer ? "blitArray" : "blitLow");
|
||||
FMaterial const* const ma = material.getMaterial(mEngine);
|
||||
auto* mi = getMaterialInstance(driver, ma);
|
||||
FMaterial const* const ma = material.getMaterial(mEngine, driver);
|
||||
auto* mi = getMaterialInstance(ma);
|
||||
mi->setParameter("color", color, SamplerParams{
|
||||
.filterMag = filterMag,
|
||||
.filterMin = filterMin
|
||||
@@ -3603,7 +3588,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool co
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->use(driver);
|
||||
|
||||
auto pipeline = getPipelineState(mi);
|
||||
auto pipeline = getPipelineState(ma);
|
||||
if (translucent) {
|
||||
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
pipeline.rasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
|
||||
@@ -3901,10 +3886,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(Fra
|
||||
// set uniforms
|
||||
|
||||
PostProcessMaterial const& material = getPostProcessMaterial("blitArray");
|
||||
FMaterial const* const ma = material.getMaterial(mEngine);
|
||||
FMaterial const* const ma = material.getMaterial(mEngine, driver);
|
||||
// It should be ok to not move this getMaterialInstance to inside the loop, since
|
||||
// this is a pass meant for debug.
|
||||
auto* mi = getMaterialInstance(driver, ma);
|
||||
auto* mi = getMaterialInstance(ma);
|
||||
mi->setParameter("color", color, SamplerParams{
|
||||
.filterMag = filterMag,
|
||||
.filterMin = filterMin
|
||||
@@ -3918,7 +3903,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(Fra
|
||||
mi->commit(driver, getUboManager());
|
||||
mi->use(driver);
|
||||
|
||||
auto pipeline = getPipelineState(mi);
|
||||
auto pipeline = getPipelineState(ma);
|
||||
if (translucent) {
|
||||
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
pipeline.rasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
|
||||
|
||||
@@ -360,7 +360,13 @@ public:
|
||||
|
||||
void terminate(FEngine& engine) noexcept;
|
||||
|
||||
FMaterial* getMaterial(FEngine& engine) const noexcept;
|
||||
FMaterial* getMaterial(FEngine& engine, backend::DriverApi& driver,
|
||||
Variant::type_t variant) const noexcept;
|
||||
|
||||
FMaterial* getMaterial(FEngine& engine, backend::DriverApi& driver,
|
||||
PostProcessVariant variant = PostProcessVariant::OPAQUE) const noexcept {
|
||||
return getMaterial(engine, driver, Variant::type_t(variant));
|
||||
}
|
||||
|
||||
private:
|
||||
void loadMaterial(FEngine& engine) const noexcept;
|
||||
@@ -387,12 +393,11 @@ public:
|
||||
|
||||
void bindPerRenderableDescriptorSet(backend::DriverApi& driver) const noexcept;
|
||||
|
||||
backend::PipelineState getPipelineState(FMaterialInstance const* mi,
|
||||
Variant::type_t variant) const noexcept;
|
||||
backend::PipelineState getPipelineState(FMaterial const* ma, Variant::type_t variant) const noexcept;
|
||||
|
||||
backend::PipelineState getPipelineState(FMaterialInstance const* mi,
|
||||
PostProcessVariant variant = PostProcessVariant::OPAQUE) const noexcept {
|
||||
return getPipelineState(mi, Variant::type_t(variant));
|
||||
backend::PipelineState getPipelineState(FMaterial const* ma,
|
||||
PostProcessVariant variant = PostProcessVariant::OPAQUE) const noexcept {
|
||||
return getPipelineState(ma, Variant::type_t(variant));
|
||||
}
|
||||
|
||||
void renderFullScreenQuad(FrameGraphResources::RenderPassInfo const& out,
|
||||
@@ -422,47 +427,27 @@ public:
|
||||
|
||||
void resetForRender();
|
||||
|
||||
|
||||
MaterialInstanceManager& getMaterialInstanceManager() noexcept {
|
||||
return mMaterialInstanceManager;
|
||||
}
|
||||
|
||||
// Helper to get a MaterialInstance from a FMaterial
|
||||
// This currently just call FMaterial::getDefaultInstance().
|
||||
FMaterialInstance* getMaterialInstance(FMaterial const* ma) {
|
||||
return mMaterialInstanceManager.getMaterialInstance(ma);
|
||||
}
|
||||
|
||||
// Helper to get a MaterialInstance from a PostProcessMaterial.
|
||||
FMaterialInstance* getMaterialInstance(FEngine& engine, backend::DriverApi& driver,
|
||||
PostProcessMaterial const& material, PostProcessVariant variant = PostProcessVariant::OPAQUE) {
|
||||
FMaterial const* ma = material.getMaterial(engine, driver, variant);
|
||||
return getMaterialInstance(ma);
|
||||
}
|
||||
|
||||
static void unbindAllDescriptorSets(backend::DriverApi& driver) noexcept;
|
||||
|
||||
private:
|
||||
|
||||
// Helpers to get MaterialInstances.
|
||||
//
|
||||
// These funcions additionally ensure that the necessary shader programs are compiled via
|
||||
// prepareProgram().
|
||||
FMaterialInstance* getMaterialInstance(backend::DriverApi& driver, FMaterial const* ma,
|
||||
Variant::type_t variant) const;
|
||||
|
||||
FMaterialInstance* getMaterialInstance(backend::DriverApi& driver, FMaterial const* ma,
|
||||
PostProcessVariant variant = PostProcessVariant::OPAQUE) const {
|
||||
return getMaterialInstance(driver, ma, Variant::type_t(variant));
|
||||
}
|
||||
|
||||
FMaterialInstance* getMaterialInstance(FEngine& engine, backend::DriverApi& driver,
|
||||
PostProcessMaterial const& material,
|
||||
PostProcessVariant variant = PostProcessVariant::OPAQUE) const {
|
||||
return getMaterialInstance(driver, material.getMaterial(engine), Variant::type_t(variant));
|
||||
}
|
||||
|
||||
FMaterialInstance* getMaterialInstanceWithTag(backend::DriverApi& driver, FMaterial const* ma,
|
||||
uint32_t tag, Variant::type_t variant) const;
|
||||
|
||||
FMaterialInstance* getMaterialInstanceWithTag(backend::DriverApi& driver, FMaterial const* ma,
|
||||
uint32_t tag, PostProcessVariant variant = PostProcessVariant::OPAQUE) const {
|
||||
return getMaterialInstanceWithTag(driver, ma, tag, Variant::type_t(variant));
|
||||
}
|
||||
|
||||
FMaterialInstance* getMaterialInstanceWithTag(FEngine& engine, backend::DriverApi& driver,
|
||||
PostProcessMaterial const& material, uint32_t tag,
|
||||
PostProcessVariant variant = PostProcessVariant::OPAQUE) const {
|
||||
return getMaterialInstanceWithTag(driver, material.getMaterial(engine), tag,
|
||||
Variant::type_t(variant));
|
||||
}
|
||||
|
||||
UboManager* getUboManager() const noexcept;
|
||||
|
||||
backend::RenderPrimitiveHandle mFullScreenQuadRph;
|
||||
|
||||
@@ -243,8 +243,8 @@ void RenderPass::appendCommands(FEngine const& engine, backend::DriverApi& drive
|
||||
// This must be done from the main thread.
|
||||
for (Command const* first = curr, *last = curr + commandCount ; first != last ; ++first) {
|
||||
if (UTILS_LIKELY((first->key & CUSTOM_MASK) == uint64_t(CustomCommand::PASS))) {
|
||||
first->info.mi->prepareProgram(driver, first->info.materialVariant,
|
||||
CompilerPriorityQueue::CRITICAL);
|
||||
auto ma = first->info.mi->getMaterial();
|
||||
ma->prepareProgram(driver, first->info.materialVariant, CompilerPriorityQueue::CRITICAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -426,18 +426,16 @@ void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
|
||||
bool const isBlendingCommand = !hasScreenSpaceRefraction &&
|
||||
(blendingMode != BlendingMode::OPAQUE && blendingMode != BlendingMode::MASKED);
|
||||
|
||||
RasterState rasterState = mi->getRasterState();
|
||||
|
||||
uint64_t keyDraw = cmdDraw.key;
|
||||
keyDraw &= ~(PASS_MASK | BLENDING_MASK | MATERIAL_MASK);
|
||||
keyDraw |= uint64_t(hasScreenSpaceRefraction ? Pass::REFRACT : Pass::COLOR);
|
||||
keyDraw |= uint64_t(CustomCommand::PASS);
|
||||
keyDraw |= mi->getSortingKey(); // already all set-up for direct or'ing
|
||||
keyDraw |= makeField(variant.key, MATERIAL_VARIANT_KEY_MASK, MATERIAL_VARIANT_KEY_SHIFT);
|
||||
keyDraw |= makeField(rasterState.alphaToCoverage, BLENDING_MASK, BLENDING_SHIFT);
|
||||
keyDraw |= makeField(ma->getRasterState().alphaToCoverage, BLENDING_MASK, BLENDING_SHIFT);
|
||||
|
||||
cmdDraw.key = isBlendingCommand ? keyBlending : keyDraw;
|
||||
cmdDraw.info.rasterState = rasterState;
|
||||
cmdDraw.info.rasterState = ma->getRasterState();
|
||||
|
||||
// for SSR pass, the blending mode of opaques (including MASKED) must be off
|
||||
// see Material.cpp.
|
||||
@@ -448,6 +446,10 @@ void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
|
||||
BlendFunction::ZERO : cmdDraw.info.rasterState.blendFunctionDstAlpha;
|
||||
|
||||
cmdDraw.info.rasterState.inverseFrontFaces = inverseFrontFaces;
|
||||
cmdDraw.info.rasterState.culling = mi->getCullingMode();
|
||||
cmdDraw.info.rasterState.colorWrite = mi->isColorWriteEnabled();
|
||||
cmdDraw.info.rasterState.depthWrite = mi->isDepthWriteEnabled();
|
||||
cmdDraw.info.rasterState.depthFunc = mi->getDepthFunc();
|
||||
cmdDraw.info.rasterState.depthClamp = hasDepthClamp;
|
||||
cmdDraw.info.materialVariant = variant;
|
||||
// we keep "RasterState::colorWrite" to the value set by material (could be disabled)
|
||||
@@ -1088,7 +1090,8 @@ void RenderPass::Executor::execute(FEngine const& engine, DriverApi& driver,
|
||||
mi->use(driver, info.materialVariant);
|
||||
}
|
||||
|
||||
pipeline.program = mi->getProgram(info.materialVariant);
|
||||
assert_invariant(ma);
|
||||
pipeline.program = ma->getProgram(info.materialVariant);
|
||||
|
||||
if (UTILS_UNLIKELY(memcmp(&pipeline, ¤tPipeline, sizeof(PipelineState)) != 0)) {
|
||||
currentPipeline = pipeline;
|
||||
|
||||
@@ -636,7 +636,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::gaussianBlurSeparatedPass(
|
||||
|
||||
// get the material
|
||||
auto const& separableGaussianBlur = ppm.getPostProcessMaterial("gaussian");
|
||||
auto const ma = separableGaussianBlur.getMaterial(engine);
|
||||
auto const ma = separableGaussianBlur.getMaterial(engine, driver);
|
||||
|
||||
// Generates half of a Gaussian kernel (center + one side)
|
||||
auto generateGaussianWeights = [](std::array<float, 32>& weights,
|
||||
@@ -685,7 +685,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::gaussianBlurSeparatedPass(
|
||||
for (auto const mi : miList) {
|
||||
mi->use(driver);
|
||||
driver.scissor(mi->getScissor());
|
||||
driver.draw(ppm.getPipelineState(mi), engine.getFullScreenRenderPrimitive(), 0, 3, 1);
|
||||
driver.draw(ppm.getPipelineState(ma), engine.getFullScreenRenderPrimitive(), 0, 3, 1);
|
||||
}
|
||||
driver.endRenderPass();
|
||||
|
||||
@@ -733,12 +733,12 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::vsmMipmapPass(
|
||||
ppm.bindPerRenderableDescriptorSet(driver);
|
||||
|
||||
auto& material = ppm.getPostProcessMaterial("vsmMipmap");
|
||||
FMaterial const* const ma = material.getMaterial(engine);
|
||||
auto const mi = ppm.getMaterialInstanceManager().getMaterialInstance(ma);
|
||||
FMaterial const* const ma = material.getMaterial(engine, driver);
|
||||
|
||||
auto const pipeline = ppm.getPipelineState(mi);
|
||||
auto const pipeline = ppm.getPipelineState(ma);
|
||||
backend::Viewport const scissor = { 0, 0, dim, dim };
|
||||
|
||||
auto const mi = ppm.getMaterialInstanceManager().getMaterialInstance(ma);
|
||||
mi->setParameter("color", in, SamplerParams{
|
||||
.filterMag = SamplerMagFilter::NEAREST,
|
||||
.filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST
|
||||
|
||||
@@ -1830,7 +1830,7 @@ void FEngine::compile(
|
||||
CallbackHandler* handler,
|
||||
Invocable<void(Material*)>&& callback) {
|
||||
auto const variants = getMaterialCompileVariants(view, shadowReceiver, skinning);
|
||||
const_cast<FMaterial*>(material)->compile(priority, variants, handler, std::move(callback));
|
||||
material->compile(priority, variants, handler, std::move(callback));
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
@@ -170,6 +170,8 @@ FMaterial::FMaterial(FEngine& engine, const Builder& builder, MaterialDefinition
|
||||
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
mIsStereoSupported = driver.isStereoSupported();
|
||||
mIsParallelShaderCompileSupported = driver.isParallelShaderCompileSupported();
|
||||
mDepthPrecacheDisabled =
|
||||
driver.isWorkaroundNeeded(Workaround::DISABLE_DEPTH_PRECACHE_FOR_DEFAULT_MATERIAL);
|
||||
mDefaultMaterial = engine.getDefaultMaterial();
|
||||
@@ -238,33 +240,44 @@ filament::DescriptorSetLayout const& FMaterial::getPerViewDescriptorSetLayout(
|
||||
void FMaterial::compile(CompilerPriorityQueue const priority,
|
||||
UserVariantFilterMask variantSpec,
|
||||
CallbackHandler* handler,
|
||||
Invocable<void(Material*)>&& callback) noexcept {
|
||||
FMaterialInstance* mi = getDefaultInstance();
|
||||
if (callback) {
|
||||
mi->compile(priority, variantSpec, handler,
|
||||
[this, callback = std::move(callback)](MaterialInstance*) {
|
||||
callback(this);
|
||||
});
|
||||
} else {
|
||||
mi->compile(priority, variantSpec, handler, {});
|
||||
Invocable<void(Material*)>&& callback) const noexcept {
|
||||
DriverApi& driver = mEngine.getDriverApi();
|
||||
|
||||
// Turn off the STE variant if stereo is not supported.
|
||||
if (!mIsStereoSupported) {
|
||||
variantSpec &= ~UserVariantFilterMask(UserVariantFilterBit::STE);
|
||||
}
|
||||
|
||||
UserVariantFilterMask const variantFilter = ~variantSpec & UserVariantFilterMask(UserVariantFilterBit::ALL);
|
||||
ShaderModel const shaderModel = mEngine.getShaderModel();
|
||||
bool const isStereoSupported = mEngine.getDriverApi().isStereoSupported();
|
||||
|
||||
if (UTILS_LIKELY(mIsParallelShaderCompileSupported)) {
|
||||
for (auto const variant: mDefinition.getVariants()) {
|
||||
if (!variantFilter || variant == Variant::filterUserVariant(variant, variantFilter)) {
|
||||
if (mDefinition.hasVariant(variant, shaderModel, isStereoSupported)) {
|
||||
prepareProgram(driver, variant, priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
compileAllPrograms(priority, handler, std::move(callback));
|
||||
}
|
||||
|
||||
void FMaterial::compile(CompilerPriorityQueue const priority,
|
||||
FixedCapacityVector<Variant> const& variants,
|
||||
CallbackHandler* handler,
|
||||
Invocable<void(Material*)>&& callback) noexcept {
|
||||
Invocable<void(Material*)>&& callback) const noexcept {
|
||||
DriverApi& driver = mEngine.getDriverApi();
|
||||
FMaterialInstance* mi = getDefaultInstance();
|
||||
|
||||
ShaderModel const shaderModel = mEngine.getShaderModel();
|
||||
bool const isStereoSupported = driver.isStereoSupported();
|
||||
bool const isParallelShaderCompileSupported = driver.isParallelShaderCompileSupported();
|
||||
|
||||
if (UTILS_LIKELY(isParallelShaderCompileSupported)) {
|
||||
if (UTILS_LIKELY(mIsParallelShaderCompileSupported)) {
|
||||
for (auto const variant : variants) {
|
||||
if (mDefinition.hasVariant(variant, shaderModel, isStereoSupported)) {
|
||||
mi->prepareProgram(driver, variant, priority);
|
||||
prepareProgram(driver, variant, priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -274,7 +287,7 @@ void FMaterial::compile(CompilerPriorityQueue const priority,
|
||||
|
||||
void FMaterial::compileAllPrograms(CompilerPriorityQueue const priority,
|
||||
CallbackHandler* handler,
|
||||
Invocable<void(Material*)>&& callback) noexcept {
|
||||
Invocable<void(Material*)>&& callback) const noexcept {
|
||||
DriverApi& driver = mEngine.getDriverApi();
|
||||
|
||||
if (callback) {
|
||||
|
||||
@@ -106,12 +106,12 @@ public:
|
||||
void compile(CompilerPriorityQueue priority,
|
||||
UserVariantFilterMask variantSpec,
|
||||
backend::CallbackHandler* handler,
|
||||
utils::Invocable<void(Material*)>&& callback) noexcept;
|
||||
utils::Invocable<void(Material*)>&& callback) const noexcept;
|
||||
|
||||
void compile(CompilerPriorityQueue priority,
|
||||
utils::FixedCapacityVector<Variant> const& variants,
|
||||
backend::CallbackHandler* handler,
|
||||
utils::Invocable<void(Material*)>&& callback) noexcept;
|
||||
utils::Invocable<void(Material*)>&& callback) const noexcept;
|
||||
|
||||
// Creates an instance of this material, specifying the batching mode.
|
||||
FMaterialInstance* createInstance(const char* name) const noexcept;
|
||||
@@ -130,6 +130,40 @@ public:
|
||||
|
||||
FEngine& getEngine() const noexcept { return mEngine; }
|
||||
|
||||
// prepareProgram creates the program for the material's given variant at the backend level.
|
||||
// Must be called outside of backend render pass.
|
||||
// Must be called before getProgram() below.
|
||||
backend::Handle<backend::HwProgram> prepareProgram(backend::DriverApi& driver,
|
||||
Variant const variant,
|
||||
backend::CompilerPriorityQueue const priorityQueue) const noexcept {
|
||||
return mPrograms.prepareProgram(driver, variant, priorityQueue);
|
||||
}
|
||||
|
||||
// getProgram returns the backend program for the material's given variant.
|
||||
// Must be called after prepareProgram().
|
||||
[[nodiscard]]
|
||||
backend::Handle<backend::HwProgram> getProgram(Variant variant) const noexcept {
|
||||
|
||||
if (UTILS_UNLIKELY(mEngine.features.material.enable_fog_as_postprocess)) {
|
||||
// if the fog as post-process feature is enabled, we need to proceed "as-if" the material
|
||||
// didn't have the FOG variant bit.
|
||||
if (getMaterialDomain() == MaterialDomain::SURFACE) {
|
||||
BlendingMode const blendingMode = getBlendingMode();
|
||||
bool const hasScreenSpaceRefraction = getRefractionMode() == RefractionMode::SCREEN_SPACE;
|
||||
bool const isBlendingCommand = !hasScreenSpaceRefraction &&
|
||||
(blendingMode != BlendingMode::OPAQUE && blendingMode != BlendingMode::MASKED);
|
||||
if (!isBlendingCommand) {
|
||||
variant.setFog(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
updateActiveProgramsForMatdbg(variant);
|
||||
#endif
|
||||
return mPrograms.getProgram(variant);
|
||||
}
|
||||
|
||||
// MaterialInstance::use() binds descriptor sets before drawing. For shared variants,
|
||||
// however, the material instance will call useShared() to bind the default material's sets
|
||||
// instead.
|
||||
@@ -276,9 +310,6 @@ public:
|
||||
}
|
||||
|
||||
/** @}*/
|
||||
|
||||
// Called by getProgram() to update active program list for matdbg UI.
|
||||
void updateActiveProgramsForMatdbg(Variant const variant) const noexcept;
|
||||
#endif
|
||||
|
||||
private:
|
||||
@@ -287,13 +318,15 @@ private:
|
||||
|
||||
void compileAllPrograms(CompilerPriorityQueue priority,
|
||||
backend::CallbackHandler* handler,
|
||||
utils::Invocable<void(Material*)>&& callback) noexcept;
|
||||
utils::Invocable<void(Material*)>&& callback) const noexcept;
|
||||
|
||||
MaterialDefinition const& mDefinition;
|
||||
|
||||
bool mIsDefaultMaterial = false;
|
||||
|
||||
bool mUseUboBatching = false;
|
||||
bool mIsStereoSupported = false;
|
||||
bool mIsParallelShaderCompileSupported = false;
|
||||
bool mDepthPrecacheDisabled = false;
|
||||
|
||||
FMaterial const* mDefaultMaterial = nullptr;
|
||||
@@ -308,6 +341,8 @@ private:
|
||||
mutable utils::Mutex mPendingEditsLock;
|
||||
std::unique_ptr<MaterialParser> mPendingEdits;
|
||||
std::unique_ptr<MaterialParser> mEditedMaterialParser;
|
||||
// Called by getProgram() to update active program list for matdbg UI.
|
||||
void updateActiveProgramsForMatdbg(Variant const variant) const noexcept;
|
||||
void setPendingEdits(std::unique_ptr<MaterialParser> pendingEdits) noexcept;
|
||||
bool hasPendingEdits() const noexcept;
|
||||
void latchPendingEdits() noexcept;
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#include <filament/MaterialInstance.h>
|
||||
|
||||
#include "RenderPass.h"
|
||||
#include "MaterialParser.h"
|
||||
|
||||
#include "ds/DescriptorSetLayout.h"
|
||||
|
||||
@@ -132,7 +131,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
|
||||
mTextureParameters(other->mTextureParameters),
|
||||
mDescriptorSet(other->mDescriptorSet.duplicate(
|
||||
"MaterialInstance", mMaterial->getDescriptorSetLayout())),
|
||||
mPrograms(other->mPrograms),
|
||||
mPolygonOffset(other->mPolygonOffset),
|
||||
mStencilState(other->mStencilState),
|
||||
mMaskThreshold(other->mMaskThreshold),
|
||||
@@ -209,10 +207,6 @@ void FMaterialInstance::terminate(FEngine& engine) {
|
||||
if (ubHandle){
|
||||
driver.destroyBufferObject(*ubHandle);
|
||||
}
|
||||
|
||||
if (mPrograms.isInitialized()) {
|
||||
mPrograms.terminate(engine);
|
||||
}
|
||||
}
|
||||
|
||||
void FMaterialInstance::commit(FEngine& engine) const {
|
||||
@@ -270,39 +264,6 @@ void FMaterialInstance::commit(FEngine::DriverApi& driver, UboManager* uboManage
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
template<typename T>
|
||||
void FMaterialInstance::setConstantImpl(std::string_view name, T value) {
|
||||
auto const& constants = mMaterial->getDefinition().specializationConstantsNameToIndex;
|
||||
auto it = constants.find(name);
|
||||
FILAMENT_CHECK_PRECONDITION(it != constants.end()) << "Constant " << name << " does not exist";
|
||||
|
||||
if (UTILS_UNLIKELY(mPendingSpecializationConstants.empty())) {
|
||||
mPendingSpecializationConstants =
|
||||
FixedCapacityVector<backend::Program::SpecializationConstant>(
|
||||
getPrograms().getSpecializationConstants());
|
||||
}
|
||||
|
||||
uint32_t id = it->second + CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
|
||||
mPendingSpecializationConstants[id] = value;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T FMaterialInstance::getConstantImpl(std::string_view name) const {
|
||||
auto const& constants = mMaterial->getDefinition().specializationConstantsNameToIndex;
|
||||
auto it = constants.find(name);
|
||||
FILAMENT_CHECK_PRECONDITION(it != constants.end()) << "Constant " << name << " does not exist";
|
||||
|
||||
uint32_t id = it->second + CONFIG_MAX_RESERVED_SPEC_CONSTANTS;
|
||||
|
||||
if (UTILS_UNLIKELY(!mPendingSpecializationConstants.empty())) {
|
||||
return std::get<T>(mPendingSpecializationConstants[id]);
|
||||
}
|
||||
|
||||
return getPrograms().getConstant<T>(id);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void FMaterialInstance::setParameter(std::string_view const name,
|
||||
Handle<HwTexture> texture, SamplerParams const params) {
|
||||
auto const binding = mMaterial->getSamplerBinding(name);
|
||||
@@ -422,15 +383,6 @@ void FMaterialInstance::setTransparencyMode(TransparencyMode const mode) noexcep
|
||||
mTransparencyMode = mode;
|
||||
}
|
||||
|
||||
RasterState FMaterialInstance::getRasterState() const noexcept {
|
||||
RasterState rs = mMaterial->getRasterState();
|
||||
rs.culling = mCulling;
|
||||
rs.depthWrite = mDepthWrite;
|
||||
rs.depthFunc = mDepthFunc;
|
||||
rs.colorWrite = mColorWrite;
|
||||
return rs;
|
||||
}
|
||||
|
||||
void FMaterialInstance::setDepthCulling(bool const enable) noexcept {
|
||||
mDepthFunc = enable ? RasterState::DepthFunc::GE : RasterState::DepthFunc::A;
|
||||
}
|
||||
@@ -451,51 +403,6 @@ const char* FMaterialInstance::getName() const noexcept {
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
void FMaterialInstance::compile(CompilerPriorityQueue const priority,
|
||||
UserVariantFilterMask variantSpec, CallbackHandler* handler,
|
||||
Invocable<void(MaterialInstance*)>&& callback) noexcept {
|
||||
FEngine& engine = mMaterial->getEngine();
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
MaterialDefinition const& definition = mMaterial->getDefinition();
|
||||
|
||||
bool const isStereoSupported = driver.isStereoSupported();
|
||||
|
||||
// Turn off the STE variant if stereo is not supported.
|
||||
if (UTILS_LIKELY(!isStereoSupported)) {
|
||||
variantSpec &= ~UserVariantFilterMask(UserVariantFilterBit::STE);
|
||||
}
|
||||
|
||||
UserVariantFilterMask const variantFilter =
|
||||
~variantSpec & UserVariantFilterMask(UserVariantFilterBit::ALL);
|
||||
ShaderModel const shaderModel = engine.getShaderModel();
|
||||
|
||||
if (UTILS_LIKELY(driver.isParallelShaderCompileSupported())) {
|
||||
for (auto const variant: definition.getVariants()) {
|
||||
if (!variantFilter || variant == Variant::filterUserVariant(variant, variantFilter)) {
|
||||
if (definition.hasVariant(variant, shaderModel, isStereoSupported)) {
|
||||
prepareProgram(driver, variant, priority);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
struct Callback {
|
||||
Invocable<void(MaterialInstance*)> f;
|
||||
MaterialInstance* m;
|
||||
static void func(void* user) {
|
||||
auto* const c = static_cast<Callback*>(user);
|
||||
c->f(c->m);
|
||||
delete c;
|
||||
}
|
||||
};
|
||||
auto* const user = new (std::nothrow) Callback{ std::move(callback), this };
|
||||
driver.compilePrograms(priority, handler, &Callback::func, user);
|
||||
} else {
|
||||
driver.compilePrograms(priority, nullptr, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void FMaterialInstance::use(FEngine::DriverApi& driver, Variant variant) const {
|
||||
assert_invariant(mDescriptorSet.getHandle());
|
||||
assert_invariant(!isUsingUboBatching() || BufferAllocator::isValid(getAllocationId()));
|
||||
@@ -600,36 +507,4 @@ void FMaterialInstance::fixMissingSamplers() const {
|
||||
}
|
||||
}
|
||||
|
||||
LocalProgramCache const& FMaterialInstance::getPrograms() const noexcept {
|
||||
return mPrograms.isInitialized() ? mPrograms : mMaterial->getPrograms();
|
||||
}
|
||||
|
||||
void FMaterialInstance::flushSpecializationConstants() const noexcept {
|
||||
if (mPendingSpecializationConstants.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mPrograms.isInitialized()) {
|
||||
mPrograms.initializeForMaterialInstance(mMaterial->getEngine(), *mMaterial);
|
||||
}
|
||||
mPrograms.setConstants(std::move(mPendingSpecializationConstants));
|
||||
mPendingSpecializationConstants.clear();
|
||||
}
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
|
||||
void FMaterialInstance::updateActiveProgramsForMatdbg(Variant const variant) const noexcept {
|
||||
mMaterial->updateActiveProgramsForMatdbg(variant);
|
||||
}
|
||||
|
||||
#endif // FILAMENT_ENABLE_MATDBG
|
||||
|
||||
template void FMaterialInstance::setConstantImpl<int32_t>(std::string_view name, int32_t value);
|
||||
template void FMaterialInstance::setConstantImpl<float>(std::string_view name, float value);
|
||||
template void FMaterialInstance::setConstantImpl<bool>(std::string_view name, bool value);
|
||||
|
||||
template int32_t FMaterialInstance::getConstantImpl<int32_t>(std::string_view name) const;
|
||||
template float FMaterialInstance::getConstantImpl<float>(std::string_view name) const;
|
||||
template bool FMaterialInstance::getConstantImpl<bool>(std::string_view name) const;
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
#define TNT_FILAMENT_DETAILS_MATERIALINSTANCE_H
|
||||
|
||||
#include "downcast.h"
|
||||
#include "LocalProgramCache.h"
|
||||
|
||||
#include "UniformBuffer.h"
|
||||
|
||||
#include "ds/DescriptorSet.h"
|
||||
@@ -26,6 +26,8 @@
|
||||
#include "details/BufferAllocator.h"
|
||||
#include "details/Engine.h"
|
||||
|
||||
#include "private/backend/DriverApi.h"
|
||||
|
||||
#include <filament/MaterialInstance.h>
|
||||
|
||||
#include <private/filament/Variant.h>
|
||||
@@ -65,7 +67,7 @@ public:
|
||||
~FMaterialInstance() noexcept;
|
||||
|
||||
void terminate(FEngine& engine);
|
||||
|
||||
|
||||
void commit(FEngine& engine) const;
|
||||
|
||||
void commit(FEngine::DriverApi& driver, UboManager* uboManager) const;
|
||||
@@ -84,32 +86,6 @@ public:
|
||||
|
||||
UniformBuffer const& getUniformBuffer() const noexcept { return mUniforms; }
|
||||
|
||||
void compile(backend::CompilerPriorityQueue priority, UserVariantFilterMask variantSpec,
|
||||
backend::CallbackHandler* handler,
|
||||
utils::Invocable<void(MaterialInstance*)>&& callback) noexcept;
|
||||
|
||||
// prepareProgram creates the program for the material's given variant at the backend level.
|
||||
// Must be called outside of backend render pass.
|
||||
// Must be called before getProgram() below.
|
||||
backend::Handle<backend::HwProgram> prepareProgram(backend::DriverApi& driver,
|
||||
Variant const variant,
|
||||
backend::CompilerPriorityQueue const priorityQueue) const noexcept {
|
||||
flushSpecializationConstants();
|
||||
return getPrograms().prepareProgram(driver, variant, priorityQueue);
|
||||
}
|
||||
|
||||
// getProgram returns the backend program for the material's given variant.
|
||||
// Must be called after prepareProgram().
|
||||
//
|
||||
// See also Material::getProgram().
|
||||
[[nodiscard]]
|
||||
backend::Handle<backend::HwProgram> getProgram(Variant const variant) const noexcept {
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
updateActiveProgramsForMatdbg(variant);
|
||||
#endif
|
||||
return getPrograms().getProgram(variant);
|
||||
}
|
||||
|
||||
void setScissor(uint32_t const left, uint32_t const bottom, uint32_t const width, uint32_t const height) noexcept {
|
||||
constexpr uint32_t maxvalu = std::numeric_limits<int32_t>::max();
|
||||
mScissorRect = { int32_t(left), int32_t(bottom),
|
||||
@@ -131,8 +107,6 @@ public:
|
||||
|
||||
bool hasScissor() const noexcept { return mHasScissor; }
|
||||
|
||||
backend::RasterState getRasterState() const noexcept;
|
||||
|
||||
backend::CullingMode getCullingMode() const noexcept { return mCulling; }
|
||||
|
||||
backend::CullingMode getShadowCullingMode() const noexcept { return mShadowCulling; }
|
||||
@@ -280,15 +254,11 @@ public:
|
||||
backend::Handle<backend::HwTexture> texture, backend::SamplerParams params);
|
||||
|
||||
using MaterialInstance::setParameter;
|
||||
using MaterialInstance::setConstant;
|
||||
|
||||
private:
|
||||
friend class FMaterial;
|
||||
friend class MaterialInstance;
|
||||
|
||||
// Cannot inline since it inspects the FMaterial class.
|
||||
LocalProgramCache const& getPrograms() const noexcept;
|
||||
|
||||
template<size_t Size>
|
||||
void setParameterUntypedImpl(std::string_view name, const void* value);
|
||||
|
||||
@@ -307,19 +277,6 @@ private:
|
||||
template<typename T>
|
||||
T getParameterImpl(std::string_view name) const;
|
||||
|
||||
template<typename T>
|
||||
void setConstantImpl(std::string_view name, T value);
|
||||
|
||||
template<typename T>
|
||||
T getConstantImpl(std::string_view name) const;
|
||||
|
||||
void flushSpecializationConstants() const noexcept;
|
||||
|
||||
#if FILAMENT_ENABLE_MATDBG
|
||||
// Called by getProgram() to update active program list for matdbg UI.
|
||||
void updateActiveProgramsForMatdbg(Variant const variant) const noexcept;
|
||||
#endif
|
||||
|
||||
// keep these grouped, they're accessed together in the render-loop
|
||||
FMaterial const* mMaterial = nullptr;
|
||||
|
||||
@@ -334,11 +291,6 @@ private:
|
||||
mutable DescriptorSet mDescriptorSet;
|
||||
UniformBuffer mUniforms;
|
||||
|
||||
// HACK: Mutable so that prepareProgram() can update specialization constants.
|
||||
mutable LocalProgramCache mPrograms;
|
||||
mutable utils::FixedCapacityVector<backend::Program::SpecializationConstant>
|
||||
mPendingSpecializationConstants;
|
||||
|
||||
backend::PolygonOffset mPolygonOffset{};
|
||||
backend::StencilState mStencilState{};
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
|
||||
#include <filament/Engine.h>
|
||||
#include <filament/Material.h>
|
||||
#include <filament/MaterialInstance.h>
|
||||
|
||||
#include "filament_test_resources.h"
|
||||
|
||||
@@ -168,52 +167,3 @@ TEST(Material, MaterialSettingInvalidApiLevelReturnsAnInvalidPackage) {
|
||||
|
||||
Engine::destroy(engine);
|
||||
}
|
||||
|
||||
TEST(MaterialInstanceTest, SetConstant) {
|
||||
Engine* engine = Engine::create(Engine::Backend::NOOP);
|
||||
|
||||
std::string shaderCode(R"(
|
||||
void material(inout MaterialInputs material) {
|
||||
prepareMaterial(material);
|
||||
material.baseColor = vec4(1.0);
|
||||
}
|
||||
)");
|
||||
|
||||
filamat::MaterialBuilder builder;
|
||||
builder.init();
|
||||
builder.name("MaterialInstanceTest");
|
||||
builder.material(shaderCode.c_str());
|
||||
builder.constant("myFloat", filamat::MaterialBuilder::ConstantType::FLOAT, 1.0f);
|
||||
builder.constant("myInt", filamat::MaterialBuilder::ConstantType::INT, 2);
|
||||
builder.constant("myBool", filamat::MaterialBuilder::ConstantType::BOOL, false);
|
||||
|
||||
filamat::Package result = builder.build(engine->getJobSystem());
|
||||
ASSERT_TRUE(result.isValid());
|
||||
|
||||
Material* material = Material::Builder()
|
||||
.package(result.getData(), result.getSize())
|
||||
.build(*engine);
|
||||
ASSERT_NE(material, nullptr);
|
||||
|
||||
MaterialInstance* instance = material->createInstance();
|
||||
ASSERT_NE(instance, nullptr);
|
||||
|
||||
// Verify default values
|
||||
EXPECT_EQ(instance->getConstant<float>("myFloat"), 1.0f);
|
||||
EXPECT_EQ(instance->getConstant<int32_t>("myInt"), 2);
|
||||
EXPECT_EQ(instance->getConstant<bool>("myBool"), false);
|
||||
|
||||
// Set new values
|
||||
instance->setConstant("myFloat", 3.0f);
|
||||
instance->setConstant("myInt", 4);
|
||||
instance->setConstant("myBool", true);
|
||||
|
||||
// Verify new values
|
||||
EXPECT_EQ(instance->getConstant<float>("myFloat"), 3.0f);
|
||||
EXPECT_EQ(instance->getConstant<int32_t>("myInt"), 4);
|
||||
EXPECT_EQ(instance->getConstant<bool>("myBool"), true);
|
||||
|
||||
engine->destroy(instance);
|
||||
engine->destroy(material);
|
||||
Engine::destroy(engine);
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ constexpr size_t CONFIG_MINSPEC_UBO_SIZE = 16384;
|
||||
// https://crbug.com/1348363 Lighting looks wrong with D3D11 but not OpenGL
|
||||
// Note that __EMSCRIPTEN__ is not defined when running matc, but that's okay because we're
|
||||
// actually using a specification constant.
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#if defined(__EMSCRIPTEN__) && !defined(FILAMENT_SUPPORTS_WEBGPU)
|
||||
constexpr size_t CONFIG_MAX_INSTANCES = 8;
|
||||
#else
|
||||
constexpr size_t CONFIG_MAX_INSTANCES = 64;
|
||||
|
||||
@@ -881,14 +881,6 @@ utils::io::sstream& CodeGenerator::generateSpecializationConstant(utils::io::sst
|
||||
|
||||
static const char* types[] = { "int", "float", "bool" };
|
||||
|
||||
// Spec constants aren't fully supported in Tint,
|
||||
// workaround until https://issues.chromium.org/issues/42250586 is resolved
|
||||
if (mTargetApi == TargetApi::WEBGPU) {
|
||||
std::string const variableName = "FILAMENT_SPEC_CONST_" + std::to_string(id) + "_" + name;
|
||||
out << " const " << types[value.index()] << " " << variableName << " = " << constantString << ";\n";
|
||||
out << types[value.index()] << " " << name << " = " << variableName << ";\n";
|
||||
return out;
|
||||
}
|
||||
if (mTargetLanguage == MaterialBuilderBase::TargetLanguage::SPIRV) {
|
||||
out << "layout (constant_id = " << id << ") const "
|
||||
<< types[value.index()] << " " << name << " = " << constantString << ";\n";
|
||||
|
||||
Vendored
+1
-2
@@ -52,8 +52,6 @@ if (ANDROID AND FILAMENT_BUILD_FILAMAT)
|
||||
tint_lang_spirv_intrinsic
|
||||
tint_lang_spirv_ir
|
||||
tint_lang_spirv_reader
|
||||
tint_lang_spirv_reader_ast_lower
|
||||
tint_lang_spirv_reader_ast_parser
|
||||
tint_lang_spirv_reader_common
|
||||
tint_lang_spirv_reader_lower
|
||||
tint_lang_spirv_reader_parser
|
||||
@@ -77,6 +75,7 @@ if (ANDROID AND FILAMENT_BUILD_FILAMAT)
|
||||
tint_utils_rtti
|
||||
tint_utils_strconv
|
||||
tint_utils_symbol
|
||||
tint_utils_system
|
||||
tint_utils_text
|
||||
tint_utils_text_generator
|
||||
)
|
||||
|
||||
@@ -17,6 +17,9 @@ function(build_material MAT_FILE TARGET_DIR MAT_NAME OUT_LIST)
|
||||
if (NOT CMAKE_BUILD_TYPE MATCHES Release)
|
||||
set(MATC_FLAGS -g ${MATC_FLAGS})
|
||||
endif()
|
||||
if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
set(MATC_FLAGS -a webgpu ${MATC_FLAGS})
|
||||
endif()
|
||||
set(mat_src "${CMAKE_CURRENT_SOURCE_DIR}/${MAT_FILE}")
|
||||
set(output_path "${SERVER_DIR}/${TARGET_DIR}/${MAT_NAME}.filamat")
|
||||
add_custom_command(
|
||||
@@ -87,7 +90,7 @@ function(add_envmap SOURCE TARGET TARGET_DIR OUT_LIST)
|
||||
WORKING_DIRECTORY "${SERVER_DIR}/${TARGET_DIR}"
|
||||
MAIN_DEPENDENCY ${source_envmap}
|
||||
DEPENDS cmgen)
|
||||
|
||||
|
||||
set(${OUT_LIST} ${${OUT_LIST}} ${target_skybox} ${target_skybox_tiny} ${target_envmap} PARENT_SCOPE)
|
||||
endfunction()
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ material {
|
||||
color
|
||||
],
|
||||
shadingModel : unlit,
|
||||
culling : none
|
||||
culling : none,
|
||||
featureLevel: 0
|
||||
}
|
||||
|
||||
fragment {
|
||||
|
||||
@@ -31,6 +31,7 @@ set(COPTS "${COPTS} -DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0")
|
||||
set(LOPTS "${LOPTS} --bind")
|
||||
set(LOPTS "${LOPTS} -s ALLOW_MEMORY_GROWTH=1")
|
||||
set(LOPTS "${LOPTS} -s EXPORTED_FUNCTIONS=['_malloc','_free']")
|
||||
set(LOPTS "${LOPTS} -s EXPORTED_RUNTIME_METHODS=['HEAP8','HEAP16','HEAP32','HEAPU8','HEAPU16','HEAPU32','HEAPF32','HEAPF64']")
|
||||
set(LOPTS "${LOPTS} -s MODULARIZE=1")
|
||||
set(LOPTS "${LOPTS} -s EXPORT_NAME=Filament")
|
||||
set(LOPTS "${LOPTS} -s USE_WEBGL2=1")
|
||||
@@ -38,6 +39,10 @@ set(LOPTS "${LOPTS} -s FULL_ES3")
|
||||
set(LOPTS "${LOPTS} -s MIN_WEBGL_VERSION=2")
|
||||
set(LOPTS "${LOPTS} -s MAX_WEBGL_VERSION=2")
|
||||
|
||||
if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
set(LOPTS "${LOPTS} --use-port=emdawnwebgpu")
|
||||
endif()
|
||||
|
||||
foreach (JS_FILENAME ${EXTERN_POSTJS_SRC})
|
||||
set(LOPTS "${LOPTS} --extern-post-js ${JS_FILENAME}")
|
||||
endforeach()
|
||||
|
||||
@@ -80,36 +80,49 @@ Filament.loadClassExtensions = function() {
|
||||
/// options ::argument:: optional WebGL 2.0 context configuration
|
||||
/// ::retval:: an instance of [Engine]
|
||||
Filament.Engine.create = function (canvas, options, config) {
|
||||
const defaults = {
|
||||
majorVersion: 2,
|
||||
minorVersion: 0,
|
||||
antialias: false,
|
||||
depth: true,
|
||||
alpha: false
|
||||
};
|
||||
options = Object.assign(defaults, options);
|
||||
if (!canvas.id) {
|
||||
canvas.id = 'filament-canvas-' + Math.random().toString(36).substr(2, 9);
|
||||
}
|
||||
const canvasId = '#' + canvas.id;
|
||||
|
||||
// Create the WebGL 2.0 context.
|
||||
const ctx = canvas.getContext("webgl2", options);
|
||||
const backend = (options && options.backend !== undefined) ?
|
||||
options.backend : Filament.Backend.DEFAULT;
|
||||
|
||||
// Enable all desired extensions by calling getExtension on each one.
|
||||
ctx.getExtension('WEBGL_compressed_texture_s3tc');
|
||||
ctx.getExtension('WEBGL_compressed_texture_s3tc_srgb');
|
||||
ctx.getExtension('WEBGL_compressed_texture_astc');
|
||||
ctx.getExtension('WEBGL_compressed_texture_etc');
|
||||
if (backend !== Filament.Backend.WEBGPU) {
|
||||
const defaults = {
|
||||
majorVersion: 2,
|
||||
minorVersion: 0,
|
||||
antialias: false,
|
||||
depth: true,
|
||||
alpha: false
|
||||
};
|
||||
const glOptions = Object.assign(defaults, options);
|
||||
|
||||
// These transient globals are used temporarily during Engine construction.
|
||||
window.filament_glOptions = options;
|
||||
window.filament_glContext = ctx;
|
||||
// Create the WebGL 2.0 context.
|
||||
const ctx = canvas.getContext("webgl2", glOptions);
|
||||
|
||||
// Enable all desired extensions by calling getExtension on each one.
|
||||
ctx.getExtension('WEBGL_compressed_texture_s3tc');
|
||||
ctx.getExtension('WEBGL_compressed_texture_s3tc_srgb');
|
||||
ctx.getExtension('WEBGL_compressed_texture_astc');
|
||||
ctx.getExtension('WEBGL_compressed_texture_etc');
|
||||
|
||||
// These transient globals are used temporarily during Engine construction.
|
||||
window.filament_glOptions = glOptions;
|
||||
window.filament_glContext = ctx;
|
||||
}
|
||||
|
||||
// Register the GL context with emscripten and create the Engine.
|
||||
const defaultConfig = Filament.Engine.createDefaultConfig();
|
||||
const finalConfig = Object.assign(defaultConfig, config);
|
||||
const engine = Filament.Engine._create(finalConfig);
|
||||
const engine = Filament.Engine._create(backend, finalConfig);
|
||||
|
||||
// Annotate the engine with the GL context to support multiple canvases.
|
||||
engine.context = window.filament_glContext;
|
||||
engine.handle = window.filament_contextHandle;
|
||||
if (backend !== Filament.Backend.WEBGPU) {
|
||||
engine.context = window.filament_glContext;
|
||||
engine.handle = window.filament_contextHandle;
|
||||
}
|
||||
engine.canvasId = canvasId;
|
||||
|
||||
// Ensure that we do not pollute the global namespace.
|
||||
delete window.filament_glOptions;
|
||||
@@ -119,6 +132,13 @@ Filament.loadClassExtensions = function() {
|
||||
return engine;
|
||||
};
|
||||
|
||||
Filament.Engine.prototype.createSwapChain = function() {
|
||||
if (this.canvasId) {
|
||||
return this._createSwapChainForCanvas(this.canvasId);
|
||||
}
|
||||
return this._createSwapChain();
|
||||
};
|
||||
|
||||
Filament.Engine.prototype.execute = function() {
|
||||
window.filament_contextHandle = this.handle;
|
||||
this._execute();
|
||||
|
||||
Vendored
+17
-1
@@ -26,6 +26,22 @@ import * as glm from "gl-matrix";
|
||||
export as namespace Filament;
|
||||
|
||||
export function getSupportedFormatSuffix(desired: string): void;
|
||||
|
||||
/**
|
||||
* Asynchronously initializes the WebGPU adapter and device.
|
||||
* This must be awaited before initializing the Filament Engine with the WebGPU backend.
|
||||
*/
|
||||
export function initWebGPU(): Promise<void>;
|
||||
|
||||
export enum Backend {
|
||||
DEFAULT,
|
||||
OPENGL,
|
||||
VULKAN,
|
||||
METAL,
|
||||
WEBGPU,
|
||||
NOOP,
|
||||
}
|
||||
|
||||
export function init(assets: string[], onready?: (() => void) | null): void;
|
||||
export function fetch(assets: string[], onDone?: (() => void) | null, onFetched?: ((name: string) => void) | null): void;
|
||||
export function clearAssetCache(): void;
|
||||
@@ -542,7 +558,7 @@ interface Filamesh {
|
||||
}
|
||||
|
||||
export class Engine {
|
||||
public static create(canvas: HTMLCanvasElement, contextOptions?: object): Engine;
|
||||
public static create(canvas: HTMLCanvasElement, options?: { backend?: Backend }): Engine;
|
||||
public static destroy(engine: Engine): void;
|
||||
public execute(): void;
|
||||
public createCamera(entity: Entity): Camera;
|
||||
|
||||
@@ -422,15 +422,17 @@ enum_<Engine::Config::ShaderLanguage>("ShaderLanguage")
|
||||
|
||||
/// Engine ::core class:: Central manager and resource owner.
|
||||
class_<Engine>("Engine")
|
||||
.class_function("_create", (Engine* (*)(Engine::Config)) [] (Engine::Config config) {
|
||||
EM_ASM_INT({
|
||||
const options = window.filament_glOptions;
|
||||
const context = window.filament_glContext;
|
||||
const handle = GL.registerContext(context, options);
|
||||
window.filament_contextHandle = handle;
|
||||
GL.makeContextCurrent(handle);
|
||||
});
|
||||
return Engine::create(Engine::Backend::DEFAULT, nullptr, nullptr, &config);
|
||||
.class_function("_create", (Engine* (*)(backend::Backend, Engine::Config)) [] (backend::Backend backend, Engine::Config config) {
|
||||
if (backend == backend::Backend::DEFAULT || backend == backend::Backend::OPENGL) {
|
||||
EM_ASM_INT({
|
||||
const options = window.filament_glOptions;
|
||||
const context = window.filament_glContext;
|
||||
const handle = GL.registerContext(context, options);
|
||||
window.filament_contextHandle = handle;
|
||||
GL.makeContextCurrent(handle);
|
||||
});
|
||||
}
|
||||
return Engine::create(backend, nullptr, nullptr, &config);
|
||||
}, allow_raw_pointers())
|
||||
|
||||
// Create a default Engine configuration. This is for internal use to ensure that engine
|
||||
@@ -503,9 +505,17 @@ class_<Engine>("Engine")
|
||||
|
||||
/// createSwapChain ::method::
|
||||
/// ::retval:: an instance of [SwapChain]
|
||||
.function("createSwapChain", (SwapChain* (*)(Engine*)) []
|
||||
.function("_createSwapChain", (SwapChain* (*)(Engine*)) []
|
||||
(Engine* engine) { return engine->createSwapChain(nullptr); },
|
||||
allow_raw_pointers())
|
||||
.function("_createSwapChainForCanvas", (SwapChain* (*)(Engine*, std::string)) []
|
||||
(Engine* engine, std::string canvasId) {
|
||||
// Allocate on the heap because nativeWindow is passed asynchronously through the
|
||||
// driver command buffer to the backend thread.
|
||||
std::string* persistentCanvasId = new std::string(canvasId);
|
||||
return engine->createSwapChain((void*)persistentCanvasId->c_str());
|
||||
},
|
||||
allow_raw_pointers())
|
||||
/// destroySwapChain ::method::
|
||||
/// swapChain ::argument:: an instance of [SwapChain]
|
||||
.function("destroySwapChain", (void (*)(Engine*, SwapChain*)) []
|
||||
@@ -2147,7 +2157,7 @@ class_<Ktx2Provider>("gltfio$Ktx2Provider")
|
||||
class_<WebpProvider>("gltfio$WebpProvider")
|
||||
.constructor(EMBIND_LAMBDA(WebpProvider, (Engine* engine), {
|
||||
return WebpProvider { createWebpProvider(engine) };
|
||||
}))
|
||||
}))
|
||||
.class_function("isWebpSupported", &isWebpSupported);
|
||||
|
||||
class_<AssetLoader>("gltfio$AssetLoader")
|
||||
|
||||
@@ -452,6 +452,7 @@ enum_<ktxreader::Ktx2Reader::Result>("Ktx2Reader$Result")
|
||||
.value("OPENGL", backend::Backend::OPENGL)
|
||||
.value("VULKAN", backend::Backend::VULKAN)
|
||||
.value("METAL", backend::Backend::METAL)
|
||||
.value("WEBGPU", backend::Backend::WEBGPU)
|
||||
.value("NOOP", backend::Backend::NOOP);
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,53 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// ---------------
|
||||
// WebGPU Initialization
|
||||
// ---------------
|
||||
|
||||
/// initWebGPU ::function:: Asynchronously initializes the WebGPU adapter and device.
|
||||
/// This must be awaited before initializing the Filament Engine with the WebGPU backend.
|
||||
/// ::retval:: Promise that resolves when WebGPU is ready.
|
||||
Filament.initWebGPU = async function() {
|
||||
if (!navigator.gpu) {
|
||||
throw new Error("WebGPU is not supported by this browser.");
|
||||
}
|
||||
const adapter = await navigator.gpu.requestAdapter();
|
||||
if (!adapter) {
|
||||
throw new Error("No appropriate WebGPU adapter found.");
|
||||
}
|
||||
const requiredFeatures = [];
|
||||
const optionalFeatures = [
|
||||
'immediates',
|
||||
'chromium-experimental-immediate-data',
|
||||
'rg11b10ufloat-renderable',
|
||||
'float32-filterable',
|
||||
'float32-blendable',
|
||||
'depth-clip-control',
|
||||
'depth32float-stencil8',
|
||||
'texture-compression-bc',
|
||||
'texture-compression-etc2',
|
||||
'texture-compression-astc'
|
||||
];
|
||||
for (const feature of optionalFeatures) {
|
||||
if (adapter.features.has(feature)) {
|
||||
requiredFeatures.push(feature);
|
||||
}
|
||||
}
|
||||
const requiredLimits = {};
|
||||
for (const key in adapter.limits) {
|
||||
requiredLimits[key] = adapter.limits[key];
|
||||
}
|
||||
if (adapter.limits.maxImmediateSize !== undefined) {
|
||||
requiredLimits.maxImmediateSize = adapter.limits.maxImmediateSize;
|
||||
} else if (requiredFeatures.includes('chromium-experimental-immediate-data')) {
|
||||
requiredLimits.maxImmediateSize = 64;
|
||||
}
|
||||
|
||||
const device = await adapter.requestDevice({ requiredFeatures, requiredLimits });
|
||||
Filament.preinitializedWebGPUDevice = device;
|
||||
};
|
||||
|
||||
// ---------------
|
||||
// Buffer Wrappers
|
||||
// ---------------
|
||||
|
||||
@@ -66,7 +66,12 @@ Filament.init = (assets, onready) => {
|
||||
// Emscripten creates a global function called "Filament" that returns a promise that
|
||||
// resolves to a module. Here we replace the function with the module. Note that our
|
||||
// TypeScript bindings assume that Filament is a namespace, not a function.
|
||||
Filament().then(module => {
|
||||
const moduleConfig = {};
|
||||
if (Filament.preinitializedWebGPUDevice) {
|
||||
moduleConfig.preinitializedWebGPUDevice = Filament.preinitializedWebGPUDevice;
|
||||
}
|
||||
|
||||
Filament(moduleConfig).then(module => {
|
||||
|
||||
// Merge our extension functions into the emscripten module, not the other
|
||||
// way around, because Emscripten potentially replaces the HEAPU8 views in
|
||||
|
||||
Reference in New Issue
Block a user