Compare commits

..
Author SHA1 Message Date
Powei Feng ee4d59a743 mesa: fix race conditions for CI's backend test
- *Enable ASAN/UBSAN for CI's backend test*
 - Make local fix to mesa and embed patch in get-mesa.sh
 - Disable int128 instrinsics for abseil when asan+arm
 - Fix race conditions in PlatformOSMesa
 - Fix race in OpenGLDriver::terminate()
 - Delete mPlatform in BackendTest
2026-05-01 22:40:06 -07:00
40 changed files with 149 additions and 560 deletions
+5 -5
View File
@@ -474,12 +474,12 @@ if (NOT MSVC)
endif()
if (ANDROID OR WASM)
# On Android and WASM RELEASE builds, we omit unwind info to save space.
# On Android and WebGL 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 WASM, we disable RTTI because we pass emscripten::val back and forth
# With WebGL, 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-WASM Apple platforms.
# Build with Metal support on non-WebGL Apple platforms.
if (APPLE AND NOT WASM)
option(FILAMENT_SUPPORTS_METAL "Include the Metal backend" ON)
else()
@@ -969,7 +969,7 @@ if (FILAMENT_SUPPORTS_VULKAN)
add_subdirectory(${EXTERNAL}/spirv-headers)
endif()
if (FILAMENT_SUPPORTS_WEBGPU AND NOT WASM)
if (FILAMENT_SUPPORTS_WEBGPU)
add_subdirectory(${EXTERNAL}/dawn/tnt/)
endif()
@@ -1021,7 +1021,7 @@ if (IS_HOST_PLATFORM)
add_subdirectory(${TOOLS}/specgen)
endif()
# Generate exported executables for cross-compiled builds (Android, WASM, and iOS)
# Generate exported executables for cross-compiled builds (Android, WebGL, 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()
+2 -8
View File
@@ -3,7 +3,7 @@ set -e
# Host tools required by Android, WASM, and iOS builds
MOBILE_HOST_TOOLS="matc resgen cmgen filamesh uberz"
WEB_HOST_TOOLS="${MOBILE_HOST_TOOLS} mipgen filamesh"
WEB_HOST_TOOLS="${MOBILE_HOST_TOOLS} mipgen filamesh glslminifier"
function print_help {
local self_name=$(basename "$0")
@@ -368,11 +368,6 @@ 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.
(
@@ -1032,8 +1027,7 @@ 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."
;;
+17 -1
View File
@@ -134,7 +134,23 @@ pushd .
cd ${MESA_DIR}
# Need >= 24 to have llvmpipe for swrast. llvmpipe is needed for GL >= 4.1.
git checkout mesa-${MESA_VERSION}
git checkout -f mesa-${MESA_VERSION}
# Apply custom patch to fix a double-free in OSMesa
git apply << 'EOF'
diff --git a/src/mesa/program/program.c b/src/mesa/program/program.c
index 74bd6a6c33b..a70814e53a1 100644
--- a/src/mesa/program/program.c
+++ b/src/mesa/program/program.c
@@ -130,6 +130,7 @@ _mesa_free_program_data(struct gl_context *ctx)
ctx->ATIFragmentShader.Current->RefCount--;
if (ctx->ATIFragmentShader.Current->RefCount <= 0) {
free(ctx->ATIFragmentShader.Current);
+ ctx->ATIFragmentShader.Current = NULL;
}
}
EOF
mkdir -p out
+3 -8
View File
@@ -358,11 +358,6 @@ 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)
@@ -436,7 +431,7 @@ if (FILAMENT_USE_ABSEIL_LOGGING)
target_link_libraries(${TARGET} PRIVATE absl::log)
endif()
# Android, iOS, and WASM do not use bluegl.
# Android, iOS, and WebGL do not use bluegl.
if(FILAMENT_SUPPORTS_OPENGL AND NOT IOS AND NOT ANDROID AND NOT WASM)
target_link_libraries(${TARGET} PRIVATE bluegl)
endif()
@@ -446,7 +441,7 @@ if (FILAMENT_SUPPORTS_VULKAN)
target_link_libraries(${TARGET} PRIVATE SPIRV-Headers)
endif()
if (FILAMENT_SUPPORTS_WEBGPU AND NOT WASM)
if (FILAMENT_SUPPORTS_WEBGPU)
target_link_libraries(${TARGET} PRIVATE webgpu_dawn dawncpp_headers)
endif()
@@ -564,7 +559,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 AND NOT WASM)
if (FILAMENT_SUPPORTS_WEBGPU)
install(TARGETS webgpu_dawn ${INSTALL_TYPE} DESTINATION lib/${DIST_DIR})
endif()
@@ -75,7 +75,8 @@ private:
OSMesaContext context;
std::unique_ptr<uint8_t[]> buffer;
};
std::unordered_map<std::thread::id, ContextInfo> mAdditionalContexts;
using ContextMap = std::unordered_map<std::thread::id, ContextInfo>;
ContextMap mAdditionalContexts;
mutable std::shared_mutex mAdditionalContextsLock;
};
@@ -31,11 +31,6 @@
#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>
@@ -66,9 +61,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]] virtual wgpu::Adapter requestAdapter(wgpu::Surface const& surface);
[[nodiscard]] wgpu::Adapter requestAdapter(wgpu::Surface const& surface);
// either returns a valid device or panics
[[nodiscard]] virtual wgpu::Device requestDevice(wgpu::Adapter const& adapter);
[[nodiscard]] wgpu::Device requestDevice(wgpu::Adapter const& adapter);
struct Configuration {
wgpu::BackendType forceBackendType = wgpu::BackendType::Undefined;
@@ -1,40 +0,0 @@
/*
* 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
@@ -1,70 +0,0 @@
/*
* 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
-5
View File
@@ -30,8 +30,6 @@
#include "backend/platforms/WebGPUPlatformLinux.h"
#elif defined(WIN32)
#include "backend/platforms/WebGPUPlatformWindows.h"
#elif defined(__EMSCRIPTEN__)
#include "backend/platforms/WebGPUPlatformWasm.h"
#endif
#endif
@@ -154,8 +152,6 @@ 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
@@ -213,4 +209,3 @@ void PlatformFactory::destroy(Platform** platform) noexcept {
}
} // namespace filament::backend
@@ -403,6 +403,9 @@ void OpenGLDriver::terminate() {
if (getJobWorker()) {
getJobWorker()->terminate();
}
// wait for the GPU again because JobWorker might have queued more work.
glFinish();
if constexpr (UTILS_HAS_THREADING) {
stopServiceThread();
}
@@ -20,6 +20,7 @@
#include <utils/Panic.h>
#include <utils/ThreadUtils.h>
#include <algorithm>
#include <dlfcn.h>
#include <memory>
@@ -203,6 +204,7 @@ void PlatformOSMesa::createContext(bool shared) {
void PlatformOSMesa::releaseContext() noexcept {
std::thread::id currentThreadId = utils::ThreadUtils::getThreadId();
OSMesaContext context = nullptr;
std::unique_ptr<uint8_t[]> buffer;
{
std::lock_guard<std::shared_mutex> lock(mAdditionalContextsLock);
@@ -212,16 +214,15 @@ void PlatformOSMesa::releaseContext() noexcept {
return;
}
context = it->second.context;
buffer = std::move(it->second.buffer);
mAdditionalContexts.erase(it);
}
OSMesaAPI* api = (OSMesaAPI*) mOsMesaApi;
// Passing NULL as the context is the standard way to unbind in OSMesa.
api->fOSMesaMakeCurrent(NULL, NULL, 0, 0, 0);
api->fOSMesaDestroyContext(context);
{
std::lock_guard<std::shared_mutex> lock(mAdditionalContextsLock);
mAdditionalContexts.erase(currentThreadId);
if (api) {
// Passing NULL as the context is the standard way to unbind in OSMesa.
api->fOSMesaMakeCurrent(NULL, NULL, 0, 0, 0);
api->fOSMesaDestroyContext(context);
}
}
@@ -19,10 +19,6 @@
#include <backend/DriverEnums.h>
#if defined(__EMSCRIPTEN__)
#include <backend/platforms/WebGPUWasmPolyfill.h>
#endif
#include <tsl/robin_map.h>
#include <webgpu/webgpu_cpp.h>
+6 -36
View File
@@ -64,22 +64,6 @@
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 {
@@ -163,10 +147,8 @@ void WebGPUDriver::terminate() {
}
void WebGPUDriver::tick(int) {
#if !defined(__EMSCRIPTEN__)
mDevice.Tick();
mAdapter.GetInstance().ProcessEvents();
#endif
}
void WebGPUDriver::beginFrame(int64_t monotonic_clock_ns,
@@ -216,9 +198,7 @@ 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));
}
}
@@ -802,7 +782,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();
@@ -820,15 +800,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());
@@ -857,11 +837,7 @@ 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) {
@@ -883,7 +859,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;
}
@@ -1348,8 +1324,7 @@ 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(
@@ -1508,12 +1483,7 @@ 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,10 +168,8 @@ 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,33 +28,6 @@
#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)
@@ -99,28 +72,13 @@ 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 = immediateSize,
.immediateSize = supportedLimits.maxImmediateSize,
};
const wgpu::PipelineLayout layout =
#if defined(__EMSCRIPTEN__)
wgpu::PipelineLayout::Acquire(
filamentCreatePipelineLayoutWithImmediateData(mDevice.Get(),
reinterpret_cast<WGPUPipelineLayoutDescriptor const*>(&descriptor)));
#else
mDevice.CreatePipelineLayout(&descriptor);
#endif
const wgpu::PipelineLayout layout{ mDevice.CreatePipelineLayout(&descriptor) };
FILAMENT_CHECK_POSTCONDITION(layout)
<< "Failed to create pipeline layout " << descriptor.label << ".";
return layout;
@@ -76,10 +76,8 @@ 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.");
@@ -75,10 +75,6 @@ namespace {
.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.
@@ -147,7 +143,6 @@ 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;
}
@@ -81,9 +81,7 @@ 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,9 +392,6 @@ 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:
@@ -403,7 +400,6 @@ 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,12 +67,10 @@ 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) {
@@ -233,7 +231,6 @@ 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";
@@ -242,7 +239,6 @@ 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,12 +387,9 @@ 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,15 +17,11 @@
#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>
+10 -24
View File
@@ -41,14 +41,6 @@ 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) {
@@ -217,9 +209,9 @@ WebGPUTexture::WebGPUTexture(const SamplerType samplerType, const uint8_t levels
mWebGPUUsage{ fToWGPUTextureUsage(usage, samples,
mMipmapGenerationStrategy == MipmapGenerationStrategy::SPD_COMPUTE_PASS,
mMipmapGenerationStrategy == MipmapGenerationStrategy::RENDER_PASS,
supportsTransientAttachment(device)) },
device.HasFeature(wgpu::FeatureName::TransientAttachments)) },
mViewUsage{ fToWGPUTextureUsage(usage, samples, false, false,
supportsTransientAttachment(device)) },
device.HasFeature(wgpu::FeatureName::TransientAttachments)) },
mDimension{ toWebGPUTextureViewDimension(samplerType) },
mBlockWidth{ filament::backend::getBlockWidth(format) },
mBlockHeight{ filament::backend::getBlockHeight(format) },
@@ -299,7 +291,10 @@ WebGPUTexture::WebGPUTexture(WebGPUTexture const* src, const uint8_t baseLevel,
mDefaultTextureView = makeTextureView(mDefaultMipLevel, levelCount, 0,
mDefaultBaseArrayLayer, src->getViewDimension());
} else {
wgpu::TextureViewDescriptor viewDesc{
wgpu::TextureComponentSwizzleDescriptor swizzleDesc{};
swizzleDesc.swizzle = mSwizzle;
const wgpu::TextureViewDescriptor viewDesc{
.nextInChain = &swizzleDesc,
.label = "swizzled_texture_view",
.format = mTexture.GetFormat(),
.dimension = src->getViewDimension(),
@@ -308,12 +303,6 @@ 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";
@@ -338,18 +327,15 @@ WebGPUTexture::WebGPUTexture(const WebGPUTexture* src,
mDefaultBaseArrayLayer{ 0 },
mMsaaSidecarTexture{src->mMsaaSidecarTexture},
mSwizzle{ composeSwizzle(src->getSwizzle(), nextSwizzle) } {
wgpu::TextureComponentSwizzleDescriptor swizzleDesc{};
swizzleDesc.swizzle = mSwizzle;
wgpu::TextureViewDescriptor viewDesc{
const wgpu::TextureViewDescriptor viewDesc{
.nextInChain = &swizzleDesc,
.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,9 +20,6 @@
#include "DriverBase.h"
#include <backend/DriverEnums.h>
#if defined(__EMSCRIPTEN__)
#include <backend/platforms/WebGPUWasmPolyfill.h>
#endif
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
@@ -21,10 +21,6 @@
#include <backend/DriverEnums.h>
#if defined(__EMSCRIPTEN__)
#include <backend/platforms/WebGPUWasmPolyfill.h>
#endif
#include <webgpu/webgpu_cpp.h>
#include <string_view>
@@ -601,7 +597,7 @@ namespace filament::backend {
}
wgpu::TextureUsage transientAttachmentNeeded{ wgpu::TextureUsage::None };
bool const useTransientAttachment =
const bool useTransientAttachment {
deviceSupportsTransientAttachments &&
// Usage consists of attachment flags only.
none(fUsage & ~TextureUsage::ALL_ATTACHMENTS) &&
@@ -612,12 +608,9 @@ 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
@@ -57,29 +57,20 @@ 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 {
@@ -275,21 +266,12 @@ void printInstanceDetails(wgpu::Instance const& instance) {
#endif
dawnTogglesDescriptor.enabledToggleCount = toggles.size();
dawnTogglesDescriptor.enabledToggles = toggles.data();
#if defined(__EMSCRIPTEN__)
constexpr std::array<wgpu::InstanceFeatureName, 0> features;
#else
constexpr std::array features = {
wgpu::InstanceFeatureName::TimedWaitAny
};
#endif
const wgpu::InstanceFeatureName features[] = {wgpu::InstanceFeatureName::TimedWaitAny};
wgpu::InstanceDescriptor instanceDescriptor{
.nextInChain = &dawnTogglesDescriptor,
.requiredFeatureCount = features.size(),
.requiredFeatures = features.data(),
.requiredFeatures = features,
};
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)
@@ -369,8 +351,6 @@ 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, {});
@@ -393,10 +373,8 @@ struct AdapterDetails final {
[[nodiscard]] std::string toString(AdapterDetails const& details) {
std::stringstream out;
out << adapterInfoToString(details.info);
#if !defined(__EMSCRIPTEN__)
out << " power preference " << powerPreferenceToString(details.powerPreference);
#endif
out << adapterInfoToString(details.info)
<< " power preference " << powerPreferenceToString(details.powerPreference);
return out.str();
}
@@ -1,85 +0,0 @@
/*
* 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
+2
View File
@@ -83,6 +83,8 @@ BackendTest::~BackendTest() {
driver->terminate();
delete driver;
recordFailedImages();
delete mPlatform;
}
void BackendTest::initializeDriver() {
@@ -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__) && !defined(FILAMENT_SUPPORTS_WEBGPU)
#if defined(__EMSCRIPTEN__)
constexpr size_t CONFIG_MAX_INSTANCES = 8;
#else
constexpr size_t CONFIG_MAX_INSTANCES = 64;
+18 -4
View File
@@ -14,6 +14,7 @@ if [ ! -d "${PROJECT_ROOT_DIR}/mesa/out" ]; then
fi
BACKEND_TEST_TARGET=''
ASAN_FLAG=''
# Set environment variables to use Mesa drivers.
os_name=$(uname -s)
@@ -27,15 +28,18 @@ if [[ "$os_name" == "Linux" ]]; then
export VK_ICD_FILENAMES="${PROJECT_ROOT_DIR}/mesa/out/share/vulkan/icd.d/lvp_icd.x86_64.json"
fi
BACKEND_TEST_TARGET=backend_test_linux
ASAN_FLAG="-b"
elif [[ "$os_name" == "Darwin" ]]; then
export DYLD_LIBRARY_PATH="${PROJECT_ROOT_DIR}/mesa/out/lib"
export VK_ICD_FILENAMES="${PROJECT_ROOT_DIR}/mesa/out/share/vulkan/icd.d/lvp_icd.aarch64.json"
BACKEND_TEST_TARGET=backend_test_mac
# asan is too slow for macOs build of the backend test
ASAN_FLAG=""
fi
# Build backend_test_mac
echo "Building backend_test_mac..."
"${PROJECT_ROOT_DIR}/build.sh" -W -p desktop -X "${PROJECT_ROOT_DIR}/mesa" debug ${BACKEND_TEST_TARGET}
# Build backend test
echo "Building ${BACKEND_TEST_TARGET}..."
"${PROJECT_ROOT_DIR}/build.sh" ${ASAN_FLAG} -W -y release -p desktop -X "${PROJECT_ROOT_DIR}/mesa" debug ${BACKEND_TEST_TARGET}
set +e
@@ -51,10 +55,20 @@ do
fi
done
# Mesa OSMesa is known to leak part of the context.
LSAN_CMD_PREFIX=""
if [[ "${ASAN_FLAG}" == "-b" ]]; then
echo "leak:PlatformOSMesa.cpp" > /tmp/leak_skip.txt
LSAN_CMD_PREFIX="LSAN_OPTIONS=suppressions=/tmp/leak_skip.txt"
fi
FINAL_RESULT=0
for BACKEND in ${BACKENDS[@]}; do
echo "----- ${BACKEND} backend test -----"
${PROJECT_ROOT_DIR}/out/cmake-debug/filament/backend/${BACKEND_TEST_TARGET} -a ${BACKEND} --ci --headless_only ${GTEST_FILTER_ARG}
${LSAN_CMD_PREFIX} ${PROJECT_ROOT_DIR}/out/cmake-debug/filament/backend/${BACKEND_TEST_TARGET} \
-a ${BACKEND} --ci --headless_only ${GTEST_FILTER_ARG}
RESULT=$(echo $?)
if [ ${RESULT} -gt 0 ]; then
echo "----- Error: backend ${BACKEND} test failed with result ${RESULT} -----"
+13 -1
View File
@@ -302,7 +302,19 @@ static_assert(ABSL_INTERNAL_INLINE_NAMESPACE_STR[0] != 'h' ||
#if (defined(__clang__) && !defined(_WIN32)) || \
(defined(__CUDACC__) && __CUDACC_VER_MAJOR__ >= 9) || \
(defined(__GNUC__) && !defined(__clang__) && !defined(__CUDACC__))
#define ABSL_HAVE_INTRINSIC_INT128 1
// Disable int128 instrinsic on arm when asan is enabled
#if defined(__arm__) || defined(__aarch64__)
#if defined(__has_feature) || defined(__SANITIZE_ADDRESS__)
#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__)
#define ABSL_ASAN_ON_ARM_DISABLE_INT128 1
#endif
#endif
#endif
#ifndef ABSL_ASAN_ON_ARM_DISABLE_INT128
#define ABSL_HAVE_INTRINSIC_INT128 1
#endif
#elif defined(__CUDACC__)
// __CUDACC_VER__ is a full version number before CUDA 9, and is defined to a
// string explaining that it has been removed starting with CUDA 9. We use
+10
View File
@@ -32,3 +32,13 @@ This folder previously last updated as follows:
rsync -r abseil_new/ abseil/ --delete --exclude tnt
rm -rf master.zip abseil_new
git add abseil ; git status
## Custom Changes
A custom patch has been applied to fix compilation on ARM architectures when AddressSanitizer (ASAN) is enabled (specifically regarding `ABSL_HAVE_INTRINSIC_INT128`).
If you update the `abseil` folder, you may need to re-apply the patch. You can do so by running the following command from the repository root:
```shell
git apply third_party/abseil/tnt/asan_arm_int128.diff
```
+1 -4
View File
@@ -17,9 +17,6 @@ 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(
@@ -90,7 +87,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
View File
@@ -39,10 +39,6 @@ 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()
+21 -41
View File
@@ -80,49 +80,36 @@ Filament.loadClassExtensions = function() {
/// options ::argument:: optional WebGL 2.0 context configuration
/// ::retval:: an instance of [Engine]
Filament.Engine.create = function (canvas, options, config) {
if (!canvas.id) {
canvas.id = 'filament-canvas-' + Math.random().toString(36).substr(2, 9);
}
const canvasId = '#' + canvas.id;
const defaults = {
majorVersion: 2,
minorVersion: 0,
antialias: false,
depth: true,
alpha: false
};
options = Object.assign(defaults, options);
const backend = (options && options.backend !== undefined) ?
options.backend : Filament.Backend.DEFAULT;
// Create the WebGL 2.0 context.
const ctx = canvas.getContext("webgl2", options);
if (backend !== Filament.Backend.WEBGPU) {
const defaults = {
majorVersion: 2,
minorVersion: 0,
antialias: false,
depth: true,
alpha: false
};
const glOptions = Object.assign(defaults, options);
// 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');
// 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;
}
// These transient globals are used temporarily during Engine construction.
window.filament_glOptions = options;
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(backend, finalConfig);
const engine = Filament.Engine._create(finalConfig);
// Annotate the engine with the GL context to support multiple canvases.
if (backend !== Filament.Backend.WEBGPU) {
engine.context = window.filament_glContext;
engine.handle = window.filament_contextHandle;
}
engine.canvasId = canvasId;
engine.context = window.filament_glContext;
engine.handle = window.filament_contextHandle;
// Ensure that we do not pollute the global namespace.
delete window.filament_glOptions;
@@ -132,13 +119,6 @@ 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();
+1 -17
View File
@@ -26,22 +26,6 @@ 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;
@@ -558,7 +542,7 @@ interface Filamesh {
}
export class Engine {
public static create(canvas: HTMLCanvasElement, options?: { backend?: Backend }): Engine;
public static create(canvas: HTMLCanvasElement, contextOptions?: object): Engine;
public static destroy(engine: Engine): void;
public execute(): void;
public createCamera(entity: Entity): Camera;
+11 -21
View File
@@ -422,17 +422,15 @@ enum_<Engine::Config::ShaderLanguage>("ShaderLanguage")
/// Engine ::core class:: Central manager and resource owner.
class_<Engine>("Engine")
.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);
.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);
}, allow_raw_pointers())
// Create a default Engine configuration. This is for internal use to ensure that engine
@@ -505,17 +503,9 @@ 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*)) []
@@ -2157,7 +2147,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")
-1
View File
@@ -452,7 +452,6 @@ 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);
}
-47
View File
@@ -14,53 +14,6 @@
* 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
// ---------------
+1 -6
View File
@@ -66,12 +66,7 @@ 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.
const moduleConfig = {};
if (Filament.preinitializedWebGPUDevice) {
moduleConfig.preinitializedWebGPUDevice = Filament.preinitializedWebGPUDevice;
}
Filament(moduleConfig).then(module => {
Filament().then(module => {
// Merge our extension functions into the emscripten module, not the other
// way around, because Emscripten potentially replaces the HEAPU8 views in