Compare commits

..
Author SHA1 Message Date
Sungun Park 07356a2949 tmp - testing edge cases 2025-06-02 20:02:21 -07:00
Juan Caldas c73d11858e Webgpu: Move Filter mode check (#8804)
BUGS=[397432947]
2025-06-02 14:10:33 +00:00
Powei Feng 23b67be41a Fix always bind uniform logic in MaterialInstance (#8801)
The logic for duplicating UBO was omitted after #8739
2025-06-01 23:27:55 -07:00
bridgewaterrobbie 7d53baad5c Add float32 filtering feature, so that we can safely assume non-int non-depth types are filtering. 2025-05-30 17:53:04 -04:00
doriswu 1ae33a23fe Use highp for square distance 2025-05-30 13:52:44 -07:00
Rafael Dominguez 351d9287af Update the clang-format rule for comments
Make sure theres a space before the comment

So now instead of getting
 #endif// comment

it will be
 #endif // comment
2025-05-30 13:52:01 -07:00
Mathias Agopian e7e5004946 attempt to fix external streams with protected context
There was several issues:

1) when we're switching contexts (e.g. between protect and regular) we
   needed up reattach all SurfaceView (i.e. streams), because they need
   to be attached on currently active context.

2) reattaching, because it's implemented as detach + attach, would 
   destroy the current gl texture id and create a new one. However,
   because of the way descriptor-sets were implemented, that GL
   texture id was kept inside the descriptor, later leading to using
   a destroyed texture id.
   The fix here is to store texture handles in descriptors, so that
   we can update the id independently. 

3) we also needed to invalidate all bound descriptor sets because it's
   now possible for descriptor sets to have outdated descriptors
