Compare commits
1 Commits
main
...
pf/webgpu-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d49eb5e111 |
@@ -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()
|
||||
@@ -969,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()
|
||||
|
||||
@@ -1021,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()
|
||||
|
||||
8
build.sh
8
build.sh
@@ -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."
|
||||
;;
|
||||
|
||||
@@ -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.");
|
||||
|
||||
|
||||
@@ -75,6 +75,10 @@ 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.
|
||||
@@ -143,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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
85
filament/backend/src/webgpu/platform/WebGPUPlatformWasm.cpp
Normal file
85
filament/backend/src/webgpu/platform/WebGPUPlatformWasm.cpp
Normal file
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -39,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();
|
||||
|
||||
18
web/filament-js/filament.d.ts
vendored
18
web/filament-js/filament.d.ts
vendored
@@ -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