2025-05-30 13:15:40 -07:00
Juan Caldas b9be9b4f2d Set Render Pipeline 2025-05-30 15:06:50 -05:00
Powei Feng ce88a56579 Fix leaking dummy depth array texture (#8796) 2025-05-30 17:19:55 +00:00
Powei Feng 689e769f9a utils: Fix FixedCircularBufferTest.Exceptions (#8794)
Switching from assert_invariant to assert produced a different
error message.
2025-05-30 10:02:31 -07:00
Jeremy Nelson 2f0a8b19b5 use params in draw call 2025-05-30 11:49:05 -05:00
Andy Hovingh 1bed68afb7 webgpu: support user-defined spec constants in addition to native Filament ones 2025-05-30 11:33:14 -05:00
Konrad Piascik 130e02da5c Add helper script to linux documentation (#8783) 2025-05-30 14:16:36 +00:00
bridgewaterrobbie 8e06a68446 Relocate calling mRenderPassEncoder.SetBindGroup to draw call, deferring it to handle non-default render targets being used. 2025-05-29 20:23:06 -04:00
bridgewaterrobbie 656b50b304 Manually remove Tint annotations that Dawn complains about when consuming.
Dawn complains when consuming these lines with
WebGPU device error: ErrorType::Validation Error while parsing WGSL: :24:4 error: expected attribute
Did you mean 'size'?
Possible values: 'align', 'binding', 'blend_src', 'builtin', 'color', 'compute', 'diagnostic', 'fragment', 'group', 'id', 'input_attachment_index', 'interpolate', 'invariant', 'location', 'must_use', 'size', 'vertex', 'workgroup_size'
  @stride(16) @internal(disable_validation__ignore_stride)

And we don't see a better workaround at this time.
2025-05-29 17:59:12 -04:00
Mathias Agopian 880db4ec43 fix a buffer overflow during init
the default cubemap has RGBA pixels (4 bytes per pixel).
2025-05-29 14:27:38 -07:00
Syed Idris Shah 2ce294720d vertex buffer info hardening.
Implement vertexbufferinfo taking both interleaved and block attributes into account.
Handle the unused buffers by dedicating a slot for that.
Update the usage of vertexbufferinfo for setVertexBuffer call
2025-05-29 15:51:55 -05:00
Juan Caldas eb07decb4b wgpu: Ignore spec constants greater than 10 (#8789) 2025-05-29 15:58:35 -04:00
Mathias Agopian 47930edf70 don't use assert_invariant in public headers
this is to eventually suppress the dependency on utils/debug.h from
public headers
2025-05-29 09:56:52 -07:00
Mathias Agopian 05b89905d8 fix a use after free of texture data during init (#8786) 2025-05-28 22:32:24 -07:00
Powei Feng 3e0df36c1c Add option to disable GTAO (#8785) 2025-05-28 22:35:14 +00:00
Powei Feng c9ce384f14 vk: add missing header (#8781) 2025-05-28 22:17:55 +00:00
Sungun Park 6a6bdd5be7 Fix broken android samples (#8784)
that use DescriptorType::SAMPLER_EXTERNAL.
2025-05-28 20:56:20 +00:00
Sungun Park d56ade924e Add checks for MSAA (#8782) 2025-05-28 19:48:12 +00:00
Andy Hovingh fe561e3e27 webgpu: support selection from multiple adapters/graphics devices 2025-05-28 12:52:04 -05:00
Jeremy Nelson 1d98e350e4 Set stencil ops to undefined if nonexistent 2025-05-27 16:28:01 -07:00
Matthew Hoffman d34e018acb Replace 512 magic number in backend tests. (#8775) 2025-05-27 21:39:50 +00:00
Mathias Agopian 169c8d57ff assert vertex attribute alignment
offset and size must now be a multiple of 4
2025-05-27 10:19:53 -07:00
Mathias Agopian a1e0cfa33c fix a few noexcept through out the code base 2025-05-27 10:18:12 -07:00
Sungun Park 6d44db3ca0 Fix a compile error
This is a fix for the compile error caused by
https://github.com/google/filament/commit/86a500c8465cfac76cdc4dcc86ab184e9edeac19
2025-05-23 10:33:14 -07:00
Sungun Park a261429d06 Add multiview MSAA support for custom swapchain (#8767)
BUGS=[417311684]
2025-05-23 00:42:04 +00:00
Matthew Hoffman 69d0de3c6d In backend tests replace calls to draw with draw2. (#8765)
BUGS=[398199600]
2025-05-22 13:14:52 -05:00
114 changed files with 2387 additions and 1211 deletions
+1 -1
View File
@@ -57,7 +57,7 @@ SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 0
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
+4
View File
@@ -97,6 +97,10 @@ Make sure you've installed the following dependencies:
- `libxcomposite-dev` (`libXcomposite-devel` on Fedora)
- `libxxf86vm-dev` (`libXxf86vm-devel` on Fedora)
```shell
sudo apt install clang-14 libglu1-mesa-dev libc++-14-dev libc++abi-14-dev ninja-build libxi-dev libxcomposite-dev libxxf86vm-dev -y
```
After dependencies have been installed, we highly recommend using the [easy build](#easy-build)
script.
+4
View File
@@ -49,6 +49,10 @@ option(FILAMENT_SUPPORTS_OSMESA "Enable OSMesa (headless GL context) for Filamen
option(FILAMENT_ENABLE_FGVIEWER "Enable the frame graph viewer" OFF)
# This is to disable GTAO for the short-term while we investigate a way to better manage size increases.
# On the regular filament build (where size is of less concern), we enable GTAO by default.
option(FILAMENT_DISABLE_GTAO "Disable GTAO" OFF)
set(FILAMENT_NDK_VERSION "" CACHE STRING
"Android NDK version or version prefix to be used when building for Android."
)
+33 -22
View File
@@ -262,11 +262,16 @@ set(MATERIAL_SRCS
src/materials/ssao/mipmapDepth.mat
src/materials/ssao/sao.mat
src/materials/ssao/saoBentNormals.mat
src/materials/ssao/gtao.mat
src/materials/ssao/gtaoBentNormals.mat
src/materials/vsmMipmap.mat
)
if (NOT FILAMENT_DISABLE_GTAO)
list(APPEND MATERIAL_SRCS
src/materials/ssao/gtao.mat
src/materials/ssao/gtaoBentNormals.mat
)
endif()
set(MATERIAL_FL0_SRCS
src/materials/defaultMaterial.mat
src/materials/skybox.mat
@@ -316,6 +321,10 @@ if (FILAMENT_FORCE_PROFILING_MODE)
add_definitions(-DFILAMENT_FORCE_PROFILING_MODE)
endif()
if (FILAMENT_DISABLE_GTAO)
add_definitions(-DFILAMENT_DISABLE_GTAO)
endif()
# ==================================================================================================
# Definitions
# ==================================================================================================
@@ -463,27 +472,29 @@ add_custom_command(
APPEND
)
add_custom_command(
OUTPUT "${MATERIAL_DIR}/gtao.filamat"
DEPENDS src/materials/ssao/ssaoUtils.fs
DEPENDS src/materials/ssao/ssct.fs
DEPENDS src/materials/utils/depthUtils.fs
DEPENDS src/materials/utils/geometry.fs
DEPENDS src/materials/ssao/gtaoImpl.fs
DEPENDS src/materials/ssao/ssctImpl.fs
APPEND
)
if (NOT FILAMENT_DISABLE_GTAO)
add_custom_command(
OUTPUT "${MATERIAL_DIR}/gtao.filamat"
DEPENDS src/materials/ssao/ssaoUtils.fs
DEPENDS src/materials/ssao/ssct.fs
DEPENDS src/materials/utils/depthUtils.fs
DEPENDS src/materials/utils/geometry.fs
DEPENDS src/materials/ssao/gtaoImpl.fs
DEPENDS src/materials/ssao/ssctImpl.fs
APPEND
)
add_custom_command(
OUTPUT "${MATERIAL_DIR}/gtaoBentNormals.filamat"
DEPENDS src/materials/ssao/ssaoUtils.fs
DEPENDS src/materials/ssao/ssct.fs
DEPENDS src/materials/utils/depthUtils.fs
DEPENDS src/materials/utils/geometry.fs
DEPENDS src/materials/ssao/gtaoImpl.fs
DEPENDS src/materials/ssao/ssctImpl.fs
APPEND
)
add_custom_command(
OUTPUT "${MATERIAL_DIR}/gtaoBentNormals.filamat"
DEPENDS src/materials/ssao/ssaoUtils.fs
DEPENDS src/materials/ssao/ssct.fs
DEPENDS src/materials/utils/depthUtils.fs
DEPENDS src/materials/utils/geometry.fs
DEPENDS src/materials/ssao/gtaoImpl.fs
DEPENDS src/materials/ssao/ssctImpl.fs
APPEND
)
endif()
add_custom_command(
OUTPUT "${MATERIAL_DIR}/bilateralBlur.filamat"
+1
View File
@@ -261,6 +261,7 @@ if (FILAMENT_SUPPORTS_WEBGPU)
src/webgpu/WebGPUHandles.h
src/webgpu/WebGPUPipelineCreation.cpp
src/webgpu/WebGPUPipelineCreation.h
src/webgpu/WebGPUStrings.h
src/webgpu/WebGPUSwapChain.cpp
src/webgpu/WebGPUSwapChain.h
src/webgpu/WGPUProgram.cpp
@@ -199,7 +199,7 @@ public:
*/
virtual bool makeCurrent(ContextType type,
SwapChain* UTILS_NONNULL drawSwapChain,
SwapChain* UTILS_NONNULL readSwapChain) noexcept = 0;
SwapChain* UTILS_NONNULL readSwapChain) = 0;
/**
* Called by the driver to make the OpenGL context active on the calling thread and bind
@@ -219,7 +219,7 @@ public:
SwapChain* UTILS_NONNULL drawSwapChain,
SwapChain* UTILS_NONNULL readSwapChain,
utils::Invocable<void()> preContextChange,
utils::Invocable<void(size_t index)> postContextChange) noexcept;
utils::Invocable<void(size_t index)> postContextChange);
/**
* Called by the backend just before calling commit()
@@ -58,7 +58,7 @@ protected:
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override;
void commit(SwapChain* swapChain) noexcept override;
ExternalTexture* createExternalImageTexture() noexcept override;
void destroyExternalImageTexture(ExternalTexture* texture) noexcept override;
@@ -55,7 +55,7 @@ public:
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override;
void commit(SwapChain* swapChain) noexcept override;
ExternalTexture* createExternalImageTexture() noexcept override;
@@ -109,11 +109,11 @@ protected:
bool makeCurrent(ContextType type,
SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept override;
SwapChain* readSwapChain) override;
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain,
utils::Invocable<void()> preContextChange,
utils::Invocable<void(size_t index)> postContextChange) noexcept override;
utils::Invocable<void(size_t index)> postContextChange) override;
void commit(SwapChain* swapChain) noexcept override;
@@ -148,12 +148,12 @@ protected:
EGLContext getContextForType(ContextType type) const noexcept;
// makes the draw and read surface current without changing the current context
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept {
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) {
return egl.makeCurrent(drawSurface, readSurface);
}
// makes context current and set draw and read surfaces to EGL_NO_SURFACE
EGLBoolean makeCurrent(EGLContext context) noexcept {
EGLBoolean makeCurrent(EGLContext context) {
return egl.makeCurrent(context, mEGLDummySurface, mEGLDummySurface);
}
@@ -211,9 +211,9 @@ private:
public:
explicit EGL(EGLDisplay& dpy) : mEGLDisplay(dpy) {}
EGLBoolean makeCurrent(EGLContext context,
EGLSurface drawSurface, EGLSurface readSurface) noexcept;
EGLSurface drawSurface, EGLSurface readSurface);
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept {
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) {
return makeCurrent(mCurrentContext, drawSurface, readSurface);
}
} egl{ mEGLDisplay };
@@ -127,7 +127,7 @@ protected:
protected:
bool makeCurrent(ContextType type,
SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept override;
SwapChain* readSwapChain) override;
private:
struct InitializeJvmForPerformanceManagerIfNeeded {
@@ -51,7 +51,7 @@ protected:
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override;
void commit(SwapChain* swapChain) noexcept override;
private:
@@ -56,7 +56,7 @@ protected:
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept override;
SwapChain* readSwapChain) override;
void commit(SwapChain* swapChain) noexcept override;
private:
@@ -53,7 +53,7 @@ protected:
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override;
void commit(SwapChain* swapChain) noexcept override;
protected:
@@ -46,7 +46,7 @@ protected:
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
void destroySwapChain(SwapChain* swapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override;
void commit(SwapChain* swapChain) noexcept override;
};
@@ -22,6 +22,7 @@
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
#include <vector>
namespace filament::backend {
@@ -56,6 +57,10 @@ protected:
const Platform::DriverConfig& driverConfig) noexcept override;
private:
// returns adapter request option variations applicable for the particular
// platform
[[nodiscard]] static std::vector<wgpu::RequestAdapterOptions> getAdapterOptions();
// we may consider having the driver own this in the future
wgpu::Instance mInstance;
};
@@ -72,7 +72,7 @@ public:
Range getBuffer() noexcept;
private:
void* alloc(size_t size) noexcept;
void* alloc(size_t size);
void dealloc() noexcept;
// pointer to the beginning of the circular buffer (constant)
@@ -76,7 +76,7 @@ public:
// all commands buffers (Slices) written to this point are returned by waitForCommand(). This
// call blocks until the CircularBuffer has at least mRequiredSize bytes available.
void flush() noexcept;
void flush();
// returns from waitForCommands() immediately.
void requestExit();
@@ -222,6 +222,56 @@ public:
return static_cast<Dp>(p);
}
template<typename Dp, typename B>
inline std::enable_if_t<
std::is_pointer_v<Dp> && std::is_base_of_v<B, std::remove_pointer_t<Dp>>, int16_t>
handle_set_value(Handle<B>& handle, uint8_t v) {
assert_invariant(handle);
auto [p, tag] = handleToPointer(handle.getId());
if (isPoolHandle(handle.getId())) {
// check for pool handle use-after-free
if (UTILS_UNLIKELY(!mUseAfterFreeCheckDisabled)) {
auto const pNode = static_cast<typename Allocator::Node*>(p);
pNode[-1].test_value = v;
return 1;
}
return 0;
} else {
// check for heap handle use-after-free
if (UTILS_UNLIKELY(!mUseAfterFreeCheckDisabled)) {
return -1;
}
return -2;
}
}
template<typename Dp, typename B>
inline std::enable_if_t<
std::is_pointer_v<Dp> && std::is_base_of_v<B, std::remove_pointer_t<Dp>>, int16_t>
handle_get_value(Handle<B>& handle) {
assert_invariant(handle);
auto [p, tag] = handleToPointer(handle.getId());
if (isPoolHandle(handle.getId())) {
// check for pool handle use-after-free
if (UTILS_UNLIKELY(!mUseAfterFreeCheckDisabled)) {
auto const pNode = static_cast<typename Allocator::Node*>(p);
uint8_t age = pNode[-1].age;
uint8_t test_value = pNode[-1].test_value;
int16_t ret = (int16_t) ((age << 8) | test_value);
return ret;
}
return 0;
} else {
// check for heap handle use-after-free
if (UTILS_UNLIKELY(!mUseAfterFreeCheckDisabled)) {
return -1;
}
return -2;
}
}
utils::CString getHandleTag(HandleBase::HandleId key) const noexcept;
template<typename B>
@@ -276,7 +326,10 @@ private:
class Allocator {
friend class HandleAllocator;
static constexpr size_t MIN_ALIGNMENT = alignof(std::max_align_t);
struct Node { uint8_t age; };
struct Node {
uint8_t age;
uint8_t test_value = 0xff;
};
// Note: using the `extra` parameter of PoolAllocator<>, even with a 1-byte structure,
// generally increases all pool allocations by 8-bytes because of alignment restrictions.
template<size_t SIZE>
@@ -27,13 +27,13 @@ namespace filament {
class VirtualMachineEnv {
public:
// must be called before VirtualMachineEnv::get() from a thread that is attached to the JavaVM
static jint JNI_OnLoad(JavaVM* vm) noexcept;
static jint JNI_OnLoad(JavaVM* vm);
// must be called on backend thread
static VirtualMachineEnv& get() noexcept;
// can be called from any thread that already has a JniEnv
static JNIEnv* getThreadEnvironment() noexcept;
static JNIEnv* getThreadEnvironment();
// must be called from the backend thread
JNIEnv* getEnvironment() noexcept {
@@ -49,7 +49,7 @@ public:
private:
explicit VirtualMachineEnv(JavaVM* vm) noexcept;
~VirtualMachineEnv() noexcept;
JNIEnv* getEnvironmentSlow() noexcept;
JNIEnv* getEnvironmentSlow();
static utils::Mutex sLock;
static JavaVM* sVirtualMachine;
+1 -1
View File
@@ -65,7 +65,7 @@ CircularBuffer::~CircularBuffer() noexcept {
// to each others and a special case in circularize()
UTILS_NOINLINE
void* CircularBuffer::alloc(size_t size) noexcept {
void* CircularBuffer::alloc(size_t size) {
#if HAS_MMAP
void* data = nullptr;
void* vaddr = MAP_FAILED;
+9 -9
View File
@@ -53,18 +53,18 @@ CommandBufferQueue::~CommandBufferQueue() {
}
void CommandBufferQueue::requestExit() {
std::lock_guard<utils::Mutex> const lock(mLock);
std::lock_guard const lock(mLock);
mExitRequested = EXIT_REQUESTED;
mCondition.notify_one();
}
bool CommandBufferQueue::isPaused() const noexcept {
std::lock_guard<utils::Mutex> const lock(mLock);
std::lock_guard const lock(mLock);
return mPaused;
}
void CommandBufferQueue::setPaused(bool paused) {
std::lock_guard<utils::Mutex> const lock(mLock);
std::lock_guard const lock(mLock);
if (paused) {
mPaused = true;
} else {
@@ -74,12 +74,12 @@ void CommandBufferQueue::setPaused(bool paused) {
}
bool CommandBufferQueue::isExitRequested() const {
std::lock_guard<utils::Mutex> const lock(mLock);
return (bool)mExitRequested;
std::lock_guard const lock(mLock);
return bool(mExitRequested);
}
void CommandBufferQueue::flush() noexcept {
void CommandBufferQueue::flush() {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
CircularBuffer& circularBuffer = mCircularBuffer;
@@ -103,7 +103,7 @@ void CommandBufferQueue::flush() noexcept {
static_cast<char const*>(begin), static_cast<char const*>(end));
std::unique_lock<utils::Mutex> lock(mLock);
std::unique_lock lock(mLock);
// circular buffer is too small, we corrupted the stream
FILAMENT_CHECK_POSTCONDITION(used <= mFreeSpace) <<
@@ -147,7 +147,7 @@ std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() con
if (!UTILS_HAS_THREADING) {
return std::move(mCommandBuffersToExecute);
}
std::unique_lock<utils::Mutex> lock(mLock);
std::unique_lock lock(mLock);
while ((mCommandBuffersToExecute.empty() || mPaused) && !mExitRequested) {
mCondition.wait(lock);
}
@@ -157,7 +157,7 @@ std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() con
void CommandBufferQueue::releaseBuffer(CommandBufferQueue::Range const& buffer) {
size_t const used = std::distance(
static_cast<char const*>(buffer.begin), static_cast<char const*>(buffer.end));
std::lock_guard<utils::Mutex> const lock(mLock);
std::lock_guard const lock(mLock);
mFreeSpace += used;
mCondition.notify_one();
}
+3 -3
View File
@@ -50,7 +50,7 @@ JavaVM* VirtualMachineEnv::getVirtualMachine() {
*/
UTILS_PUBLIC
UTILS_NOINLINE
jint VirtualMachineEnv::JNI_OnLoad(JavaVM* vm) noexcept {
jint VirtualMachineEnv::JNI_OnLoad(JavaVM* vm) {
std::lock_guard const lock(sLock);
if (sVirtualMachine) {
// It doesn't make sense for JNI_OnLoad() to be called more than once
@@ -77,7 +77,7 @@ VirtualMachineEnv& VirtualMachineEnv::get() noexcept {
}
UTILS_NOINLINE
JNIEnv* VirtualMachineEnv::getThreadEnvironment() noexcept {
JNIEnv* VirtualMachineEnv::getThreadEnvironment() {
JavaVM* const vm = getVirtualMachine();
JNIEnv* env = nullptr;
jint const result = vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
@@ -101,7 +101,7 @@ VirtualMachineEnv::~VirtualMachineEnv() noexcept {
}
UTILS_NOINLINE
JNIEnv* VirtualMachineEnv::getEnvironmentSlow() noexcept {
JNIEnv* VirtualMachineEnv::getEnvironmentSlow() {
FILAMENT_CHECK_PRECONDITION(mVirtualMachine)
<< "JNI_OnLoad() has not been called";
+44 -44
View File
@@ -162,8 +162,11 @@ void GLDescriptorSet::update(OpenGLContext&,
}, descriptors[binding].desc);
}
void GLDescriptorSet::update(OpenGLContext& gl,
descriptor_binding_t binding, GLTexture* t, SamplerParams params) noexcept {
void GLDescriptorSet::update(OpenGLContext& gl, HandleAllocatorGL& handleAllocator,
descriptor_binding_t binding, TextureHandle th, SamplerParams params) noexcept {
GLTexture* t = th ? handleAllocator.handle_cast<GLTexture*>(th) : nullptr;
assert_invariant(binding < descriptors.size());
std::visit([=, &gl](auto&& arg) mutable {
using T = std::decay_t<decltype(arg)>;
@@ -196,20 +199,12 @@ void GLDescriptorSet::update(OpenGLContext& gl,
}
}
arg.target = t ? t->gl.target : 0;
arg.id = t ? t->gl.id : 0;
arg.external = t ? t->gl.external : false;
arg.handle = th;
if constexpr (std::is_same_v<T, Sampler> ||
std::is_same_v<T, SamplerWithAnisotropyWorkaround>) {
if constexpr (std::is_same_v<T, SamplerWithAnisotropyWorkaround>) {
arg.anisotropy = float(1u << params.anisotropyLog2);
}
if (t) {
arg.ref = t->ref;
arg.baseLevel = t->gl.baseLevel;
arg.maxLevel = t->gl.maxLevel;
arg.swizzle = t->gl.swizzle;
}
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
arg.sampler = gl.getSampler(params);
#else
@@ -225,39 +220,39 @@ void GLDescriptorSet::update(OpenGLContext& gl,
}, descriptors[binding].desc);
}
template<typename T>
void GLDescriptorSet::updateTextureView(OpenGLContext& gl,
HandleAllocatorGL& handleAllocator, GLuint unit, T const& desc) noexcept {
HandleAllocatorGL& handleAllocator, GLuint unit, GLTexture const* t) noexcept {
// The common case is that we don't have a ref handle (we only have one if
// the texture ever had a View on it).
assert_invariant(desc.ref);
GLTextureRef* const ref = handleAllocator.handle_cast<GLTextureRef*>(desc.ref);
if (UTILS_UNLIKELY((desc.baseLevel != ref->baseLevel || desc.maxLevel != ref->maxLevel))) {
assert_invariant(t);
assert_invariant(t->ref);
GLTextureRef* const ref = handleAllocator.handle_cast<GLTextureRef*>(t->ref);
if (UTILS_UNLIKELY((t->gl.baseLevel != ref->baseLevel || t->gl.maxLevel != ref->maxLevel))) {
// If we have views, then it's still uncommon that we'll switch often
// handle the case where we reset to the original texture
GLint baseLevel = GLint(desc.baseLevel); // NOLINT(*-signed-char-misuse)
GLint maxLevel = GLint(desc.maxLevel); // NOLINT(*-signed-char-misuse)
GLint baseLevel = GLint(t->gl.baseLevel); // NOLINT(*-signed-char-misuse)
GLint maxLevel = GLint(t->gl.maxLevel); // NOLINT(*-signed-char-misuse)
if (baseLevel > maxLevel) {
baseLevel = 0;
maxLevel = 1000; // per OpenGL spec
}
// that is very unfortunate that we have to call activeTexture here
gl.activeTexture(unit);
glTexParameteri(desc.target, GL_TEXTURE_BASE_LEVEL, baseLevel);
glTexParameteri(desc.target, GL_TEXTURE_MAX_LEVEL, maxLevel);
ref->baseLevel = desc.baseLevel;
ref->maxLevel = desc.maxLevel;
glTexParameteri(t->gl.target, GL_TEXTURE_BASE_LEVEL, baseLevel);
glTexParameteri(t->gl.target, GL_TEXTURE_MAX_LEVEL, maxLevel);
ref->baseLevel = t->gl.baseLevel;
ref->maxLevel = t->gl.maxLevel;
}
if (UTILS_UNLIKELY(desc.swizzle != ref->swizzle)) {
if (UTILS_UNLIKELY(t->gl.swizzle != ref->swizzle)) {
using namespace GLUtils;
gl.activeTexture(unit);
#if !defined(__EMSCRIPTEN__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
glTexParameteri(desc.target, GL_TEXTURE_SWIZZLE_R, (GLint)getSwizzleChannel(desc.swizzle[0]));
glTexParameteri(desc.target, GL_TEXTURE_SWIZZLE_G, (GLint)getSwizzleChannel(desc.swizzle[1]));
glTexParameteri(desc.target, GL_TEXTURE_SWIZZLE_B, (GLint)getSwizzleChannel(desc.swizzle[2]));
glTexParameteri(desc.target, GL_TEXTURE_SWIZZLE_A, (GLint)getSwizzleChannel(desc.swizzle[3]));
glTexParameteri(t->gl.target, GL_TEXTURE_SWIZZLE_R, (GLint)getSwizzleChannel(t->gl.swizzle[0]));
glTexParameteri(t->gl.target, GL_TEXTURE_SWIZZLE_G, (GLint)getSwizzleChannel(t->gl.swizzle[1]));
glTexParameteri(t->gl.target, GL_TEXTURE_SWIZZLE_B, (GLint)getSwizzleChannel(t->gl.swizzle[2]));
glTexParameteri(t->gl.target, GL_TEXTURE_SWIZZLE_A, (GLint)getSwizzleChannel(t->gl.swizzle[3]));
#endif
ref->swizzle = desc.swizzle;
ref->swizzle = t->gl.swizzle;
}
}
@@ -310,27 +305,31 @@ void GLDescriptorSet::bind(
}
} else if constexpr (std::is_same_v<T, Sampler>) {
GLuint const unit = p.getTextureUnit(set, binding);
if (arg.target) {
gl.bindTexture(unit, arg.target, arg.id, arg.external);
if (arg.handle) {
GLTexture const* const t = handleAllocator.handle_cast<GLTexture*>(arg.handle);
gl.bindTexture(unit, t->gl.target, t->gl.id, t->gl.external);
gl.bindSampler(unit, arg.sampler);
if (UTILS_UNLIKELY(arg.ref)) {
updateTextureView(gl, handleAllocator, unit, arg);
if (UTILS_UNLIKELY(t->ref)) {
updateTextureView(gl, handleAllocator, unit, t);
}
} else {
gl.unbindTextureUnit(unit);
}
} else if constexpr (std::is_same_v<T, SamplerWithAnisotropyWorkaround>) {
GLuint const unit = p.getTextureUnit(set, binding);
if (arg.target) {
gl.bindTexture(unit, arg.target, arg.id, arg.external);
if (arg.handle) {
GLTexture const* const t = handleAllocator.handle_cast<GLTexture*>(arg.handle);
gl.bindTexture(unit, t->gl.target, t->gl.id, t->gl.external);
gl.bindSampler(unit, arg.sampler);
if (UTILS_UNLIKELY(arg.ref)) {
updateTextureView(gl, handleAllocator, unit, arg);
if (UTILS_UNLIKELY(t->ref)) {
updateTextureView(gl, handleAllocator, unit, t);
}
#if defined(GL_EXT_texture_filter_anisotropic)
// Driver claims to support anisotropic filtering, but it fails when set on
// the sampler, we have to set it on the texture instead.
glTexParameterf(arg.target, GL_TEXTURE_MAX_ANISOTROPY_EXT,
glTexParameterf(t->gl.target, GL_TEXTURE_MAX_ANISOTROPY_EXT,
std::min(gl.gets.max_anisotropy, float(arg.anisotropy)));
#endif
} else {
@@ -339,19 +338,20 @@ void GLDescriptorSet::bind(
} else if constexpr (std::is_same_v<T, SamplerGLES2>) {
// in ES2 the sampler parameters need to be set on the texture itself
GLuint const unit = p.getTextureUnit(set, binding);
if (arg.target) {
gl.bindTexture(unit, arg.target, arg.id, arg.external);
if (arg.handle) {
GLTexture const* const t = handleAllocator.handle_cast<GLTexture*>(arg.handle);
gl.bindTexture(unit, t->gl.target, t->gl.id, t->gl.external);
SamplerParams const params = arg.params;
glTexParameteri(arg.target, GL_TEXTURE_MIN_FILTER,
glTexParameteri(t->gl.target, GL_TEXTURE_MIN_FILTER,
(GLint)GLUtils::getTextureFilter(params.filterMin));
glTexParameteri(arg.target, GL_TEXTURE_MAG_FILTER,
glTexParameteri(t->gl.target, GL_TEXTURE_MAG_FILTER,
(GLint)GLUtils::getTextureFilter(params.filterMag));
glTexParameteri(arg.target, GL_TEXTURE_WRAP_S,
glTexParameteri(t->gl.target, GL_TEXTURE_WRAP_S,
(GLint)GLUtils::getWrapMode(params.wrapS));
glTexParameteri(arg.target, GL_TEXTURE_WRAP_T,
glTexParameteri(t->gl.target, GL_TEXTURE_WRAP_T,
(GLint)GLUtils::getWrapMode(params.wrapT));
#if defined(GL_EXT_texture_filter_anisotropic)
glTexParameterf(arg.target, GL_TEXTURE_MAX_ANISOTROPY_EXT,
glTexParameterf(t->gl.target, GL_TEXTURE_MAX_ANISOTROPY_EXT,
std::min(gl.gets.max_anisotropy, arg.anisotropy));
#endif
} else {
+6 -34
View File
@@ -59,8 +59,8 @@ struct GLDescriptorSet : public HwDescriptorSet {
descriptor_binding_t binding, GLBufferObject* bo, size_t offset, size_t size) noexcept;
// update a sampler descriptor in the set
void update(OpenGLContext& gl,
descriptor_binding_t binding, GLTexture* t, SamplerParams params) noexcept;
void update(OpenGLContext& gl, HandleAllocatorGL& handleAllocator,
descriptor_binding_t binding, TextureHandle th, SamplerParams params) noexcept;
// conceptually bind the set to the command buffer
void bind(
@@ -111,46 +111,19 @@ private:
// A sampler descriptor
struct Sampler {
uint16_t target; // 2 (GLenum)
bool external = false; // 1
bool reserved = false; // 1
GLuint id = 0; // 4
TextureHandle handle; // 4
GLuint sampler = 0; // 4
Handle<GLTextureRef> ref; // 4
int8_t baseLevel = 0x7f; // 1
int8_t maxLevel = -1; // 1
std::array<TextureSwizzle, 4> swizzle{ // 4
TextureSwizzle::CHANNEL_0,
TextureSwizzle::CHANNEL_1,
TextureSwizzle::CHANNEL_2,
TextureSwizzle::CHANNEL_3
};
};
struct SamplerWithAnisotropyWorkaround {
uint16_t target; // 2 (GLenum)
bool external = false; // 1
bool reserved = false; // 1
GLuint id = 0; // 4
TextureHandle handle; // 4
GLuint sampler = 0; // 4
Handle<GLTextureRef> ref; // 4
math::half anisotropy = 1.0f; // 2
int8_t baseLevel = 0x7f; // 1
int8_t maxLevel = -1; // 1
std::array<TextureSwizzle, 4> swizzle{ // 4
TextureSwizzle::CHANNEL_0,
TextureSwizzle::CHANNEL_1,
TextureSwizzle::CHANNEL_2,
TextureSwizzle::CHANNEL_3
};
};
// A sampler descriptor for ES2
struct SamplerGLES2 {
uint16_t target; // 2 (GLenum)
bool external = false; // 1
bool reserved = false; // 1
GLuint id = 0; // 4
TextureHandle handle; // 4
SamplerParams params{}; // 4
float anisotropy = 1.0f; // 4
};
@@ -165,9 +138,8 @@ private:
};
static_assert(sizeof(Descriptor) <= 32);
template<typename T>
static void updateTextureView(OpenGLContext& gl,
HandleAllocatorGL& handleAllocator, GLuint unit, T const& desc) noexcept;
HandleAllocatorGL& handleAllocator, GLuint unit, GLTexture const* t) noexcept;
utils::FixedCapacityVector<Descriptor> descriptors; // 16
utils::bitset64 dynamicBuffers; // 8
+115 -6
View File
@@ -643,6 +643,25 @@ void OpenGLDriver::createVertexBufferInfoR(
AttributeArray attributes) {
DEBUG_MARKER()
construct<GLVertexBufferInfo>(vbih, bufferCount, attributeCount, attributes);
// test ----
int16_t returned_value = handle_set_value<GLVertexBufferInfo*>(vbih, 90);
uint8_t age = (uint8_t) (returned_value >> 8);
uint8_t test_value = (returned_value & 0xff);
switch (test_value) {
case -2:
case -1:
case 0:
utils::slog.w << "@@@ createVertexBufferInfoR: unexpected result, age:" << (int) age
<< ", test_value:" << (int) test_value << utils::io::endl;
break;
default:
FILAMENT_CHECK_POSTCONDITION(test_value == 1)
<< "@@@ createVertexBufferInfoR: shouldn't happen, age:" << (int) age
<< ", test_value:" << (int) test_value << utils::io::endl;
break;
}
//----------
}
void OpenGLDriver::createVertexBufferR(
@@ -761,6 +780,25 @@ void OpenGLDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
construct<OpenGLProgram>(ph, *this, std::move(program));
CHECK_GL_ERROR(utils::slog.e)
// test ----
int16_t returned_value = handle_set_value<OpenGLProgram*>(ph, 100);
uint8_t age = (uint8_t) (returned_value >> 8);
uint8_t test_value = (returned_value & 0xff);
switch (test_value) {
case -2:
case -1:
case 0:
utils::slog.w << "@@@ createProgramR: unexpected result, age:" << (int)age << ", test_value:" << (int)test_value
<< utils::io::endl;
break;
default:
FILAMENT_CHECK_POSTCONDITION(test_value == 1)
<< "@@@ createProgramR: shouldn't happen, age:" << (int)age
<< ", test_value:" << (int)test_value << utils::io::endl;
break;
}
//----------
}
UTILS_NOINLINE
@@ -932,7 +970,12 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
if (gl.features.multisample_texture) {
// multi-sample texture on GL 3.2 / GLES 3.1 and above
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
if (depth <= 1) {
// We forcibly change the target to 2D-multisample only for flat texture.
// A depth value greater than 1 may indicate multiview usage, which requires
// GL_TEXTURE_2D_ARRAY. Also 2D MSAA won't work with non-flat texture anyway.
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
}
} else {
// Turn off multi-sampling for that texture. It's just not supported.
}
@@ -1208,7 +1251,12 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
if (gl.features.multisample_texture) {
// multi-sample texture on GL 3.2 / GLES 3.1 and above
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
if (depth <= 1) {
// We forcibly change the target to 2D-multisample only for flat texture.
// A depth value greater than 1 may indicate multiview usage, which requires
// GL_TEXTURE_2D_ARRAY. Also 2D MSAA won't work with non-flat texture anyway.
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
}
} else {
// Turn off multi-sampling for that texture. It's just not supported.
}
@@ -1453,8 +1501,15 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
#if !defined(__EMSCRIPTEN__) && !defined(FILAMENT_IOS)
if (layerCount > 1) {
// if layerCount > 1, it means we use the multiview extension.
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, attachment,
t->gl.id, 0, binfo.layer, layerCount);
if (rt->gl.samples > 1) {
// For MSAA
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, attachment,
t->gl.id, 0, rt->gl.samples, binfo.layer, layerCount);
}
else {
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, attachment, t->gl.id, 0,
binfo.layer, layerCount);
}
} else
#endif // !defined(__EMSCRIPTEN__) && !defined(FILAMENT_IOS)
{
@@ -1901,6 +1956,25 @@ void OpenGLDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
void OpenGLDriver::destroyProgram(Handle<HwProgram> ph) {
DEBUG_MARKER()
if (ph) {
// test ----
int16_t returned_value = handle_set_value<OpenGLProgram*>(ph, 101);
uint8_t age = (uint8_t) (returned_value >> 8);
uint8_t test_value = (returned_value & 0xff);
switch (test_value) {
case -2:
case -1:
case 0:
utils::slog.w << "@@@ destroyProgram: unexpected result, age:" << (int) age
<< ", test_value:" << (int) test_value << utils::io::endl;
break;
default:
FILAMENT_CHECK_POSTCONDITION(test_value == 1)
<< "@@@ destroyProgram: shouldn't happen, age:" << (int) age
<< ", test_value:" << (int) test_value << utils::io::endl;
break;
}
//----------
OpenGLProgram* p = handle_cast<OpenGLProgram*>(ph);
destruct(ph, p);
}
@@ -2514,10 +2588,28 @@ void OpenGLDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain>
mPlatform.makeCurrent(scDraw->swapChain, scRead->swapChain,
[this]() {
for (auto t: mTexturesWithStreamsAttached) {
if (t->hwStream->streamType == StreamType::NATIVE) {
mPlatform.detach(t->hwStream->stream);
}
}
// OpenGL context is about to change, unbind everything
mContext.unbindEverything();
},
[this](size_t index) {
for (auto t: mTexturesWithStreamsAttached) {
if (t->hwStream->streamType == StreamType::NATIVE) {
glGenTextures(1, &t->gl.id);
mPlatform.attach(t->hwStream->stream, t->gl.id);
mContext.updateTexImage(GL_TEXTURE_EXTERNAL_OES, t->gl.id);
}
}
// force invalidation of all bound descriptor sets
decltype(mInvalidDescriptorSetBindings) changed;
changed.setValue((1 << MAX_DESCRIPTOR_SET_COUNT) - 1);
mInvalidDescriptorSetBindings |= changed;
// OpenGL context has changed, resynchronize the state with the cache
mContext.synchronizeStateAndCache(index);
slog.d << "*** OpenGL context change : " << (index ? "protected" : "default") << io::endl;
@@ -3708,8 +3800,7 @@ void OpenGLDriver::updateDescriptorSetTexture(
TextureHandle th,
SamplerParams params) {
GLDescriptorSet* ds = handle_cast<GLDescriptorSet*>(dsh);
GLTexture* t = th ? handle_cast<GLTexture*>(th) : nullptr;
ds->update(mContext, binding, t, params);
ds->update(mContext, mHandleAllocator, binding, th, params);
}
void OpenGLDriver::flush(int) {
@@ -4032,6 +4123,24 @@ void OpenGLDriver::bindPipeline(PipelineState const& state) {
setRasterState(state.rasterState);
setStencilState(state.stencilState);
gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant);
// test ----
Handle<HwProgram> pro = state.program;
int16_t returned_value = handle_get_value<OpenGLProgram*>(pro);
uint8_t age = (uint8_t)(returned_value >> 8);
uint8_t test_value = (returned_value & 0xff);
switch (test_value) {
case -2:
case -1:
case 0:
utils::slog.w << "@@@ bindPipeline: unexpected result, age:" << (int)age << ", test_value:" << (int)test_value << utils::io::endl;
break;
default:
if (test_value != 100) {
utils::slog.w << "@@@ bindPipeline: program is not valid OpenGLProgram, age:" << (int)age << ", test_value:" << (int)test_value << utils::io::endl;
}
break;
}
//----------
OpenGLProgram* const p = handle_cast<OpenGLProgram*>(state.program);
mValidProgram = useProgram(p);
(*mCurrentPushConstants) = p->getPushConstants();
@@ -282,6 +282,20 @@ private:
return mHandleAllocator.handle_cast<Dp, B>(handle);
}
template<typename Dp, typename B>
std::enable_if_t<std::is_pointer_v<Dp> && std::is_base_of_v<B, std::remove_pointer_t<Dp>>,
int16_t>
handle_set_value(Handle<B>& handle, uint8_t v) {
return mHandleAllocator.handle_set_value<Dp, B>(handle, v);
}
template<typename Dp, typename B>
std::enable_if_t<std::is_pointer_v<Dp> && std::is_base_of_v<B, std::remove_pointer_t<Dp>>,
int16_t>
handle_get_value(Handle<B>& handle) {
return mHandleAllocator.handle_get_value<Dp, B>(handle);
}
friend class OpenGLProgram;
friend class ShaderCompilerService;
@@ -64,7 +64,7 @@ utils::CString OpenGLPlatform::getRendererString(Driver const* driver) {
}
void OpenGLPlatform::makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain,
utils::Invocable<void()>, utils::Invocable<void(size_t)>) noexcept {
utils::Invocable<void()>, utils::Invocable<void(size_t)>) {
makeCurrent(getCurrentContextType(), drawSwapChain, readSwapChain);
}
@@ -70,6 +70,9 @@ PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
#ifdef GL_OVR_multiview
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
#endif
#ifdef GL_OVR_multiview_multisampled_render_to_texture
PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC glFramebufferTextureMultisampleMultiviewOVR;
#endif
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
// On Android, If we want to support a build system less than ANDROID_API 21, we need to
@@ -123,6 +126,9 @@ void importGLESExtensionsEntryPoints() {
#ifdef GL_OVR_multiview
getProcAddress(glFramebufferTextureMultiviewOVR, "glFramebufferTextureMultiviewOVR");
#endif
#ifdef GL_OVR_multiview_multisampled_render_to_texture
getProcAddress(glFramebufferTextureMultisampleMultiviewOVR, "glFramebufferTextureMultisampleMultiviewOVR");
#endif
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
getProcAddress(glDispatchCompute, "glDispatchCompute");
#endif
+3
View File
@@ -154,6 +154,9 @@ extern PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
#ifdef GL_OVR_multiview
extern PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
#endif
#ifdef GL_OVR_multiview_multisampled_render_to_texture
extern PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC glFramebufferTextureMultisampleMultiviewOVR;
#endif
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
extern PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
#endif
@@ -255,7 +255,7 @@ void PlatformCocoaGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept
}
bool PlatformCocoaGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept {
SwapChain* readSwapChain) {
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
"ContextManagerCocoa does not support using distinct draw/read swap chains.");
CocoaGLSwapChain* swapChain = (CocoaGLSwapChain*)drawSwapChain;
@@ -155,7 +155,7 @@ uint32_t PlatformCocoaTouchGL::getDefaultFramebufferObject() noexcept {
}
bool PlatformCocoaTouchGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept {
SwapChain* readSwapChain) {
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
"PlatformCocoaTouchGL does not support using distinct draw/read swap chains.");
CAEAGLLayer* const glLayer = (__bridge CAEAGLLayer*) drawSwapChain;
@@ -577,18 +577,18 @@ OpenGLPlatform::ContextType PlatformEGL::getCurrentContextType() const noexcept
}
bool PlatformEGL::makeCurrent(ContextType type,
SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept {
SwapChain* drawSwapChain, SwapChain* readSwapChain) {
SwapChainEGL const* const dsc = static_cast<SwapChainEGL const*>(drawSwapChain);
SwapChainEGL const* const rsc = static_cast<SwapChainEGL const*>(readSwapChain);
EGLContext context = getContextForType(type);
EGLBoolean const success = egl.makeCurrent(context, dsc->sur, rsc->sur);
return success == EGL_TRUE ? true : false;
return success == EGL_TRUE;
}
void PlatformEGL::makeCurrent(SwapChain* drawSwapChain,
SwapChain* readSwapChain,
Invocable<void()> preContextChange,
Invocable<void(size_t index)> postContextChange) noexcept {
Invocable<void(size_t index)> postContextChange) {
assert_invariant(drawSwapChain);
assert_invariant(readSwapChain);
@@ -796,7 +796,7 @@ void PlatformEGL::Config::erase(EGLint name) noexcept {
// ------------------------------------------------------------------------------------------------
EGLBoolean PlatformEGL::EGL::makeCurrent(EGLContext context, EGLSurface drawSurface,
EGLSurface readSurface) noexcept {
EGLSurface readSurface) {
if (UTILS_UNLIKELY((
mCurrentContext != context ||
drawSurface != mCurrentDrawSurface || readSurface != mCurrentReadSurface))) {
@@ -135,7 +135,7 @@ static constexpr const std::string_view kNativeWindowInvalidMsg =
bool PlatformEGLAndroid::makeCurrent(ContextType type,
SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept {
SwapChain* readSwapChain) {
// fast & safe path
if (UTILS_LIKELY(!mAssertNativeWindowIsValid)) {
@@ -266,7 +266,7 @@ void PlatformGLX::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
}
bool PlatformGLX::makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept {
SwapChain* readSwapChain) {
g_glx.setCurrentContext(mGLXDisplay,
(GLXDrawable)drawSwapChain, (GLXDrawable)readSwapChain, mGLXContext);
return true;
@@ -169,7 +169,7 @@ void PlatformOSMesa::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
}
bool PlatformOSMesa::makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept {
SwapChain* readSwapChain) {
OSMesaAPI* api = (OSMesaAPI*) mOsMesaApi;
OSMesaSwapchain* impl = (OSMesaSwapchain*) drawSwapChain;
@@ -262,7 +262,7 @@ void PlatformWGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
}
bool PlatformWGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept {
SwapChain* readSwapChain) {
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
"PlatformWGL does not support distinct draw/read swap chains.");
@@ -47,7 +47,7 @@ void PlatformWebGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
}
bool PlatformWebGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept {
SwapChain* readSwapChain) {
return true;
}
@@ -21,6 +21,7 @@
#include <utils/bitset.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Panic.h>
#include <bluevk/BlueVK.h>
+110 -84
View File
@@ -17,6 +17,7 @@
#include "WebGPUHandles.h"
#include "WebGPUConstants.h"
#include "WebGPUStrings.h"
#include "DriverBase.h"
#include <backend/DriverEnums.h>
@@ -28,39 +29,116 @@
#include <webgpu/webgpu_cpp.h>
#include <algorithm>
#include <cstdint>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
#include <unordered_map>
#include <variant>
namespace filament::backend {
namespace {
[[nodiscard]] constexpr std::string_view toString(ShaderStage stage) {
switch (stage) {
case ShaderStage::VERTEX:
return "vertex";
case ShaderStage::FRAGMENT:
return "fragment";
case ShaderStage::COMPUTE:
return "compute";
[[nodiscard]] std::string replaceSpecConstants(std::string_view shaderLabel,
std::string_view shaderSource,
std::unordered_map<uint32_t, std::variant<int32_t, float, bool>> const&
specConstants) {
assert_invariant(!specConstants.empty());
constexpr std::string_view specConstantPrefix = "FILAMENT_SPEC_CONST_";
constexpr size_t specConstantPrefixSize = specConstantPrefix.size();
const char* const sourceData = shaderSource.data();
std::stringstream processedShaderSourceStr{};
size_t pos = 0;
while (pos < shaderSource.size()) {
const size_t posOfNextSpecConstant = shaderSource.find(specConstantPrefix, pos);
if (posOfNextSpecConstant == std::string::npos) {
// no more spec constants, so just stream the rest of the source code string
processedShaderSourceStr
<< std::string_view(sourceData + pos, shaderSource.size() - pos);
break;
}
const size_t posOfId = posOfNextSpecConstant + specConstantPrefixSize;
const size_t posAfterId = shaderSource.find('_', posOfId);
FILAMENT_CHECK_POSTCONDITION(posAfterId != std::string::npos)
<< "malformed " << shaderLabel << ". Found spec constant prefix '"
<< specConstantPrefix << "' without an id or '_' after it.";
const std::string_view idStr =
std::string_view(sourceData + posOfId, posAfterId - posOfId);
const size_t posEndOfStatement = shaderSource.find(';', posAfterId);
FILAMENT_CHECK_POSTCONDITION(posEndOfStatement != std::string::npos)
<< "malformed " << shaderLabel << ". Found spec constant assignment with id "
<< idStr << " without a terminating ';' character?";
const std::string_view statementSegment =
std::string_view(sourceData + posAfterId, posEndOfStatement - posAfterId);
size_t posOfEqual = statementSegment.find('=');
if (posOfEqual == std::string::npos) {
// not an assignment statement, so stream to the end of the statement and continue...
processedShaderSourceStr
<< std::string_view(sourceData + pos, posEndOfStatement + 1 - pos);
pos = posEndOfStatement + 1;
continue;
}
posOfEqual += posAfterId; // position in original source, not just the segment
int id = 0;
try {
id = std::stoi(idStr.data());
} catch (const std::invalid_argument& e) {
PANIC_POSTCONDITION("Invalid spec constant id '%s' in %s (not a valid integer?): %s",
idStr.data(), shaderLabel.data(), e.what());
} catch (const std::out_of_range& e) {
PANIC_POSTCONDITION(
"Invalid spec constant id '%s' in %s (not an integer? out of range?): %s",
idStr.data(), shaderLabel.data(), e.what());
}
const auto newValueItr = specConstants.find(static_cast<uint32_t>(id));
if (newValueItr == specConstants.end()) {
// not going to override the constant (stream to the end of the statement)...
processedShaderSourceStr
<< std::string_view(sourceData + pos, posEndOfStatement + 1 - pos);
pos = posEndOfStatement + 1;
continue;
}
// need to override the constant...
const std::variant<int32_t, float, bool> newValue = newValueItr->second;
// stream up to the equal sign...
processedShaderSourceStr << std::string_view(sourceData + pos, posOfEqual + 1 - pos);
// stream the new value...
if (auto* v = std::get_if<int32_t>(&newValue)) {
processedShaderSourceStr << " " << *v << "i";
} else if (auto* f = std::get_if<float>(&newValue)) {
processedShaderSourceStr << " " << *f << "f";
} else if (auto* b = std::get_if<bool>(&newValue)) {
processedShaderSourceStr << " " << ((*b) ? "true" : "false");
}
processedShaderSourceStr << ";";
// and skip to after the end of the statement in the original source...
pos = posEndOfStatement + 1;
}
return processedShaderSourceStr.str();
}
[[nodiscard]] wgpu::ShaderModule createShaderModule(wgpu::Device& device, const char* programName,
std::array<utils::FixedCapacityVector<uint8_t>, Program::SHADER_TYPE_COUNT> const&
shaderSource,
ShaderStage stage) {
[[nodiscard]] wgpu::ShaderModule createShaderModule(wgpu::Device& device, Program& program,
ShaderStage stage,
std::unordered_map<uint32_t, std::variant<int32_t, float, bool>> const& specConstants) {
const char* const programName = program.getName().c_str_safe();
std::array<utils::FixedCapacityVector<uint8_t>, Program::SHADER_TYPE_COUNT> const&
shaderSource = program.getShadersSource();
utils::FixedCapacityVector<uint8_t> const& sourceBytes =
shaderSource[static_cast<size_t>(stage)];
if (sourceBytes.empty()) {
return nullptr;// nothing to compile, the shader was not provided
}
wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{};
wgslDescriptor.code = wgpu::StringView(reinterpret_cast<const char*>(sourceBytes.data()));
std::stringstream labelStream;
labelStream << programName << " " << toString(stage) << " shader";
labelStream << programName << " " << filamentShaderStageToString(stage) << " shader";
auto label = labelStream.str();
const std::string processedShaderSource =
specConstants.empty()
? reinterpret_cast<const char*>(sourceBytes.data())
: replaceSpecConstants(label, reinterpret_cast<const char*>(sourceBytes.data()),
specConstants);
wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{};
wgslDescriptor.code = wgpu::StringView(processedShaderSource);
wgpu::ShaderModuleDescriptor descriptor{
.nextInChain = &wgslDescriptor,
.label = label.data()
@@ -119,85 +197,33 @@ namespace {
<< ":\n"
<< errorStream.str();
}
#if FWGPU_ENABLED(FWGPU_DEBUG_VALIDATION)
FWGPU_LOGD << descriptor.label << " compiled successfully"
<< utils::io::endl;
#endif
}),
UINT16_MAX);
SHADER_COMPILATION_TIMEOUT_NANOSECONDS);
return module;
}
// This is a 1 to 1 mapping of the ReservedSpecializationConstants enum in EngineEnums.h
// The _hack is a workaround until https://issues.chromium.org/issues/42250586 is resolved
// This workaround is the same one being used on the generateSpecializationConstant() function
wgpu::StringView getSpecConstantStringId(uint32_t id) {
switch (id) {
case 0:
return "0";// BACKEND_FEATURE_LEVEL_hack
case 1:
return "1";// CONFIG_MAX_INSTANCES_hack
case 2:
return "2";// ONFIG_STATIC_TEXTURE_TARGET_WORKAROUND_hack
case 3:
return "3";// CONFIG_SRGB_SWAPCHAIN_EMULATION_hack
case 4:
return "4";// CONFIG_FROXEL_BUFFER_HEIGHT_hack
case 5:
return "5";// CONFIG_POWER_VR_SHADER_WORKAROUNDS_hack
case 6:
return "6";// CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP_hack
case 7:
return "7";// CONFIG_DEBUG_FROXEL_VISUALIZATION_hack
case 8:
return "8";// CONFIG_STEREO_EYE_COUNT_hack
case 9:
return "9";// CONFIG_SH_BANDS_COUNT_hack
case 10:
return "10";// CONFIG_SHADOW_SAMPLING_METHOD_hack
default:
PANIC_POSTCONDITION("Unknown/unhandled spec constant key/id: %d", id);
void toMap(utils::FixedCapacityVector<Program::SpecializationConstant> const& specConstants,
std::unordered_map<uint32_t, std::variant<int32_t, float, bool>>& constantById) {
constantById.reserve(specConstants.size());
for (auto const& specConstant: specConstants) {
constantById.emplace(specConstant.id, specConstant.value);
}
}
std::vector<wgpu::ConstantEntry> convertConstants(
utils::FixedCapacityVector<filament::backend::Program::SpecializationConstant> const&
constantsInfo) {
std::vector<wgpu::ConstantEntry> constants;
constants.reserve(constantsInfo.size());
for (filament::backend::Program::SpecializationConstant const& constant: constantsInfo) {
// CONFIG_MAX_INSTANCES (1) and CONFIG_FROXEL_BUFFER_HEIGHT (4) will not be present
// as constant overrides in the generated WGSL, because WGSL doesn't support specialization
// constants as an array length
// More information at https://github.com/gpuweb/gpuweb/issues/572#issuecomment-649760005
// CONFIG_SRGB_SWAPCHAIN_EMULATION (3) is being skipped all together since it's only
// included for the case of mFeatureLevel == FeatureLevel::FEATURE_LEVEL_0, which should
// not be possible for WebGPU
if (constant.id == 1 || constant.id == 3 || constant.id == 4) {
continue;
}
double value = 0.0;
if (auto* v = std::get_if<int32_t>(&constant.value)) {
value = static_cast<double>(*v);
} else if (auto* f = std::get_if<float>(&constant.value)) {
value = static_cast<double>(*f);
} else if (auto* b = std::get_if<bool>(&constant.value)) {
value = *b ? 0.0 : 1.0;
}
constants.push_back(
wgpu::ConstantEntry{ .key = getSpecConstantStringId(constant.id), .value = value });
}
return constants;
}
}// namespace
WGPUProgram::WGPUProgram(wgpu::Device& device, Program& program)
: HwProgram(program.getName()),
vertexShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
ShaderStage::VERTEX)),
fragmentShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
ShaderStage::FRAGMENT)),
computeShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
ShaderStage::COMPUTE)),
constants(convertConstants(program.getSpecializationConstants())) {}
: HwProgram(program.getName()) {
std::unordered_map<uint32_t, std::variant<int32_t, float, bool>> specConstants;
toMap(program.getSpecializationConstants(), specConstants);
vertexShaderModule = createShaderModule(device, program, ShaderStage::VERTEX, specConstants);
fragmentShaderModule =
createShaderModule(device, program, ShaderStage::FRAGMENT, specConstants);
computeShaderModule = createShaderModule(device, program, ShaderStage::COMPUTE, specConstants);
}
}// namespace filament::backend
@@ -63,4 +63,13 @@
#define FWGPU_LOGI (utils::slog.i)
#endif
constexpr uint64_t REQUEST_ADAPTER_TIMEOUT_NANOSECONDS =
/* milliseconds */ 1000u * /* converted to ns */ 1000000u;
constexpr uint64_t REQUEST_DEVICE_TIMEOUT_NANOSECONDS =
/* milliseconds */ 1000u * /* converted to ns */ 1000000u;
constexpr uint64_t SHADER_COMPILATION_TIMEOUT_NANOSECONDS =
/* milliseconds */ 1000u * /* converted to ns */ 1000000u;
#endif// TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
+165 -277
View File
@@ -32,7 +32,6 @@
#include <utils/Panic.h>
#include <utils/ostream.h>
#include <dawn/webgpu_cpp_print.h>
#include <webgpu/webgpu_cpp.h>
#include <algorithm>
@@ -41,179 +40,10 @@
#include <cstdint>
#include <memory>
#include <sstream>
#include <string_view>
#include <utility>
#include <variant>
namespace filament::backend {
namespace {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printInstanceDetails(wgpu::Instance const& instance) {
wgpu::SupportedWGSLLanguageFeatures supportedWGSLLanguageFeatures{};
if (!instance.GetWGSLLanguageFeatures(&supportedWGSLLanguageFeatures)) {
FWGPU_LOGW << "Failed to get WebGPU instance supported WGSL language features"
<< utils::io::endl;
} else {
FWGPU_LOGI << "WebGPU instance supported WGSL language features ("
<< supportedWGSLLanguageFeatures.featureCount << "):" << utils::io::endl;
if (supportedWGSLLanguageFeatures.featureCount > 0 &&
supportedWGSLLanguageFeatures.features != nullptr) {
std::for_each(supportedWGSLLanguageFeatures.features,
supportedWGSLLanguageFeatures.features +
supportedWGSLLanguageFeatures.featureCount,
[](wgpu::WGSLLanguageFeatureName const featureName) {
std::stringstream nameStream{};
nameStream << featureName;
FWGPU_LOGI << " " << nameStream.str() << utils::io::endl;
});
}
}
}
#endif
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printLimit(std::string_view name, const std::variant<uint32_t, uint64_t> value) {
FWGPU_LOGI << " " << name.data() << ": ";
bool undefined = true;
if (std::holds_alternative<uint32_t>(value)) {
if (std::get<uint32_t>(value) != WGPU_LIMIT_U32_UNDEFINED) {
undefined = false;
FWGPU_LOGI << std::get<uint32_t>(value);
}
} else if (std::holds_alternative<uint64_t>(value)) {
if (std::get<uint64_t>(value) != WGPU_LIMIT_U64_UNDEFINED) {
undefined = false;
FWGPU_LOGI << std::get<uint64_t>(value);
}
}
if (undefined) {
FWGPU_LOGI << "UNDEFINED";
}
FWGPU_LOGI << utils::io::endl;
}
#endif
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printLimits(wgpu::Limits const& limits) {
printLimit("maxTextureDimension1D", limits.maxTextureDimension1D);
printLimit("maxTextureDimension2D", limits.maxTextureDimension2D);
printLimit("maxTextureDimension3D", limits.maxTextureDimension3D);
printLimit("maxTextureArrayLayers", limits.maxTextureArrayLayers);
printLimit("maxBindGroups", limits.maxBindGroups);
printLimit("maxBindGroupsPlusVertexBuffers", limits.maxBindGroupsPlusVertexBuffers);
printLimit("maxBindingsPerBindGroup", limits.maxBindingsPerBindGroup);
printLimit("maxDynamicUniformBuffersPerPipelineLayout",
limits.maxDynamicUniformBuffersPerPipelineLayout);
printLimit("maxDynamicStorageBuffersPerPipelineLayout",
limits.maxDynamicStorageBuffersPerPipelineLayout);
printLimit("maxSampledTexturesPerShaderStage", limits.maxSampledTexturesPerShaderStage);
printLimit("maxSamplersPerShaderStage", limits.maxSamplersPerShaderStage);
printLimit("maxStorageBuffersPerShaderStage", limits.maxStorageBuffersPerShaderStage);
printLimit("maxStorageTexturesPerShaderStage", limits.maxStorageTexturesPerShaderStage);
printLimit("maxUniformBuffersPerShaderStage", limits.maxUniformBuffersPerShaderStage);
printLimit("maxUniformBufferBindingSize", limits.maxUniformBufferBindingSize);
printLimit("maxStorageBufferBindingSize", limits.maxStorageBufferBindingSize);
printLimit("minUniformBufferOffsetAlignment", limits.minUniformBufferOffsetAlignment);
printLimit("minStorageBufferOffsetAlignment", limits.minStorageBufferOffsetAlignment);
printLimit("maxVertexBuffers", limits.maxVertexBuffers);
printLimit("maxBufferSize", limits.maxBufferSize);
printLimit("maxVertexAttributes", limits.maxVertexAttributes);
printLimit("maxVertexBufferArrayStride", limits.maxVertexBufferArrayStride);
printLimit("maxInterStageShaderVariables", limits.maxInterStageShaderVariables);
printLimit("maxColorAttachments", limits.maxColorAttachments);
printLimit("maxColorAttachmentBytesPerSample", limits.maxColorAttachmentBytesPerSample);
printLimit("maxComputeWorkgroupStorageSize", limits.maxComputeWorkgroupStorageSize);
printLimit("maxComputeInvocationsPerWorkgroup", limits.maxComputeInvocationsPerWorkgroup);
printLimit("maxComputeWorkgroupSizeX", limits.maxComputeWorkgroupSizeX);
printLimit("maxComputeWorkgroupSizeY", limits.maxComputeWorkgroupSizeY);
printLimit("maxComputeWorkgroupSizeZ", limits.maxComputeWorkgroupSizeZ);
printLimit("maxComputeWorkgroupsPerDimension", limits.maxComputeWorkgroupsPerDimension);
printLimit("maxStorageBuffersInVertexStage", limits.maxStorageBuffersInVertexStage);
printLimit("maxStorageTexturesInVertexStage", limits.maxStorageTexturesInVertexStage);
printLimit("maxStorageBuffersInFragmentStage", limits.maxStorageBuffersInFragmentStage);
printLimit("maxStorageTexturesInFragmentStage", limits.maxStorageTexturesInFragmentStage);
}
#endif
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printAdapterDetails(wgpu::Adapter const& adapter) {
wgpu::DawnAdapterPropertiesPowerPreference powerPreferenceProperties{};
wgpu::AdapterInfo adapterInfo{};
adapterInfo.nextInChain = &powerPreferenceProperties;
if (!adapter.GetInfo(&adapterInfo)) {
FWGPU_LOGW << "Failed to get WebGPU adapter info" << utils::io::endl;
} else {
std::stringstream backendTypeStream{};
backendTypeStream << adapterInfo.backendType;
std::stringstream adapterTypeStream{};
adapterTypeStream << adapterInfo.adapterType;
std::stringstream powerPreferenceStream{};
powerPreferenceStream << powerPreferenceProperties.powerPreference;
FWGPU_LOGI << "WebGPU adapter info:" << utils::io::endl;
FWGPU_LOGI << " vendor: " << adapterInfo.vendor.data << utils::io::endl;
FWGPU_LOGI << " architecture: " << adapterInfo.architecture.data << utils::io::endl;
FWGPU_LOGI << " device: " << adapterInfo.device.data << utils::io::endl;
FWGPU_LOGI << " description: " << adapterInfo.description.data << utils::io::endl;
FWGPU_LOGI << " backend type: " << backendTypeStream.str().data() << utils::io::endl;
FWGPU_LOGI << " adapter type: " << adapterTypeStream.str().data() << utils::io::endl;
FWGPU_LOGI << " device ID: " << adapterInfo.deviceID << utils::io::endl;
FWGPU_LOGI << " vendor ID: " << adapterInfo.vendorID << utils::io::endl;
FWGPU_LOGI << " subgroup min size: " << adapterInfo.subgroupMinSize << utils::io::endl;
FWGPU_LOGI << " subgroup max size: " << adapterInfo.subgroupMaxSize << utils::io::endl;
FWGPU_LOGI << " power preference: " << powerPreferenceStream.str() << utils::io::endl;
}
wgpu::SupportedFeatures supportedFeatures{};
adapter.GetFeatures(&supportedFeatures);
FWGPU_LOGI << "WebGPU adapter supported features (" << supportedFeatures.featureCount
<< "):" << utils::io::endl;
if (supportedFeatures.featureCount > 0 && supportedFeatures.features != nullptr) {
std::for_each(supportedFeatures.features,
supportedFeatures.features + supportedFeatures.featureCount,
[](wgpu::FeatureName const name) {
std::stringstream nameStream{};
nameStream << name;
FWGPU_LOGI << " " << nameStream.str().data() << utils::io::endl;
});
}
wgpu::Limits supportedLimits{};
if (!adapter.GetLimits(&supportedLimits)) {
FWGPU_LOGW << "Failed to get WebGPU adapter supported limits" << utils::io::endl;
} else {
FWGPU_LOGI << "WebGPU adapter supported limits:" << utils::io::endl;
printLimits(supportedLimits);
}
}
#endif
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printDeviceDetails(wgpu::Device const& device) {
wgpu::SupportedFeatures supportedFeatures{};
device.GetFeatures(&supportedFeatures);
FWGPU_LOGI << "WebGPU device supported features (" << supportedFeatures.featureCount
<< "):" << utils::io::endl;
if (supportedFeatures.featureCount > 0 && supportedFeatures.features != nullptr) {
std::for_each(supportedFeatures.features,
supportedFeatures.features + supportedFeatures.featureCount,
[](wgpu::FeatureName const name) {
std::stringstream nameStream{};
nameStream << name;
FWGPU_LOGI << " " << nameStream.str().data() << utils::io::endl;
});
}
wgpu::Limits supportedLimits{};
if (!device.GetLimits(&supportedLimits)) {
FWGPU_LOGW << "Failed to get WebGPU supported device limits" << utils::io::endl;
} else {
FWGPU_LOGI << "WebGPU device supported limits:" << utils::io::endl;
printLimits(supportedLimits);
}
}
#endif
}// namespace
Driver* WebGPUDriver::create(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept {
constexpr size_t defaultSize = FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
Platform::DriverConfig validConfig {driverConfig};
@@ -221,26 +51,16 @@ Driver* WebGPUDriver::create(WebGPUPlatform& platform, const Platform::DriverCon
return new WebGPUDriver(platform, validConfig);
}
WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept
WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform,
const Platform::DriverConfig& driverConfig) noexcept
: mPlatform(platform),
mHandleAllocator("Handles",
driverConfig.handleArenaSize,
driverConfig.disableHandleUseAfterFreeCheck,
driverConfig.disableHeapHandleTags) {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printInstanceDetails(mPlatform.getInstance());
#endif
mHandleAllocator("Handles", driverConfig.handleArenaSize,
driverConfig.disableHandleUseAfterFreeCheck, driverConfig.disableHeapHandleTags) {
mAdapter = mPlatform.requestAdapter(nullptr);
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printAdapterDetails(mAdapter);
#endif
mDevice = mPlatform.requestDevice(mAdapter);
wgpu::Limits supportedLimits{};
mDevice.GetLimits(&supportedLimits);
mMinUniformBufferOffsetAlignment = supportedLimits.minUniformBufferOffsetAlignment;
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printDeviceDetails(mDevice);
#endif
mQueue = mDevice.GetQueue();
}
@@ -359,6 +179,9 @@ void WebGPUDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
if (rt == mDefaultRenderTarget) {
mDefaultRenderTarget = nullptr;
}
if (rt == mCurrentRenderTarget) {
mCurrentRenderTarget = nullptr;
}
// WGPURenderTarget destructor is trivial.
// The HwTexture handles stored within WGPURenderTarget (via MRT, TargetBufferInfo)
// are not owned by WGPURenderTarget, so they are not destroyed here.
@@ -387,6 +210,19 @@ void WebGPUDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh)
}
void WebGPUDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) {
auto* bindGroup = handleCast<WebGPUDescriptorSet>(tqh);
assert_invariant(bindGroup);
if (bindGroup->getBindGroup() != nullptr) {
for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) {
if (mCurrentDescriptorSets[i].bindGroup != nullptr &&
bindGroup->getBindGroup().Get() == mCurrentDescriptorSets[i].bindGroup.Get()) {
// Clear this from our current entries
mCurrentDescriptorSets[i].bindGroup = nullptr;
mCurrentDescriptorSets[i].offsets.clear();
mCurrentDescriptorSets[i].offsetCount = 0;
}
}
}
if (tqh) {
destructHandle<WebGPUDescriptorSet>(tqh);
}
@@ -582,6 +418,7 @@ void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
constructHandle<WGPUProgram>(ph, mDevice, program);
}
void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
assert_invariant(!mDefaultRenderTarget);
mDefaultRenderTarget = constructHandle<WGPURenderTarget>(rth);
@@ -871,15 +708,17 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, RenderPassParams
assert_invariant(mCommandEncoder);
auto* renderTarget = handleCast<WGPURenderTarget>(rth);
wgpu::RenderPassDescriptor renderPassDescriptor{};
wgpu::TextureView defaultColorView = nullptr;
wgpu::TextureView defaultDepthStencilView = nullptr;
std::array<wgpu::TextureView, MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT> customColorViews{};
uint32_t customColorViewCount = 0;
wgpu::TextureView customDepthView = nullptr;
wgpu::TextureFormat customDepthFormat = wgpu::TextureFormat::Undefined;
wgpu::TextureFormat customStencilFormat = wgpu::TextureFormat::Undefined;
wgpu::TextureView customStencilView = nullptr;
mCurrentRenderTarget = renderTarget;
if (renderTarget->isDefaultRenderTarget()) {
assert_invariant(mSwapChain && mTextureView);
defaultColorView = mTextureView;
@@ -892,8 +731,9 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, RenderPassParams
auto* hwTexture = handleCast<WGPUTexture>(colorInfos[i].handle);
if (hwTexture) {
// TODO: Consider colorInfos[i].level and colorInfos[i].layer for view creation
// if WGPUTexture::getTexView() isn't sufficient or needs parameters.
customColorViews[customColorViewCount++] = hwTexture->getTexView();
// if WGPUTexture::getTextureView() isn't sufficient or needs parameters.
customColorViews[customColorViewCount++] = hwTexture->getTextureView();
}
}
}
@@ -902,7 +742,8 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, RenderPassParams
if (depthInfo.handle) {
auto* hwTexture = handleCast<WGPUTexture>(depthInfo.handle);
if (hwTexture) {
customDepthView = hwTexture->getTexView();
customDepthView = hwTexture->getTextureView();
customDepthFormat = hwTexture->getFormat();
}
}
@@ -911,11 +752,12 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, RenderPassParams
// If depth and stencil use the same texture handle, this will re-cast but that's fine.
auto* hwTexture = handleCast<WGPUTexture>(stencilInfo.handle);
if (hwTexture) {
customStencilView = hwTexture->getTexView();
customStencilView = hwTexture->getTextureView();
customStencilFormat = hwTexture->getFormat();
}
}
}
wgpu::RenderPassDescriptor renderPassDescriptor{};
renderTarget->setUpRenderPassAttachments(renderPassDescriptor,
params,
defaultColorView,
@@ -923,18 +765,21 @@ void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, RenderPassParams
customColorViews.data(),
customColorViewCount,
customDepthView,
customStencilView);
customStencilView,
customDepthFormat,
customStencilFormat);
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
// Ensure viewport dimensions are positive
uint32_t viewportWidth = params.viewport.width > 0 ? params.viewport.width : 1;
uint32_t viewportHeight = params.viewport.height > 0 ? params.viewport.height : 1;
// Ensure viewport dimensions are not 0
FILAMENT_CHECK_POSTCONDITION(params.viewport.width > 0) << "viewport width is 0?";
FILAMENT_CHECK_POSTCONDITION(params.viewport.height > 0) << "viewport height is 0?";
mRenderPassEncoder.SetViewport(
static_cast<float>(params.viewport.left),
static_cast<float>(params.viewport.bottom),
static_cast<float>(viewportWidth),
static_cast<float>(viewportHeight),
static_cast<float>(params.viewport.width),
static_cast<float>(params.viewport.height),
params.depthRange.near,
params.depthRange.far);
}
@@ -1029,20 +874,10 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
// VulkanPipelineCache to handle this, may be missing nuance
static auto pipleineStateHasher = utils::hash::MurmurHashFn<filament::backend::PipelineState>();
auto hash = pipleineStateHasher(pipelineState);
if(mPipelineMap.find(hash) != mPipelineMap.end()) {
if (mPipelineMap.find(hash) != mPipelineMap.end()) {
mRenderPassEncoder.SetPipeline(mPipelineMap[hash]);
return;
}
for (size_t index = 0; index < MAX_DESCRIPTOR_SET_COUNT; ++index) {
auto& entry = currentDescriptorSets[index];
if (entry.bg != nullptr) {
mRenderPassEncoder.SetBindGroup(index, entry.bg, entry.dynamicOffsetCount,
entry.offsets.data());
entry.bg = nullptr;
entry.dynamicOffsetCount = 0;
entry.offsets.clear();
}
}
const auto* program = handleCast<WGPUProgram>(pipelineState.program);
assert_invariant(program);
assert_invariant(program->computeShaderModule == nullptr &&
@@ -1075,30 +910,67 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
<< layoutDescriptor.label;
auto const* vertexBufferInfo = handleCast<WGPUVertexBufferInfo>(pipelineState.vertexBufferInfo);
assert_invariant(vertexBufferInfo);
const wgpu::RenderPipeline pipeline = createWebGPURenderPipeline(mDevice, *program,
*vertexBufferInfo, layout, pipelineState.rasterState, pipelineState.stencilState,
pipelineState.polygonOffset, pipelineState.primitiveType, mSwapChain->getColorFormat(),
mSwapChain->getDepthFormat());
std::vector<wgpu::TextureFormat> pipelineColorFormats;
wgpu::TextureFormat pipelineDepthFormat = wgpu::TextureFormat::Undefined;
uint8_t pipelineSamples = 1;
if (mCurrentRenderTarget->isDefaultRenderTarget()) {
pipelineColorFormats.push_back(mSwapChain->getColorFormat());
pipelineDepthFormat = mSwapChain->getDepthFormat();
pipelineSamples =
mCurrentRenderTarget->getSamples();// Default RT should have samples (usually 1)
} else {
const auto& mrtColorAttachments = mCurrentRenderTarget->getColorAttachmentInfos();
for (size_t i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; ++i) {
if (mrtColorAttachments[i].handle) {
const auto* colorTexture = handleCast<WGPUTexture>(mrtColorAttachments[i].handle);
if (colorTexture) {
pipelineColorFormats.push_back(colorTexture->getTexture().GetFormat());
}
}
}
const auto& depthInfo = mCurrentRenderTarget->getDepthAttachmentInfo();
const auto& stencilInfo = mCurrentRenderTarget->getStencilAttachmentInfo();
if (depthInfo.handle) {
FILAMENT_CHECK_POSTCONDITION(!stencilInfo.handle)
<< "depth and stencil attachments cannot both be provided for WebGPU";
const auto* dfTexture = handleCast<WGPUTexture>(depthInfo.handle);
if (dfTexture) pipelineDepthFormat = dfTexture->getTexture().GetFormat();
} else {
if (stencilInfo.handle) {
const auto* sfTexture = handleCast<WGPUTexture>(stencilInfo.handle);
// Assuming combined depth/stencil format if only stencil is present
if (sfTexture) {
pipelineDepthFormat = sfTexture->getTexture().GetFormat();
}
}
}
pipelineSamples = mCurrentRenderTarget->getSamples();
}
if (program->fragmentShaderModule != nullptr) {
FILAMENT_CHECK_POSTCONDITION(!pipelineColorFormats.empty())
<< "Render pipeline with fragment shader must have at least one color target "
"format.";
}
wgpu::RenderPipeline pipeline = createWebGPURenderPipeline(mDevice, *program, *vertexBufferInfo,
layout, pipelineState.rasterState, pipelineState.stencilState,
pipelineState.polygonOffset, pipelineState.primitiveType, pipelineColorFormats,
pipelineDepthFormat, pipelineSamples);
assert_invariant(pipeline);
mPipelineMap[hash] = pipeline;
mRenderPassEncoder.SetPipeline(pipeline);
}
void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
auto* renderPrimitive = handleCast<WGPURenderPrimitive>(rph);
auto* vbi = handleCast<WGPUVertexBufferInfo>(renderPrimitive->vertexBuffer->vbih);
// Loop over the WebGPU vertex buffer slots defined by the VertexBufferInfo.
// For each WebGPU slot, find the corresponding Filament buffer that supplies the data.
for (uint32_t webGPUSlot = 0; webGPUSlot < vbi->getWebGPULayoutCount(); ++webGPUSlot) {
uint8_t filamentBufferIndex = vbi->getFilamentBufferIndexForSlot(webGPUSlot);
// Ensure the Filament buffer index is valid for the vertexBuffer's internal storage.
// This check assumes renderPrimitive->vertexBuffer->buffers is sized by the
// original Filament buffer count.
assert_invariant(filamentBufferIndex < renderPrimitive->vertexBuffer->buffers.size());
mRenderPassEncoder.SetVertexBuffer(webGPUSlot,
renderPrimitive->vertexBuffer->buffers[filamentBufferIndex]);
auto vbi = handleCast<WGPUVertexBufferInfo>(renderPrimitive->vertexBuffer->vbih);
for (const auto& webGPUSlotBindings: vbi->getWebGPUSlotBindingInfos()) {
mRenderPassEncoder.SetVertexBuffer(webGPUSlotBindings.slot,
renderPrimitive->vertexBuffer->buffers[webGPUSlotBindings.sourceBuffer],
webGPUSlotBindings.bufferOffset);
}
mRenderPassEncoder.SetIndexBuffer(renderPrimitive->indexBuffer->getBuffer(),
@@ -1106,11 +978,27 @@ void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
}
void WebGPUDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
// We defer actually binding until we actually draw
for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) {
auto& binding = mCurrentDescriptorSets[i];
if (binding.bindGroup) {
mRenderPassEncoder.SetBindGroup(i, binding.bindGroup, binding.offsetCount,
binding.offsets.data());
}
}
mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 0);
}
void WebGPUDriver::draw(PipelineState, Handle<HwRenderPrimitive>, uint32_t indexOffset,
uint32_t indexCount, uint32_t instanceCount) {
void WebGPUDriver::draw(PipelineState pipelineState,
Handle<HwRenderPrimitive> renderPrimitiveHandle, uint32_t indexOffset, uint32_t indexCount,
uint32_t instanceCount) {
WGPURenderPrimitive const* const renderPrimitive =
handleCast<WGPURenderPrimitive>(renderPrimitiveHandle);
pipelineState.primitiveType = renderPrimitive->type;
pipelineState.vertexBufferInfo = renderPrimitive->vertexBuffer->vbih;
bindPipeline(pipelineState);
bindRenderPrimitive(renderPrimitiveHandle);
draw2(indexOffset, indexCount, instanceCount);
}
@@ -1159,7 +1047,7 @@ void WebGPUDriver::updateDescriptorSetTexture(Handle<HwDescriptorSet> dsh,
auto sampler = makeSampler(params);
// TODO making assumptions that size and offset mean the same thing here.
wgpu::BindGroupEntry tEntry{ .binding = static_cast<uint32_t>(binding * 2),
.textureView = texture->getTexView() };
.textureView = texture->getTextureView() };
bindGroup->addEntry(tEntry.binding, std::move(tEntry));
wgpu::BindGroupEntry sEntry{ .binding = static_cast<uint32_t>(binding * 2 + 1),
@@ -1170,18 +1058,18 @@ void WebGPUDriver::updateDescriptorSetTexture(Handle<HwDescriptorSet> dsh,
void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> dsh,
backend::descriptor_set_t setIndex, backend::DescriptorSetOffsetArray&& offsets) {
assert_invariant(setIndex < MAX_DESCRIPTOR_SET_COUNT);
const auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
const auto wbg = bindGroup->lockAndReturn(mDevice);
const size_t dynamicOffsetCount = bindGroup->countEntitiesWithDynamicOffsets();
if(mRenderPassEncoder){
mRenderPassEncoder.SetBindGroup(setIndex, wbg, dynamicOffsetCount, offsets.data());
}
currentDescriptorSets[setIndex] = {wbg, dynamicOffsetCount, std::move(offsets)};
mCurrentDescriptorSets[setIndex] = { .bindGroup = wbg,
.offsetCount = bindGroup->countEntitiesWithDynamicOffsets(),
.offsets = std::move(offsets) };
}
void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
}
wgpu::Sampler WebGPUDriver::makeSampler(SamplerParams const& params) {
wgpu::SamplerDescriptor desc{};
@@ -1189,51 +1077,51 @@ wgpu::Sampler WebGPUDriver::makeSampler(SamplerParams const& params) {
desc.addressModeU = fWrapModeToWAddressMode(params.wrapS);
desc.addressModeV = fWrapModeToWAddressMode(params.wrapR);
desc.addressModeW = fWrapModeToWAddressMode(params.wrapT);
switch (params.filterMag) {
case SamplerMagFilter::NEAREST: {
desc.magFilter = wgpu::FilterMode::Nearest;
break;
}
case SamplerMagFilter::LINEAR: {
desc.magFilter = wgpu::FilterMode::Linear;
break;
}
}
switch (params.filterMin) {
case SamplerMinFilter::NEAREST: {
desc.minFilter = wgpu::FilterMode::Nearest;
desc.mipmapFilter = wgpu::MipmapFilterMode::Undefined;
break;
}
case SamplerMinFilter::LINEAR: {
desc.minFilter = wgpu::FilterMode::Linear;
desc.mipmapFilter = wgpu::MipmapFilterMode::Undefined;
break;
}
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST: {
desc.minFilter = wgpu::FilterMode::Nearest;
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
break;
}
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST: {
desc.minFilter = wgpu::FilterMode::Linear;
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
break;
}
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR: {
desc.minFilter = wgpu::FilterMode::Nearest;
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
break;
}
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR: {
desc.minFilter = wgpu::FilterMode::Linear;
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
break;
}
}
if (params.compareMode == SamplerCompareMode::COMPARE_TO_TEXTURE) {
switch (params.filterMag) {
case SamplerMagFilter::NEAREST: {
desc.magFilter = wgpu::FilterMode::Nearest;
break;
}
case SamplerMagFilter::LINEAR: {
desc.magFilter = wgpu::FilterMode::Linear;
break;
}
}
switch (params.filterMin) {
case SamplerMinFilter::NEAREST: {
desc.minFilter = wgpu::FilterMode::Nearest;
desc.mipmapFilter = wgpu::MipmapFilterMode::Undefined;
break;
}
case SamplerMinFilter::LINEAR: {
desc.minFilter = wgpu::FilterMode::Linear;
desc.mipmapFilter = wgpu::MipmapFilterMode::Undefined;
break;
}
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST: {
desc.minFilter = wgpu::FilterMode::Nearest;
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
break;
}
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST: {
desc.minFilter = wgpu::FilterMode::Linear;
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
break;
}
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR: {
desc.minFilter = wgpu::FilterMode::Nearest;
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
break;
}
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR: {
desc.minFilter = wgpu::FilterMode::Linear;
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
break;
}
}
switch (params.compareFunc) {
case SamplerCompareFunc::LE: {
desc.compare = wgpu::CompareFunction::LessEqual;
+8 -6
View File
@@ -72,16 +72,18 @@ private:
wgpu::CommandEncoder mCommandEncoder = nullptr;
wgpu::TextureView mTextureView = nullptr;
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
struct bgInfo{
wgpu::BindGroup bg;
size_t dynamicOffsetCount;
backend::DescriptorSetOffsetArray offsets;
};
std::array<bgInfo, MAX_DESCRIPTOR_SET_COUNT> currentDescriptorSets = {};
wgpu::CommandBuffer mCommandBuffer = nullptr;
WGPURenderTarget* mDefaultRenderTarget = nullptr;
WGPURenderTarget* mCurrentRenderTarget = nullptr;
tsl::robin_map<uint32_t, wgpu::RenderPipeline> mPipelineMap;
struct DescriptorSetBindingInfo{
wgpu::BindGroup bindGroup;
size_t offsetCount;
backend::DescriptorSetOffsetArray offsets;
};
std::array<DescriptorSetBindingInfo,MAX_DESCRIPTOR_SET_COUNT> mCurrentDescriptorSets;
/*
* Driver interface
*/
+142 -123
View File
@@ -25,7 +25,6 @@
#include <algorithm>
#include <cstdint>
#include <map>
#include <utility>
#include <vector>
@@ -198,76 +197,89 @@ void WGPUBufferBase::updateGPUBuffer(BufferDescriptor& bufferDescriptor, uint32_
}
}
static constexpr uint32_t DUMMY_WEBGPU_SLOT = 0;
WGPUVertexBufferInfo::WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount) { // Base class constructor
: HwVertexBufferInfo(bufferCount, attributeCount),
// TODO: max limits may not be supported by webgpu driver. This should be addressed in the
// hardening part.
mVertexBufferLayouts(MAX_VERTEX_BUFFER_COUNT),
mVertexAttributes(MAX_VERTEX_BUFFER_COUNT) {
assert_invariant(attributeCount > 0);
// bufferCount is Filament's buffer count. The number of WebGPU slots might differ.
// It starts from 1 because slot 0 is now reserved for the dummy.
uint32_t currentWebGPUSlotIndex = 1;
// A reasonable dummy stride (e.g., for vec4)
const uint32_t DUMMY_STRIDE = 16;
struct LayoutKey {
uint8_t filamentBufferIndex;
uint16_t stride;
bool operator<(const LayoutKey& other) const {
if (filamentBufferIndex != other.filamentBufferIndex) {
return filamentBufferIndex < other.filamentBufferIndex;
}
return stride < other.stride;
}
};
std::map<LayoutKey, uint32_t> layoutKeyToWebGPUSlot;
uint32_t nextWebGPUSlot = 0;
// Initialize the layout for the dummy slot (slot 0)
mVertexBufferLayouts[DUMMY_WEBGPU_SLOT].arrayStride = DUMMY_STRIDE;
mVertexBufferLayouts[DUMMY_WEBGPU_SLOT].stepMode = wgpu::VertexStepMode::Vertex;
mVertexBufferLayouts[DUMMY_WEBGPU_SLOT].attributeCount = 0;
for (uint32_t attribIndex = 0; attribIndex < attributes.size(); attribIndex++) {
Attribute const& attrib = attributes[attribIndex];
// Ignore attributes not bound to any vertex buffer.
if (attrib.buffer == Attribute::BUFFER_UNUSED) {
mWebGPUSlotBindingInfos.push_back({
.sourceBuffer = 0,
.slot = DUMMY_WEBGPU_SLOT,
.bufferOffset = 0,
.stride = DUMMY_STRIDE,
});
for (uint32_t attributeIndex = 0; attributeIndex < attributes.size(); ++attributeIndex) {
const auto& attribute = attributes[attributeIndex];
bool const isInteger = attribute.flags & Attribute::FLAG_INTEGER_TARGET;
bool const isNormalized = attribute.flags & Attribute::FLAG_NORMALIZED;
wgpu::VertexFormat vertexFormat = getVertexFormat(attribute.type, isNormalized, isInteger);
if (attribute.buffer == Attribute::BUFFER_UNUSED) {
// Use some dummy format
vertexFormat = isInteger ? wgpu::VertexFormat::Uint8x4 : wgpu::VertexFormat::Unorm8x4;
mVertexAttributes[DUMMY_WEBGPU_SLOT].push_back({
.format = vertexFormat,
.offset = 0,
.shaderLocation = attributeIndex,
});
mVertexBufferLayouts[DUMMY_WEBGPU_SLOT].attributeCount++;
continue;
}
assert_invariant(attrib.buffer < bufferCount); // Ensure Filament buffer index is valid
auto it = std::find_if(mWebGPUSlotBindingInfos.begin(), mWebGPUSlotBindingInfos.end(),
[&](const auto& info) {
return info.sourceBuffer == attribute.buffer && info.stride == attribute.stride;
});
LayoutKey key = {attrib.buffer, attrib.stride};
uint32_t currentWebGPUSlot;
auto it = layoutKeyToWebGPUSlot.find(key);
if (it == layoutKeyToWebGPUSlot.end()) {
currentWebGPUSlot = nextWebGPUSlot++;
layoutKeyToWebGPUSlot[key] = currentWebGPUSlot;
mActualWebGPULayouts.emplace_back();
mActualWebGPULayouts.back().arrayStride = attrib.stride;
mActualWebGPULayouts.back().stepMode = wgpu::VertexStepMode::Vertex;
// .attributes and .attributeCount will be set later
mSlotMappings.emplace_back(SlotMapping{attrib.buffer});
mActualWebGPUAttributes.emplace_back(); // Add a new vector for this slot's attributes
uint32_t assignedSlot;
if (it != mWebGPUSlotBindingInfos.end()) {
assignedSlot = it->slot;
} else {
currentWebGPUSlot = it->second;
// Sanity check: if we found an existing slot for this key, its stride should match.
assert_invariant(mActualWebGPULayouts[currentWebGPUSlot].arrayStride == attrib.stride);
// New combination, allocate a new WebGPU slot
assert_invariant(currentWebGPUSlotIndex < MAX_VERTEX_BUFFER_COUNT);
assignedSlot = currentWebGPUSlotIndex++;
mWebGPUSlotBindingInfos.push_back({
.sourceBuffer = attribute.buffer,
.slot = assignedSlot,
.bufferOffset = attribute.offset - (attribute.offset % attribute.stride),
.stride = attribute.stride,
});
mVertexBufferLayouts[assignedSlot].arrayStride = attribute.stride;
mVertexBufferLayouts[assignedSlot].stepMode = wgpu::VertexStepMode::Vertex;
mVertexBufferLayouts[assignedSlot].attributeCount = 0;
}
bool const isInteger = attrib.flags & Attribute::FLAG_INTEGER_TARGET;
bool const isNormalized = attrib.flags & Attribute::FLAG_NORMALIZED;
wgpu::VertexFormat vertexFormat = getVertexFormat(attrib.type, isNormalized, isInteger);
mActualWebGPUAttributes[currentWebGPUSlot].push_back({
mVertexAttributes[assignedSlot].push_back({
.format = vertexFormat,
.offset = attrib.offset,
.shaderLocation = attribIndex, // Use original attribIndex as shaderLocation
.offset = attribute.offset % attribute.stride,
.shaderLocation = attributeIndex,
});
mVertexBufferLayouts[assignedSlot].attributeCount++;
}
// Finalize attribute pointers and counts in mActualWebGPULayouts
for (uint32_t slot = 0; slot < mActualWebGPULayouts.size(); ++slot) {
if (!mActualWebGPUAttributes[slot].empty()) {
mActualWebGPULayouts[slot].attributeCount = mActualWebGPUAttributes[slot].size();
mActualWebGPULayouts[slot].attributes = mActualWebGPUAttributes[slot].data();
} else {
mActualWebGPULayouts[slot].attributeCount = 0;
mActualWebGPULayouts[slot].attributes = nullptr;
}
mVertexBufferLayouts.resize(currentWebGPUSlotIndex);
for (const auto& info: mWebGPUSlotBindingInfos) {
mVertexBufferLayouts[info.slot].attributes = mVertexAttributes[info.slot].data();
}
}
@@ -375,9 +387,10 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
// TODO: Set once we have the filtering values
if (isDepthDescriptor(fEntry.type)) {
samplerEntry.sampler.type = wgpu::SamplerBindingType::Comparison;
} else if (isIntDescriptor(fEntry.type)) {
samplerEntry.sampler.type = wgpu::SamplerBindingType::NonFiltering;
} else {
samplerEntry.sampler.type =
wgpu::SamplerBindingType::NonFiltering;
samplerEntry.sampler.type = wgpu::SamplerBindingType::Filtering;
}
break;
}
@@ -409,7 +422,7 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
else if (isFloatDescriptor(fEntry.type))
{
// TODO: Set once we have the filtering values
wEntry.texture.sampleType = wgpu::TextureSampleType::UnfilterableFloat;
wEntry.texture.sampleType = wgpu::TextureSampleType::Float;
}
else if (isIntDescriptor(fEntry.type))
{
@@ -580,7 +593,7 @@ WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat forma
FILAMENT_CHECK_POSTCONDITION(mTexture)
<< "Failed to create texture for " << textureDescriptor.label;
// Second, the texture view aspect
mTexView = makeTextureView(0, levels, target);
mTexureView = makeTextureView(0, levels, target);
}
WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept {
@@ -589,7 +602,7 @@ WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount
mBlockWidth = src->mBlockWidth;
mBlockHeight = src->mBlockHeight;
mTexView = makeTextureView(baseLevel, levelCount, target);
mTexureView = makeTextureView(baseLevel, levelCount, target);
}
wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(TextureUsage const& fUsage) {
@@ -968,20 +981,10 @@ WGPURenderTarget::WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samp
mColorAttachments(colorAttachmentsMRT),
mDepthAttachment(depthAttachmentInfo),
mStencilAttachment(stencilAttachmentInfo) {
// TODO Make this an array
mColorAttachmentDescriptors.reserve(MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
}
// Static helper to map MRT index to TargetBufferFlags
TargetBufferFlags WGPURenderTarget::getTargetBufferFlagsAt(int mrtIndex) {
if (mrtIndex < 0 || mrtIndex >= MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT) {
return TargetBufferFlags::NONE;
}
// This mapping assumes TargetBufferFlags::COLOR_0, COLOR_1 etc. are contiguous
return static_cast<TargetBufferFlags>(
static_cast<uint32_t>(TargetBufferFlags::COLOR0) << mrtIndex);
}
// Corrected getLoadOperation
wgpu::LoadOp WGPURenderTarget::getLoadOperation(RenderPassParams const& params,
TargetBufferFlags bufferToOperateOn) {
if (any(params.flags.clear & bufferToOperateOn)) {
@@ -993,7 +996,6 @@ wgpu::LoadOp WGPURenderTarget::getLoadOperation(RenderPassParams const& params,
return wgpu::LoadOp::Load;
}
// Corrected getStoreOperation
wgpu::StoreOp WGPURenderTarget::getStoreOperation(RenderPassParams const& params,
TargetBufferFlags bufferToOperateOn) {
if (any(params.flags.discardEnd & bufferToOperateOn)) {
@@ -1002,79 +1004,103 @@ wgpu::StoreOp WGPURenderTarget::getStoreOperation(RenderPassParams const& params
return wgpu::StoreOp::Store;
}
void WGPURenderTarget::setUpRenderPassAttachments(
wgpu::RenderPassDescriptor& descriptor,
RenderPassParams const& params,
wgpu::TextureView const& defaultColorTextureView,
void WGPURenderTarget::setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor,
RenderPassParams const& params, wgpu::TextureView const& defaultColorTextureView,
wgpu::TextureView const& defaultDepthStencilTextureView,
wgpu::TextureView const* customColorTextureViews,
uint32_t customColorTextureViewCount,
wgpu::TextureView const* customColorTextureViews, uint32_t customColorTextureViewCount,
wgpu::TextureView const& customDepthTextureView,
wgpu::TextureView const& customStencilTextureView) {
wgpu::TextureView const& customStencilTextureView, wgpu::TextureFormat customDepthFormat,
wgpu::TextureFormat customStencilFormat) {
mColorAttachmentDescriptors.clear();
mHasDepthStencilAttachment = false;
if (defaultRenderTarget) {
assert_invariant(defaultColorTextureView);
wgpu::RenderPassColorAttachment colorAttDesc{};
colorAttDesc.view = defaultColorTextureView;
colorAttDesc.resolveTarget = nullptr;
colorAttDesc.loadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::COLOR0);
colorAttDesc.storeOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::COLOR0);
colorAttDesc.clearValue = {params.clearColor.r, params.clearColor.g,
params.clearColor.b, params.clearColor.a};
mColorAttachmentDescriptors.push_back(colorAttDesc);
mColorAttachmentDescriptors.push_back({ .view = defaultColorTextureView,
.resolveTarget = nullptr,
.loadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::COLOR0),
.storeOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::COLOR0),
.clearValue = { params.clearColor.r, params.clearColor.g, params.clearColor.b,
params.clearColor.a } });
if (defaultDepthStencilTextureView) {
mDepthStencilAttachmentDescriptor = {
.view = defaultDepthStencilTextureView,
.depthLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH),
.depthStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH),
.depthStoreOp =
WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH),
.depthClearValue = static_cast<float>(params.clearDepth),
.depthReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0,
.stencilLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL),
.stencilStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL),
.depthReadOnly =
(params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0,
.stencilLoadOp =
WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL),
.stencilStoreOp =
WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL),
.stencilClearValue = params.clearStencil,
.stencilReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0,
.stencilReadOnly =
(params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0,
};
mHasDepthStencilAttachment = true;
}
} else { // Custom Render Target
} else {// Custom Render Target
for (uint32_t i = 0; i < customColorTextureViewCount; ++i) {
if (customColorTextureViews[i]) {
wgpu::RenderPassColorAttachment colorAttDesc{};
colorAttDesc.view = customColorTextureViews[i];
colorAttDesc.resolveTarget = nullptr; // TODO: MSAA resolve for custom RT
colorAttDesc.loadOp = WGPURenderTarget::getLoadOperation(params, getTargetBufferFlagsAt(i));
colorAttDesc.storeOp = WGPURenderTarget::getStoreOperation(params, getTargetBufferFlagsAt(i));
colorAttDesc.clearValue = {params.clearColor.r, params.clearColor.g,
params.clearColor.b, params.clearColor.a};
mColorAttachmentDescriptors.push_back(colorAttDesc);
mColorAttachmentDescriptors.push_back({ .view = customColorTextureViews[i],
// .resolveTarget = nullptr; // TODO: MSAA resolve for custom RT
.loadOp = WGPURenderTarget::getLoadOperation(params, getTargetBufferFlagsAt(i)),
.storeOp =
WGPURenderTarget::getStoreOperation(params, getTargetBufferFlagsAt(i)),
.clearValue = { .r = params.clearColor.r,
.g = params.clearColor.g,
.b = params.clearColor.b,
.a = params.clearColor.a } });
}
}
wgpu::TextureView combinedDsView = customDepthTextureView ? customDepthTextureView : customStencilTextureView;
FILAMENT_CHECK_POSTCONDITION(!(customDepthTextureView && customStencilTextureView))
<< "WebGPU CANNOT support separate texture views for depth + stencil. depth + "
"stencil needs to be in one texture view";
if (combinedDsView) {
const bool hasStencil =
customStencilTextureView ||
(customDepthFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
customDepthFormat == wgpu::TextureFormat::Depth32FloatStencil8);
const bool hasDepth =
customDepthTextureView ||
(customStencilFormat == wgpu::TextureFormat::Depth24PlusStencil8 ||
customDepthFormat == wgpu::TextureFormat::Depth32FloatStencil8);
if (customDepthTextureView || customStencilTextureView) {
assert_invariant((hasDepth || hasStencil) &&
"Depth or Texture view without a valid texture format");
mDepthStencilAttachmentDescriptor = {};
mDepthStencilAttachmentDescriptor.view = combinedDsView;
mDepthStencilAttachmentDescriptor.view =
customDepthTextureView ? customDepthTextureView : customStencilTextureView;
if (customDepthTextureView) {
mDepthStencilAttachmentDescriptor.depthLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH);
mDepthStencilAttachmentDescriptor.depthStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH);
mDepthStencilAttachmentDescriptor.depthClearValue = static_cast<float>(params.clearDepth);
mDepthStencilAttachmentDescriptor.depthReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0;
if (hasDepth) {
mDepthStencilAttachmentDescriptor.depthLoadOp =
WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH);
mDepthStencilAttachmentDescriptor.depthStoreOp =
WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH);
mDepthStencilAttachmentDescriptor.depthClearValue =
static_cast<float>(params.clearDepth);
mDepthStencilAttachmentDescriptor.depthReadOnly =
(params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) > 0;
} else {
mDepthStencilAttachmentDescriptor.depthLoadOp = wgpu::LoadOp::Undefined;
mDepthStencilAttachmentDescriptor.depthStoreOp = wgpu::StoreOp::Undefined;
mDepthStencilAttachmentDescriptor.depthReadOnly = true;
}
if (customStencilTextureView) {
mDepthStencilAttachmentDescriptor.stencilLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL);
mDepthStencilAttachmentDescriptor.stencilStoreOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL);
if (hasStencil) {
mDepthStencilAttachmentDescriptor.stencilLoadOp =
WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::STENCIL);
mDepthStencilAttachmentDescriptor.stencilStoreOp =
WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::STENCIL);
mDepthStencilAttachmentDescriptor.stencilClearValue = params.clearStencil;
mDepthStencilAttachmentDescriptor.stencilReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0;
mDepthStencilAttachmentDescriptor.stencilReadOnly =
(params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0;
} else {
mDepthStencilAttachmentDescriptor.stencilLoadOp = wgpu::LoadOp::Undefined;
mDepthStencilAttachmentDescriptor.stencilStoreOp = wgpu::StoreOp::Undefined;
@@ -1086,19 +1112,12 @@ void WGPURenderTarget::setUpRenderPassAttachments(
descriptor.colorAttachmentCount = mColorAttachmentDescriptors.size();
descriptor.colorAttachments = mColorAttachmentDescriptors.data();
descriptor.depthStencilAttachment = mHasDepthStencilAttachment ? &mDepthStencilAttachmentDescriptor : nullptr;
descriptor.depthStencilAttachment =
mHasDepthStencilAttachment ? &mDepthStencilAttachmentDescriptor : nullptr;
// descriptor.sampleCount was removed from the core spec. If your webgpu.h still has it,
// and your Dawn version expects it, you might need to set it here based on this->samples.
// e.g., descriptor.sampleCount = this->samples;
}
math::uint2 WGPURenderTarget::getAttachmentSize() const noexcept {
if (!defaultRenderTarget) {
return {width, height};
}
// For default RT, size is dynamic and usually obtained from the swapchain.
// The caller (WebGPUDriver::beginRenderPass) should know the current swapchain size.
return {0,0};
}
}// namespace filament::backend
+37 -37
View File
@@ -44,48 +44,48 @@ public:
};
// VertexBufferInfo contains layout info for Vertex Buffer based on WebGPU structs. In WebGPU each
// VertexBufferLayout is associated with a single vertex buffer. So number of mVertexBufferLayout
// is equal to bufferCount. Each VertexBufferLayout can contain multiple VertexAttribute. Bind index
// of vertex buffer is implicitly calculated by the position of VertexBufferLayout in an array.
// WGPUVertexBufferInfo maps Filament vertex attributes to WebGPU buffer binding model.
class WGPUVertexBufferInfo : public HwVertexBufferInfo {
public:
WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes);
// Returns the WebGPU native vertex buffer layouts
inline wgpu::VertexBufferLayout const* getWebGPULayouts() const {
return mActualWebGPULayouts.data();
inline wgpu::VertexBufferLayout const* getVertexBufferLayouts() const {
return mVertexBufferLayouts.data();
}
inline uint32_t getVertexBufferLayoutCount() const {
return static_cast<uint32_t>(mVertexBufferLayouts.size());
}
// Returns the count of WebGPU native vertex buffer layouts
inline uint32_t getWebGPULayoutCount() const {
return mActualWebGPULayouts.size();
inline wgpu::VertexAttribute const* getVertexAttributes(uint32_t i) const {
return mVertexAttributes[i].data();
}
inline uint32_t getVertexAttributeCount(uint32_t i) const {
return static_cast<uint32_t>(mVertexAttributes[i].size());
}
// Returns the Filament buffer index for a given WebGPU layout slot
inline uint8_t getFilamentBufferIndexForSlot(uint32_t webgpuSlotIndex) const {
assert_invariant(webgpuSlotIndex < mSlotMappings.size());
return mSlotMappings[webgpuSlotIndex].filamentBufferIndex;
struct WebGPUSlotBindingInfo {
uint8_t sourceBuffer;
uint32_t slot;
uint64_t bufferOffset;
uint32_t stride;
};
inline const std::vector<WebGPUSlotBindingInfo>& getWebGPUSlotBindingInfos() const {
return mWebGPUSlotBindingInfos;
}
private:
// Stores the final WebGPU vertex buffer layouts
std::vector<wgpu::VertexBufferLayout> mActualWebGPULayouts;
// Stores attributes per WebGPU layout, parallel to mActualWebGPULayouts.
// Each wgpu::VertexBufferLayout in mActualWebGPULayouts will point to its attributes here.
std::vector<std::vector<wgpu::VertexAttribute>> mActualWebGPUAttributes;
// This stores the final wgpu::VertexBufferLayout objects, one per WebGPU slot.
std::vector<wgpu::VertexBufferLayout> mVertexBufferLayouts;
struct SlotMapping {
uint8_t filamentBufferIndex; // Original Filament buffer index for this WebGPU slot
};
// Maps a WebGPU slot index to its original Filament buffer index
std::vector<SlotMapping> mSlotMappings;
// This stores all wgpu::VertexAttribute structs, indexed by their original
// Filament attributeIndex.
std::vector<std::vector<wgpu::VertexAttribute>> mVertexAttributes;
// Remove or comment out old members if they were similarly named:
// std::vector<wgpu::VertexBufferLayout> mVertexBufferLayout {}; // OLD
// std::vector<std::vector<wgpu::VertexAttribute>> mAttributes {}; // OLD
// Stores information for the driver to perform setVertexBuffer calls
std::vector<WebGPUSlotBindingInfo> mWebGPUSlotBindingInfos;
};
struct WGPUVertexBuffer : public HwVertexBuffer {
WGPUVertexBuffer(wgpu::Device const &device, uint32_t vertexCount, uint32_t bufferCount,
Handle<HwVertexBufferInfo> vbih);
@@ -155,6 +155,9 @@ public:
[[nodiscard]] bool getIsLocked() const { return mBindGroup != nullptr; }
[[nodiscard]] size_t countEntitiesWithDynamicOffsets() const;
// May be nullptr. Use lockAndReturn to create the bind group when appropriate
[[nodiscard]] const wgpu::BindGroup& getBindGroup() const { return mBindGroup; }
private:
wgpu::BindGroupLayout mLayout = nullptr;
static constexpr uint8_t INVALID_INDEX = MAX_DESCRIPTOR_COUNT + 1;
@@ -176,7 +179,8 @@ public:
size_t getBlockHeight() const { return mBlockHeight; }
[[nodiscard]] const wgpu::Texture& getTexture() const { return mTexture; }
[[nodiscard]] const wgpu::TextureView& getTexView() const { return mTexView; }
[[nodiscard]] const wgpu::TextureView& getTextureView() const { return mTexureView; }
[[nodiscard]] wgpu::TextureFormat getFormat() const { return mFormat; }
static wgpu::TextureFormat fToWGPUTextureFormat(
filament::backend::TextureFormat const& fFormat);
@@ -195,7 +199,7 @@ private:
wgpu::TextureFormat mFormat = wgpu::TextureFormat::Undefined;
wgpu::TextureAspect mAspect = wgpu::TextureAspect::Undefined;
uint32_t mArrayLayerCount = 1;
wgpu::TextureView mTexView = nullptr;
wgpu::TextureView mTexureView = nullptr;
wgpu::TextureUsage fToWGPUTextureUsage(filament::backend::TextureUsage const& fUsage);
size_t mBlockWidth;
size_t mBlockHeight;
@@ -207,8 +211,6 @@ struct WGPURenderPrimitive : public HwRenderPrimitive {
WGPUIndexBuffer* indexBuffer = nullptr;
};
class WGPURenderTarget : public HwRenderTarget {
public:
using Attachment = TargetBufferInfo; // Using TargetBufferInfo directly for attachments
@@ -235,11 +237,11 @@ public:
wgpu::TextureView const* customColorTextureViews, // Array of views
uint32_t customColorTextureViewCount,
wgpu::TextureView const& customDepthTextureView,
wgpu::TextureView const& customStencilTextureView);
wgpu::TextureView const& customStencilTextureView,
wgpu::TextureFormat customDepthFormat,
wgpu::TextureFormat customStencilFormat);
math::uint2 getAttachmentSize() const noexcept;
bool isDefaultRenderTarget() const { return defaultRenderTarget; }
uint8_t getSamples() const { return samples; }
@@ -253,13 +255,11 @@ public:
static wgpu::StoreOp getStoreOperation(const RenderPassParams& params, TargetBufferFlags buffer);
private:
// Helper to map MRT index to TargetBufferFlags
static TargetBufferFlags getTargetBufferFlagsAt(int mrtIndex);
bool defaultRenderTarget = false;
uint8_t samples = 1;
MRT mColorAttachments{};
// TODO WebGPU only supports a DepthStencil attachment, should this be just mDepthStencilAttachment?
Attachment mDepthAttachment{};
Attachment mStencilAttachment{};
@@ -154,9 +154,9 @@ constexpr wgpu::BlendFactor toWebGPU(BlendFunction blendFunction) {
wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
WGPUProgram const& program, WGPUVertexBufferInfo const& vertexBufferInfo,
wgpu::PipelineLayout const& layout, RasterState const& rasterState,
StencilState const& stencilState, PolygonOffset const& polygonOffset,
PrimitiveType primitiveType, wgpu::TextureFormat colorFormat,
wgpu::TextureFormat depthFormat) {
StencilState const& stencilState, PolygonOffset const& polygonOffset, PrimitiveType primitiveType,
std::vector<wgpu::TextureFormat> const& colorFormats,
wgpu::TextureFormat depthFormat, uint8_t samplesCount) {
assert_invariant(program.vertexShaderModule);
const wgpu::DepthStencilState depthStencilState {
.format = depthFormat,
@@ -191,8 +191,8 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
.entryPoint = "main",
.constantCount = program.constants.size(),
.constants = program.constants.data(),
.bufferCount = vertexBufferInfo.getWebGPULayoutCount(),
.buffers = vertexBufferInfo.getWebGPULayouts()
.bufferCount = vertexBufferInfo.getVertexBufferLayoutCount(),
.buffers = vertexBufferInfo.getVertexBufferLayouts()
},
.primitive = {
.topology = toWebGPU(primitiveType),
@@ -207,9 +207,9 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
.unclippedDepth = !rasterState.depthClamp &&
device.HasFeature(wgpu::FeatureName::DepthClipControl)
},
.depthStencil = &depthStencilState,
.depthStencil = depthFormat != wgpu::TextureFormat::Undefined ? &depthStencilState: nullptr,
.multisample = {
.count = 1, // TODO need to get this from the render target
.count = samplesCount,
.mask = 0xFFFFFFFF,
.alphaToCoverageEnabled = rasterState.alphaToCoverage
},
@@ -234,12 +234,15 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
fragmentState.entryPoint = "main";
fragmentState.constantCount = program.constants.size(),
fragmentState.constants = program.constants.data(),
fragmentState.targetCount = 1; // TODO need to get this from the render target
fragmentState.targetCount = colorFormats.size();
fragmentState.targets = colorTargets.data();
assert_invariant(fragmentState.targetCount <= MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
// A fragment shader implies at least one color target if it outputs color.
// This should be guaranteed by the caller ensuring colorFormats is not empty.
assert_invariant(fragmentState.targetCount > 0);
for (size_t targetIndex = 0; targetIndex < fragmentState.targetCount; targetIndex++) {
auto& colorTarget = colorTargets[targetIndex];
colorTarget.format = colorFormat;
colorTarget.format = colorFormats[targetIndex];
colorTarget.blend = rasterState.hasBlending() ? &blendState : nullptr;
colorTarget.writeMask =
rasterState.colorWrite ? wgpu::ColorWriteMask::All : wgpu::ColorWriteMask::None;
@@ -18,6 +18,7 @@
#define TNT_FILAMENT_BACKEND_WEBGPUPIPELINECREATION_H
#include <cstdint>
#include <vector>
namespace wgpu {
class Device;
@@ -38,8 +39,9 @@ class WGPUProgram;
[[nodiscard]] wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const&,
WGPUProgram const&, WGPUVertexBufferInfo const&, wgpu::PipelineLayout const&,
RasterState const&, StencilState const&, PolygonOffset const&, PrimitiveType,
wgpu::TextureFormat colorFormat, wgpu::TextureFormat depthFormat);
RasterState const&, StencilState const&, PolygonOffset const&, PrimitiveType primitiveType,
std::vector<wgpu::TextureFormat> const& colorFormats,
wgpu::TextureFormat depthFormat, uint8_t samplesCount);
}// namespace filament::backend
+188
View File
@@ -0,0 +1,188 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_WEBGPUSTRINGS_H
#define TNT_FILAMENT_BACKEND_WEBGPUSTRINGS_H
#include "WebGPUConstants.h"
#include <backend/DriverEnums.h>
#include <utils/Panic.h>
#include <utils/ostream.h>
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
#include <dawn/webgpu_cpp_print.h>
#endif
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
#include <sstream>
#endif
#include <string_view>
/**
* Reusable set of convenience functions for strings -- generally string views, literals, &
* streaming -- used in the WebGPU backend
*/
namespace filament::backend {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
/**
* Convenience template to print WGPU types (which don't support Filament's utils::io::ostream)
* @tparam WebGPUPrintable e.g. wgpu::FeatureName type
* @param out stream to print to
* @param printable the instance to print
* @return the stream printed to
*
* Example usage (because including the template operator override itself does not compile):
* #if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
* utils::io::ostream& operator<<(utils::io::ostream& out,
* const wgpu::FeatureName featureName) noexcept {
* return streamInsertWebGPUPrintable(out, featureName);
* }
* #endif
*/
template<typename WebGPUPrintable>
utils::io::ostream& streamInsertWebGPUPrintable(utils::io::ostream& out,
const WebGPUPrintable printable) {
std::stringstream printableStream;
printableStream << printable;
out << printableStream.str();
return out;
}
#endif
[[nodiscard]] constexpr std::string_view errorTypeToString(const wgpu::ErrorType errorType) {
switch (errorType) {
case wgpu::ErrorType::NoError: return "NO_ERROR";
case wgpu::ErrorType::Validation: return "VALIDATION";
case wgpu::ErrorType::OutOfMemory: return "OUT_OF_MEMORY";
case wgpu::ErrorType::Internal: return "INTERNAL";
case wgpu::ErrorType::Unknown: return "UNKNOWN";
}
}
[[nodiscard]] constexpr std::string_view powerPreferenceToString(
const wgpu::PowerPreference powerPreference) {
switch (powerPreference) {
case wgpu::PowerPreference::Undefined: return "UNDEFINED";
case wgpu::PowerPreference::LowPower: return "LOW_POWER";
case wgpu::PowerPreference::HighPerformance: return "HIGH_PERFORMANCE";
}
}
[[nodiscard]] static inline std::string_view powerPreferenceToString(
const wgpu::DawnAdapterPropertiesPowerPreference powerPreference) {
return powerPreferenceToString(powerPreference.powerPreference);
}
[[nodiscard]] constexpr std::string_view backendTypeToString(const wgpu::BackendType backendType) {
switch (backendType) {
case wgpu::BackendType::Undefined: return "UNDEFINED";
case wgpu::BackendType::Null: return "NULL";
case wgpu::BackendType::WebGPU: return "WEBGPU";
case wgpu::BackendType::D3D11: return "D3D11";
case wgpu::BackendType::D3D12: return "D3D12";
case wgpu::BackendType::Metal: return "METAL";
case wgpu::BackendType::Vulkan: return "VULKAN";
case wgpu::BackendType::OpenGL: return "OPENGL";
case wgpu::BackendType::OpenGLES: return "OPENGLES";
}
}
[[nodiscard]] constexpr std::string_view adapterTypeToString(const wgpu::AdapterType adapterType) {
switch (adapterType) {
case wgpu::AdapterType::DiscreteGPU: return "DISCRETE_GPU";
case wgpu::AdapterType::IntegratedGPU: return "INTEGRATED_GPU";
case wgpu::AdapterType::CPU: return "CPU";
case wgpu::AdapterType::Unknown: return "UNKNOWN";
}
}
/**
* Convenience template to print wgpu::RequestAdapterOptions
* @tparam STREAM_TYPE useful namely for streaming to either an utils::io::ostream OR
* utils::details::PanicStream, but also for any kind of stream
* @param out stream to print to
* @param options instance to be printed
* @return stream printed to
*
* Example usage (because including the template operator override itself does not compile):
* template<typename STREAM_TYPE>
* STREAM_TYPE& operator<<(STREAM_TYPE& out, wgpu::RequestAdapterOptions const& options) noexcept {
* return streamInsertRequestAdapterOptions(out, options);
* }
*/
template<typename STREAM_TYPE>
STREAM_TYPE& streamInsertRequestAdapterOptions(STREAM_TYPE& out,
wgpu::RequestAdapterOptions const& options) noexcept {
out << "power preference " << powerPreferenceToString(options.powerPreference)
<< " force fallback adapter " << bool(options.forceFallbackAdapter) << " backend type "
<< backendTypeToString(options.backendType);
return out;
}
/**
* Convenience template to print wgpu::AdapterInfo
* @tparam STREAM_TYPE useful namely for streaming to either an utils::io::ostream OR
* utils::details::PanicStream, but also for any kind of stream
* @param out stream to print to
* @param options instance to be printed
* @return stream printed to
*
* Example usage (because including the template operator override itself does not compile):
* template<typename STREAM_TYPE>
* STREAM_TYPE& operator<<(STREAM_TYPE& out, wgpu::AdapterInfo const& info) noexcept {
* return streamInsertRequestAdapterInfo(out, info);
* }
*/
template<class STREAM_TYPE>
STREAM_TYPE& streamInsertRequestAdapterInfo(STREAM_TYPE& out,
wgpu::AdapterInfo const& info) noexcept {
out << "vendor (" << info.vendorID << ") '" << info.vendor
<< "' device (" << info.deviceID << ") '" << info.device
<< "' adapter " << adapterTypeToString(info.adapterType)
<< " backend " << backendTypeToString(info.backendType)
<< " architecture '" << info.architecture
<< "' subgroupMinSize " << info.subgroupMinSize
<< " subgroupMaxSize " << info.subgroupMaxSize;
return out;
}
[[nodiscard]] constexpr std::string_view deviceLostReasonToString(
const wgpu::DeviceLostReason reason) {
switch (reason) {
case wgpu::DeviceLostReason::Unknown: return "UNKNOWN";
case wgpu::DeviceLostReason::Destroyed: return "DESTROYED";
case wgpu::DeviceLostReason::CallbackCancelled: return "CALLBACK_CANCELLED";
case wgpu::DeviceLostReason::FailedCreation: return "FAILED_CREATION";
}
}
[[nodiscard]] constexpr std::string_view filamentShaderStageToString(ShaderStage stage) {
switch (stage) {
case ShaderStage::VERTEX: return "vertex";
case ShaderStage::FRAGMENT: return "fragment";
case ShaderStage::COMPUTE: return "compute";
}
}
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_WEBGPUSTRINGS_H
+34 -40
View File
@@ -17,34 +17,50 @@
#include "webgpu/WebGPUSwapChain.h"
#include "webgpu/WebGPUConstants.h"
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
#include "webgpu/WebGPUStrings.h"
#endif
#include "backend/DriverEnums.h"
#include <utils/Panic.h>
#include <utils/ostream.h>
#include <dawn/webgpu_cpp_print.h>
#include <webgpu/webgpu_cpp.h>
#include <algorithm>
#include <cstdint>
#include <sstream>
namespace {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
utils::io::ostream& operator<<(utils::io::ostream& out, const wgpu::TextureFormat format) noexcept {
return filament::backend::streamInsertWebGPUPrintable(out, format);
}
utils::io::ostream& operator<<(utils::io::ostream& out,
const wgpu::TextureUsage textureUsage) noexcept {
return filament::backend::streamInsertWebGPUPrintable(out, textureUsage);
}
utils::io::ostream& operator<<(utils::io::ostream& out,
const wgpu::PresentMode presentMode) noexcept {
return filament::backend::streamInsertWebGPUPrintable(out, presentMode);
}
utils::io::ostream& operator<<(utils::io::ostream& out,
const wgpu::CompositeAlphaMode alphaMode) noexcept {
return filament::backend::streamInsertWebGPUPrintable(out, alphaMode);
}
void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabilities) {
std::stringstream usagesStream{};
usagesStream << capabilities.usages;
FWGPU_LOGI << "WebGPU surface capabilities:" << utils::io::endl;
FWGPU_LOGI << " surface usages: " << usagesStream.str().data() << utils::io::endl;
FWGPU_LOGI << " surface usages: " << capabilities.usages << utils::io::endl;
FWGPU_LOGI << " surface formats (" << capabilities.formatCount << "):" << utils::io::endl;
if (capabilities.formatCount > 0 && capabilities.formats != nullptr) {
std::for_each(capabilities.formats, capabilities.formats + capabilities.formatCount,
[](wgpu::TextureFormat const format) {
std::stringstream formatStream{};
formatStream << format;
FWGPU_LOGI << " " << formatStream.str().data() << utils::io::endl;
FWGPU_LOGI << " " << format << utils::io::endl;
});
}
FWGPU_LOGI << " surface present modes (" << capabilities.presentModeCount
@@ -53,9 +69,7 @@ void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabiliti
std::for_each(capabilities.presentModes,
capabilities.presentModes + capabilities.presentModeCount,
[](wgpu::PresentMode const presentMode) {
std::stringstream presentModeStream{};
presentModeStream << presentMode;
FWGPU_LOGI << " " << presentModeStream.str().data() << utils::io::endl;
FWGPU_LOGI << " " << presentMode << utils::io::endl;
});
}
FWGPU_LOGI << " surface alpha modes (" << capabilities.alphaModeCount
@@ -64,46 +78,30 @@ void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabiliti
std::for_each(capabilities.alphaModes,
capabilities.alphaModes + capabilities.alphaModeCount,
[](wgpu::CompositeAlphaMode const alphaMode) {
std::stringstream alphaModeStream{};
alphaModeStream << alphaMode;
FWGPU_LOGI << " " << alphaModeStream.str().data() << utils::io::endl;
FWGPU_LOGI << " " << alphaMode << utils::io::endl;
});
}
}
#endif
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
wgpu::TextureFormat depthFormat) {
std::stringstream formatStream{};
formatStream << config.format;
std::stringstream usageStream{};
usageStream << config.usage;
std::stringstream alphaModeStream{};
alphaModeStream << config.alphaMode;
std::stringstream presentModeStream{};
presentModeStream << config.presentMode;
std::stringstream depthFormatStream;
depthFormatStream << depthFormat;
FWGPU_LOGI << "WebGPU surface configuration:" << utils::io::endl;
FWGPU_LOGI << " surface format: " << formatStream.str() << utils::io::endl;
FWGPU_LOGI << " surface usage: " << usageStream.str() << utils::io::endl;
FWGPU_LOGI << " surface format: " << config.format << utils::io::endl;
FWGPU_LOGI << " surface usage: " << config.usage << utils::io::endl;
FWGPU_LOGI << " surface view formats (" << config.viewFormatCount << "):" << utils::io::endl;
if (config.viewFormatCount > 0 && config.viewFormats != nullptr) {
std::for_each(config.viewFormats, config.viewFormats + config.viewFormatCount,
[](wgpu::TextureFormat const viewFormat) {
std::stringstream viewFormatStream{};
viewFormatStream << viewFormat;
FWGPU_LOGI << " " << viewFormatStream.str().data() << utils::io::endl;
FWGPU_LOGI << " " << viewFormat << utils::io::endl;
});
}
FWGPU_LOGI << " surface alpha mode: " << alphaModeStream.str() << utils::io::endl;
FWGPU_LOGI << " surface alpha mode: " << config.alphaMode << utils::io::endl;
FWGPU_LOGI << " surface width: " << config.width << utils::io::endl;
FWGPU_LOGI << " surface height: " << config.height << utils::io::endl;
FWGPU_LOGI << " surface present mode: " << presentModeStream.str() << utils::io::endl;
FWGPU_LOGI << "WebGPU selected depth format: " << depthFormatStream.str() << utils::io::endl;
FWGPU_LOGI << " surface present mode: " << config.presentMode << utils::io::endl;
FWGPU_LOGI << "WebGPU selected depth format: " << depthFormat << utils::io::endl;
}
#endif
#endif// FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
[[nodiscard]] constexpr wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
wgpu::TextureFormat const* availableFormats, bool useSRGBColorSpace) {
@@ -320,14 +318,11 @@ wgpu::TextureView WebGPUSwapChain::getCurrentSurfaceTextureView(
if (surfaceTexture.status != wgpu::SurfaceGetCurrentTextureStatus::SuccessOptimal) {
return nullptr;
}
mCurrentSurfaceTexture = nullptr;
mCurrentSurfaceTexture = surfaceTexture.texture;
// Create a view for this surface texture
// TODO: review these initiliazations as webgpu pipeline gets mature
wgpu::TextureViewDescriptor textureViewDescriptor = {
.label = "surface_texture_view",
.format = mCurrentSurfaceTexture.GetFormat(),
.format = surfaceTexture.texture.GetFormat(),
.dimension = wgpu::TextureViewDimension::e2D,
.baseMipLevel = 0,
.mipLevelCount = 1,
@@ -340,7 +335,6 @@ wgpu::TextureView WebGPUSwapChain::getCurrentSurfaceTextureView(
void WebGPUSwapChain::present() {
assert_invariant(mSurface);
mSurface.Present();
mCurrentSurfaceTexture = nullptr;
}
}// namespace filament::backend
@@ -52,8 +52,6 @@ private:
wgpu::TextureFormat mDepthFormat = wgpu::TextureFormat::Undefined;
wgpu::Texture mDepthTexture = nullptr;
wgpu::TextureView mDepthTextureView = nullptr;
wgpu::Texture mCurrentSurfaceTexture = nullptr; // To hold the texture from getCurrentTexture()
};
} // namespace filament::backend
@@ -18,16 +18,30 @@
#include "webgpu/WebGPUConstants.h"
#include "webgpu/WebGPUDriver.h"
#include "webgpu/WebGPUStrings.h"
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include <utils/Hash.h>
#include <utils/Panic.h>
#include <utils/ostream.h>
#include <dawn/webgpu_cpp_print.h>
#include <webgpu/webgpu_cpp.h>
#include <algorithm>
#include <array>
#include <cstdint>
#include <sstream>
#include <functional>
#include <mutex>
#include <sstream>// for one-time-ish setup string concatenation, namely error messaging
#include <unordered_set>
#include <utility>
#include <vector>
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
#include <string_view>
#include <variant>
#endif
/**
* WebGPU Backend implementation common across platforms or operating systems (at least for now).
@@ -39,6 +53,215 @@ namespace filament::backend {
namespace {
constexpr std::array REQUIRED_FEATURES = { wgpu::FeatureName::TransientAttachments,
/*To make filtering assumptions like we want while waiting for Filament to provide that info,
float 32 needs to be filterable*/
wgpu::FeatureName::Float32Filterable };
constexpr std::array OPTIONAL_FEATURES = {
wgpu::FeatureName::DepthClipControl,
wgpu::FeatureName::Depth32FloatStencil8,
wgpu::FeatureName::CoreFeaturesAndLimits };
enum class LimitToValidate : uint8_t {
begin = 0,// needs to be first for iterating through all possible values in the enum
MAX_BIND_GROUPS,
MAX_BINDINGS_PER_BIND_GROUP,
MAX_SAMPLERS_PER_SHADER_STAGE,
MAX_STORAGE_BUFFERS_PER_SHADER_STAGE,
MAX_VERTEX_BUFFERS,
MAX_VERTEX_ATTRIBUTES,
end// needs to be last for iterating through all possible values in the enum
// (Sentinel value)
};
// Only the attributes identified by the LimitToValidate enum above will actually be validated
// at runtime. Thus, if you add a limit here add the associated enum value in LimitToValidate!
constexpr wgpu::Limits REQUIRED_LIMITS = {
.maxBindGroups = filament::backend::MAX_DESCRIPTOR_SET_COUNT,
.maxBindingsPerBindGroup = filament::backend::MAX_DESCRIPTOR_COUNT,
.maxSamplersPerShaderStage = 16, // TODO should be set to filament::backend::MAX_SAMPLER_COUNT,
.maxStorageBuffersPerShaderStage = filament::backend::MAX_SSBO_COUNT,
.maxVertexBuffers = 8, // TODO should be set to filament::backend::MAX_VERTEX_BUFFER_COUNT,
.maxVertexAttributes = filament::backend::MAX_VERTEX_ATTRIBUTE_COUNT,
};
constexpr void forEachLimitToValidate(std::function<bool(LimitToValidate const)> const& func) {
for (auto limit = static_cast<uint8_t>(LimitToValidate::begin) + 1;
limit < static_cast<uint8_t>(LimitToValidate::end); limit++) {
auto castedLimit = static_cast<LimitToValidate>(limit);
bool castedToValidValue = false;
switch (castedLimit) {
case LimitToValidate::begin:
case LimitToValidate::MAX_BIND_GROUPS:
case LimitToValidate::MAX_BINDINGS_PER_BIND_GROUP:
case LimitToValidate::MAX_SAMPLERS_PER_SHADER_STAGE:
case LimitToValidate::MAX_STORAGE_BUFFERS_PER_SHADER_STAGE:
case LimitToValidate::MAX_VERTEX_BUFFERS:
case LimitToValidate::MAX_VERTEX_ATTRIBUTES:
case LimitToValidate::end:
castedToValidValue = true;
break;
}
assert_invariant(castedToValidValue &&
"LimitToValidate enum values are not sequentially incremented by 1? "
"Compilers normally do this by default; which compiler was used here?");
if (func(castedLimit)) {
break;
}
}
}
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
utils::io::ostream& operator<<(utils::io::ostream& out,
const wgpu::WGSLLanguageFeatureName languageFeatureName) noexcept {
return streamInsertWebGPUPrintable(out, languageFeatureName);
}
utils::io::ostream& operator<<(utils::io::ostream& out,
const wgpu::FeatureName featureName) noexcept {
return streamInsertWebGPUPrintable(out, featureName);
}
#endif
template<typename STREAM_TYPE>
STREAM_TYPE& operator<<(STREAM_TYPE& out, wgpu::RequestAdapterOptions const& options) noexcept {
return streamInsertRequestAdapterOptions(out, options);
}
template<class STREAM_TYPE>
STREAM_TYPE& operator<<(STREAM_TYPE& out, wgpu::AdapterInfo const& info) noexcept {
return streamInsertRequestAdapterInfo(out, info);
}
constexpr bool isDefined(uint32_t limit) { return limit < wgpu::kLimitU32Undefined; }
[[maybe_unused]] constexpr bool isDefined(uint64_t limit) {
return limit < wgpu::kLimitU64Undefined;
}
void assertLimitsAreExpressedInRequirementsStruct() {
forEachLimitToValidate([](LimitToValidate const limit) {
switch (limit) {
case LimitToValidate::MAX_BIND_GROUPS:
static_assert(isDefined(REQUIRED_LIMITS.maxBindGroups));
break;
case LimitToValidate::MAX_BINDINGS_PER_BIND_GROUP:
static_assert(isDefined(REQUIRED_LIMITS.maxBindingsPerBindGroup));
break;
case LimitToValidate::MAX_SAMPLERS_PER_SHADER_STAGE:
static_assert(isDefined(REQUIRED_LIMITS.maxSamplersPerShaderStage));
break;
case LimitToValidate::MAX_STORAGE_BUFFERS_PER_SHADER_STAGE:
static_assert(isDefined(REQUIRED_LIMITS.maxStorageBuffersPerShaderStage));
break;
case LimitToValidate::MAX_VERTEX_BUFFERS:
static_assert(isDefined(REQUIRED_LIMITS.maxVertexBuffers));
break;
case LimitToValidate::MAX_VERTEX_ATTRIBUTES:
static_assert(isDefined(REQUIRED_LIMITS.maxVertexAttributes));
break;
case LimitToValidate::begin:
case LimitToValidate::end:
break;
}
return false; // should not break
});
}
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
[[nodiscard]] std::string_view toString(LimitToValidate limit) {
switch (limit) {
case LimitToValidate::MAX_BIND_GROUPS:
return "MAX_BIND_GROUPS";
case LimitToValidate::MAX_BINDINGS_PER_BIND_GROUP:
return "MAX_BINDINGS_PER_BIND_GROUP";
case LimitToValidate::MAX_SAMPLERS_PER_SHADER_STAGE:
return "MAX_SAMPLERS_PER_SHADER_STAGE";
case LimitToValidate::MAX_STORAGE_BUFFERS_PER_SHADER_STAGE:
return "MAX_STORAGE_BUFFERS_PER_SHADER_STAGE";
case LimitToValidate::MAX_VERTEX_BUFFERS:
return "MAX_VERTEX_BUFFERS";
case LimitToValidate::MAX_VERTEX_ATTRIBUTES:
return "MAX_VERTEX_ATTRIBUTES";
case LimitToValidate::begin:
case LimitToValidate::end:
return "UNKNOWN";
}
}
#endif
[[nodiscard]] uint64_t valueForLimit(wgpu::Limits const& limits, LimitToValidate limit) {
switch (limit) {
case LimitToValidate::MAX_BIND_GROUPS:
return limits.maxBindGroups;
case LimitToValidate::MAX_BINDINGS_PER_BIND_GROUP:
return limits.maxBindingsPerBindGroup;
case LimitToValidate::MAX_SAMPLERS_PER_SHADER_STAGE:
return limits.maxSamplersPerShaderStage;
case LimitToValidate::MAX_STORAGE_BUFFERS_PER_SHADER_STAGE:
return limits.maxStorageBuffersPerShaderStage;
case LimitToValidate::MAX_VERTEX_BUFFERS:
return limits.maxVertexBuffers;
case LimitToValidate::MAX_VERTEX_ATTRIBUTES:
return limits.maxVertexAttributes;
case LimitToValidate::begin:
case LimitToValidate::end:
return 0;
}
}
[[nodiscard]] bool satisfiesLimits(wgpu::Limits const& limits) {
size_t failedLimitCount = 0;
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
std::stringstream failedLimitsStream;
#endif
forEachLimitToValidate([&](LimitToValidate const limit) {
uint64_t supportedValue = valueForLimit(limits, limit);
uint64_t requiredValue = valueForLimit(REQUIRED_LIMITS, limit);
if (supportedValue < requiredValue) {
failedLimitCount++;
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
failedLimitsStream << toString(limit) << " required " << requiredValue << " but found "
<< supportedValue << ". ";
#else
return true;// break early if not printing anything
#endif
}
return false;// don't break, keep going
});
if (failedLimitCount == 0) {
return true;
}
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
FWGPU_LOGI << "Failed to satisfy " << failedLimitCount
<< " limit(s): " << failedLimitsStream.str();
#endif
return false;
}
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printInstanceDetails(wgpu::Instance const& instance) {
wgpu::SupportedWGSLLanguageFeatures supportedWGSLLanguageFeatures{};
if (!instance.GetWGSLLanguageFeatures(&supportedWGSLLanguageFeatures)) {
FWGPU_LOGW << "Failed to get WebGPU instance supported WGSL language features"
<< utils::io::endl;
} else {
FWGPU_LOGI << "WebGPU instance supported WGSL language features ("
<< supportedWGSLLanguageFeatures.featureCount << "):" << utils::io::endl;
if (supportedWGSLLanguageFeatures.featureCount > 0 &&
supportedWGSLLanguageFeatures.features != nullptr) {
std::for_each(supportedWGSLLanguageFeatures.features,
supportedWGSLLanguageFeatures.features +
supportedWGSLLanguageFeatures.featureCount,
[](wgpu::WGSLLanguageFeatureName const featureName) {
FWGPU_LOGI << " " << featureName << utils::io::endl;
});
}
}
}
#endif
//either returns a valid instance or panics
[[nodiscard]] wgpu::Instance createInstance() {
wgpu::DawnTogglesDescriptor dawnTogglesDescriptor{};
@@ -63,119 +286,409 @@ namespace {
}
};
wgpu::Instance instance = wgpu::CreateInstance(&instanceDescriptor);
FILAMENT_CHECK_POSTCONDITION(instance != nullptr) << "Unable to create webgpu instance.";
FILAMENT_CHECK_POSTCONDITION(instance != nullptr) << "Unable to create WebGPU instance.";
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printInstanceDetails(instance);
#endif
return instance;
}
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printLimit(std::string_view name, const std::variant<uint32_t, uint64_t> value) {
FWGPU_LOGI << " " << name.data() << ": ";
bool undefined = true;
if (std::holds_alternative<uint32_t>(value)) {
if (std::get<uint32_t>(value) != WGPU_LIMIT_U32_UNDEFINED) {
undefined = false;
FWGPU_LOGI << std::get<uint32_t>(value);
}
} else if (std::holds_alternative<uint64_t>(value)) {
if (std::get<uint64_t>(value) != WGPU_LIMIT_U64_UNDEFINED) {
undefined = false;
FWGPU_LOGI << std::get<uint64_t>(value);
}
}
if (undefined) {
FWGPU_LOGI << "UNDEFINED";
}
FWGPU_LOGI << utils::io::endl;
}
#endif// FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printLimits(wgpu::Limits const& limits) {
printLimit("maxTextureDimension1D", limits.maxTextureDimension1D);
printLimit("maxTextureDimension2D", limits.maxTextureDimension2D);
printLimit("maxTextureDimension3D", limits.maxTextureDimension3D);
printLimit("maxTextureArrayLayers", limits.maxTextureArrayLayers);
printLimit("maxBindGroups", limits.maxBindGroups);
printLimit("maxBindGroupsPlusVertexBuffers", limits.maxBindGroupsPlusVertexBuffers);
printLimit("maxBindingsPerBindGroup", limits.maxBindingsPerBindGroup);
printLimit("maxDynamicUniformBuffersPerPipelineLayout",
limits.maxDynamicUniformBuffersPerPipelineLayout);
printLimit("maxDynamicStorageBuffersPerPipelineLayout",
limits.maxDynamicStorageBuffersPerPipelineLayout);
printLimit("maxSampledTexturesPerShaderStage", limits.maxSampledTexturesPerShaderStage);
printLimit("maxSamplersPerShaderStage", limits.maxSamplersPerShaderStage);
printLimit("maxStorageBuffersPerShaderStage", limits.maxStorageBuffersPerShaderStage);
printLimit("maxStorageTexturesPerShaderStage", limits.maxStorageTexturesPerShaderStage);
printLimit("maxUniformBuffersPerShaderStage", limits.maxUniformBuffersPerShaderStage);
printLimit("maxUniformBufferBindingSize", limits.maxUniformBufferBindingSize);
printLimit("maxStorageBufferBindingSize", limits.maxStorageBufferBindingSize);
printLimit("minUniformBufferOffsetAlignment", limits.minUniformBufferOffsetAlignment);
printLimit("minStorageBufferOffsetAlignment", limits.minStorageBufferOffsetAlignment);
printLimit("maxVertexBuffers", limits.maxVertexBuffers);
printLimit("maxBufferSize", limits.maxBufferSize);
printLimit("maxVertexAttributes", limits.maxVertexAttributes);
printLimit("maxVertexBufferArrayStride", limits.maxVertexBufferArrayStride);
printLimit("maxInterStageShaderVariables", limits.maxInterStageShaderVariables);
printLimit("maxColorAttachments", limits.maxColorAttachments);
printLimit("maxColorAttachmentBytesPerSample", limits.maxColorAttachmentBytesPerSample);
printLimit("maxComputeWorkgroupStorageSize", limits.maxComputeWorkgroupStorageSize);
printLimit("maxComputeInvocationsPerWorkgroup", limits.maxComputeInvocationsPerWorkgroup);
printLimit("maxComputeWorkgroupSizeX", limits.maxComputeWorkgroupSizeX);
printLimit("maxComputeWorkgroupSizeY", limits.maxComputeWorkgroupSizeY);
printLimit("maxComputeWorkgroupSizeZ", limits.maxComputeWorkgroupSizeZ);
printLimit("maxComputeWorkgroupsPerDimension", limits.maxComputeWorkgroupsPerDimension);
printLimit("maxStorageBuffersInVertexStage", limits.maxStorageBuffersInVertexStage);
printLimit("maxStorageTexturesInVertexStage", limits.maxStorageTexturesInVertexStage);
printLimit("maxStorageBuffersInFragmentStage", limits.maxStorageBuffersInFragmentStage);
printLimit("maxStorageTexturesInFragmentStage", limits.maxStorageTexturesInFragmentStage);
}
#endif
struct AdapterDetails final {
AdapterDetails()
: AdapterDetails(nullptr) {}
explicit AdapterDetails(wgpu::Adapter adapter)
: adapter(std::move(adapter)) {
info.nextInChain = &powerPreference;
}
AdapterDetails(wgpu::AdapterInfo&& info,
wgpu::DawnAdapterPropertiesPowerPreference& powerPreference, wgpu::Adapter&& adapter)
: info(std::move(info)),
powerPreference(powerPreference),
adapter(std::move(adapter)) {}
AdapterDetails& operator=(AdapterDetails&& other) noexcept {
adapter = std::exchange(other.adapter, nullptr);
info = std::exchange(other.info, {});
powerPreference = std::exchange(other.powerPreference, {});
return *this;
}
wgpu::AdapterInfo info{};
wgpu::DawnAdapterPropertiesPowerPreference powerPreference{};
wgpu::Adapter adapter = nullptr;
bool operator==(AdapterDetails const& other) const {
return info.vendorID == other.info.vendorID &&
info.deviceID == other.info.deviceID &&
info.backendType == other.info.backendType &&
info.adapterType == other.info.adapterType &&
powerPreference.powerPreference == other.powerPreference.powerPreference;
}
};
template<class STREAM_TYPE>
STREAM_TYPE& operator<<(STREAM_TYPE& out, AdapterDetails const& details) noexcept {
out << details.info
<< " power preference " << powerPreferenceToString(details.powerPreference);
return out;
}
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printAdapterDetails(AdapterDetails const& details) {
FWGPU_LOGI << "Selected WebGPU adapter info: " << details << utils::io::endl;
wgpu::SupportedFeatures supportedFeatures{};
details.adapter.GetFeatures(&supportedFeatures);
FWGPU_LOGI << "WebGPU adapter supported features (" << supportedFeatures.featureCount
<< "):" << utils::io::endl;
if (supportedFeatures.featureCount > 0 && supportedFeatures.features != nullptr) {
std::for_each(supportedFeatures.features,
supportedFeatures.features + supportedFeatures.featureCount,
[](wgpu::FeatureName const featureName) {
FWGPU_LOGI << " " << featureName << utils::io::endl;
});
}
wgpu::Limits supportedLimits{};
if (!details.adapter.GetLimits(&supportedLimits)) {
FWGPU_LOGW << "Failed to get WebGPU adapter supported limits" << utils::io::endl;
} else {
FWGPU_LOGI << "WebGPU adapter supported limits:" << utils::io::endl;
printLimits(supportedLimits);
}
}
#endif
struct AdapterDetailsHash final {
size_t operator()(AdapterDetails const& details) const {
using utils::hash::combine;
size_t hash = 0;
combine(hash, details.info.vendorID);
combine(hash, details.info.deviceID);
combine(hash, static_cast<uint32_t>(details.info.backendType));
combine(hash, static_cast<uint32_t>(details.info.adapterType));
combine(hash, static_cast<uint32_t>(details.powerPreference.powerPreference));
return hash;
}
};
[[nodiscard]] bool adapterMeetsMinimumRequirements(AdapterDetails const& details) {
// check if the adapter has all required features...
if (!std::all_of(REQUIRED_FEATURES.begin(), REQUIRED_FEATURES.end(),
[&details](auto const& featureName) {
return details.adapter.HasFeature(featureName);
})) {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
FWGPU_LOGI << "WebGPU adapter " << details
<< " does not have the minimum required features." << utils::io::endl;
FWGPU_LOGI << " Missing required feature(s): ";
for (wgpu::FeatureName const& requiredFeature: REQUIRED_FEATURES) {
if (!details.adapter.HasFeature(requiredFeature)) {
FWGPU_LOGI << requiredFeature << " ";
}
}
FWGPU_LOGI << utils::io::endl;
#endif
return false;
}
wgpu::Limits supportedLimits {};
FILAMENT_CHECK_POSTCONDITION(details.adapter.GetLimits(&supportedLimits))
<< "Failed to get limits for WebGPU adapter: " << details;
if (!satisfiesLimits(supportedLimits)) {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
FWGPU_LOGI << " (for WebGPU adapter " << details << ")" << utils::io::endl;
#endif
return false;
}
return true;
}
// trys to get a set of compatible adapters (whether the meet minimum requirements or not).
// It panics if none are found.
[[nodiscard]] std::unordered_set<AdapterDetails, AdapterDetailsHash> requestCompatibleAdapters(
wgpu::Instance const& instance, std::vector<wgpu::RequestAdapterOptions> const& requests) {
// make the series of requests asynchronously, collecting compatible adapter results...
std::unordered_set<AdapterDetails, AdapterDetailsHash> compatibleAdapters;
compatibleAdapters.reserve(requests.size());
std::mutex adaptersMutex;
std::vector<wgpu::Future> futures(requests.size());
for (size_t i = 0; i < requests.size(); i++) {
wgpu::RequestAdapterOptions const& options = requests[i];
futures[i] = instance.RequestAdapter(&options, wgpu::CallbackMode::WaitAnyOnly,
[&options, &compatibleAdapters,
&adaptersMutex](wgpu::RequestAdapterStatus const status,
wgpu::Adapter const& readyAdapter, wgpu::StringView const message) {
FILAMENT_CHECK_POSTCONDITION(
status != wgpu::RequestAdapterStatus::CallbackCancelled)
<< "Failed to request a WebGPU adapter due to the request callback "
"being cancelled? Options: "
<< options << " " << message.data;
FILAMENT_CHECK_POSTCONDITION(status != wgpu::RequestAdapterStatus::Error)
<< "Failed to request a WebGPU adapter due to an error. Options: "
<< options << " Error: " << message.data;
if (status == wgpu::RequestAdapterStatus::Success) {
AdapterDetails details = AdapterDetails(readyAdapter);
FILAMENT_CHECK_POSTCONDITION(readyAdapter.GetInfo(&details.info))
<< "Failed to get info for adapter (options: " << options << ")";
const std::lock_guard<std::mutex> lock(adaptersMutex);
compatibleAdapters.emplace(std::move(details.info), details.powerPreference,
std::move(details.adapter));
return;
}
assert_invariant(status == wgpu::RequestAdapterStatus::Unavailable);
});
}
// wait for all the results to return...
for (size_t i = 0; i < futures.size(); i++) {
wgpu::RequestAdapterOptions const& options = requests[i];
wgpu::Future& future = futures[i];
wgpu::WaitStatus status = instance.WaitAny(future, REQUEST_ADAPTER_TIMEOUT_NANOSECONDS);
FILAMENT_CHECK_POSTCONDITION(status != wgpu::WaitStatus::TimedOut)
<< "Timed out requesting a WebGPU adapter with options " << options;
FILAMENT_CHECK_POSTCONDITION(status != wgpu::WaitStatus::Error)
<< "Failed to request a WebGPU adapter with options " << options
<< " due to an error (as request was made synchronous)";
assert_invariant(status == wgpu::WaitStatus::Success);
}
FILAMENT_CHECK_POSTCONDITION(!compatibleAdapters.empty()) << "No WebGPU adapters found!";
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
FWGPU_LOGI << compatibleAdapters.size() << " WebGPU adapter(s) found:" << utils::io::endl;
for (auto& details: compatibleAdapters) {
FWGPU_LOGI << " WebGPU adapter: " << details << utils::io::endl;
}
#endif
return compatibleAdapters;
}
// selects one preferred adapter or panics if none can be found
wgpu::Adapter selectPreferredAdapter(
std::unordered_set<AdapterDetails, AdapterDetailsHash> const& compatibleAdapters) {
// for each unique adapter...
AdapterDetails const* selectedAdapter = nullptr;
size_t selectedOptionalFeaturesCount = 0;
// choose the most desirable adapter that meets the minimum requirements...
for (AdapterDetails const& details: compatibleAdapters) {
if (!adapterMeetsMinimumRequirements(details)) {
continue;
}
size_t supportedOptionalFeaturesCount = std::count_if(OPTIONAL_FEATURES.begin(),
OPTIONAL_FEATURES.end(), [&details](auto const& featureName) {
return details.adapter.HasFeature(featureName);
});
// select this one if it is the first (perhaps it is the only one anyway)...
if (selectedAdapter == nullptr) {
selectedAdapter = &details;
selectedOptionalFeaturesCount = supportedOptionalFeaturesCount;
continue;
}
// NOTE: we can make this selection logic more dynamic, configurable,
// and robust in the future as needed, such as the app preferring lower power
// (prioritizing battery life) or weighting specific optional features in its decision, etc.
// This is just a good start that works for now.
//
// first, prefer higher performance...
if (details.powerPreference.powerPreference >
selectedAdapter->powerPreference.powerPreference) {
selectedAdapter = &details;
selectedOptionalFeaturesCount = supportedOptionalFeaturesCount;
continue;
}
// second, prefer more optional features supported...
if (supportedOptionalFeaturesCount > selectedOptionalFeaturesCount) {
selectedAdapter = &details;
selectedOptionalFeaturesCount = supportedOptionalFeaturesCount;
continue;
}
// otherwise keep the selected adapter
assert_invariant(selectedAdapter != nullptr);
}
FILAMENT_CHECK_POSTCONDITION(selectedAdapter != nullptr)
<< "Could not find a WebGPU adapter that meets the minimum requirements.";
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printAdapterDetails(*selectedAdapter);
#endif
return selectedAdapter->adapter;
}
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printDeviceDetails(wgpu::Device const& device) {
wgpu::SupportedFeatures supportedFeatures{};
device.GetFeatures(&supportedFeatures);
FWGPU_LOGI << "WebGPU device supported features (" << supportedFeatures.featureCount
<< "):" << utils::io::endl;
if (supportedFeatures.featureCount > 0 && supportedFeatures.features != nullptr) {
std::for_each(supportedFeatures.features,
supportedFeatures.features + supportedFeatures.featureCount,
[](wgpu::FeatureName const featureName) {
FWGPU_LOGI << " " << featureName << utils::io::endl;
});
}
wgpu::Limits supportedLimits{};
if (!device.GetLimits(&supportedLimits)) {
FWGPU_LOGW << "Failed to get WebGPU supported device limits" << utils::io::endl;
} else {
FWGPU_LOGI << "WebGPU device supported limits:" << utils::io::endl;
printLimits(supportedLimits);
}
}
#endif
}// namespace
wgpu::Adapter WebGPUPlatform::requestAdapter(wgpu::Surface const& surface) {
// TODO consider power preference etc. (can be custom preferences passed to the platform or
// based on whether this is a Mobile or Desktop system,
// etc...)
wgpu::RequestAdapterOptions adaptorOptions{ .compatibleSurface = surface };
// note this just gets the first adapter
wgpu::Adapter adapter = nullptr;
wgpu::WaitStatus status = mInstance.WaitAny(
mInstance.RequestAdapter(&adaptorOptions, wgpu::CallbackMode::WaitAnyOnly,
[&adapter](wgpu::RequestAdapterStatus const status,
wgpu::Adapter const& readyAdapter, wgpu::StringView const message) {
// TODO consider more robust error handling
FILAMENT_CHECK_POSTCONDITION(status == wgpu::RequestAdapterStatus::Success)
<< "Unable to request a WebGPU adapter. Status "
<< static_cast<uint32_t>(status)
<< " with message: " << message.data;
adapter = readyAdapter;
}),
UINT16_MAX);// TODO define reasonable timeout (or do this asynchronously)
FILAMENT_CHECK_POSTCONDITION(status == wgpu::WaitStatus::Success)
<< "Non-successful wait status requesting a WebGPU adapter "
<< static_cast<uint32_t>(status);
FILAMENT_CHECK_POSTCONDITION(adapter != nullptr)
<< "Failed to get a WebGPU adapter for the platform";
// TODO consider validating adapter has required features and/or limits
return adapter;
assertLimitsAreExpressedInRequirementsStruct();
std::vector<wgpu::RequestAdapterOptions> requests = getAdapterOptions();
for (auto& request: requests) {
request.compatibleSurface = surface;
}
const std::unordered_set<AdapterDetails, AdapterDetailsHash> compatibleAdapters =
requestCompatibleAdapters(mInstance, requests);
return selectPreferredAdapter(compatibleAdapters);
}
wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
// TODO consider passing limits
constexpr std::array optionalFeatures = { wgpu::FeatureName::DepthClipControl,
wgpu::FeatureName::Depth32FloatStencil8, wgpu::FeatureName::CoreFeaturesAndLimits };
constexpr std::array requiredFeatures = { wgpu::FeatureName::TransientAttachments };
wgpu::SupportedFeatures supportedFeatures;
adapter.GetFeatures(&supportedFeatures);
std::vector<wgpu::FeatureName> enabledFeatures;
enabledFeatures.reserve(requiredFeatures.size() + optionalFeatures.size());
std::set_intersection(supportedFeatures.features,
supportedFeatures.features + supportedFeatures.featureCount, requiredFeatures.begin(),
requiredFeatures.end(), std::back_inserter(enabledFeatures));
if (enabledFeatures.size() != requiredFeatures.size()) {
std::vector<wgpu::FeatureName> missingFeatures;
std::set_difference(requiredFeatures.begin(), requiredFeatures.end(),
supportedFeatures.features,
supportedFeatures.features + supportedFeatures.featureCount,
std::back_inserter(missingFeatures));
std::stringstream missingFeaturesStream{};
for (const auto& entry: missingFeatures) {
missingFeaturesStream << std::to_string(static_cast<uint32_t>(entry)) << " ";
enabledFeatures.reserve(REQUIRED_FEATURES.size() + OPTIONAL_FEATURES.size());
for (auto const& requiredFeature : REQUIRED_FEATURES) {
enabledFeatures.push_back(requiredFeature);
}
for (auto const& optionalFeature : OPTIONAL_FEATURES) {
if (adapter.HasFeature(optionalFeature)) {
enabledFeatures.push_back(optionalFeature);
}
PANIC_POSTCONDITION("Some required features are not available %s/n",
missingFeaturesStream.str().c_str());
}
std::set_intersection(supportedFeatures.features,
supportedFeatures.features + supportedFeatures.featureCount, optionalFeatures.begin(),
optionalFeatures.end(), std::back_inserter(enabledFeatures));
wgpu::DeviceDescriptor deviceDescriptor{};
deviceDescriptor.label = "graphics_device";
deviceDescriptor.defaultQueue.label = "default_queue";
deviceDescriptor.requiredFeatureCount = enabledFeatures.size();
deviceDescriptor.requiredFeatures = enabledFeatures.data();
deviceDescriptor.requiredLimits = &REQUIRED_LIMITS;
deviceDescriptor.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous,
[](wgpu::Device const&, wgpu::DeviceLostReason const& reason,
wgpu::StringView message) {
if (reason == wgpu::DeviceLostReason::Destroyed) {
#if FWGPU_ENABLED(FWGPU_DEBUG_VALIDATION)
FWGPU_LOGD << "WebGPU device lost due to being destroyed (expected)"
<< utils::io::endl;
#endif
return;
}
// TODO try recreating the device instead of just panicking
std::stringstream reasonStream{};
reasonStream << reason;
FILAMENT_CHECK_POSTCONDITION(reason != wgpu::DeviceLostReason::Destroyed)
<< "WebGPU device lost: " << reasonStream.str() << " " << message.data;
<< "WebGPU device lost: " << deviceLostReasonToString(reason) << " "
<< message.data;
});
deviceDescriptor.SetUncapturedErrorCallback(
[](wgpu::Device const&, wgpu::ErrorType errorType, wgpu::StringView message) {
std::stringstream typeStream{};
typeStream << errorType;
FWGPU_LOGE << "WebGPU device error: " << typeStream.str() << " " << message.data
<< utils::io::endl;
FWGPU_LOGE << "WebGPU device error: " << errorTypeToString(errorType) << " "
<< message.data << utils::io::endl;
});
wgpu::Device device = nullptr;
wgpu::WaitStatus status = mInstance.WaitAny(
adapter.RequestDevice(&deviceDescriptor, wgpu::CallbackMode::WaitAnyOnly,
[&device](wgpu::RequestDeviceStatus const status,
wgpu::Device const& readyDevice, wgpu::StringView const message) {
FILAMENT_CHECK_POSTCONDITION(status == wgpu::RequestDeviceStatus::Success)
<< "Unable to request a WebGPU device. Status: "
<< static_cast<uint32_t>(status)
<< " with message: " << message.data;
FILAMENT_CHECK_POSTCONDITION(
status != wgpu::RequestDeviceStatus::CallbackCancelled)
<< "Failed to request a WebGPU device due to the callback being "
"cancelled? "
<< message;
FILAMENT_CHECK_POSTCONDITION(status != wgpu::RequestDeviceStatus::Error)
<< "Failed to request a WebGPU device due to en error: " << message;
assert_invariant(status == wgpu::RequestDeviceStatus::Success);
device = readyDevice;
}),
UINT64_MAX);// TODO define reasonable timeout (or do this asynchronously)
FILAMENT_CHECK_POSTCONDITION(status == wgpu::WaitStatus::Success)
<< "Non-successful wait status requesting a WebGPU device "
<< static_cast<uint32_t>(status);
REQUEST_DEVICE_TIMEOUT_NANOSECONDS);
FILAMENT_CHECK_POSTCONDITION(status != wgpu::WaitStatus::TimedOut)
<< "Failed to request a WebGPU device due to a timeout.";
FILAMENT_CHECK_POSTCONDITION(status != wgpu::WaitStatus::Error)
<< "Failed to request a WebGPU device due to an error.";
assert_invariant(status == wgpu::WaitStatus::Success);
FILAMENT_CHECK_POSTCONDITION(device != nullptr)
<< "Failed to get a WebGPU device for the platform.";
<< "Failed to get a WebGPU device for the platform. null device returned?";
size_t missingFeatures = 0;
std::stringstream featureNamesStream;
for (wgpu::FeatureName const& enabledFeature : enabledFeatures) {
if (!device.HasFeature(enabledFeature)) {
missingFeatures += 1;
featureNamesStream << enabledFeature << " ";
}
}
if (missingFeatures > 0) {
PANIC_POSTCONDITION("WebGPU device is missing %d requested feature(s) even though the "
"adapter should support them: %s\n",
missingFeatures, featureNamesStream.str().data());
}
wgpu::Limits supportedLimits {};
FILAMENT_CHECK_POSTCONDITION(device.GetLimits(&supportedLimits))
<< "Failed to get limits for the device?";
FILAMENT_CHECK_POSTCONDITION(satisfiesLimits(supportedLimits))
<< "WebGPU device failed to statify required limits.";
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printDeviceDetails(device);
#endif
return device;
}
@@ -21,7 +21,9 @@
#include <android/native_window.h>
#include <webgpu/webgpu_cpp.h>
#include <array>
#include <cstdint>
#include <vector>
/**
* Android OS specific implementation aspects of the WebGPU backend
@@ -29,6 +31,30 @@
namespace filament::backend {
std::vector<wgpu::RequestAdapterOptions> WebGPUPlatform::getAdapterOptions() {
constexpr std::array powerPreferences = {
wgpu::PowerPreference::HighPerformance,
wgpu::PowerPreference::LowPower };
constexpr std::array backendTypes = { wgpu::BackendType::Vulkan, wgpu::BackendType::OpenGLES };
constexpr std::array forceFallbackAdapters = { false, true };
constexpr size_t totalCombinations =
powerPreferences.size() * backendTypes.size() * forceFallbackAdapters.size();
std::vector<wgpu::RequestAdapterOptions> requests;
requests.reserve(totalCombinations);
for (auto powerPreference: powerPreferences) {
for (auto backendType: backendTypes) {
for (auto forceFallbackAdapter: forceFallbackAdapters) {
requests.emplace_back(
wgpu::RequestAdapterOptions{
.powerPreference = powerPreference,
.forceFallbackAdapter = forceFallbackAdapter,
.backendType = backendType });
}
}
}
return requests;
}
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
ANativeWindow* window = static_cast<ANativeWindow*>(nativeWindow);
return wgpu::Extent2D{
@@ -21,7 +21,9 @@
#include <webgpu/webgpu_cpp.h>
#include <array>
#include <cstdint>
#include <vector>
// Platform specific includes and defines
#include <Cocoa/Cocoa.h>
@@ -33,6 +35,30 @@
namespace filament::backend {
std::vector<wgpu::RequestAdapterOptions> WebGPUPlatform::getAdapterOptions() {
constexpr std::array powerPreferences = {
wgpu::PowerPreference::HighPerformance,
wgpu::PowerPreference::LowPower };
constexpr std::array backendTypes = { wgpu::BackendType::Metal };
constexpr std::array forceFallbackAdapters = { false, true };
constexpr size_t totalCombinations =
powerPreferences.size() * backendTypes.size() * forceFallbackAdapters.size();
std::vector<wgpu::RequestAdapterOptions> requests;
requests.reserve(totalCombinations);
for (auto powerPreference: powerPreferences) {
for (auto backendType: backendTypes) {
for (auto forceFallbackAdapter: forceFallbackAdapters) {
requests.emplace_back(
wgpu::RequestAdapterOptions{
.powerPreference = powerPreference,
.forceFallbackAdapter = forceFallbackAdapter,
.backendType = backendType });
}
}
}
return requests;
}
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
// Both IOS and MacOS expects CAMetalLayer.
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*) nativeWindow;
@@ -21,8 +21,10 @@
#include <webgpu/webgpu_cpp.h>
#include <array>
#include <cstddef>
#include <cstdint>
#include <vector>
#if defined(__linux__) || defined(__FreeBSD__)
#define LINUX_OR_FREEBSD 1
@@ -79,6 +81,33 @@
namespace filament::backend {
std::vector<wgpu::RequestAdapterOptions> WebGPUPlatform::getAdapterOptions() {
constexpr std::array powerPreferences = {
wgpu::PowerPreference::HighPerformance,
wgpu::PowerPreference::LowPower };
constexpr std::array backendTypes = {
wgpu::BackendType::Vulkan,
wgpu::BackendType::OpenGL,
wgpu::BackendType::OpenGLES };
constexpr std::array forceFallbackAdapters = { false, true };
constexpr size_t totalCombinations =
powerPreferences.size() * backendTypes.size() * forceFallbackAdapters.size();
std::vector<wgpu::RequestAdapterOptions> requests;
requests.reserve(totalCombinations);
for (auto powerPreference: powerPreferences) {
for (auto backendType: backendTypes) {
for (auto forceFallbackAdapter: forceFallbackAdapters) {
requests.emplace_back(
wgpu::RequestAdapterOptions{
.powerPreference = powerPreference,
.forceFallbackAdapter = forceFallbackAdapter,
.backendType = backendType });
}
}
}
return requests;
}
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
auto surfaceExtent = wgpu::Extent2D{};
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
@@ -20,7 +20,9 @@
#include <webgpu/webgpu_cpp.h>
#include <array>
#include <cstdint>
#include <vector>
#include <Windows.h>
@@ -30,6 +32,35 @@
namespace filament::backend {
std::vector<wgpu::RequestAdapterOptions> WebGPUPlatform::getAdapterOptions() {
constexpr std::array powerPreferences = {
wgpu::PowerPreference::HighPerformance,
wgpu::PowerPreference::LowPower };
constexpr std::array backendTypes = {
wgpu::BackendType::Vulkan,
wgpu::BackendType::OpenGL,
wgpu::BackendType::OpenGLES,
wgpu::BackendType::D3D12,
wgpu::BackendType::D3D11 };
constexpr std::array forceFallbackAdapters = { false, true };
constexpr size_t totalCombinations =
powerPreferences.size() * backendTypes.size() * forceFallbackAdapters.size();
std::vector<wgpu::RequestAdapterOptions> requests;
requests.reserve(totalCombinations);
for (auto powerPreference: powerPreferences) {
for (auto backendType: backendTypes) {
for (auto forceFallbackAdapter: forceFallbackAdapters) {
requests.emplace_back(
wgpu::RequestAdapterOptions{
.powerPreference = powerPreference,
.forceFallbackAdapter = forceFallbackAdapter,
.backendType = backendType });
}
}
}
return requests;
}
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
HWND window = static_cast<HWND>(nativeWindow);
RECT windowRect;
+6 -34
View File
@@ -58,6 +58,8 @@ BackendTest::BackendTest() : commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE,
CONFIG_COMMAND_BUFFERS_SIZE, /*mPaused=*/false) {
initializeDriver();
mImageExpectations.emplace(getDriverApi());
NativeView nativeView = getNativeView();
mScreenSize = {nativeView.width, nativeView.height};
}
BackendTest::~BackendTest() {
@@ -138,42 +140,12 @@ filament::backend::RenderPassParams BackendTest::getNoClearRenderPass() {
return RenderPassParams{};
}
void BackendTest::renderTriangle(
PipelineLayout const& pipelineLayout,
Handle<filament::backend::HwRenderTarget> renderTarget,
Handle<filament::backend::HwSwapChain> swapChain,
Handle<filament::backend::HwProgram> program) {
RenderPassParams params = getClearColorRenderPass();
params.viewport.width = 512;
params.viewport.height = 512;
renderTriangle(pipelineLayout, renderTarget, swapChain, program, params);
std::size_t BackendTest::screenWidth() const {
return mScreenSize[0];
}
void BackendTest::renderTriangle(
PipelineLayout const& pipelineLayout,
Handle<HwRenderTarget> renderTarget,
Handle<HwSwapChain> swapChain,
Handle<HwProgram> program,
const RenderPassParams& params) {
auto& api = getDriverApi();
TrianglePrimitive triangle(api);
api.makeCurrent(swapChain, swapChain);
api.beginRenderPass(renderTarget, params);
PipelineState state;
state.program = program;
state.pipelineLayout = pipelineLayout;
state.rasterState.colorWrite = true;
state.rasterState.depthWrite = false;
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
std::size_t BackendTest::screenHeight() const {
return mScreenSize[1];
}
bool BackendTest::matchesEnvironment(Backend backend) {
+5 -13
View File
@@ -61,24 +61,14 @@ protected:
filament::math::float4 color = filament::math::float4(0, 1, 1, 1));
static filament::backend::RenderPassParams getNoClearRenderPass();
void renderTriangle(
filament::backend::PipelineLayout const& pipelineLayout,
filament::backend::Handle<filament::backend::HwRenderTarget> renderTarget,
filament::backend::Handle<filament::backend::HwSwapChain> swapChain,
filament::backend::Handle<filament::backend::HwProgram> program);
void renderTriangle(
filament::backend::PipelineLayout const& pipelineLayout,
filament::backend::Handle<filament::backend::HwRenderTarget> renderTarget,
filament::backend::Handle<filament::backend::HwSwapChain> swapChain,
filament::backend::Handle<filament::backend::HwProgram> program,
const filament::backend::RenderPassParams& params);
filament::backend::DriverApi& getDriverApi() { return *commandStream; }
filament::backend::Driver& getDriver() { return *driver; }
ImageExpectations& getExpectations() { return *mImageExpectations; }
std::size_t screenWidth() const;
std::size_t screenHeight() const;
static bool matchesEnvironment(Backend backend);
static bool matchesEnvironment(OperatingSystem operatingSystem);
private:
@@ -98,6 +88,8 @@ private:
// This isn't truly optional, it just needs to delay construction until after the driver has
// been initialized
std::optional<ImageExpectations> mImageExpectations;
std::array<size_t, 2> mScreenSize;
};
} // namespace test
+7 -3
View File
@@ -322,7 +322,11 @@ TEST_F(BlitTest, ColorResolve) {
{
RenderFrame frame(api);
api.beginRenderPass(srcRenderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(state);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
}
@@ -489,8 +493,8 @@ TEST_F(BlitTest, BlitRegionToSwapChain) {
constexpr int kSrcTexWidth = 1024;
constexpr int kSrcTexHeight = 1024;
constexpr auto kSrcTexFormat = TextureFormat::RGBA8;
constexpr int kDstTexWidth = 512;
constexpr int kDstTexHeight = 512;
const uint32_t kDstTexWidth = screenWidth();
const uint32_t kDstTexHeight = screenHeight();
constexpr int kNumLevels = 3;
// Create a SwapChain and make it current.
+53 -30
View File
@@ -132,7 +132,11 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
}
api.beginRenderPass(defaultRenderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(state);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
triangleIndex++;
@@ -153,12 +157,12 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"All values including alpha are written as 0, see b/417254943");
constexpr int kTexWidth = 512;
constexpr int kTexHeight = 512;
auto& api = getDriverApi();
Cleanup cleanup(api);
const TrianglePrimitive triangle(api);
// Create a platform-specific SwapChain and make it current.
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
@@ -175,10 +179,11 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
shader.bindUniform<SimpleMaterialParams>(api, ubuffer, kBindingConfig);
// Create a render target.
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, kTexWidth, kTexHeight, 1, TextureUsage::COLOR_ATTACHMENT));
auto renderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR0, kTexWidth, kTexHeight, 1, 0, {{ colorTexture }}, {}, {}));
auto colorTexture =
cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1, TextureFormat::RGBA8, 1,
screenWidth(), screenHeight(), 1, TextureUsage::COLOR_ATTACHMENT));
auto renderTarget = cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR0, screenWidth(),
screenHeight(), 1, 0, { { colorTexture } }, {}, {}));
// Upload uniforms for the first triangle.
// Upload the uniform, but with an offset to accommodate the padding in the shader's
@@ -189,34 +194,52 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
});
RenderPassParams params = getClearColorRenderPass();
params.viewport.height = kTexWidth;
params.viewport.width = kTexHeight;
renderTriangle({ { shader.getDescriptorSetLayout() } }, renderTarget, swapChain,
shader.getProgram(), params);
PipelineState state = getColorWritePipelineState();
shader.addProgramToPipelineState(state);
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = triangle.getVertexBufferInfo();
// Upload uniforms for the second triangle. To test partial buffer updates, we'll only update
// color.b, color.a, scaleMinusOne, offset.x, and offset.y.
const UniformBindingConfig partialBindingConfig = {
.dataSize = sizeof(float) * 8,
.bufferSize = sizeof(SimpleMaterialParams) + 64,
.byteOffset = 64 + offsetof(SimpleMaterialParams, color.b)
};
shader.uploadUniform(api, ubuffer, partialBindingConfig,
std::array<float, 8>{
1.0f, 1.0f, // color.b, color.a
0.0f, 0.0f, 0.0f, 0.0f, // scale
0.5f, 0.5f // offset.x, offset.y
});
{
RenderFrame frame(api);
params.flags.clear = TargetBufferFlags::NONE;
params.flags.discardStart = TargetBufferFlags::NONE;
renderTriangle({{ shader.getDescriptorSetLayout() }},
renderTarget, swapChain, shader.getProgram(), params);
RenderPassParams clearParams = getClearColorRenderPass();
clearParams.viewport.height = screenWidth();
clearParams.viewport.width = screenHeight();
api.beginRenderPass(renderTarget, clearParams);
api.bindPipeline(state);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
// Upload uniforms for the second triangle. To test partial buffer updates, we'll only
// update color.b, color.a, scaleMinusOne, offset.x, and offset.y.
const UniformBindingConfig partialBindingConfig = {
.dataSize = sizeof(float) * 8,
.bufferSize = sizeof(SimpleMaterialParams) + 64,
.byteOffset = 64 + offsetof(SimpleMaterialParams, color.b) };
shader.uploadUniform(api, ubuffer, partialBindingConfig,
std::array<float, 8>{
1.0f, 1.0f, // color.b, color.a
0.0f, 0.0f, 0.0f, 0.0f, // scale
0.5f, 0.5f // offset.x, offset.y
});
RenderPassParams noClearParams = getNoClearRenderPass();
noClearParams.viewport.height = screenWidth();
noClearParams.viewport.width = screenHeight();
api.beginRenderPass(renderTarget, noClearParams);
api.bindPipeline(state);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
}
EXPECT_IMAGE(renderTarget, getExpectations(),
ScreenshotParams(kTexWidth, kTexHeight, "BufferObjectUpdateWithOffset", 2320747245));
ScreenshotParams(screenWidth(), screenHeight(), "BufferObjectUpdateWithOffset",
2320747245));
api.flush();
api.commit(swapChain);
+10 -2
View File
@@ -197,7 +197,11 @@ TEST_F(BackendTest, FeedbackLoops) {
});
api.beginRenderPass(renderTargets[targetLevel], params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(state);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
}
@@ -230,7 +234,11 @@ TEST_F(BackendTest, FeedbackLoops) {
});
api.beginRenderPass(renderTargets[targetLevel], params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(state);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
}
+127 -62
View File
@@ -25,6 +25,7 @@
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include "TrianglePrimitive.h"
#include "private/filament/SamplerInterfaceBlock.h"
#include <vector>
@@ -50,7 +51,7 @@ layout(location = 0) out vec4 fragColor;
layout(location = 0, set = 0) uniform {samplerType} test_tex;
void main() {
vec2 fbsize = vec2(512);
vec2 fbsize = vec2({texSize});
vec2 uv = gl_FragCoord.xy / fbsize;
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT) || defined(TARGET_WEBGPU_ENVIRONMENT)
uv.y = 1.0 - uv.y;
@@ -71,7 +72,7 @@ float getLayer(in sampler3D s) { return 2.5f / 4.0f; }
float getLayer(in sampler2DArray s) { return 2.0f; }
void main() {
vec2 fbsize = vec2(512);
vec2 fbsize = vec2({texSize});
vec2 uv = gl_FragCoord.xy / fbsize;
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT) || defined(TARGET_WEBGPU_ENVIRONMENT)
uv.y = 1.0 - uv.y;
@@ -89,7 +90,7 @@ layout(location = 0) out vec4 fragColor;
layout(location = 0, set = 0) uniform sampler2D test_tex;
void main() {
vec2 fbsize = vec2(512);
vec2 fbsize = vec2({texSize});
vec2 uv = gl_FragCoord.xy / fbsize;
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT) || defined(TARGET_WEBGPU_ENVIRONMENT)
uv.y = 1.0 - uv.y;
@@ -105,17 +106,6 @@ namespace test {
template<typename componentType> inline componentType getMaxValue();
class LoadImageTest : public BackendTest {
public:
LoadImageTest() {
mVertexShader = SharedShaders::getVertexShaderText(VertexShaderType::Noop,
ShaderUniformType::None);
}
std::string mVertexShader;
};
inline std::string stringReplace(const std::string& find, const std::string& replace,
std::string source) {
std::string::size_type pos = source.find(find);
@@ -211,6 +201,35 @@ static SamplerFormat getSamplerFormat(TextureFormat textureFormat) {
}
}
class LoadImageTest : public BackendTest {
public:
LoadImageTest() : mTriangle(getDriverApi()) {
mVertexShader = SharedShaders::getVertexShaderText(VertexShaderType::Noop,
ShaderUniformType::None);
// Checkerboard utils require square textures
EXPECT_THAT(screenWidth(), testing::Eq(screenWidth()));
}
uint32_t kTexSize = screenWidth();
uint32_t kHalfTexSize = kTexSize / 2;
uint32_t kDoubleTexSize = kTexSize * 2;
std::string mVertexShader;
TrianglePrimitive mTriangle;
std::string getFormattedFragment(const std::string& fragment, TextureFormat textureFormat) {
std::string withSampler =
stringReplace("{samplerType}", getSamplerTypeName(textureFormat), fragment);
return stringReplace("{texSize}", std::to_string(kTexSize), withSampler);
}
std::string getFormattedFragment(const std::string& fragment, SamplerType samplerType) {
std::string withSampler =
stringReplace("{samplerType}", getSamplerTypeName(samplerType), fragment);
return stringReplace("{texSize}", std::to_string(kTexSize), withSampler);
}
};
TEST_F(LoadImageTest, UpdateImage2D) {
FAIL_IF(Backend::VULKAN, "Multiple test cases crash, see b/417481434");
@@ -303,8 +322,7 @@ TEST_F(LoadImageTest, UpdateImage2D) {
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
SamplerType::SAMPLER_2D, getSamplerFormat(t.textureFormat), Precision::HIGH, false };
std::string const fragment = stringReplace("{samplerType}",
getSamplerTypeName(t.textureFormat), fragmentTemplate);
std::string const fragment = getFormattedFragment(fragmentTemplate, t.textureFormat);
Shader shader(api, cleanup, ShaderConfig{
.vertexShader = mVertexShader,
.fragmentShader= fragment,
@@ -314,21 +332,26 @@ TEST_F(LoadImageTest, UpdateImage2D) {
// Create a Texture.
auto usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
Handle<HwTexture> const texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
t.textureFormat, 1, 512, 512, 1u, usage));
t.textureFormat, 1, kTexSize, kTexSize, 1u, usage));
// Upload some pixel data.
if (t.uploadSubregions) {
api.update3DImage(texture, 0, 0, 0, 0, 256, 256, 1,
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 256, t.bufferPadding));
api.update3DImage(texture, 0, 256, 0, 0, 256, 256, 1,
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 256, t.bufferPadding));
api.update3DImage(texture, 0, 0, 256, 0, 256, 256, 1,
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 256, t.bufferPadding));
api.update3DImage(texture, 0, 256, 256, 0, 256, 256, 1,
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 256, t.bufferPadding));
api.update3DImage(texture, 0, 0, 0, 0, kHalfTexSize, kHalfTexSize, 1,
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, kHalfTexSize,
t.bufferPadding));
api.update3DImage(texture, 0, kHalfTexSize, 0, 0, kHalfTexSize, kHalfTexSize, 1,
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, kHalfTexSize,
t.bufferPadding));
api.update3DImage(texture, 0, 0, kHalfTexSize, 0, kHalfTexSize, kHalfTexSize, 1,
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, kHalfTexSize,
t.bufferPadding));
api.update3DImage(texture, 0, kHalfTexSize, kHalfTexSize, 0, kHalfTexSize, kHalfTexSize,
1,
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, kHalfTexSize,
t.bufferPadding));
} else {
api.update3DImage(texture, 0, 0, 0, 0, 512, 512, 1,
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 512, t.bufferPadding));
api.update3DImage(texture, 0, 0, 0, 0, kTexSize, kTexSize, 1,
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, kTexSize, t.bufferPadding));
}
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
@@ -338,11 +361,21 @@ TEST_F(LoadImageTest, UpdateImage2D) {
api.bindDescriptorSet(descriptorSet, 0, {});
renderTriangle({{ shader.getDescriptorSetLayout() }},
defaultRenderTarget, swapChain, shader.getProgram());
RenderPassParams params = getClearColorRenderPass();
params.viewport.width = kTexSize;
params.viewport.height = kTexSize;
PipelineState state = getColorWritePipelineState();
shader.addProgramToPipelineState(state);
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = mTriangle.getVertexBufferInfo();
api.beginRenderPass(defaultRenderTarget, params);
api.bindPipeline(state);
api.bindRenderPrimitive(mTriangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
ScreenshotParams(512, 512, t.name, expectedHash));
ScreenshotParams(kTexSize, kTexSize, t.name, expectedHash));
api.commit(swapChain);
api.endFrame(0);
@@ -368,39 +401,39 @@ TEST_F(LoadImageTest, UpdateImageSRGB) {
// Create a program.
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
SamplerType::SAMPLER_2D, getSamplerFormat(textureFormat), Precision::HIGH, false };
std::string const fragment = stringReplace("{samplerType}",
getSamplerTypeName(textureFormat), fragmentTemplate);
std::string const fragment = getFormattedFragment(fragmentTemplate, textureFormat);
Shader shader(api, cleanup, ShaderConfig{
.vertexShader = mVertexShader, .fragmentShader = fragment, .uniforms = {{
"test_tex", DescriptorType::SAMPLER_2D_FLOAT, samplerInfo
}}});
// Create a texture.
Handle<HwTexture> const texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
textureFormat, 1, 512, 512, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE));
Handle<HwTexture> const texture =
cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1, textureFormat, 1, kTexSize,
kTexSize, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE));
// Create image data.
size_t components; int bpp;
getPixelInfo(pixelFormat, pixelType, components, bpp);
size_t bpl = 512 * 512 * bpp;
size_t bpl = kTexSize * kTexSize * bpp;
size_t bufferSize = bpl;
void* buffer = calloc(1, bufferSize);
PixelBufferDescriptor descriptor(buffer, bufferSize, pixelFormat, pixelType,
1, 0, 0, 512, [](void* buffer, size_t size, void* user) {
1, 0, 0, kTexSize, [](void* buffer, size_t size, void* user) {
free(buffer);
}, nullptr);
// Add a gradient.
uint8_t* pixel = (uint8_t*) buffer;
for (int r = 0; r < 512; r++) {
for (int c = 0; c < 512; c++) {
for (int r = 0; r < kTexSize; r++) {
for (int c = 0; c < kTexSize; c++) {
for (int n = 0; n < components; n++) {
*pixel++ = (c / 512.0f) * 255;
*pixel++ = (c / static_cast<float>(kTexSize)) * 255;
}
}
}
api.update3DImage(texture, 0, 0, 0, 0, 512, 512, 1, std::move(descriptor));
api.update3DImage(texture, 0, 0, 0, 0, kTexSize, kTexSize, 1, std::move(descriptor));
api.beginFrame(0, 0, 0);
@@ -413,11 +446,21 @@ TEST_F(LoadImageTest, UpdateImageSRGB) {
api.bindDescriptorSet(descriptorSet, 0, {});
renderTriangle({{ shader.getDescriptorSetLayout() }},
defaultRenderTarget, swapChain, shader.getProgram());
RenderPassParams params = getClearColorRenderPass();
params.viewport.width = kTexSize;
params.viewport.height = kTexSize;
PipelineState state = getColorWritePipelineState();
shader.addProgramToPipelineState(state);
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = mTriangle.getVertexBufferInfo();
api.beginRenderPass(defaultRenderTarget, params);
api.bindPipeline(state);
api.bindRenderPrimitive(mTriangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
ScreenshotParams(512, 512, "UpdateImageSRGB", 3300305265));
ScreenshotParams(kTexSize, kTexSize, "UpdateImageSRGB", 3300305265));
api.commit(swapChain);
api.endFrame(0);
@@ -442,8 +485,7 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
// Create a program.
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
SamplerType::SAMPLER_2D, getSamplerFormat(textureFormat), Precision::HIGH, false };
std::string const fragment = stringReplace("{samplerType}",
getSamplerTypeName(textureFormat), fragmentUpdateImageMip);
std::string const fragment = getFormattedFragment(fragmentUpdateImageMip, textureFormat);
Shader shader(api, cleanup, ShaderConfig {
.vertexShader = mVertexShader,
.fragmentShader = fragment,
@@ -454,12 +496,13 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
// Base level: 1024
// Level 1: 512 <-- upload data and sample from this level
// Level 2: 256
Handle<HwTexture> texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 3,
textureFormat, 1, 1024, 1024, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE));
Handle<HwTexture> texture = cleanup.add(
api.createTexture(SamplerType::SAMPLER_2D, 3, textureFormat, 1, kDoubleTexSize,
kDoubleTexSize, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE));
// Create image data.
PixelBufferDescriptor descriptor = checkerboardPixelBuffer(pixelFormat, pixelType, 512);
api.update3DImage(texture, /* level*/ 1, 0, 0, 0, 512, 512, 1, std::move(descriptor));
PixelBufferDescriptor descriptor = checkerboardPixelBuffer(pixelFormat, pixelType, kTexSize);
api.update3DImage(texture, /* level*/ 1, 0, 0, 0, kTexSize, kTexSize, 1, std::move(descriptor));
api.beginFrame(0, 0, 0);
@@ -472,11 +515,24 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
api.bindDescriptorSet(descriptorSet, 0, {});
renderTriangle({{ shader.getDescriptorSetLayout() }},
defaultRenderTarget, swapChain, shader.getProgram());
{
RenderFrame frame(api);
RenderPassParams params = getClearColorRenderPass();
params.viewport.width = kTexSize;
params.viewport.height = kTexSize;
PipelineState state = getColorWritePipelineState();
shader.addProgramToPipelineState(state);
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = mTriangle.getVertexBufferInfo();
api.beginRenderPass(defaultRenderTarget, params);
api.bindPipeline(state);
api.bindRenderPrimitive(mTriangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
}
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
ScreenshotParams(512, 512, "UpdateImageMipLevel", 1875922935));
ScreenshotParams(kTexSize, kTexSize, "UpdateImageMipLevel", 1875922935));
api.commit(swapChain);
api.endFrame(0);
@@ -506,8 +562,7 @@ TEST_F(LoadImageTest, UpdateImage3D) {
// Create a program.
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
SamplerType::SAMPLER_2D_ARRAY, getSamplerFormat(textureFormat), Precision::HIGH, false };
std::string fragment = stringReplace("{samplerType}",
getSamplerTypeName(samplerType), fragmentUpdateImage3DTemplate);
std::string fragment = getFormattedFragment(fragmentUpdateImage3DTemplate, samplerType);
Shader shader(api, cleanup, ShaderConfig {
.vertexShader = mVertexShader,
.fragmentShader = fragment,
@@ -516,24 +571,24 @@ TEST_F(LoadImageTest, UpdateImage3D) {
// Create a texture.
Handle<HwTexture> texture = cleanup.add(api.createTexture(samplerType, 1,
textureFormat, 1, 512, 512, 4, usage));
textureFormat, 1, kTexSize, kTexSize, 4, usage));
// Create image data for all 4 layers.
size_t components; int bpp;
getPixelInfo(pixelFormat, pixelType, components, bpp);
size_t bpl = 512 * 512 * bpp;
size_t bpl = kTexSize * kTexSize * bpp;
size_t bufferSize = bpl * 4;
void* buffer = calloc(1, bufferSize);
PixelBufferDescriptor descriptor(buffer, bufferSize, pixelFormat, pixelType,
1, 0, 0, 512, [](void* buffer, size_t size, void* user) {
1, 0, 0, kTexSize, [](void* buffer, size_t size, void* user) {
free(buffer);
}, nullptr);
// Only add checkerboard data to the 3rd layer, which we'll sample from.
uint8_t* thirdLayer = (uint8_t*) buffer + (bpl * 2);
fillCheckerboard<float>(thirdLayer, 512, 512, components, 1.0f);
fillCheckerboard<float>(thirdLayer, kTexSize, kTexSize, components, 1.0f);
api.update3DImage(texture, 0, 0, 0, 0, 512, 512, 4, std::move(descriptor));
api.update3DImage(texture, 0, 0, 0, 0, kTexSize, kTexSize, 4, std::move(descriptor));
{
RenderFrame frame(api);
@@ -546,11 +601,21 @@ TEST_F(LoadImageTest, UpdateImage3D) {
api.bindDescriptorSet(descriptorSet, 0, {});
renderTriangle({ { shader.getDescriptorSetLayout() } },
defaultRenderTarget, swapChain, shader.getProgram());
RenderPassParams params = getClearColorRenderPass();
params.viewport.width = kTexSize;
params.viewport.height = kTexSize;
PipelineState state = getColorWritePipelineState();
shader.addProgramToPipelineState(state);
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = mTriangle.getVertexBufferInfo();
api.beginRenderPass(defaultRenderTarget, params);
api.bindPipeline(state);
api.bindRenderPrimitive(mTriangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
ScreenshotParams(512, 512, "UpdateImage3D", 1875922935));
ScreenshotParams(kTexSize, kTexSize, "UpdateImage3D", 1875922935));
}
api.stopCapture();
+18 -14
View File
@@ -75,32 +75,32 @@ TEST_F(BackendTest, MRT) {
1, // levels
TextureFormat::RGBA8, // format
1, // samples
512, // width
512, // height
screenWidth(), // width
screenHeight(), // height
1, // depth
usage)); // usage
usage)); // usage
Handle<HwTexture> textureB = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, // target
1, // levels
TextureFormat::RGBA8, // format
1, // samples
512, // width
512, // height
screenWidth(), // width
screenHeight(), // height
1, // depth
usage)); // usage
usage)); // usage
// Create a RenderTarget with two attachments.
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::COLOR1,
// The width and height must match the width and height of the respective mip
// level (at least for OpenGL).
512, // width
512, // height
1, // samples
0, // layerCount
{{textureA },{textureB }}, // color
{}, // depth
{})); // stencil
screenWidth(), // width
screenHeight(), // height
1, // samples
0, // layerCount
{{textureA },{textureB }}, // color
{}, // depth
{})); // stencil
PipelineState state = getColorWritePipelineState();
shader.addProgramToPipelineState(state);
@@ -115,7 +115,11 @@ TEST_F(BackendTest, MRT) {
// Draw a triangle.
api.beginRenderPass(renderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(state);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
api.flush();
+15 -3
View File
@@ -145,7 +145,11 @@ TEST_F(BackendTest, TextureViewLod) {
params.viewport = getFullViewport();
api.beginRenderPass(renderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(state);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
}
@@ -170,7 +174,11 @@ TEST_F(BackendTest, TextureViewLod) {
// previous pass.
api.beginRenderPass(defaultRenderTarget, params);
api.scissor(params.viewport);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(state);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
// Adjust the base mip to 2.
@@ -195,7 +203,11 @@ TEST_F(BackendTest, TextureViewLod) {
params.flags.discardStart = TargetBufferFlags::NONE;
api.beginRenderPass(defaultRenderTarget, params);
api.scissor(params.viewport);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(state);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
api.commit(swapChain);
@@ -92,7 +92,11 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
// Render a triangle.
api.beginRenderPass(defaultRenderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(state);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
api.flush();
+29 -11
View File
@@ -71,6 +71,11 @@ namespace test {
class ReadPixelsTest : public BackendTest {
public:
ReadPixelsTest() {
// Expect a square screen
EXPECT_THAT(screenWidth(), ::testing::Eq(screenHeight()));
}
bool readPixelsFinished = false;
};
@@ -82,9 +87,14 @@ TEST_F(ReadPixelsTest, ReadPixels) {
// deterministic results. Take this test with a grain of salt, however, as other platform / GPU
// combinations may vary ever-so-slightly, which would cause this test to fail.
const size_t renderTargetBaseSize = 512;
const size_t renderTargetBaseSize = screenWidth();
struct TestCase {
explicit TestCase(size_t renderTargetBaseSize)
: renderTargetBaseSize(renderTargetBaseSize) {}
size_t renderTargetBaseSize;
const char* testName = "readPixels_normal";
// The murmur3 hash of the read pixel buffer result, used to determine success.
@@ -163,10 +173,10 @@ TEST_F(ReadPixelsTest, ReadPixels) {
// The normative read pixels test case. Render a white triangle over a blue background and read
// the full viewport into a pixel buffer.
TestCase const t0;
TestCase const t0(renderTargetBaseSize);
// Check that a subregion of the render target can be read into a pixel buffer.
TestCase t2;
TestCase t2(renderTargetBaseSize);
t2.testName = "readPixels_subregion";
t2.readRect.x = 90;
t2.readRect.y = 403;
@@ -176,7 +186,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
t2.hash = 0xcba7675a;
// Check that readPixels works when rendering into and reading from a mip level.
TestCase t3;
TestCase t3(renderTargetBaseSize);
t3.testName = "readPixels_mip";
t3.mipLevels = 4;
t3.mipLevel = 2;
@@ -186,7 +196,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
t3.hash = 0xe6fa6c55;
// Check that readPixels can return pixels in floating point RGBA format.
TestCase t4;
TestCase t4(renderTargetBaseSize);
t4.testName = "readPixels_float";
t4.format = PixelDataFormat::RGBA;
t4.type = PixelDataType::FLOAT;
@@ -194,7 +204,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
// Check that readPixels can read a region of the render target into a subregion of a large
// buffer.
TestCase t5;
TestCase t5(renderTargetBaseSize);
t5.testName = "readPixels_subbuffer";
t5.readRect.x = 90;
t5.readRect.y = 403;
@@ -206,7 +216,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
t5.hash = 0xbaefdb54;
// Check that readPixels works with integer formats.
TestCase t6;
TestCase t6(renderTargetBaseSize);
t6.testName = "readPixels_UINT";
t6.format = PixelDataFormat::R_INTEGER;
t6.type = PixelDataType::UINT;
@@ -214,7 +224,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
t6.hash = 0x9d91227;
// Check that readPixels works with half formats.
TestCase t7;
TestCase t7(renderTargetBaseSize);
t7.testName = "readPixels_half";
t7.format = PixelDataFormat::RG;
t7.type = PixelDataType::HALF;
@@ -223,7 +233,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
// Check that readPixels works when rendering into the SwapChain.
// This requires that the test runner's native window size is 512x512.
TestCase t8;
TestCase t8(renderTargetBaseSize);
t8.testName = "readPixels_swapchain";
t8.useDefaultRT = true;
@@ -295,7 +305,11 @@ TEST_F(ReadPixelsTest, ReadPixels) {
} else {
floatShader.addProgramToPipelineState(state);
}
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(state);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
@@ -413,7 +427,11 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
// Render some content, just so we don't read back uninitialized data.
api.beginRenderPass(renderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(state);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
PixelBufferDescriptor descriptor(buffer, renderTargetSize * renderTargetSize * 4,
@@ -90,7 +90,11 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
// Render a triangle.
api.beginRenderPass(defaultRenderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(state);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
api.flush();
@@ -185,14 +189,18 @@ TEST_F(BackendTest, RenderExternalImage) {
// Render a triangle.
api.beginRenderPass(defaultRenderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
state.primitiveType = PrimitiveType::TRIANGLES;
state.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(state);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
api.flush();
api.commit(swapChain);
api.endFrame(0);
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
ScreenshotParams(512, 512, "RenderExternalImage", 1206264951));
ScreenshotParams(screenWidth(), screenHeight(), "RenderExternalImage", 1206264951));
api.stopCapture(0);
api.finish();
+15 -3
View File
@@ -125,7 +125,11 @@ TEST_F(BackendTest, ScissorViewportRegion) {
api.beginRenderPass(srcRenderTarget, params);
api.scissor(scissor);
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
ps.primitiveType = PrimitiveType::TRIANGLES;
ps.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(ps);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
EXPECT_IMAGE(fullRenderTarget, getExpectations(),
@@ -201,7 +205,11 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
api.beginRenderPass(renderTarget, params);
api.scissor(scissor);
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
ps.primitiveType = PrimitiveType::TRIANGLES;
ps.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(ps);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
params.viewport = topLeftViewport;
@@ -209,7 +217,11 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
params.flags.discardStart = TargetBufferFlags::NONE;
api.beginRenderPass(renderTarget, params);
api.scissor(scissor);
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
ps.primitiveType = PrimitiveType::TRIANGLES;
ps.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(ps);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
EXPECT_IMAGE(renderTarget, getExpectations(),
+50 -27
View File
@@ -74,7 +74,8 @@ public:
// Render a small triangle only to the stencil buffer, increasing the stencil buffer to 1.
RenderPassParams params = {};
params.flags.clear = TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL;
params.viewport = { 0, 0, 512, 512 };
params.viewport = { 0, 0, static_cast<uint32_t>(screenWidth()),
static_cast<uint32_t>(screenHeight()) };
params.clearColor = math::float4(0.0f, 0.0f, 1.0f, 1.0f);
params.clearStencil = 0u;
params.flags.discardStart = TargetBufferFlags::ALL;
@@ -91,7 +92,11 @@ public:
api.beginFrame(0, 0, 0);
api.beginRenderPass(renderTarget, params);
api.draw(ps, smallTriangle.getRenderPrimitive(), 0, 3, 1);
ps.primitiveType = PrimitiveType::TRIANGLES;
ps.vertexBufferInfo = smallTriangle.getVertexBufferInfo();
api.bindPipeline(ps);
api.bindRenderPrimitive(smallTriangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
// Step 2: Render a larger triangle with the stencil test enabled.
@@ -104,7 +109,11 @@ public:
ps.stencilState.front.ref = 0u;
api.beginRenderPass(renderTarget, params);
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
ps.primitiveType = PrimitiveType::TRIANGLES;
ps.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(ps);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
api.commit(mSwapChain);
@@ -123,18 +132,20 @@ TEST_F(BasicStencilBufferTest, StencilBuffer) {
Cleanup cleanup(api);
// Create two textures: a color and a stencil, and an associated RenderTarget.
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
auto stencilTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT));
auto renderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
{{ colorTexture }}, {}, {{ stencilTexture }}));
auto colorTexture =
cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1, TextureFormat::RGBA8, 1,
screenWidth(), screenHeight(), 1, TextureUsage::COLOR_ATTACHMENT));
auto stencilTexture =
cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1, TextureFormat::STENCIL8, 1,
screenWidth(), screenHeight(), 1, TextureUsage::STENCIL_ATTACHMENT));
auto renderTarget = cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR0 |
TargetBufferFlags::STENCIL,
screenWidth(), screenHeight(), 1, 0, { { colorTexture } }, {}, { { stencilTexture } }));
RunTest(renderTarget);
EXPECT_IMAGE(renderTarget, getExpectations(),
ScreenshotParams(512, 512, "StencilBuffer", 0x3B1AEF0F));
ScreenshotParams(screenWidth(), screenHeight(), "StencilBuffer", 0x3B1AEF0F));
flushAndWait();
getDriver().purge();
@@ -147,19 +158,20 @@ TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
Cleanup cleanup(api);
// Create two textures: a color and a stencil, and an associated RenderTarget.
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
auto colorTexture =
cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1, TextureFormat::RGBA8, 1,
screenWidth(), screenHeight(), 1, TextureUsage::COLOR_ATTACHMENT));
auto depthStencilTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::DEPTH24_STENCIL8, 1, 512, 512, 1,
TextureFormat::DEPTH24_STENCIL8, 1, screenWidth(), screenHeight(), 1,
TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
auto renderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
{{ colorTexture }}, { depthStencilTexture }, {{ depthStencilTexture }}));
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, screenWidth(), screenHeight(),
1, 0, { { colorTexture } }, { depthStencilTexture }, { { depthStencilTexture } }));
RunTest(renderTarget);
EXPECT_IMAGE(renderTarget, getExpectations(),
ScreenshotParams(512, 512, "DepthAndStencilBuffer", 0x3B1AEF0F));
ScreenshotParams(screenWidth(), screenHeight(), "DepthAndStencilBuffer", 0x3B1AEF0F));
flushAndWait();
getDriver().purge();
@@ -178,17 +190,18 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
// Pass 1: Render a triangle into (an auto-created) MSAA color buffer using the stencil test.
// Performs an auto-resolve on the color.
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::RGBA8, 1, 512, 512, 1,
TextureFormat::RGBA8, 1, screenWidth(), screenHeight(), 1,
TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE));
auto depthStencilTextureMSAA = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
TextureFormat::DEPTH24_STENCIL8, 4, 512, 512, 1,
TextureFormat::DEPTH24_STENCIL8, 4, screenWidth(), screenHeight(), 1,
TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
auto renderTarget0 = cleanup.add(api.createRenderTarget(
TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
TargetBufferFlags::DEPTH_AND_STENCIL, screenWidth(), screenHeight(), 4, 0,
{{}}, { depthStencilTextureMSAA }, { depthStencilTextureMSAA }));
auto renderTarget1 = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
{{ colorTexture }}, { depthStencilTextureMSAA }, { depthStencilTextureMSAA }));
auto renderTarget1 = cleanup.add(
api.createRenderTarget(TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH_AND_STENCIL,
screenWidth(), screenHeight(), 4, 0, { { colorTexture } },
{ depthStencilTextureMSAA }, { depthStencilTextureMSAA }));
api.startCapture(0);
@@ -206,7 +219,8 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
// Render a small triangle only to the stencil buffer, increasing the stencil buffer to 1.
RenderPassParams params = {};
params.flags.clear = TargetBufferFlags::STENCIL;
params.viewport = { 0, 0, 512, 512 };
params.viewport = { 0, 0, static_cast<uint32_t>(screenWidth()),
static_cast<uint32_t>(screenHeight()) };
params.clearStencil = 0u;
params.flags.discardStart = TargetBufferFlags::ALL;
params.flags.discardEnd = TargetBufferFlags::NONE;
@@ -222,7 +236,11 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
api.beginFrame(0, 0, 0);
api.beginRenderPass(renderTarget0, params);
api.draw(ps, smallTriangle.getRenderPrimitive(), 0, 3, 1);
ps.primitiveType = PrimitiveType::TRIANGLES;
ps.vertexBufferInfo = smallTriangle.getVertexBufferInfo();
api.bindPipeline(ps);
api.bindRenderPrimitive(smallTriangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
// Step 2: Render a larger triangle with the stencil test enabled.
@@ -237,7 +255,11 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
ps.stencilState.front.ref = 0u;
api.beginRenderPass(renderTarget1, params);
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
ps.primitiveType = PrimitiveType::TRIANGLES;
ps.vertexBufferInfo = triangle.getVertexBufferInfo();
api.bindPipeline(ps);
api.bindRenderPrimitive(triangle.getRenderPrimitive());
api.draw2(0, 3, 1);
api.endRenderPass();
api.commit(mSwapChain);
@@ -245,7 +267,8 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
api.endFrame(0);
EXPECT_IMAGE(renderTarget1, getExpectations(),
ScreenshotParams(512, 512, "StencilBufferAutoResolve", 3353562179));
ScreenshotParams(screenWidth(), screenHeight(), "StencilBufferAutoResolve",
3353562179));
flushAndWait();
getDriver().purge();
+1 -1
View File
@@ -1017,7 +1017,7 @@ public:
*
* @see setPaused
*/
bool isPaused() const noexcept;
bool isPaused() const noexcept(UTILS_HAS_THREADING);
/**
* Pause or resume rendering thread.
+8
View File
@@ -153,6 +153,14 @@ public:
*/
Builder& multiview(AttachmentPoint attachment, uint8_t layerCount, uint8_t baseLayer = 0) noexcept;
/**
* Sets the number of samples used for MSAA (Multisample Anti-Aliasing).
*
* @param samples The number of samples used for multisampling.
* @return A reference to this Builder for chaining calls.
*/
Builder& samples(uint8_t samples) noexcept;
/**
* Creates the RenderTarget object and returns a pointer to it.
*
+14
View File
@@ -172,6 +172,20 @@ public:
*/
Builder& levels(uint8_t levels) noexcept;
/**
* Specifies the numbers of samples used for MSAA (Multisample Anti-Aliasing).
*
* Calling this method implicitly indicates the texture is used as a render target. Hence,
* this method should not be used in conjunction with other methods that are semantically
* conflicting like `setImage`.
*
* If this is invoked for array textures, it means this texture is used for multiview.
*
* @param samples Number of samples for this texture.
* @return This Builder, for chaining calls.
*/
Builder& samples(uint8_t samples) noexcept;
/**
* Specifies the type of sampler to use.
* @param target Sampler type
+1 -1
View File
@@ -387,7 +387,7 @@ JobSystem& Engine::getJobSystem() noexcept {
return downcast(this)->getJobSystem();
}
bool Engine::isPaused() const noexcept {
bool Engine::isPaused() const noexcept(UTILS_HAS_THREADING) {
FILAMENT_CHECK_PRECONDITION(UTILS_HAS_THREADING)
<< "Pause is meant for multi-threaded platforms.";
return downcast(this)->isPaused();
+10 -3
View File
@@ -304,8 +304,10 @@ static const PostProcessManager::StaticMaterialInfo sMaterialList[] = {
{ "mipmapDepth", MATERIAL(MIPMAPDEPTH) },
{ "sao", MATERIAL(SAO) },
{ "saoBentNormals", MATERIAL(SAOBENTNORMALS) },
#ifndef FILAMENT_DISABLE_GTAO
{ "gtao", MATERIAL(GTAO) },
{ "gtaoBentNormals", MATERIAL(GTAOBENTNORMALS) },
#endif
{ "separableGaussianBlur1", MATERIAL(SEPARABLEGAUSSIANBLUR),
{ {"arraySampler", false}, {"componentCount", 1} } },
{ "separableGaussianBlur1L", MATERIAL(SEPARABLEGAUSSIANBLUR),
@@ -874,20 +876,25 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
}};
std::string_view materialName;
if (options.aoType ==
auto aoType = options.aoType;
#ifdef FILAMENT_DISABLE_GTAO
materialName = computeBentNormals ? "saoBentNormals" : "sao";
aoType = AmbientOcclusionOptions::AmbientOcclusionType::SAO;
#else
if (aoType ==
AmbientOcclusionOptions::AmbientOcclusionType::GTAO) {
materialName = computeBentNormals ? "gtaoBentNormals" : "gtao";
} else {
materialName = computeBentNormals ? "saoBentNormals" : "sao";
}
#endif
auto& material = getPostProcessMaterial(materialName);
FMaterial const * const ma = material.getMaterial(mEngine);
FMaterialInstance* const mi = PostProcessMaterial::getMaterialInstance(ma);
// Set AO type specific material parameters
switch (options.aoType) {
switch (aoType) {
case AmbientOcclusionOptions::AmbientOcclusionType::SAO: {
// Where the falloff function peaks
const float peak = 0.1f * options.radius;
+16 -12
View File
@@ -67,6 +67,7 @@
#include <math/vec4.h>
#include <algorithm>
#include <array>
#include <chrono>
#include <initializer_list>
#include <memory>
@@ -369,9 +370,16 @@ void FEngine::init() {
.sampler(Texture::Sampler::SAMPLER_CUBEMAP)
.build(*this));
static constexpr std::array<uint32_t, 6> zeroCubemap{};
static constexpr std::array<uint32_t, 1> zeroRGBA{};
static constexpr std::array<uint32_t, 1> oneRGBA{ 0xffffffff };
static constexpr std::array<float , 1> oneFloat{ 1.0f };
auto const size = [](auto&& array) {
return array.size() * sizeof(decltype(array[0]));
};
driverApi.update3DImage(mDefaultIblTexture->getHwHandle(), 0, 0, 0, 0, 1, 1, 6,
{ std::array<uint8_t, 6>{}.data(), 6,
Texture::Format::RGBA, Texture::Type::UBYTE });
{ zeroCubemap.data(), size(zeroCubemap), Texture::Format::RGBA, Texture::Type::UBYTE });
// 3 bands = 9 float3
constexpr float sh[9 * 3] = { 0.0f };
@@ -393,12 +401,10 @@ void FEngine::init() {
TextureFormat::RGBA8, 1, 1, 1, 1, TextureUsage::DEFAULT);
driverApi.update3DImage(mDummyOneTexture, 0, 0, 0, 0, 1, 1, 1,
{ std::array<uint8_t, 4>{0xff, 0xff, 0xff, 0xff}.data(), 4,
Texture::Format::RGBA, Texture::Type::UBYTE });
{ oneRGBA.data(), size(oneRGBA), Texture::Format::RGBA, Texture::Type::UBYTE });
driverApi.update3DImage(mDummyZeroTexture, 0, 0, 0, 0, 1, 1, 1,
{ std::array<uint8_t, 4>{}.data(), 4,
Texture::Format::RGBA, Texture::Type::UBYTE });
{ zeroRGBA.data(), size(zeroRGBA), Texture::Format::RGBA, Texture::Type::UBYTE });
mPerViewDescriptorSetLayoutSsrVariant = {
@@ -463,16 +469,13 @@ void FEngine::init() {
TextureFormat::RGBA8, 1, 1, 1, 1, TextureUsage::DEFAULT);
driverApi.update3DImage(mDummyOneTextureArray, 0, 0, 0, 0, 1, 1, 1,
{ std::array<uint8_t, 4>{0xff, 0xff, 0xff, 0xff}.data(), 4,
Texture::Format::RGBA, Texture::Type::UBYTE });
{ oneRGBA.data(), size(oneRGBA), Texture::Format::RGBA, Texture::Type::UBYTE });
driverApi.update3DImage(mDummyOneTextureArrayDepth, 0, 0, 0, 0, 1, 1, 1,
{ std::array<float, 1>{1.0f}.data(), 4,
Texture::Format::DEPTH_COMPONENT, Texture::Type::FLOAT });
{ oneFloat.data(), size(oneFloat), Texture::Format::DEPTH_COMPONENT, Texture::Type::FLOAT });
driverApi.update3DImage(mDummyZeroTextureArray, 0, 0, 0, 0, 1, 1, 1,
{ std::array<uint8_t, 4>{}.data(), 4,
Texture::Format::RGBA, Texture::Type::UBYTE });
{ zeroRGBA.data(), size(zeroRGBA), Texture::Format::RGBA, Texture::Type::UBYTE });
mDummyUniformBuffer = driverApi.createBufferObject(CONFIG_MINSPEC_UBO_SIZE,
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
@@ -624,6 +627,7 @@ void FEngine::shutdown() {
driver.destroyTexture(std::move(mDummyOneTextureArray));
driver.destroyTexture(std::move(mDummyZeroTexture));
driver.destroyTexture(std::move(mDummyZeroTextureArray));
driver.destroyTexture(std::move(mDummyOneTextureArrayDepth));
driver.destroyBufferObject(std::move(mDummyUniformBuffer));
+17 -7
View File
@@ -20,9 +20,19 @@
#include <filament/Fence.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/Panic.h>
#include <utils/debug.h>
#include <condition_variable>
#include <chrono>
#include <memory>
#include <mutex>
#include <stdint.h>
namespace filament {
using namespace backend;
@@ -30,7 +40,7 @@ using namespace backend;
utils::Mutex FFence::sLock;
utils::Condition FFence::sCondition;
static const constexpr uint64_t PUMP_INTERVAL_MILLISECONDS = 1;
static constexpr uint64_t PUMP_INTERVAL_MILLISECONDS = 1;
using ms = std::chrono::milliseconds;
using ns = std::chrono::nanoseconds;
@@ -60,7 +70,7 @@ FenceStatus FFence::waitAndDestroy(FFence* fence, Mode const mode) noexcept {
}
UTILS_NOINLINE
FenceStatus FFence::wait(Mode const mode, uint64_t const timeout) noexcept {
FenceStatus FFence::wait(Mode const mode, uint64_t const timeout) {
FILAMENT_CHECK_PRECONDITION(UTILS_HAS_THREADING || timeout == 0)
<< "Non-zero timeout requires threads.";
@@ -103,18 +113,15 @@ FenceStatus FFence::wait(Mode const mode, uint64_t const timeout) noexcept {
UTILS_NOINLINE
void FFence::FenceSignal::signal(State const s) noexcept {
std::lock_guard<utils::Mutex> const lock(sLock);
std::lock_guard const lock(sLock);
mState = s;
sCondition.notify_all();
}
UTILS_NOINLINE
Fence::FenceStatus FFence::FenceSignal::wait(uint64_t const timeout) noexcept {
std::unique_lock<utils::Mutex> lock(sLock);
std::unique_lock lock(sLock);
while (mState == UNSIGNALED) {
if (mState == DESTROYED) {
return FenceStatus::ERROR;
}
if (timeout == FENCE_WAIT_FOR_EVER) {
sCondition.wait(lock);
} else {
@@ -124,6 +131,9 @@ Fence::FenceStatus FFence::FenceSignal::wait(uint64_t const timeout) noexcept {
}
}
}
if (mState == DESTROYED) {
return FenceStatus::ERROR;
}
return FenceStatus::CONDITION_SATISFIED;
}
+1 -1
View File
@@ -37,7 +37,7 @@ public:
void terminate(FEngine& engine) noexcept;
FenceStatus wait(Mode mode, uint64_t timeout) noexcept;
FenceStatus wait(Mode mode, uint64_t timeout);
static FenceStatus waitAndDestroy(FFence* fence, Mode mode) noexcept;
+4 -4
View File
@@ -94,7 +94,7 @@ static std::unique_ptr<MaterialParser> createParser(Backend const backend,
}
}
FILAMENT_CHECK_PRECONDITION(
FILAMENT_CHECK_POSTCONDITION(
materialResult != MaterialParser::ParseResult::ERROR_MISSING_BACKEND)
<< "the material was not built for any of the " << to_string(backend)
<< " backend's supported shader languages (" << languageNames.c_str() << ")\n";
@@ -104,12 +104,12 @@ static std::unique_ptr<MaterialParser> createParser(Backend const backend,
return materialParser;
}
FILAMENT_CHECK_PRECONDITION(materialResult == MaterialParser::ParseResult::SUCCESS)
FILAMENT_CHECK_POSTCONDITION(materialResult == MaterialParser::ParseResult::SUCCESS)
<< "could not parse the material package";
uint32_t version = 0;
materialParser->getMaterialVersion(&version);
FILAMENT_CHECK_PRECONDITION(version == MATERIAL_VERSION)
FILAMENT_CHECK_POSTCONDITION(version == MATERIAL_VERSION)
<< "Material version mismatch. Expected " << MATERIAL_VERSION << " but received "
<< version << ".";
@@ -573,7 +573,7 @@ void FMaterial::getPostProcessProgramSlow(Variant const variant,
Program FMaterial::getProgramWithVariants(
Variant variant,
Variant vertexVariant,
Variant fragmentVariant) const noexcept {
Variant fragmentVariant) const {
FEngine const& engine = mEngine;
const ShaderModel sm = engine.getShaderModel();
const bool isNoop = engine.getBackend() == Backend::NOOP;
+1 -1
View File
@@ -267,7 +267,7 @@ private:
void getPostProcessProgramSlow(Variant variant,
CompilerPriorityQueue priorityQueue) const noexcept;
backend::Program getProgramWithVariants(Variant variant,
Variant vertexVariant, Variant fragmentVariant) const noexcept;
Variant vertexVariant, Variant fragmentVariant) const;
void processBlendingMode(MaterialParser const* parser);
+1 -2
View File
@@ -147,8 +147,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
FEngine::DriverApi& driver = engine.getDriverApi();
FMaterial const* const material = other->getMaterial();
size_t const uboSize = std::max(size_t(16), material->getUniformInterfaceBlock().getSize());
mUniforms = UniformBuffer(uboSize);
mUniforms.setUniforms(other->getUniformBuffer());
mUbHandle = driver.createBufferObject(mUniforms.getSize(),
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
driver.setDebugTag(mUbHandle.getId(), material->getName());
+2 -2
View File
@@ -178,7 +178,7 @@ void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t const targetInde
void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t const targetIndex,
float4 const* positions, size_t const count, size_t const offset) {
FILAMENT_CHECK_PRECONDITION(offset + count <= mVertexCount)
<< "MorphTargetBuffer (size=" << (unsigned)mVertexCount
<< "MorphTargetBuffer (size=" << mVertexCount
<< ") overflow (count=" << (unsigned)count << ", offset=" << (unsigned)offset << ")";
auto size = getSize<POSITION>(count);
@@ -200,7 +200,7 @@ void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t const targetInde
void FMorphTargetBuffer::setTangentsAt(FEngine& engine, size_t const targetIndex,
short4 const* tangents, size_t const count, size_t const offset) {
FILAMENT_CHECK_PRECONDITION(offset + count <= mVertexCount)
<< "MorphTargetBuffer (size=" << (unsigned)mVertexCount
<< "MorphTargetBuffer (size=" << mVertexCount
<< ") overflow (count=" << (unsigned)count << ", offset=" << (unsigned)offset << ")";
const auto size = getSize<TANGENTS>(count);
+5 -1
View File
@@ -43,7 +43,7 @@ struct RenderTarget::BuilderDetails {
FRenderTarget::Attachment mAttachments[FRenderTarget::ATTACHMENT_COUNT] = {};
uint32_t mWidth{};
uint32_t mHeight{};
uint8_t mSamples = 1; // currently not settable in the public facing API
uint8_t mSamples = 1;
// The number of layers for the render target. The value should be 1 except for multiview.
// If multiview is enabled, this value is appropriately updated based on the layerCount value
// from each attachment. Hence, #>1 means using multiview
@@ -85,6 +85,10 @@ RenderTarget::Builder& RenderTarget::Builder::multiview(AttachmentPoint pt, uint
return *this;
}
RenderTarget::Builder& RenderTarget::Builder::samples(uint8_t samples) noexcept {
mImpl->mSamples = samples;
return *this;
}
RenderTarget* RenderTarget::Builder::build(Engine& engine) {
using backend::TextureUsage;
+5 -4
View File
@@ -447,11 +447,12 @@ void FRenderer::endFrame() {
void FRenderer::readPixels(uint32_t const xoffset, uint32_t const yoffset, uint32_t const width, uint32_t const height,
PixelBufferDescriptor&& buffer) {
#ifndef NDEBUG
const bool withinFrame = mSwapChain != nullptr;
FILAMENT_CHECK_PRECONDITION(withinFrame) << "readPixels() on a SwapChain must be called after"
" beginFrame() and before endFrame().";
#endif
FILAMENT_CHECK_PRECONDITION(withinFrame)
<< "readPixels() on a SwapChain must be called after "
"beginFrame() and before endFrame().";
RendererUtils::readPixels(mEngine.getDriverApi(), mRenderTargetHandle,
xoffset, yoffset, width, height, std::move(buffer));
}
+19 -1
View File
@@ -78,6 +78,7 @@ struct Texture::BuilderDetails {
uint32_t mHeight = 1;
uint32_t mDepth = 1;
uint8_t mLevels = 1;
uint8_t mSamples = 1;
Sampler mTarget = Sampler::SAMPLER_2D;
InternalFormat mFormat = InternalFormat::RGBA8;
Usage mUsage = Usage::NONE;
@@ -118,6 +119,11 @@ Texture::Builder& Texture::Builder::levels(uint8_t const levels) noexcept {
return *this;
}
Texture::Builder& Texture::Builder::samples(uint8_t const samples) noexcept {
mImpl->mSamples = std::max(uint8_t(1), samples);
return *this;
}
Texture::Builder& Texture::Builder::sampler(Sampler const target) noexcept {
mImpl->mTarget = target;
return *this;
@@ -169,6 +175,13 @@ Texture* Texture::Builder::build(Engine& engine) {
<< "Texture has invalid dimensions: (" << mImpl->mWidth << ", " << mImpl->mHeight
<< "), texture name=" << getNameOrDefault().c_str_safe();
}
if (mImpl->mSamples > 1) {
FILAMENT_CHECK_PRECONDITION(any(mImpl->mUsage & Texture::Usage::SAMPLEABLE))
<< "Multisample (" << unsigned(mImpl->mSamples)
<< ") texture is not sampleable, texture name=" << getNameOrDefault().c_str_safe();
}
const bool isProtectedTexturesSupported =
downcast(engine).getDriverApi().isProtectedTexturesSupported();
const bool useProtectedMemory = bool(mImpl->mUsage & TextureUsage::PROTECTED);
@@ -286,6 +299,7 @@ FTexture::FTexture(FEngine& engine, const Builder& builder)
mUsage = builder->mUsage;
mTarget = builder->mTarget;
mLevelCount = builder->mLevels;
mSampleCount = builder->mSamples;
mSwizzle = builder->mSwizzle;
mTextureIsSwizzled = builder->mTextureIsSwizzled;
mHasBlitSrc = builder->mHasBlitSrc;
@@ -369,6 +383,9 @@ void FTexture::setImage(FEngine& engine, size_t const level,
FILAMENT_CHECK_PRECONDITION(!mExternal)
<< "External Texture not supported for this operation.";
FILAMENT_CHECK_PRECONDITION(any(mUsage & Texture::Usage::UPLOADABLE))
<< "Texture is not uploadable.";
FILAMENT_CHECK_PRECONDITION(mSampleCount <= 1) << "Operation not supported with multisample ("
<< unsigned(mSampleCount) << ") texture.";
@@ -384,7 +401,7 @@ void FTexture::setImage(FEngine& engine, size_t const level,
FILAMENT_CHECK_PRECONDITION(p.buffer) << "Data buffer is nullptr.";
uint32_t effectiveTextureDepthOrLayers;
uint32_t effectiveTextureDepthOrLayers = 0;
switch (mTarget) {
case SamplerType::SAMPLER_EXTERNAL:
// can't happen by construction, fallthrough...
@@ -447,6 +464,7 @@ void FTexture::setImage(FEngine& engine, size_t const level,
case SamplerType::SAMPLER_EXTERNAL:
return false;
}
return false;
};
// this should have been validated already
-3
View File
@@ -76,9 +76,6 @@ public:
void generateMipmaps(FEngine& engine) const noexcept;
void setSampleCount(size_t const sampleCount) noexcept { mSampleCount = uint8_t(sampleCount); }
size_t getSampleCount() const noexcept { return mSampleCount; }
bool isMultisample() const noexcept { return mSampleCount > 1; }
bool isCompressed() const noexcept { return isCompressedFormat(mFormat); }
bool isCubemap() const noexcept { return mTarget == Sampler::SAMPLER_CUBEMAP; }
+14 -16
View File
@@ -100,19 +100,7 @@ VertexBuffer::Builder& VertexBuffer::Builder::attribute(VertexAttribute const at
}
if (size_t(attribute) < MAX_VERTEX_ATTRIBUTE_COUNT &&
size_t(bufferIndex) < MAX_VERTEX_ATTRIBUTE_COUNT) {
#ifndef NDEBUG
if (byteOffset & 0x3u) {
utils::slog.d << "[performance] VertexBuffer::Builder::attribute() "
"byteOffset not multiple of 4" << utils::io::endl;
}
if (byteStride & 0x3u) {
utils::slog.d << "[performance] VertexBuffer::Builder::attribute() "
"byteStride not multiple of 4" << utils::io::endl;
}
#endif
size_t(bufferIndex) < MAX_VERTEX_ATTRIBUTE_COUNT) {
auto& entry = mImpl->mAttributes[attribute];
entry.buffer = bufferIndex;
entry.offset = byteOffset;
@@ -169,9 +157,15 @@ VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
auto const& attributes = mImpl->mAttributes;
utils::bitset32 attributedBuffers;
declaredAttributes.forEachSetBit([&](size_t const j){
// update set of used buffers
attributedBuffers.set(attributes[j].buffer);
declaredAttributes.forEachSetBit([&](size_t const j) {
FILAMENT_CHECK_PRECONDITION((attributes[j].offset & 0x3u) == 0)
<< "attribute " << j << " offset=" << attributes[j].offset
<< " is not multiple of 4";
FILAMENT_CHECK_PRECONDITION((attributes[j].stride & 0x3u) == 0)
<< "attribute " << j << " stride=" << attributes[j].stride
<< " is not multiple of 4";
if (engine.getActiveFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0) {
FILAMENT_CHECK_PRECONDITION(!(attributes[j].flags & Attribute::FLAG_INTEGER_TARGET))
@@ -190,9 +184,13 @@ VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
(1 << int(ET::HALF2)) |
(1 << int(ET::HALF3)) |
(1 << int(ET::HALF4));
FILAMENT_CHECK_PRECONDITION(!(invalidIntegerTypes & (1 << int(attributes[j].type))))
<< "invalid integer vertex attribute type " << int(attributes[j].type);
}
// update set of used buffers
attributedBuffers.set(attributes[j].buffer);
});
FILAMENT_CHECK_PRECONDITION(attributedBuffers.count() == mImpl->mBufferCount)
+2
View File
@@ -241,6 +241,8 @@ bool DescriptorSet::isTextureCompatibleWithDescriptor(
return t == TextureType::DEPTH || t == TextureType::DEPTH_STENCIL;
case DescriptorType::SAMPLER_EXTERNAL:
return true;
case DescriptorType::UNIFORM_BUFFER:
case DescriptorType::SHADER_STORAGE_BUFFER:
case DescriptorType::INPUT_ATTACHMENT:
+1 -1
View File
@@ -116,7 +116,7 @@ void groundTruthAmbientOcclusion(out float obscurance, out vec3 bentNormal,
highp vec3 sampleDelta0 = (samplePos0 - origin);
highp vec3 sampleDelta1 = (samplePos1 - origin);
vec2 sqSampleDist = vec2(dot(sampleDelta0, sampleDelta0), dot(sampleDelta1, sampleDelta1));
highp vec2 sqSampleDist = vec2(dot(sampleDelta0, sampleDelta0), dot(sampleDelta1, sampleDelta1));
vec2 invSampleDist = rsqrt(sqSampleDist);
// Use the view space radius to calculate the fallOff
@@ -18,6 +18,7 @@
#define TNT_FILAFLAT_UNFLATTENER_H
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/CString.h>
#include <private/filament/Variant.h>
@@ -305,7 +305,7 @@ public:
//! Add a parameter array to this material.
MaterialBuilder& parameter(const char* name, size_t size, UniformType type,
ParameterPrecision precision = ParameterPrecision::DEFAULT) noexcept;
ParameterPrecision precision = ParameterPrecision::DEFAULT);
//! Add a constant parameter to this material.
template<typename T>
@@ -325,9 +325,9 @@ public:
SamplerFormat format = SamplerFormat::FLOAT,
ParameterPrecision precision = ParameterPrecision::DEFAULT, bool multisample = false,
const char* transformName = "",
ShaderStageFlags stages = ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) noexcept;
ShaderStageFlags stages = ShaderStageFlags::ALL_SHADER_STAGE_FLAGS);
MaterialBuilder& buffer(filament::BufferInterfaceBlock bib) noexcept;
MaterialBuilder& buffer(filament::BufferInterfaceBlock bib);
//! Custom variables (all float4).
MaterialBuilder& variable(Variable v, const char* name) noexcept;
@@ -617,7 +617,7 @@ public:
//! Add a new fragment shader output variable. Only valid for materials in the POST_PROCESS domain.
MaterialBuilder& output(VariableQualifier qualifier, OutputTarget target, Precision precision,
OutputType type, const char* name, int location = -1) noexcept;
OutputType type, const char* name, int location = -1);
MaterialBuilder& enableFramebufferFetch() noexcept;
@@ -646,12 +646,12 @@ public:
* Add a subpass parameter to this material.
*/
MaterialBuilder& subpass(SubpassType subpassType,
SamplerFormat format, ParameterPrecision precision, const char* name) noexcept;
SamplerFormat format, ParameterPrecision precision, const char* name);
MaterialBuilder& subpass(SubpassType subpassType,
SamplerFormat format, const char* name) noexcept;
SamplerFormat format, const char* name);
MaterialBuilder& subpass(SubpassType subpassType,
ParameterPrecision precision, const char* name) noexcept;
MaterialBuilder& subpass(SubpassType subpassType, const char* name) noexcept;
ParameterPrecision precision, const char* name);
MaterialBuilder& subpass(SubpassType subpassType, const char* name);
struct Parameter {
Parameter() noexcept: parameterType(INVALID) {}
+11 -4
View File
@@ -48,7 +48,7 @@
#include <unordered_map>
#include <utility>
#include <vector>
#include "regex"
#include <stddef.h>
#include <stdint.h>
@@ -601,6 +601,7 @@ bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
return false;
#endif
}
tint::Result<tint::wgsl::writer::Output> wgslOut = tint::wgsl::writer::Generate(tintRead,writerOpts);
/// An instance of SuccessType that can be used to check a tint Result.
tint::SuccessType tintSuccess;
@@ -609,9 +610,15 @@ bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
slog.e << "Tint writer error: " << wgslOut.Failure().reason << io::endl;
return false;
}
//Tint adds some annotations that Dawn complains about and needs removal
std::regex stride_regex(R"(@stride\([0-9]+\)\s*@internal\(disable_validation__ignore_stride\))");
*outWsl = std::regex_replace(wgslOut->wgsl, stride_regex, "");
// Tint adds annotations that Dawn complains about when consuming. remove for now
// https://dawn.googlesource.com/dawn/+/efb17b02543fb52c0b2e21d6082c0c9fbc2168a9%5E%21/
char const* annotationStr = "@stride(16) @internal(disable_validation__ignore_stride)";
size_t pos = wgslOut->wgsl.find(annotationStr);
while (pos != std::string::npos) {
wgslOut->wgsl.erase(pos, strlen(annotationStr));
pos = wgslOut->wgsl.find(annotationStr);
}
*outWsl = wgslOut->wgsl;
return true;
#else
slog.i << "Trying to emit WGSL without including WebGPU dependencies,"
+8 -8
View File
@@ -289,7 +289,7 @@ MaterialBuilder& MaterialBuilder::variable(Variable v,
}
MaterialBuilder& MaterialBuilder::parameter(const char* name, size_t size, UniformType type,
ParameterPrecision precision) noexcept {
ParameterPrecision precision) {
FILAMENT_CHECK_POSTCONDITION(mParameterCount < MAX_PARAMETERS_COUNT) << "Too many parameters";
mParameters[mParameterCount++] = { name, type, size, precision };
return *this;
@@ -303,7 +303,7 @@ MaterialBuilder& MaterialBuilder::parameter(const char* name, UniformType const
MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType samplerType,
SamplerFormat format, ParameterPrecision precision, bool multisample,
const char* transformName, ShaderStageFlags stages) noexcept {
const char* transformName, ShaderStageFlags stages) {
FILAMENT_CHECK_PRECONDITION(!multisample ||
(format != SamplerFormat::SHADOW &&
(samplerType == SamplerType::SAMPLER_2D ||
@@ -364,14 +364,14 @@ template MaterialBuilder& MaterialBuilder::constant<float>(
template MaterialBuilder& MaterialBuilder::constant<bool>(
const char* name, ConstantType type, bool defaultValue);
MaterialBuilder& MaterialBuilder::buffer(BufferInterfaceBlock bib) noexcept {
MaterialBuilder& MaterialBuilder::buffer(BufferInterfaceBlock bib) {
FILAMENT_CHECK_POSTCONDITION(mBuffers.size() < MAX_BUFFERS_COUNT) << "Too many buffers";
mBuffers.emplace_back(std::make_unique<BufferInterfaceBlock>(std::move(bib)));
return *this;
}
MaterialBuilder& MaterialBuilder::subpass(SubpassType subpassType, SamplerFormat format,
ParameterPrecision precision, const char* name) noexcept {
ParameterPrecision precision, const char* name) {
FILAMENT_CHECK_PRECONDITION(format == SamplerFormat::FLOAT)
<< "Subpass parameters must have FLOAT format.";
@@ -381,16 +381,16 @@ MaterialBuilder& MaterialBuilder::subpass(SubpassType subpassType, SamplerFormat
}
MaterialBuilder& MaterialBuilder::subpass(SubpassType const subpassType, SamplerFormat const format,
const char* name) noexcept {
const char* name) {
return subpass(subpassType, format, ParameterPrecision::DEFAULT, name);
}
MaterialBuilder& MaterialBuilder::subpass(SubpassType const subpassType, ParameterPrecision const precision,
const char* name) noexcept {
const char* name) {
return subpass(subpassType, SamplerFormat::FLOAT, precision, name);
}
MaterialBuilder& MaterialBuilder::subpass(SubpassType const subpassType, const char* name) noexcept {
MaterialBuilder& MaterialBuilder::subpass(SubpassType const subpassType, const char* name) {
return subpass(subpassType, SamplerFormat::FLOAT, ParameterPrecision::DEFAULT, name);
}
@@ -1191,7 +1191,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
}
MaterialBuilder& MaterialBuilder::output(VariableQualifier qualifier, OutputTarget target,
Precision precision, OutputType type, const char* name, int location) noexcept {
Precision precision, OutputType type, const char* name, int location) {
FILAMENT_CHECK_PRECONDITION(target != OutputTarget::DEPTH || type == OutputType::FLOAT)
<< "Depth outputs must be of type FLOAT.";
FILAMENT_CHECK_PRECONDITION(
+3 -3
View File
@@ -939,9 +939,9 @@ utils::io::sstream& CodeGenerator::generateSpecializationConstant(utils::io::sst
// Spec constants aren't fully supported in Tint,
// workaround until https://issues.chromium.org/issues/42250586 is resolved
if (mTargetApi == TargetApi::WEBGPU) {
out << "layout (constant_id = " << id << ") const "
<< types[value.index()] << " " << name << "_hack = " << constantString << ";\n"
<< types[value.index()] << " " << name << " = " << name << "_hack;\n";
std::string const variableName = "FILAMENT_SPEC_CONST_" + std::to_string(id) + "_" + name;
out << " " << types[value.index()] << " " << variableName << " = " << constantString << ";\n";
out << types[value.index()] << " " << name << " = " << variableName << ";\n";
return out;
}
if (mTargetLanguage == MaterialBuilderBase::TargetLanguage::SPIRV) {
@@ -35,6 +35,7 @@ struct Config {
bool resizeable = true;
bool headless = false;
int stereoscopicEyeCount = 2;
uint8_t samples = 1;
// Provided to indicate GPU preference for vulkan
std::string vulkanGPUHint;
@@ -340,7 +340,7 @@ public:
std::is_same<filament::math::ushort3, T>::value ||
std::is_same<filament::math::ushort4, T>::value>::type;
template<typename T, typename = is_supported_aux_t<T>>
void getAux(AuxAttribute attribute, T* out, size_t stride = 0) const noexcept;
void getAux(AuxAttribute attribute, T* out, size_t stride = 0) const;
/**
* Get number of output triangles.
+6 -6
View File
@@ -747,22 +747,22 @@ void TangentSpaceMesh::getQuats(quath* out, size_t stride) const noexcept {
}
template void TangentSpaceMesh::getAux<float2>(AuxAttribute attribute, float2* out,
size_t stride) const noexcept;
size_t stride) const;
template void TangentSpaceMesh::getAux<float3>(AuxAttribute attribute, float3* out,
size_t stride) const noexcept;
size_t stride) const;
template void TangentSpaceMesh::getAux<float4>(AuxAttribute attribute, float4* out,
size_t stride) const noexcept;
size_t stride) const;
template void TangentSpaceMesh::getAux<ushort3>(AuxAttribute attribute, ushort3* out,
size_t stride) const noexcept;
size_t stride) const;
template void TangentSpaceMesh::getAux<ushort4>(AuxAttribute attribute, ushort4* out,
size_t stride) const noexcept;
size_t stride) const;
template<typename T, typename>
void TangentSpaceMesh::getAux(AuxAttribute attribute, T* out, size_t stride) const noexcept {
void TangentSpaceMesh::getAux(AuxAttribute attribute, T* out, size_t stride) const {
AttributeImpl attrib = static_cast<AttributeImpl>(attribute);
auto inAux = mInput->data<T>(attrib);
FILAMENT_CHECK_PRECONDITION(inAux) << "Must provide input auxilliary attribute";
@@ -23,6 +23,7 @@
#include <math/norm.h>
#include <math/quat.h>
#include <utils/debug.h>
#include <utils/Panic.h>
#include <unordered_map>
+1 -1
View File
@@ -263,7 +263,7 @@ struct FAssetLoader : public AssetLoader {
mEngine(*config.engine),
mDefaultNodeName(config.defaultNodeName) {
if (config.ext) {
FILAMENT_CHECK_PRECONDITION(AssetConfigurationExtended::isSupported())
FILAMENT_CHECK_POSTCONDITION(AssetConfigurationExtended::isSupported())
<< "Extend asset loading is not supported on this platform";
mLoaderExtended = std::make_unique<AssetLoaderExtended>(
*config.ext, config.engine, mMaterials);
+1
View File
@@ -16,6 +16,7 @@
#include "DependencyGraph.h"
#include <utils/debug.h>
#include <utils/Panic.h>
using namespace filament;
+1
View File
@@ -22,6 +22,7 @@
#include <gltfio/NodeManager.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/SingleInstanceComponentManager.h>
#include <utils/Entity.h>
#include <utils/Slice.h>
@@ -16,6 +16,7 @@
#include "TangentSpaceMeshWrapper.h"
#include <utils/debug.h>
#include <utils/Panic.h>
#include <memory>
+13 -14
View File
@@ -99,7 +99,7 @@ IBLPrefilterContext::IBLPrefilterContext(Engine& engine)
mVertexBuffer = VertexBuffer::Builder()
.vertexCount(3)
.bufferCount(1)
.attribute(VertexAttribute::POSITION, 0,
.attribute(POSITION, 0,
VertexBuffer::AttributeType::FLOAT4, 0)
.build(engine);
@@ -180,7 +180,7 @@ IBLPrefilterContext& IBLPrefilterContext::operator=(IBLPrefilterContext&& rhs) n
IBLPrefilterContext::EquirectangularToCubemap::EquirectangularToCubemap(
IBLPrefilterContext& context,
IBLPrefilterContext::EquirectangularToCubemap::Config const& config)
Config const& config)
: mContext(context), mConfig(config) {
Engine& engine = mContext.mEngine;
mEquirectMaterial = Material::Builder().package(
@@ -198,7 +198,7 @@ IBLPrefilterContext::EquirectangularToCubemap::~EquirectangularToCubemap() noexc
}
IBLPrefilterContext::EquirectangularToCubemap::EquirectangularToCubemap(
IBLPrefilterContext::EquirectangularToCubemap&& rhs) noexcept
EquirectangularToCubemap&& rhs) noexcept
: mContext(rhs.mContext) {
using std::swap;
swap(mEquirectMaterial, rhs.mEquirectMaterial);
@@ -206,7 +206,7 @@ IBLPrefilterContext::EquirectangularToCubemap::EquirectangularToCubemap(
IBLPrefilterContext::EquirectangularToCubemap&
IBLPrefilterContext::EquirectangularToCubemap::operator=(
IBLPrefilterContext::EquirectangularToCubemap&& rhs) noexcept {
EquirectangularToCubemap&& rhs) noexcept {
using std::swap;
if (this != &rhs) {
swap(mEquirectMaterial, rhs.mEquirectMaterial);
@@ -300,7 +300,7 @@ Texture* IBLPrefilterContext::EquirectangularToCubemap::operator()(
// ------------------------------------------------------------------------------------------------
IBLPrefilterContext::IrradianceFilter::IrradianceFilter(IBLPrefilterContext& context,
IBLPrefilterContext::IrradianceFilter::Config config)
Config config)
: mContext(context),
mSampleCount(std::min(config.sampleCount, uint16_t(2048))) {
@@ -362,13 +362,13 @@ IBLPrefilterContext::IrradianceFilter::~IrradianceFilter() noexcept {
}
IBLPrefilterContext::IrradianceFilter::IrradianceFilter(
IBLPrefilterContext::IrradianceFilter&& rhs) noexcept
IrradianceFilter&& rhs) noexcept
: mContext(rhs.mContext) {
this->operator=(std::move(rhs));
}
IBLPrefilterContext::IrradianceFilter& IBLPrefilterContext::IrradianceFilter::operator=(
IBLPrefilterContext::IrradianceFilter&& rhs) noexcept {
IrradianceFilter&& rhs) noexcept {
using std::swap;
if (this != & rhs) {
swap(mKernelMaterial, rhs.mKernelMaterial);
@@ -378,9 +378,8 @@ IBLPrefilterContext::IrradianceFilter& IBLPrefilterContext::IrradianceFilter::op
return *this;
}
filament::Texture* IBLPrefilterContext::IrradianceFilter::operator()(
IBLPrefilterContext::IrradianceFilter::Options options,
filament::Texture const* environmentCubemap, filament::Texture* outIrradianceTexture) {
Texture* IBLPrefilterContext::IrradianceFilter::operator()(Options options,
Texture const* environmentCubemap, Texture* outIrradianceTexture) {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
using namespace backend;
@@ -470,12 +469,12 @@ filament::Texture* IBLPrefilterContext::IrradianceFilter::operator()(
}
UTILS_NOINLINE
filament::Texture* IBLPrefilterContext::IrradianceFilter::operator()(
filament::Texture const* environmentCubemap, filament::Texture* outIrradianceTexture) {
Texture* IBLPrefilterContext::IrradianceFilter::operator()(
Texture const* environmentCubemap, Texture* outIrradianceTexture) {
return operator()({}, environmentCubemap, outIrradianceTexture);
}
filament::Texture* IBLPrefilterContext::IrradianceFilter::createIrradianceTexture() {
Texture* IBLPrefilterContext::IrradianceFilter::createIrradianceTexture() {
Engine& engine = mContext.mEngine;
Texture* const outCubemap = Texture::Builder()
@@ -614,7 +613,7 @@ Texture* IBLPrefilterContext::SpecularFilter::operator()(
}
Texture* IBLPrefilterContext::SpecularFilter::operator()(
IBLPrefilterContext::SpecularFilter::Options options,
Options options,
Texture const* environmentCubemap, Texture* outReflectionsTexture) {
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);

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