Compare commits
31
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3e6fd9d044 | ||
|
|
d943293fd8 | ||
|
|
acc6c801ec | ||
|
|
a09ab9625d | ||
|
|
2c65946217 | ||
|
|
c166bea8fc | ||
|
|
b9be9b4f2d | ||
|
|
ce88a56579 | ||
|
|
689e769f9a | ||
|
|
2f0a8b19b5 | ||
|
|
1bed68afb7 | ||
|
|
130e02da5c | ||
|
|
8e06a68446 | ||
|
|
656b50b304 | ||
|
|
880db4ec43 | ||
|
|
2ce294720d | ||
|
|
eb07decb4b | ||
|
|
47930edf70 | ||
|
|
05b89905d8 | ||
|
|
3e0df36c1c | ||
|
|
c9ce384f14 | ||
|
|
6a6bdd5be7 | ||
|
|
d56ade924e | ||
|
|
fe561e3e27 | ||
|
|
1d98e350e4 | ||
|
|
d34e018acb | ||
|
|
169c8d57ff | ||
|
|
a1e0cfa33c | ||
|
|
6d44db3ca0 | ||
|
|
a261429d06 | ||
|
|
69d0de3c6d |
@@ -97,6 +97,10 @@ Make sure you've installed the following dependencies:
|
|||||||
- `libxcomposite-dev` (`libXcomposite-devel` on Fedora)
|
- `libxcomposite-dev` (`libXcomposite-devel` on Fedora)
|
||||||
- `libxxf86vm-dev` (`libXxf86vm-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)
|
After dependencies have been installed, we highly recommend using the [easy build](#easy-build)
|
||||||
script.
|
script.
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
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
|
set(FILAMENT_NDK_VERSION "" CACHE STRING
|
||||||
"Android NDK version or version prefix to be used when building for Android."
|
"Android NDK version or version prefix to be used when building for Android."
|
||||||
)
|
)
|
||||||
|
|||||||
+33
-22
@@ -262,11 +262,16 @@ set(MATERIAL_SRCS
|
|||||||
src/materials/ssao/mipmapDepth.mat
|
src/materials/ssao/mipmapDepth.mat
|
||||||
src/materials/ssao/sao.mat
|
src/materials/ssao/sao.mat
|
||||||
src/materials/ssao/saoBentNormals.mat
|
src/materials/ssao/saoBentNormals.mat
|
||||||
src/materials/ssao/gtao.mat
|
|
||||||
src/materials/ssao/gtaoBentNormals.mat
|
|
||||||
src/materials/vsmMipmap.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
|
set(MATERIAL_FL0_SRCS
|
||||||
src/materials/defaultMaterial.mat
|
src/materials/defaultMaterial.mat
|
||||||
src/materials/skybox.mat
|
src/materials/skybox.mat
|
||||||
@@ -316,6 +321,10 @@ if (FILAMENT_FORCE_PROFILING_MODE)
|
|||||||
add_definitions(-DFILAMENT_FORCE_PROFILING_MODE)
|
add_definitions(-DFILAMENT_FORCE_PROFILING_MODE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (FILAMENT_DISABLE_GTAO)
|
||||||
|
add_definitions(-DFILAMENT_DISABLE_GTAO)
|
||||||
|
endif()
|
||||||
|
|
||||||
# ==================================================================================================
|
# ==================================================================================================
|
||||||
# Definitions
|
# Definitions
|
||||||
# ==================================================================================================
|
# ==================================================================================================
|
||||||
@@ -463,27 +472,29 @@ add_custom_command(
|
|||||||
APPEND
|
APPEND
|
||||||
)
|
)
|
||||||
|
|
||||||
add_custom_command(
|
if (NOT FILAMENT_DISABLE_GTAO)
|
||||||
OUTPUT "${MATERIAL_DIR}/gtao.filamat"
|
add_custom_command(
|
||||||
DEPENDS src/materials/ssao/ssaoUtils.fs
|
OUTPUT "${MATERIAL_DIR}/gtao.filamat"
|
||||||
DEPENDS src/materials/ssao/ssct.fs
|
DEPENDS src/materials/ssao/ssaoUtils.fs
|
||||||
DEPENDS src/materials/utils/depthUtils.fs
|
DEPENDS src/materials/ssao/ssct.fs
|
||||||
DEPENDS src/materials/utils/geometry.fs
|
DEPENDS src/materials/utils/depthUtils.fs
|
||||||
DEPENDS src/materials/ssao/gtaoImpl.fs
|
DEPENDS src/materials/utils/geometry.fs
|
||||||
DEPENDS src/materials/ssao/ssctImpl.fs
|
DEPENDS src/materials/ssao/gtaoImpl.fs
|
||||||
APPEND
|
DEPENDS src/materials/ssao/ssctImpl.fs
|
||||||
)
|
APPEND
|
||||||
|
)
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT "${MATERIAL_DIR}/gtaoBentNormals.filamat"
|
OUTPUT "${MATERIAL_DIR}/gtaoBentNormals.filamat"
|
||||||
DEPENDS src/materials/ssao/ssaoUtils.fs
|
DEPENDS src/materials/ssao/ssaoUtils.fs
|
||||||
DEPENDS src/materials/ssao/ssct.fs
|
DEPENDS src/materials/ssao/ssct.fs
|
||||||
DEPENDS src/materials/utils/depthUtils.fs
|
DEPENDS src/materials/utils/depthUtils.fs
|
||||||
DEPENDS src/materials/utils/geometry.fs
|
DEPENDS src/materials/utils/geometry.fs
|
||||||
DEPENDS src/materials/ssao/gtaoImpl.fs
|
DEPENDS src/materials/ssao/gtaoImpl.fs
|
||||||
DEPENDS src/materials/ssao/ssctImpl.fs
|
DEPENDS src/materials/ssao/ssctImpl.fs
|
||||||
APPEND
|
APPEND
|
||||||
)
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT "${MATERIAL_DIR}/bilateralBlur.filamat"
|
OUTPUT "${MATERIAL_DIR}/bilateralBlur.filamat"
|
||||||
|
|||||||
@@ -261,6 +261,7 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
|||||||
src/webgpu/WebGPUHandles.h
|
src/webgpu/WebGPUHandles.h
|
||||||
src/webgpu/WebGPUPipelineCreation.cpp
|
src/webgpu/WebGPUPipelineCreation.cpp
|
||||||
src/webgpu/WebGPUPipelineCreation.h
|
src/webgpu/WebGPUPipelineCreation.h
|
||||||
|
src/webgpu/WebGPUStrings.h
|
||||||
src/webgpu/WebGPUSwapChain.cpp
|
src/webgpu/WebGPUSwapChain.cpp
|
||||||
src/webgpu/WebGPUSwapChain.h
|
src/webgpu/WebGPUSwapChain.h
|
||||||
src/webgpu/WGPUProgram.cpp
|
src/webgpu/WGPUProgram.cpp
|
||||||
|
|||||||
@@ -199,7 +199,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual bool makeCurrent(ContextType type,
|
virtual bool makeCurrent(ContextType type,
|
||||||
SwapChain* UTILS_NONNULL drawSwapChain,
|
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
|
* 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 drawSwapChain,
|
||||||
SwapChain* UTILS_NONNULL readSwapChain,
|
SwapChain* UTILS_NONNULL readSwapChain,
|
||||||
utils::Invocable<void()> preContextChange,
|
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()
|
* Called by the backend just before calling commit()
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ protected:
|
|||||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||||
void destroySwapChain(SwapChain* swapChain) 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;
|
void commit(SwapChain* swapChain) noexcept override;
|
||||||
ExternalTexture* createExternalImageTexture() noexcept override;
|
ExternalTexture* createExternalImageTexture() noexcept override;
|
||||||
void destroyExternalImageTexture(ExternalTexture* texture) noexcept override;
|
void destroyExternalImageTexture(ExternalTexture* texture) noexcept override;
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ public:
|
|||||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||||
void destroySwapChain(SwapChain* swapChain) 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;
|
void commit(SwapChain* swapChain) noexcept override;
|
||||||
|
|
||||||
ExternalTexture* createExternalImageTexture() noexcept override;
|
ExternalTexture* createExternalImageTexture() noexcept override;
|
||||||
|
|||||||
@@ -109,11 +109,11 @@ protected:
|
|||||||
|
|
||||||
bool makeCurrent(ContextType type,
|
bool makeCurrent(ContextType type,
|
||||||
SwapChain* drawSwapChain,
|
SwapChain* drawSwapChain,
|
||||||
SwapChain* readSwapChain) noexcept override;
|
SwapChain* readSwapChain) override;
|
||||||
|
|
||||||
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain,
|
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain,
|
||||||
utils::Invocable<void()> preContextChange,
|
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;
|
void commit(SwapChain* swapChain) noexcept override;
|
||||||
|
|
||||||
@@ -148,12 +148,12 @@ protected:
|
|||||||
EGLContext getContextForType(ContextType type) const noexcept;
|
EGLContext getContextForType(ContextType type) const noexcept;
|
||||||
|
|
||||||
// makes the draw and read surface current without changing the current context
|
// 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);
|
return egl.makeCurrent(drawSurface, readSurface);
|
||||||
}
|
}
|
||||||
|
|
||||||
// makes context current and set draw and read surfaces to EGL_NO_SURFACE
|
// 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);
|
return egl.makeCurrent(context, mEGLDummySurface, mEGLDummySurface);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -211,9 +211,9 @@ private:
|
|||||||
public:
|
public:
|
||||||
explicit EGL(EGLDisplay& dpy) : mEGLDisplay(dpy) {}
|
explicit EGL(EGLDisplay& dpy) : mEGLDisplay(dpy) {}
|
||||||
EGLBoolean makeCurrent(EGLContext context,
|
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);
|
return makeCurrent(mCurrentContext, drawSurface, readSurface);
|
||||||
}
|
}
|
||||||
} egl{ mEGLDisplay };
|
} egl{ mEGLDisplay };
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ protected:
|
|||||||
protected:
|
protected:
|
||||||
bool makeCurrent(ContextType type,
|
bool makeCurrent(ContextType type,
|
||||||
SwapChain* drawSwapChain,
|
SwapChain* drawSwapChain,
|
||||||
SwapChain* readSwapChain) noexcept override;
|
SwapChain* readSwapChain) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct InitializeJvmForPerformanceManagerIfNeeded {
|
struct InitializeJvmForPerformanceManagerIfNeeded {
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ protected:
|
|||||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||||
void destroySwapChain(SwapChain* swapChain) 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;
|
void commit(SwapChain* swapChain) noexcept override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ protected:
|
|||||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
bool makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||||
SwapChain* readSwapChain) noexcept override;
|
SwapChain* readSwapChain) override;
|
||||||
void commit(SwapChain* swapChain) noexcept override;
|
void commit(SwapChain* swapChain) noexcept override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ protected:
|
|||||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||||
void destroySwapChain(SwapChain* swapChain) 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;
|
void commit(SwapChain* swapChain) noexcept override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ protected:
|
|||||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||||
void destroySwapChain(SwapChain* swapChain) 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;
|
void commit(SwapChain* swapChain) noexcept override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <webgpu/webgpu_cpp.h>
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
@@ -56,6 +57,10 @@ protected:
|
|||||||
const Platform::DriverConfig& driverConfig) noexcept override;
|
const Platform::DriverConfig& driverConfig) noexcept override;
|
||||||
|
|
||||||
private:
|
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
|
// we may consider having the driver own this in the future
|
||||||
wgpu::Instance mInstance;
|
wgpu::Instance mInstance;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public:
|
|||||||
Range getBuffer() noexcept;
|
Range getBuffer() noexcept;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void* alloc(size_t size) noexcept;
|
void* alloc(size_t size);
|
||||||
void dealloc() noexcept;
|
void dealloc() noexcept;
|
||||||
|
|
||||||
// pointer to the beginning of the circular buffer (constant)
|
// 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
|
// all commands buffers (Slices) written to this point are returned by waitForCommand(). This
|
||||||
// call blocks until the CircularBuffer has at least mRequiredSize bytes available.
|
// call blocks until the CircularBuffer has at least mRequiredSize bytes available.
|
||||||
void flush() noexcept;
|
void flush();
|
||||||
|
|
||||||
// returns from waitForCommands() immediately.
|
// returns from waitForCommands() immediately.
|
||||||
void requestExit();
|
void requestExit();
|
||||||
|
|||||||
@@ -27,13 +27,13 @@ namespace filament {
|
|||||||
class VirtualMachineEnv {
|
class VirtualMachineEnv {
|
||||||
public:
|
public:
|
||||||
// must be called before VirtualMachineEnv::get() from a thread that is attached to the JavaVM
|
// 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
|
// must be called on backend thread
|
||||||
static VirtualMachineEnv& get() noexcept;
|
static VirtualMachineEnv& get() noexcept;
|
||||||
|
|
||||||
// can be called from any thread that already has a JniEnv
|
// 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
|
// must be called from the backend thread
|
||||||
JNIEnv* getEnvironment() noexcept {
|
JNIEnv* getEnvironment() noexcept {
|
||||||
@@ -49,7 +49,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
explicit VirtualMachineEnv(JavaVM* vm) noexcept;
|
explicit VirtualMachineEnv(JavaVM* vm) noexcept;
|
||||||
~VirtualMachineEnv() noexcept;
|
~VirtualMachineEnv() noexcept;
|
||||||
JNIEnv* getEnvironmentSlow() noexcept;
|
JNIEnv* getEnvironmentSlow();
|
||||||
|
|
||||||
static utils::Mutex sLock;
|
static utils::Mutex sLock;
|
||||||
static JavaVM* sVirtualMachine;
|
static JavaVM* sVirtualMachine;
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ CircularBuffer::~CircularBuffer() noexcept {
|
|||||||
// to each others and a special case in circularize()
|
// to each others and a special case in circularize()
|
||||||
|
|
||||||
UTILS_NOINLINE
|
UTILS_NOINLINE
|
||||||
void* CircularBuffer::alloc(size_t size) noexcept {
|
void* CircularBuffer::alloc(size_t size) {
|
||||||
#if HAS_MMAP
|
#if HAS_MMAP
|
||||||
void* data = nullptr;
|
void* data = nullptr;
|
||||||
void* vaddr = MAP_FAILED;
|
void* vaddr = MAP_FAILED;
|
||||||
|
|||||||
@@ -53,18 +53,18 @@ CommandBufferQueue::~CommandBufferQueue() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CommandBufferQueue::requestExit() {
|
void CommandBufferQueue::requestExit() {
|
||||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
std::lock_guard const lock(mLock);
|
||||||
mExitRequested = EXIT_REQUESTED;
|
mExitRequested = EXIT_REQUESTED;
|
||||||
mCondition.notify_one();
|
mCondition.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CommandBufferQueue::isPaused() const noexcept {
|
bool CommandBufferQueue::isPaused() const noexcept {
|
||||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
std::lock_guard const lock(mLock);
|
||||||
return mPaused;
|
return mPaused;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandBufferQueue::setPaused(bool paused) {
|
void CommandBufferQueue::setPaused(bool paused) {
|
||||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
std::lock_guard const lock(mLock);
|
||||||
if (paused) {
|
if (paused) {
|
||||||
mPaused = true;
|
mPaused = true;
|
||||||
} else {
|
} else {
|
||||||
@@ -74,12 +74,12 @@ void CommandBufferQueue::setPaused(bool paused) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CommandBufferQueue::isExitRequested() const {
|
bool CommandBufferQueue::isExitRequested() const {
|
||||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
std::lock_guard const lock(mLock);
|
||||||
return (bool)mExitRequested;
|
return bool(mExitRequested);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CommandBufferQueue::flush() noexcept {
|
void CommandBufferQueue::flush() {
|
||||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||||
|
|
||||||
CircularBuffer& circularBuffer = mCircularBuffer;
|
CircularBuffer& circularBuffer = mCircularBuffer;
|
||||||
@@ -103,7 +103,7 @@ void CommandBufferQueue::flush() noexcept {
|
|||||||
static_cast<char const*>(begin), static_cast<char const*>(end));
|
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
|
// circular buffer is too small, we corrupted the stream
|
||||||
FILAMENT_CHECK_POSTCONDITION(used <= mFreeSpace) <<
|
FILAMENT_CHECK_POSTCONDITION(used <= mFreeSpace) <<
|
||||||
@@ -147,7 +147,7 @@ std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() con
|
|||||||
if (!UTILS_HAS_THREADING) {
|
if (!UTILS_HAS_THREADING) {
|
||||||
return std::move(mCommandBuffersToExecute);
|
return std::move(mCommandBuffersToExecute);
|
||||||
}
|
}
|
||||||
std::unique_lock<utils::Mutex> lock(mLock);
|
std::unique_lock lock(mLock);
|
||||||
while ((mCommandBuffersToExecute.empty() || mPaused) && !mExitRequested) {
|
while ((mCommandBuffersToExecute.empty() || mPaused) && !mExitRequested) {
|
||||||
mCondition.wait(lock);
|
mCondition.wait(lock);
|
||||||
}
|
}
|
||||||
@@ -157,7 +157,7 @@ std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() con
|
|||||||
void CommandBufferQueue::releaseBuffer(CommandBufferQueue::Range const& buffer) {
|
void CommandBufferQueue::releaseBuffer(CommandBufferQueue::Range const& buffer) {
|
||||||
size_t const used = std::distance(
|
size_t const used = std::distance(
|
||||||
static_cast<char const*>(buffer.begin), static_cast<char const*>(buffer.end));
|
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;
|
mFreeSpace += used;
|
||||||
mCondition.notify_one();
|
mCondition.notify_one();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ JavaVM* VirtualMachineEnv::getVirtualMachine() {
|
|||||||
*/
|
*/
|
||||||
UTILS_PUBLIC
|
UTILS_PUBLIC
|
||||||
UTILS_NOINLINE
|
UTILS_NOINLINE
|
||||||
jint VirtualMachineEnv::JNI_OnLoad(JavaVM* vm) noexcept {
|
jint VirtualMachineEnv::JNI_OnLoad(JavaVM* vm) {
|
||||||
std::lock_guard const lock(sLock);
|
std::lock_guard const lock(sLock);
|
||||||
if (sVirtualMachine) {
|
if (sVirtualMachine) {
|
||||||
// It doesn't make sense for JNI_OnLoad() to be called more than once
|
// It doesn't make sense for JNI_OnLoad() to be called more than once
|
||||||
@@ -77,7 +77,7 @@ VirtualMachineEnv& VirtualMachineEnv::get() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
UTILS_NOINLINE
|
UTILS_NOINLINE
|
||||||
JNIEnv* VirtualMachineEnv::getThreadEnvironment() noexcept {
|
JNIEnv* VirtualMachineEnv::getThreadEnvironment() {
|
||||||
JavaVM* const vm = getVirtualMachine();
|
JavaVM* const vm = getVirtualMachine();
|
||||||
JNIEnv* env = nullptr;
|
JNIEnv* env = nullptr;
|
||||||
jint const result = vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
|
jint const result = vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
|
||||||
@@ -101,7 +101,7 @@ VirtualMachineEnv::~VirtualMachineEnv() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
UTILS_NOINLINE
|
UTILS_NOINLINE
|
||||||
JNIEnv* VirtualMachineEnv::getEnvironmentSlow() noexcept {
|
JNIEnv* VirtualMachineEnv::getEnvironmentSlow() {
|
||||||
FILAMENT_CHECK_PRECONDITION(mVirtualMachine)
|
FILAMENT_CHECK_PRECONDITION(mVirtualMachine)
|
||||||
<< "JNI_OnLoad() has not been called";
|
<< "JNI_OnLoad() has not been called";
|
||||||
|
|
||||||
|
|||||||
@@ -218,11 +218,11 @@ void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, const BlitArgs
|
|||||||
}
|
}
|
||||||
[encoder setFragmentTexture:srcTextureColor atIndex:0];
|
[encoder setFragmentTexture:srcTextureColor atIndex:0];
|
||||||
|
|
||||||
SamplerMinFilter filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST;
|
SamplerMinFilter filterMin = SamplerMinFilter::LINEAR;
|
||||||
if (args.filter == SamplerMagFilter::NEAREST) {
|
if (args.filter == SamplerMagFilter::NEAREST) {
|
||||||
filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST;
|
filterMin = SamplerMinFilter::LINEAR;
|
||||||
} else if (args.filter == SamplerMagFilter::LINEAR) {
|
} else if (args.filter == SamplerMagFilter::LINEAR) {
|
||||||
filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST;
|
filterMin = SamplerMinFilter::LINEAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
SamplerState const s {
|
SamplerState const s {
|
||||||
|
|||||||
@@ -1533,7 +1533,7 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
|
|||||||
id<MTLTexture> readPixelsTexture = [mContext->device newTextureWithDescriptor:textureDescriptor];
|
id<MTLTexture> readPixelsTexture = [mContext->device newTextureWithDescriptor:textureDescriptor];
|
||||||
|
|
||||||
MetalBlitter::BlitArgs args{};
|
MetalBlitter::BlitArgs args{};
|
||||||
args.filter = SamplerMagFilter::NEAREST;
|
args.filter = SamplerMagFilter::LINEAR;
|
||||||
args.source.level = miplevel;
|
args.source.level = miplevel;
|
||||||
args.source.region = MTLRegionMake2D(0, 0, srcTexture.width >> miplevel, srcTexture.height >> miplevel);
|
args.source.region = MTLRegionMake2D(0, 0, srcTexture.width >> miplevel, srcTexture.height >> miplevel);
|
||||||
args.source.texture = srcTexture;
|
args.source.texture = srcTexture;
|
||||||
@@ -1650,7 +1650,7 @@ void MetalDriver::blit(
|
|||||||
<< "Metal does not support blitting to/from non-2D textures.";
|
<< "Metal does not support blitting to/from non-2D textures.";
|
||||||
|
|
||||||
MetalBlitter::BlitArgs args{};
|
MetalBlitter::BlitArgs args{};
|
||||||
args.filter = SamplerMagFilter::NEAREST;
|
args.filter = SamplerMagFilter::LINEAR;
|
||||||
args.source.region = MTLRegionMake2D(
|
args.source.region = MTLRegionMake2D(
|
||||||
(NSUInteger)srcOrigin.x,
|
(NSUInteger)srcOrigin.x,
|
||||||
std::max(srcTexture->height - (int64_t)srcOrigin.y - size.y, (int64_t)0),
|
std::max(srcTexture->height - (int64_t)srcOrigin.y - size.y, (int64_t)0),
|
||||||
|
|||||||
@@ -348,7 +348,7 @@ constexpr inline MTLSamplerMinMagFilter getFilter(SamplerMinFilter filter) noexc
|
|||||||
case SamplerMinFilter::NEAREST:
|
case SamplerMinFilter::NEAREST:
|
||||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
|
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
|
||||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
|
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
|
||||||
return MTLSamplerMinMagFilterNearest;
|
return MTLSamplerMinMagFilterLinear;
|
||||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
|
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
|
||||||
case SamplerMinFilter::LINEAR:
|
case SamplerMinFilter::LINEAR:
|
||||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
|
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
|
||||||
@@ -359,7 +359,7 @@ constexpr inline MTLSamplerMinMagFilter getFilter(SamplerMinFilter filter) noexc
|
|||||||
constexpr inline MTLSamplerMinMagFilter getFilter(SamplerMagFilter filter) noexcept {
|
constexpr inline MTLSamplerMinMagFilter getFilter(SamplerMagFilter filter) noexcept {
|
||||||
switch (filter) {
|
switch (filter) {
|
||||||
case SamplerMagFilter::NEAREST:
|
case SamplerMagFilter::NEAREST:
|
||||||
return MTLSamplerMinMagFilterNearest;
|
return MTLSamplerMinMagFilterLinear;
|
||||||
case SamplerMagFilter::LINEAR:
|
case SamplerMagFilter::LINEAR:
|
||||||
return MTLSamplerMinMagFilterLinear;
|
return MTLSamplerMinMagFilterLinear;
|
||||||
}
|
}
|
||||||
@@ -369,10 +369,10 @@ constexpr inline MTLSamplerMipFilter getMipFilter(SamplerMinFilter filter) noexc
|
|||||||
switch (filter) {
|
switch (filter) {
|
||||||
case SamplerMinFilter::NEAREST:
|
case SamplerMinFilter::NEAREST:
|
||||||
case SamplerMinFilter::LINEAR:
|
case SamplerMinFilter::LINEAR:
|
||||||
return MTLSamplerMipFilterNotMipmapped;
|
return MTLSamplerMipFilterLinear;
|
||||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
|
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
|
||||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
|
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
|
||||||
return MTLSamplerMipFilterNearest;
|
return MTLSamplerMipFilterLinear;
|
||||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
|
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
|
||||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
|
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
|
||||||
return MTLSamplerMipFilterLinear;
|
return MTLSamplerMipFilterLinear;
|
||||||
|
|||||||
@@ -943,7 +943,7 @@ void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region
|
|||||||
}
|
}
|
||||||
|
|
||||||
MetalBlitter::BlitArgs args{};
|
MetalBlitter::BlitArgs args{};
|
||||||
args.filter = SamplerMagFilter::NEAREST;
|
args.filter = SamplerMagFilter::LINEAR;
|
||||||
args.source.level = 0;
|
args.source.level = 0;
|
||||||
args.source.slice = 0;
|
args.source.slice = 0;
|
||||||
args.source.region = sourceRegion;
|
args.source.region = sourceRegion;
|
||||||
|
|||||||
@@ -932,7 +932,12 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
|||||||
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
|
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
|
||||||
if (gl.features.multisample_texture) {
|
if (gl.features.multisample_texture) {
|
||||||
// multi-sample texture on GL 3.2 / GLES 3.1 and above
|
// 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 {
|
} else {
|
||||||
// Turn off multi-sampling for that texture. It's just not supported.
|
// Turn off multi-sampling for that texture. It's just not supported.
|
||||||
}
|
}
|
||||||
@@ -1208,7 +1213,12 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
|
|||||||
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
|
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
|
||||||
if (gl.features.multisample_texture) {
|
if (gl.features.multisample_texture) {
|
||||||
// multi-sample texture on GL 3.2 / GLES 3.1 and above
|
// 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 {
|
} else {
|
||||||
// Turn off multi-sampling for that texture. It's just not supported.
|
// Turn off multi-sampling for that texture. It's just not supported.
|
||||||
}
|
}
|
||||||
@@ -1453,8 +1463,15 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
|||||||
#if !defined(__EMSCRIPTEN__) && !defined(FILAMENT_IOS)
|
#if !defined(__EMSCRIPTEN__) && !defined(FILAMENT_IOS)
|
||||||
if (layerCount > 1) {
|
if (layerCount > 1) {
|
||||||
// if layerCount > 1, it means we use the multiview extension.
|
// if layerCount > 1, it means we use the multiview extension.
|
||||||
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, attachment,
|
if (rt->gl.samples > 1) {
|
||||||
t->gl.id, 0, binfo.layer, layerCount);
|
// 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
|
} else
|
||||||
#endif // !defined(__EMSCRIPTEN__) && !defined(FILAMENT_IOS)
|
#endif // !defined(__EMSCRIPTEN__) && !defined(FILAMENT_IOS)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ utils::CString OpenGLPlatform::getRendererString(Driver const* driver) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLPlatform::makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain,
|
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);
|
makeCurrent(getCurrentContextType(), drawSwapChain, readSwapChain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,9 @@ PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
|
|||||||
#ifdef GL_OVR_multiview
|
#ifdef GL_OVR_multiview
|
||||||
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
|
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef GL_OVR_multiview_multisampled_render_to_texture
|
||||||
|
PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC glFramebufferTextureMultisampleMultiviewOVR;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
|
#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
|
// 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
|
#ifdef GL_OVR_multiview
|
||||||
getProcAddress(glFramebufferTextureMultiviewOVR, "glFramebufferTextureMultiviewOVR");
|
getProcAddress(glFramebufferTextureMultiviewOVR, "glFramebufferTextureMultiviewOVR");
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef GL_OVR_multiview_multisampled_render_to_texture
|
||||||
|
getProcAddress(glFramebufferTextureMultisampleMultiviewOVR, "glFramebufferTextureMultisampleMultiviewOVR");
|
||||||
|
#endif
|
||||||
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
|
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
|
||||||
getProcAddress(glDispatchCompute, "glDispatchCompute");
|
getProcAddress(glDispatchCompute, "glDispatchCompute");
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -154,6 +154,9 @@ extern PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
|
|||||||
#ifdef GL_OVR_multiview
|
#ifdef GL_OVR_multiview
|
||||||
extern PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
|
extern PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef GL_OVR_multiview_multisampled_render_to_texture
|
||||||
|
extern PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC glFramebufferTextureMultisampleMultiviewOVR;
|
||||||
|
#endif
|
||||||
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
|
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
|
||||||
extern PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
|
extern PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -255,7 +255,7 @@ void PlatformCocoaGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool PlatformCocoaGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
bool PlatformCocoaGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||||
SwapChain* readSwapChain) noexcept {
|
SwapChain* readSwapChain) {
|
||||||
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
|
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
|
||||||
"ContextManagerCocoa does not support using distinct draw/read swap chains.");
|
"ContextManagerCocoa does not support using distinct draw/read swap chains.");
|
||||||
CocoaGLSwapChain* swapChain = (CocoaGLSwapChain*)drawSwapChain;
|
CocoaGLSwapChain* swapChain = (CocoaGLSwapChain*)drawSwapChain;
|
||||||
|
|||||||
@@ -155,7 +155,7 @@ uint32_t PlatformCocoaTouchGL::getDefaultFramebufferObject() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool PlatformCocoaTouchGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
bool PlatformCocoaTouchGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||||
SwapChain* readSwapChain) noexcept {
|
SwapChain* readSwapChain) {
|
||||||
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
|
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
|
||||||
"PlatformCocoaTouchGL does not support using distinct draw/read swap chains.");
|
"PlatformCocoaTouchGL does not support using distinct draw/read swap chains.");
|
||||||
CAEAGLLayer* const glLayer = (__bridge CAEAGLLayer*) drawSwapChain;
|
CAEAGLLayer* const glLayer = (__bridge CAEAGLLayer*) drawSwapChain;
|
||||||
|
|||||||
@@ -577,18 +577,18 @@ OpenGLPlatform::ContextType PlatformEGL::getCurrentContextType() const noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool PlatformEGL::makeCurrent(ContextType type,
|
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 dsc = static_cast<SwapChainEGL const*>(drawSwapChain);
|
||||||
SwapChainEGL const* const rsc = static_cast<SwapChainEGL const*>(readSwapChain);
|
SwapChainEGL const* const rsc = static_cast<SwapChainEGL const*>(readSwapChain);
|
||||||
EGLContext context = getContextForType(type);
|
EGLContext context = getContextForType(type);
|
||||||
EGLBoolean const success = egl.makeCurrent(context, dsc->sur, rsc->sur);
|
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,
|
void PlatformEGL::makeCurrent(SwapChain* drawSwapChain,
|
||||||
SwapChain* readSwapChain,
|
SwapChain* readSwapChain,
|
||||||
Invocable<void()> preContextChange,
|
Invocable<void()> preContextChange,
|
||||||
Invocable<void(size_t index)> postContextChange) noexcept {
|
Invocable<void(size_t index)> postContextChange) {
|
||||||
|
|
||||||
assert_invariant(drawSwapChain);
|
assert_invariant(drawSwapChain);
|
||||||
assert_invariant(readSwapChain);
|
assert_invariant(readSwapChain);
|
||||||
@@ -796,7 +796,7 @@ void PlatformEGL::Config::erase(EGLint name) noexcept {
|
|||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
EGLBoolean PlatformEGL::EGL::makeCurrent(EGLContext context, EGLSurface drawSurface,
|
EGLBoolean PlatformEGL::EGL::makeCurrent(EGLContext context, EGLSurface drawSurface,
|
||||||
EGLSurface readSurface) noexcept {
|
EGLSurface readSurface) {
|
||||||
if (UTILS_UNLIKELY((
|
if (UTILS_UNLIKELY((
|
||||||
mCurrentContext != context ||
|
mCurrentContext != context ||
|
||||||
drawSurface != mCurrentDrawSurface || readSurface != mCurrentReadSurface))) {
|
drawSurface != mCurrentDrawSurface || readSurface != mCurrentReadSurface))) {
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ static constexpr const std::string_view kNativeWindowInvalidMsg =
|
|||||||
|
|
||||||
bool PlatformEGLAndroid::makeCurrent(ContextType type,
|
bool PlatformEGLAndroid::makeCurrent(ContextType type,
|
||||||
SwapChain* drawSwapChain,
|
SwapChain* drawSwapChain,
|
||||||
SwapChain* readSwapChain) noexcept {
|
SwapChain* readSwapChain) {
|
||||||
|
|
||||||
// fast & safe path
|
// fast & safe path
|
||||||
if (UTILS_LIKELY(!mAssertNativeWindowIsValid)) {
|
if (UTILS_LIKELY(!mAssertNativeWindowIsValid)) {
|
||||||
|
|||||||
@@ -266,7 +266,7 @@ void PlatformGLX::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool PlatformGLX::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
bool PlatformGLX::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||||
SwapChain* readSwapChain) noexcept {
|
SwapChain* readSwapChain) {
|
||||||
g_glx.setCurrentContext(mGLXDisplay,
|
g_glx.setCurrentContext(mGLXDisplay,
|
||||||
(GLXDrawable)drawSwapChain, (GLXDrawable)readSwapChain, mGLXContext);
|
(GLXDrawable)drawSwapChain, (GLXDrawable)readSwapChain, mGLXContext);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ void PlatformOSMesa::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool PlatformOSMesa::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
bool PlatformOSMesa::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||||
SwapChain* readSwapChain) noexcept {
|
SwapChain* readSwapChain) {
|
||||||
OSMesaAPI* api = (OSMesaAPI*) mOsMesaApi;
|
OSMesaAPI* api = (OSMesaAPI*) mOsMesaApi;
|
||||||
OSMesaSwapchain* impl = (OSMesaSwapchain*) drawSwapChain;
|
OSMesaSwapchain* impl = (OSMesaSwapchain*) drawSwapChain;
|
||||||
|
|
||||||
|
|||||||
@@ -262,7 +262,7 @@ void PlatformWGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool PlatformWGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
bool PlatformWGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||||
SwapChain* readSwapChain) noexcept {
|
SwapChain* readSwapChain) {
|
||||||
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
|
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
|
||||||
"PlatformWGL does not support distinct draw/read swap chains.");
|
"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,
|
bool PlatformWebGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||||
SwapChain* readSwapChain) noexcept {
|
SwapChain* readSwapChain) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
|
|
||||||
#include <utils/bitset.h>
|
#include <utils/bitset.h>
|
||||||
#include <utils/FixedCapacityVector.h>
|
#include <utils/FixedCapacityVector.h>
|
||||||
|
#include <utils/Panic.h>
|
||||||
|
|
||||||
#include <bluevk/BlueVK.h>
|
#include <bluevk/BlueVK.h>
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#include "WebGPUHandles.h"
|
#include "WebGPUHandles.h"
|
||||||
|
|
||||||
#include "WebGPUConstants.h"
|
#include "WebGPUConstants.h"
|
||||||
|
#include "WebGPUStrings.h"
|
||||||
|
|
||||||
#include "DriverBase.h"
|
#include "DriverBase.h"
|
||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
@@ -28,39 +29,116 @@
|
|||||||
#include <webgpu/webgpu_cpp.h>
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <string>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <unordered_map>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
namespace filament::backend {
|
namespace filament::backend {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
[[nodiscard]] constexpr std::string_view toString(ShaderStage stage) {
|
[[nodiscard]] std::string replaceSpecConstants(std::string_view shaderLabel,
|
||||||
switch (stage) {
|
std::string_view shaderSource,
|
||||||
case ShaderStage::VERTEX:
|
std::unordered_map<uint32_t, std::variant<int32_t, float, bool>> const&
|
||||||
return "vertex";
|
specConstants) {
|
||||||
case ShaderStage::FRAGMENT:
|
assert_invariant(!specConstants.empty());
|
||||||
return "fragment";
|
constexpr std::string_view specConstantPrefix = "FILAMENT_SPEC_CONST_";
|
||||||
case ShaderStage::COMPUTE:
|
constexpr size_t specConstantPrefixSize = specConstantPrefix.size();
|
||||||
return "compute";
|
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,
|
[[nodiscard]] wgpu::ShaderModule createShaderModule(wgpu::Device& device, Program& program,
|
||||||
std::array<utils::FixedCapacityVector<uint8_t>, Program::SHADER_TYPE_COUNT> const&
|
ShaderStage stage,
|
||||||
shaderSource,
|
std::unordered_map<uint32_t, std::variant<int32_t, float, bool>> const& specConstants) {
|
||||||
ShaderStage stage) {
|
const char* const programName = program.getName().c_str_safe();
|
||||||
|
std::array<utils::FixedCapacityVector<uint8_t>, Program::SHADER_TYPE_COUNT> const&
|
||||||
|
shaderSource = program.getShadersSource();
|
||||||
utils::FixedCapacityVector<uint8_t> const& sourceBytes =
|
utils::FixedCapacityVector<uint8_t> const& sourceBytes =
|
||||||
shaderSource[static_cast<size_t>(stage)];
|
shaderSource[static_cast<size_t>(stage)];
|
||||||
if (sourceBytes.empty()) {
|
if (sourceBytes.empty()) {
|
||||||
return nullptr;// nothing to compile, the shader was not provided
|
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;
|
std::stringstream labelStream;
|
||||||
labelStream << programName << " " << toString(stage) << " shader";
|
labelStream << programName << " " << filamentShaderStageToString(stage) << " shader";
|
||||||
auto label = labelStream.str();
|
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{
|
wgpu::ShaderModuleDescriptor descriptor{
|
||||||
.nextInChain = &wgslDescriptor,
|
.nextInChain = &wgslDescriptor,
|
||||||
.label = label.data()
|
.label = label.data()
|
||||||
@@ -119,85 +197,33 @@ namespace {
|
|||||||
<< ":\n"
|
<< ":\n"
|
||||||
<< errorStream.str();
|
<< errorStream.str();
|
||||||
}
|
}
|
||||||
|
#if FWGPU_ENABLED(FWGPU_DEBUG_VALIDATION)
|
||||||
FWGPU_LOGD << descriptor.label << " compiled successfully"
|
FWGPU_LOGD << descriptor.label << " compiled successfully"
|
||||||
<< utils::io::endl;
|
<< utils::io::endl;
|
||||||
|
#endif
|
||||||
}),
|
}),
|
||||||
UINT16_MAX);
|
SHADER_COMPILATION_TIMEOUT_NANOSECONDS);
|
||||||
return module;
|
return module;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a 1 to 1 mapping of the ReservedSpecializationConstants enum in EngineEnums.h
|
void toMap(utils::FixedCapacityVector<Program::SpecializationConstant> const& specConstants,
|
||||||
// The _hack is a workaround until https://issues.chromium.org/issues/42250586 is resolved
|
std::unordered_map<uint32_t, std::variant<int32_t, float, bool>>& constantById) {
|
||||||
// This workaround is the same one being used on the generateSpecializationConstant() function
|
constantById.reserve(specConstants.size());
|
||||||
wgpu::StringView getSpecConstantStringId(uint32_t id) {
|
for (auto const& specConstant: specConstants) {
|
||||||
switch (id) {
|
constantById.emplace(specConstant.id, specConstant.value);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
}// namespace
|
||||||
|
|
||||||
WGPUProgram::WGPUProgram(wgpu::Device& device, Program& program)
|
WGPUProgram::WGPUProgram(wgpu::Device& device, Program& program)
|
||||||
: HwProgram(program.getName()),
|
: HwProgram(program.getName()) {
|
||||||
vertexShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
|
std::unordered_map<uint32_t, std::variant<int32_t, float, bool>> specConstants;
|
||||||
ShaderStage::VERTEX)),
|
toMap(program.getSpecializationConstants(), specConstants);
|
||||||
fragmentShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
|
vertexShaderModule = createShaderModule(device, program, ShaderStage::VERTEX, specConstants);
|
||||||
ShaderStage::FRAGMENT)),
|
fragmentShaderModule =
|
||||||
computeShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
|
createShaderModule(device, program, ShaderStage::FRAGMENT, specConstants);
|
||||||
ShaderStage::COMPUTE)),
|
computeShaderModule = createShaderModule(device, program, ShaderStage::COMPUTE, specConstants);
|
||||||
constants(convertConstants(program.getSpecializationConstants())) {}
|
}
|
||||||
|
|
||||||
}// namespace filament::backend
|
}// namespace filament::backend
|
||||||
|
|||||||
@@ -63,4 +63,13 @@
|
|||||||
#define FWGPU_LOGI (utils::slog.i)
|
#define FWGPU_LOGI (utils::slog.i)
|
||||||
#endif
|
#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
|
#endif// TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
|
||||||
|
|||||||
@@ -32,7 +32,6 @@
|
|||||||
#include <utils/Panic.h>
|
#include <utils/Panic.h>
|
||||||
#include <utils/ostream.h>
|
#include <utils/ostream.h>
|
||||||
|
|
||||||
#include <dawn/webgpu_cpp_print.h>
|
|
||||||
#include <webgpu/webgpu_cpp.h>
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@@ -41,179 +40,10 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string_view>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
namespace filament::backend {
|
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 {
|
Driver* WebGPUDriver::create(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept {
|
||||||
constexpr size_t defaultSize = FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
|
constexpr size_t defaultSize = FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
|
||||||
Platform::DriverConfig validConfig {driverConfig};
|
Platform::DriverConfig validConfig {driverConfig};
|
||||||
@@ -221,26 +51,16 @@ Driver* WebGPUDriver::create(WebGPUPlatform& platform, const Platform::DriverCon
|
|||||||
return new WebGPUDriver(platform, validConfig);
|
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),
|
: mPlatform(platform),
|
||||||
mHandleAllocator("Handles",
|
mHandleAllocator("Handles", driverConfig.handleArenaSize,
|
||||||
driverConfig.handleArenaSize,
|
driverConfig.disableHandleUseAfterFreeCheck, driverConfig.disableHeapHandleTags) {
|
||||||
driverConfig.disableHandleUseAfterFreeCheck,
|
|
||||||
driverConfig.disableHeapHandleTags) {
|
|
||||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
|
||||||
printInstanceDetails(mPlatform.getInstance());
|
|
||||||
#endif
|
|
||||||
mAdapter = mPlatform.requestAdapter(nullptr);
|
mAdapter = mPlatform.requestAdapter(nullptr);
|
||||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
|
||||||
printAdapterDetails(mAdapter);
|
|
||||||
#endif
|
|
||||||
mDevice = mPlatform.requestDevice(mAdapter);
|
mDevice = mPlatform.requestDevice(mAdapter);
|
||||||
wgpu::Limits supportedLimits{};
|
wgpu::Limits supportedLimits{};
|
||||||
mDevice.GetLimits(&supportedLimits);
|
mDevice.GetLimits(&supportedLimits);
|
||||||
mMinUniformBufferOffsetAlignment = supportedLimits.minUniformBufferOffsetAlignment;
|
mMinUniformBufferOffsetAlignment = supportedLimits.minUniformBufferOffsetAlignment;
|
||||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
|
||||||
printDeviceDetails(mDevice);
|
|
||||||
#endif
|
|
||||||
mQueue = mDevice.GetQueue();
|
mQueue = mDevice.GetQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -354,6 +174,20 @@ void WebGPUDriver::destroyProgram(Handle<HwProgram> ph) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
|
void WebGPUDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
|
||||||
|
if (rth) {
|
||||||
|
WGPURenderTarget* rt = handleCast<WGPURenderTarget>(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.
|
||||||
|
// They are destroyed via WebGPUDriver::destroyTexture.
|
||||||
|
destructHandle<WGPURenderTarget>(rth);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::destroySwapChain(Handle<HwSwapChain> sch) {
|
void WebGPUDriver::destroySwapChain(Handle<HwSwapChain> sch) {
|
||||||
@@ -376,6 +210,19 @@ void WebGPUDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::destroyDescriptorSet(Handle<HwDescriptorSet> 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) {
|
if (tqh) {
|
||||||
destructHandle<WebGPUDescriptorSet>(tqh);
|
destructHandle<WebGPUDescriptorSet>(tqh);
|
||||||
}
|
}
|
||||||
@@ -580,7 +427,21 @@ void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
|
|||||||
|
|
||||||
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets,
|
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets,
|
||||||
uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color,
|
uint32_t width, uint32_t height, uint8_t samples, uint8_t layerCount, MRT color,
|
||||||
TargetBufferInfo depth, TargetBufferInfo stencil) {}
|
TargetBufferInfo depth, TargetBufferInfo stencil) {
|
||||||
|
// The `targets` flags indicate which of the `color`, `depth`, `stencil` TargetBufferInfo
|
||||||
|
// are actually active for this render target.
|
||||||
|
// We'll pass all TargetBufferInfo to WGPURenderTarget; it will use them if their handles are valid.
|
||||||
|
|
||||||
|
// Ensure that textures intended for use as attachments were created with
|
||||||
|
// wgpu::TextureUsage::RenderAttachment. This check should ideally be in createTextureR
|
||||||
|
// or validated here if possible.
|
||||||
|
|
||||||
|
// The `layerCount` parameter might be for creating array textures that this RT targets.
|
||||||
|
// Individual attachments (color[i].layer, depth.layer, stencil.layer) specify which layer
|
||||||
|
// of an array texture to bind. For now, we assume textures are pre-configured.
|
||||||
|
|
||||||
|
constructHandle<WGPURenderTarget>(rth, width, height, samples, color, depth, stencil);
|
||||||
|
}
|
||||||
|
|
||||||
void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {}
|
void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {}
|
||||||
|
|
||||||
@@ -663,7 +524,7 @@ bool WebGPUDriver::isAutoDepthResolveSupported() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool WebGPUDriver::isSRGBSwapChainSupported() {
|
bool WebGPUDriver::isSRGBSwapChainSupported() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool WebGPUDriver::isProtectedContentSupported() {
|
bool WebGPUDriver::isProtectedContentSupported() {
|
||||||
@@ -845,35 +706,81 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
|
|||||||
|
|
||||||
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, RenderPassParams const& params) {
|
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, RenderPassParams const& params) {
|
||||||
assert_invariant(mCommandEncoder);
|
assert_invariant(mCommandEncoder);
|
||||||
|
|
||||||
auto* renderTarget = handleCast<WGPURenderTarget>(rth);
|
auto* renderTarget = handleCast<WGPURenderTarget>(rth);
|
||||||
// if (renderTarget == mDefaultRenderTarget) {
|
|
||||||
// FWGPU_LOGW << "Default render target"
|
|
||||||
// << utils::io::endl;
|
|
||||||
// } else {
|
|
||||||
// FWGPU_LOGW << "Non Default render target"
|
|
||||||
// << utils::io::endl;
|
|
||||||
// }
|
|
||||||
wgpu::RenderPassDescriptor renderPassDescriptor;
|
|
||||||
wgpu::RenderPassDepthStencilAttachment depthStencilAttachment{
|
|
||||||
.view = mSwapChain->getDepthTextureView(),
|
|
||||||
.depthLoadOp = WGPURenderTarget::getLoadOperation(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),
|
|
||||||
.stencilClearValue = params.clearStencil,
|
|
||||||
.stencilReadOnly = (params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0
|
|
||||||
};
|
|
||||||
renderTarget->setUpRenderPassAttachments(renderPassDescriptor, mTextureView, params);
|
|
||||||
renderPassDescriptor.depthStencilAttachment = &depthStencilAttachment;
|
|
||||||
|
|
||||||
assert_invariant(mTextureView);
|
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;
|
||||||
|
defaultDepthStencilView = mSwapChain->getDepthTextureView();
|
||||||
|
} else {
|
||||||
|
// Resolve views for custom render target
|
||||||
|
const auto& colorInfos = renderTarget->getColorAttachmentInfos();
|
||||||
|
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; ++i) {
|
||||||
|
if (colorInfos[i].handle) {
|
||||||
|
auto* hwTexture = handleCast<WGPUTexture>(colorInfos[i].handle);
|
||||||
|
if (hwTexture) {
|
||||||
|
// TODO: Consider colorInfos[i].level and colorInfos[i].layer for view creation
|
||||||
|
// if WGPUTexture::getTextureView() isn't sufficient or needs parameters.
|
||||||
|
customColorViews[customColorViewCount++] = hwTexture->getTextureView();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& depthInfo = renderTarget->getDepthAttachmentInfo();
|
||||||
|
if (depthInfo.handle) {
|
||||||
|
auto* hwTexture = handleCast<WGPUTexture>(depthInfo.handle);
|
||||||
|
if (hwTexture) {
|
||||||
|
customDepthView = hwTexture->getTextureView();
|
||||||
|
customDepthFormat = hwTexture->getFormat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& stencilInfo = renderTarget->getStencilAttachmentInfo();
|
||||||
|
if (stencilInfo.handle) {
|
||||||
|
// 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->getTextureView();
|
||||||
|
customStencilFormat = hwTexture->getFormat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wgpu::RenderPassDescriptor renderPassDescriptor{};
|
||||||
|
renderTarget->setUpRenderPassAttachments(renderPassDescriptor,
|
||||||
|
params,
|
||||||
|
defaultColorView,
|
||||||
|
defaultDepthStencilView,
|
||||||
|
customColorViews.data(),
|
||||||
|
customColorViewCount,
|
||||||
|
customDepthView,
|
||||||
|
customStencilView,
|
||||||
|
customDepthFormat,
|
||||||
|
customStencilFormat);
|
||||||
|
|
||||||
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
|
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
|
||||||
mRenderPassEncoder.SetViewport(params.viewport.left, params.viewport.bottom,
|
|
||||||
params.viewport.width, params.viewport.height, params.depthRange.near, params.depthRange.far);
|
// 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>(params.viewport.width),
|
||||||
|
static_cast<float>(params.viewport.height),
|
||||||
|
params.depthRange.near,
|
||||||
|
params.depthRange.far);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::endRenderPass(int /* dummy */) {
|
void WebGPUDriver::endRenderPass(int /* dummy */) {
|
||||||
@@ -966,7 +873,7 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
|
|||||||
// VulkanPipelineCache to handle this, may be missing nuance
|
// VulkanPipelineCache to handle this, may be missing nuance
|
||||||
static auto pipleineStateHasher = utils::hash::MurmurHashFn<filament::backend::PipelineState>();
|
static auto pipleineStateHasher = utils::hash::MurmurHashFn<filament::backend::PipelineState>();
|
||||||
auto hash = pipleineStateHasher(pipelineState);
|
auto hash = pipleineStateHasher(pipelineState);
|
||||||
if(mPipelineMap.find(hash) != mPipelineMap.end()){
|
if (mPipelineMap.find(hash) != mPipelineMap.end()) {
|
||||||
mRenderPassEncoder.SetPipeline(mPipelineMap[hash]);
|
mRenderPassEncoder.SetPipeline(mPipelineMap[hash]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1002,24 +909,67 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
|
|||||||
<< layoutDescriptor.label;
|
<< layoutDescriptor.label;
|
||||||
auto const* vertexBufferInfo = handleCast<WGPUVertexBufferInfo>(pipelineState.vertexBufferInfo);
|
auto const* vertexBufferInfo = handleCast<WGPUVertexBufferInfo>(pipelineState.vertexBufferInfo);
|
||||||
assert_invariant(vertexBufferInfo);
|
assert_invariant(vertexBufferInfo);
|
||||||
const wgpu::RenderPipeline pipeline = createWebGPURenderPipeline(mDevice, *program,
|
|
||||||
*vertexBufferInfo, layout, pipelineState.rasterState, pipelineState.stencilState,
|
std::vector<wgpu::TextureFormat> pipelineColorFormats;
|
||||||
pipelineState.polygonOffset, pipelineState.primitiveType, mSwapChain->getColorFormat(),
|
wgpu::TextureFormat pipelineDepthFormat = wgpu::TextureFormat::Undefined;
|
||||||
mSwapChain->getDepthFormat());
|
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;
|
mPipelineMap[hash] = pipeline;
|
||||||
mRenderPassEncoder.SetPipeline(pipeline);
|
mRenderPassEncoder.SetPipeline(pipeline);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||||
auto* renderPrimitive = handleCast<WGPURenderPrimitive>(rph);
|
auto* renderPrimitive = handleCast<WGPURenderPrimitive>(rph);
|
||||||
|
|
||||||
// This *must* match the WGPUVertexBufferInfo that was bound in bindPipeline(). But we want
|
|
||||||
// to allow to call this before bindPipeline(), so the validation can only happen in draw()
|
|
||||||
auto vbi = handleCast<WGPUVertexBufferInfo>(renderPrimitive->vertexBuffer->vbih);
|
auto vbi = handleCast<WGPUVertexBufferInfo>(renderPrimitive->vertexBuffer->vbih);
|
||||||
assert_invariant(
|
for (const auto& webGPUSlotBindings: vbi->getWebGPUSlotBindingInfos()) {
|
||||||
vbi->getVertexBufferLayoutSize() == renderPrimitive->vertexBuffer->buffers.size());
|
mRenderPassEncoder.SetVertexBuffer(webGPUSlotBindings.slot,
|
||||||
for (uint32_t i = 0; i < vbi->getVertexBufferLayoutSize(); i++) {
|
renderPrimitive->vertexBuffer->buffers[webGPUSlotBindings.sourceBuffer],
|
||||||
mRenderPassEncoder.SetVertexBuffer(i, renderPrimitive->vertexBuffer->buffers[i]);
|
webGPUSlotBindings.bufferOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
mRenderPassEncoder.SetIndexBuffer(renderPrimitive->indexBuffer->getBuffer(),
|
mRenderPassEncoder.SetIndexBuffer(renderPrimitive->indexBuffer->getBuffer(),
|
||||||
@@ -1027,11 +977,27 @@ void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
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);
|
mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::draw(PipelineState, Handle<HwRenderPrimitive>, uint32_t indexOffset,
|
void WebGPUDriver::draw(PipelineState pipelineState,
|
||||||
uint32_t indexCount, uint32_t instanceCount) {
|
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);
|
draw2(indexOffset, indexCount, instanceCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1080,7 +1046,7 @@ void WebGPUDriver::updateDescriptorSetTexture(Handle<HwDescriptorSet> dsh,
|
|||||||
auto sampler = makeSampler(params);
|
auto sampler = makeSampler(params);
|
||||||
// TODO making assumptions that size and offset mean the same thing here.
|
// TODO making assumptions that size and offset mean the same thing here.
|
||||||
wgpu::BindGroupEntry tEntry{ .binding = static_cast<uint32_t>(binding * 2),
|
wgpu::BindGroupEntry tEntry{ .binding = static_cast<uint32_t>(binding * 2),
|
||||||
.textureView = texture->getTexView() };
|
.textureView = texture->getTextureView() };
|
||||||
bindGroup->addEntry(tEntry.binding, std::move(tEntry));
|
bindGroup->addEntry(tEntry.binding, std::move(tEntry));
|
||||||
|
|
||||||
wgpu::BindGroupEntry sEntry{ .binding = static_cast<uint32_t>(binding * 2 + 1),
|
wgpu::BindGroupEntry sEntry{ .binding = static_cast<uint32_t>(binding * 2 + 1),
|
||||||
@@ -1091,15 +1057,18 @@ void WebGPUDriver::updateDescriptorSetTexture(Handle<HwDescriptorSet> dsh,
|
|||||||
|
|
||||||
void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> dsh,
|
void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> dsh,
|
||||||
backend::descriptor_set_t setIndex, backend::DescriptorSetOffsetArray&& offsets) {
|
backend::descriptor_set_t setIndex, backend::DescriptorSetOffsetArray&& offsets) {
|
||||||
|
assert_invariant(setIndex < MAX_DESCRIPTOR_SET_COUNT);
|
||||||
const auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
|
const auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
|
||||||
const auto wbg = bindGroup->lockAndReturn(mDevice);
|
const auto wbg = bindGroup->lockAndReturn(mDevice);
|
||||||
assert_invariant(mRenderPassEncoder);
|
|
||||||
const size_t dynamicOffsetCount = bindGroup->countEntitiesWithDynamicOffsets();
|
mCurrentDescriptorSets[setIndex] = { .bindGroup = wbg,
|
||||||
mRenderPassEncoder.SetBindGroup(setIndex, wbg, dynamicOffsetCount, offsets.data());
|
.offsetCount = bindGroup->countEntitiesWithDynamicOffsets(),
|
||||||
|
.offsets = std::move(offsets) };
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
|
void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
|
||||||
}
|
}
|
||||||
|
|
||||||
wgpu::Sampler WebGPUDriver::makeSampler(SamplerParams const& params) {
|
wgpu::Sampler WebGPUDriver::makeSampler(SamplerParams const& params) {
|
||||||
wgpu::SamplerDescriptor desc{};
|
wgpu::SamplerDescriptor desc{};
|
||||||
|
|
||||||
|
|||||||
@@ -74,8 +74,16 @@ private:
|
|||||||
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
|
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
|
||||||
wgpu::CommandBuffer mCommandBuffer = nullptr;
|
wgpu::CommandBuffer mCommandBuffer = nullptr;
|
||||||
WGPURenderTarget* mDefaultRenderTarget = nullptr;
|
WGPURenderTarget* mDefaultRenderTarget = nullptr;
|
||||||
|
WGPURenderTarget* mCurrentRenderTarget = nullptr;
|
||||||
|
|
||||||
tsl::robin_map<uint32_t, wgpu::RenderPipeline> mPipelineMap;
|
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
|
* Driver interface
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -196,43 +196,90 @@ void WGPUBufferBase::updateGPUBuffer(BufferDescriptor& bufferDescriptor, uint32_
|
|||||||
queue.WriteBuffer(buffer, byteOffset + legalSize, &mRemainderChunk, 4);
|
queue.WriteBuffer(buffer, byteOffset + legalSize, &mRemainderChunk, 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static constexpr uint32_t DUMMY_WEBGPU_SLOT = 0;
|
||||||
|
|
||||||
WGPUVertexBufferInfo::WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
|
WGPUVertexBufferInfo::WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
|
||||||
AttributeArray const& attributes)
|
AttributeArray const& attributes)
|
||||||
: HwVertexBufferInfo(bufferCount, attributeCount),
|
: HwVertexBufferInfo(bufferCount, attributeCount),
|
||||||
mVertexBufferLayout(bufferCount),
|
// TODO: max limits may not be supported by webgpu driver. This should be addressed in the
|
||||||
mAttributes(bufferCount) {
|
// hardening part.
|
||||||
assert_invariant(attributeCount > 0);
|
mVertexBufferLayouts(MAX_VERTEX_BUFFER_COUNT),
|
||||||
assert_invariant(bufferCount > 0);
|
mVertexAttributes(MAX_VERTEX_BUFFER_COUNT) {
|
||||||
for (uint32_t attribIndex = 0; attribIndex < attributes.size(); attribIndex++) {
|
|
||||||
Attribute const& attrib = attributes[attribIndex];
|
// It starts from 1 because slot 0 is now reserved for the dummy.
|
||||||
// Ignore the attributes which are not bind to vertex buffers.
|
uint32_t currentWebGPUSlotIndex = 1;
|
||||||
if (attrib.buffer == Attribute::BUFFER_UNUSED) {
|
// A reasonable dummy stride (e.g., for vec4)
|
||||||
|
const uint32_t DUMMY_STRIDE = 16;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
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;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_invariant(attrib.buffer < bufferCount);
|
auto it = std::find_if(mWebGPUSlotBindingInfos.begin(), mWebGPUSlotBindingInfos.end(),
|
||||||
bool const isInteger = attrib.flags & Attribute::FLAG_INTEGER_TARGET;
|
[&](const auto& info) {
|
||||||
bool const isNormalized = attrib.flags & Attribute::FLAG_NORMALIZED;
|
return info.sourceBuffer == attribute.buffer && info.stride == attribute.stride;
|
||||||
wgpu::VertexFormat vertexFormat = getVertexFormat(attrib.type, isNormalized, isInteger);
|
});
|
||||||
|
|
||||||
// Attributes are sequential per buffer
|
uint32_t assignedSlot;
|
||||||
mAttributes[attrib.buffer].push_back({
|
if (it != mWebGPUSlotBindingInfos.end()) {
|
||||||
.format = vertexFormat,
|
assignedSlot = it->slot;
|
||||||
.offset = attrib.offset,
|
|
||||||
.shaderLocation = attribIndex,
|
|
||||||
});
|
|
||||||
|
|
||||||
mVertexBufferLayout[attrib.buffer].stepMode = wgpu::VertexStepMode::Vertex;
|
|
||||||
if (mVertexBufferLayout[attrib.buffer].arrayStride == 0) {
|
|
||||||
mVertexBufferLayout[attrib.buffer].arrayStride = attrib.stride;
|
|
||||||
} else {
|
} else {
|
||||||
assert_invariant(mVertexBufferLayout[attrib.buffer].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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mVertexAttributes[assignedSlot].push_back({
|
||||||
|
.format = vertexFormat,
|
||||||
|
.offset = attribute.offset % attribute.stride,
|
||||||
|
.shaderLocation = attributeIndex,
|
||||||
|
});
|
||||||
|
mVertexBufferLayouts[assignedSlot].attributeCount++;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint32_t bufferIndex = 0; bufferIndex < bufferCount; bufferIndex++) {
|
mVertexBufferLayouts.resize(currentWebGPUSlotIndex);
|
||||||
mVertexBufferLayout[bufferIndex].attributeCount = mAttributes[bufferIndex].size();
|
|
||||||
mVertexBufferLayout[bufferIndex].attributes = mAttributes[bufferIndex].data();
|
for (const auto& info: mWebGPUSlotBindingInfos) {
|
||||||
|
mVertexBufferLayouts[info.slot].attributes = mVertexAttributes[info.slot].data();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -545,7 +592,7 @@ WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat forma
|
|||||||
FILAMENT_CHECK_POSTCONDITION(mTexture)
|
FILAMENT_CHECK_POSTCONDITION(mTexture)
|
||||||
<< "Failed to create texture for " << textureDescriptor.label;
|
<< "Failed to create texture for " << textureDescriptor.label;
|
||||||
// Second, the texture view aspect
|
// 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 {
|
WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept {
|
||||||
@@ -554,7 +601,7 @@ WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount
|
|||||||
mBlockWidth = src->mBlockWidth;
|
mBlockWidth = src->mBlockWidth;
|
||||||
mBlockHeight = src->mBlockHeight;
|
mBlockHeight = src->mBlockHeight;
|
||||||
|
|
||||||
mTexView = makeTextureView(baseLevel, levelCount, target);
|
mTexureView = makeTextureView(baseLevel, levelCount, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(TextureUsage const& fUsage) {
|
wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(TextureUsage const& fUsage) {
|
||||||
@@ -923,58 +970,153 @@ wgpu::TextureView WGPUTexture::makeTextureView(const uint8_t& baseLevel, const u
|
|||||||
return textureView;
|
return textureView;
|
||||||
}
|
}
|
||||||
|
|
||||||
WGPURenderTarget::Attachment WGPURenderTarget::getDrawColorAttachment(size_t index) {
|
WGPURenderTarget::WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples,
|
||||||
assert_invariant( index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
|
const MRT& colorAttachmentsMRT,
|
||||||
auto result = color[index];
|
const Attachment& depthAttachmentInfo,
|
||||||
if (index == 0 && defaultRenderTarget) {
|
const Attachment& stencilAttachmentInfo)
|
||||||
|
: HwRenderTarget(width, height),
|
||||||
}
|
defaultRenderTarget(false),
|
||||||
|
samples(samples),
|
||||||
return result;
|
mColorAttachments(colorAttachmentsMRT),
|
||||||
|
mDepthAttachment(depthAttachmentInfo),
|
||||||
|
mStencilAttachment(stencilAttachmentInfo) {
|
||||||
|
// TODO Make this an array
|
||||||
|
mColorAttachmentDescriptors.reserve(MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
|
||||||
}
|
}
|
||||||
|
|
||||||
wgpu::LoadOp WGPURenderTarget::getLoadOperation(RenderPassParams const& params,
|
wgpu::LoadOp WGPURenderTarget::getLoadOperation(RenderPassParams const& params,
|
||||||
TargetBufferFlags buffer) {
|
TargetBufferFlags bufferToOperateOn) {
|
||||||
auto clearFlags = params.flags.clear;
|
if (any(params.flags.clear & bufferToOperateOn)) {
|
||||||
auto discardStartFlags = params.flags.discardStart;
|
|
||||||
if (any(clearFlags & buffer)) {
|
|
||||||
return wgpu::LoadOp::Clear;
|
|
||||||
} else if (any(discardStartFlags & buffer)) {
|
|
||||||
return wgpu::LoadOp::Clear;
|
return wgpu::LoadOp::Clear;
|
||||||
}
|
}
|
||||||
|
if (any(params.flags.discardStart & bufferToOperateOn)) {
|
||||||
|
return wgpu::LoadOp::Clear; // Or wgpu::LoadOp::Undefined if clear is not desired on discard
|
||||||
|
}
|
||||||
return wgpu::LoadOp::Load;
|
return wgpu::LoadOp::Load;
|
||||||
}
|
}
|
||||||
|
|
||||||
wgpu::StoreOp WGPURenderTarget::getStoreOperation(RenderPassParams const& params,
|
wgpu::StoreOp WGPURenderTarget::getStoreOperation(RenderPassParams const& params,
|
||||||
TargetBufferFlags buffer) {
|
TargetBufferFlags bufferToOperateOn) {
|
||||||
const auto discardEndFlags = params.flags.discardEnd;
|
if (any(params.flags.discardEnd & bufferToOperateOn)) {
|
||||||
if (any(discardEndFlags & buffer)) {
|
|
||||||
return wgpu::StoreOp::Discard;
|
return wgpu::StoreOp::Discard;
|
||||||
}
|
}
|
||||||
return wgpu::StoreOp::Store;
|
return wgpu::StoreOp::Store;
|
||||||
}
|
}
|
||||||
|
|
||||||
void WGPURenderTarget::setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor,
|
void WGPURenderTarget::setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor,
|
||||||
wgpu::TextureView const& textureView, RenderPassParams const& params) {
|
RenderPassParams const& params, wgpu::TextureView const& defaultColorTextureView,
|
||||||
// auto discardFlags = params.flags.discardEnd;
|
wgpu::TextureView const& defaultDepthStencilTextureView,
|
||||||
// (void) discardFlags;
|
wgpu::TextureView const* customColorTextureViews, uint32_t customColorTextureViewCount,
|
||||||
// std::vector<wgpu::RenderPassColorAttachment> colorAttachments;
|
wgpu::TextureView const& customDepthTextureView,
|
||||||
colorAttachments.clear();
|
wgpu::TextureView const& customStencilTextureView, wgpu::TextureFormat customDepthFormat,
|
||||||
for (size_t i = 0; i < 1/*MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT*/; i++) {
|
wgpu::TextureFormat customStencilFormat) {
|
||||||
// auto attachment = getDrawColorAttachment(i);
|
mColorAttachmentDescriptors.clear();
|
||||||
// if (attachment) {
|
mHasDepthStencilAttachment = false;
|
||||||
wgpu::RenderPassColorAttachment colorAttachment;
|
|
||||||
colorAttachment.view = textureView;
|
if (defaultRenderTarget) {
|
||||||
colorAttachment.loadOp = getLoadOperation(params, getTargetBufferFlagsAt(i));
|
assert_invariant(defaultColorTextureView);
|
||||||
colorAttachment.storeOp = getStoreOperation(params, getTargetBufferFlagsAt(i));
|
mColorAttachmentDescriptors.push_back({ .view = defaultColorTextureView,
|
||||||
colorAttachment.clearValue = { params.clearColor.r, params.clearColor.g, params.clearColor.b, params.clearColor.a };
|
.resolveTarget = nullptr,
|
||||||
colorAttachments.emplace_back(colorAttachment);
|
.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),
|
||||||
|
.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),
|
||||||
|
.stencilClearValue = params.clearStencil,
|
||||||
|
.stencilReadOnly =
|
||||||
|
(params.readOnlyDepthStencil & RenderPassParams::READONLY_STENCIL) > 0,
|
||||||
|
};
|
||||||
|
mHasDepthStencilAttachment = true;
|
||||||
|
}
|
||||||
|
} else {// Custom Render Target
|
||||||
|
for (uint32_t i = 0; i < customColorTextureViewCount; ++i) {
|
||||||
|
if (customColorTextureViews[i]) {
|
||||||
|
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 } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FILAMENT_CHECK_POSTCONDITION(!(customDepthTextureView && customStencilTextureView))
|
||||||
|
<< "WebGPU CANNOT support separate texture views for depth + stencil. depth + "
|
||||||
|
"stencil needs to be in one texture view";
|
||||||
|
|
||||||
|
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 =
|
||||||
|
customDepthTextureView ? customDepthTextureView : customStencilTextureView;
|
||||||
|
|
||||||
|
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 (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;
|
||||||
|
} else {
|
||||||
|
mDepthStencilAttachmentDescriptor.stencilLoadOp = wgpu::LoadOp::Undefined;
|
||||||
|
mDepthStencilAttachmentDescriptor.stencilStoreOp = wgpu::StoreOp::Undefined;
|
||||||
|
mDepthStencilAttachmentDescriptor.stencilReadOnly = true;
|
||||||
|
}
|
||||||
|
mHasDepthStencilAttachment = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
descriptor.colorAttachments = colorAttachments.data();
|
|
||||||
descriptor.colorAttachmentCount = colorAttachments.size();
|
descriptor.colorAttachmentCount = mColorAttachmentDescriptors.size();
|
||||||
descriptor.depthStencilAttachment = nullptr;
|
descriptor.colorAttachments = mColorAttachmentDescriptors.data();
|
||||||
descriptor.timestampWrites = 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}// namespace filament::backend
|
}// namespace filament::backend
|
||||||
|
|||||||
@@ -44,36 +44,46 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// VertexBufferInfo contains layout info for Vertex Buffer based on WebGPU structs. In WebGPU each
|
// WGPUVertexBufferInfo maps Filament vertex attributes to WebGPU buffer binding model.
|
||||||
// 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.
|
|
||||||
class WGPUVertexBufferInfo : public HwVertexBufferInfo {
|
class WGPUVertexBufferInfo : public HwVertexBufferInfo {
|
||||||
public:
|
public:
|
||||||
WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
|
WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
|
||||||
AttributeArray const& attributes);
|
AttributeArray const& attributes);
|
||||||
inline wgpu::VertexBufferLayout const* getVertexBufferLayout() const {
|
|
||||||
return mVertexBufferLayout.data();
|
inline wgpu::VertexBufferLayout const* getVertexBufferLayouts() const {
|
||||||
|
return mVertexBufferLayouts.data();
|
||||||
|
}
|
||||||
|
inline uint32_t getVertexBufferLayoutCount() const {
|
||||||
|
return static_cast<uint32_t>(mVertexBufferLayouts.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint32_t getVertexBufferLayoutSize() const {
|
inline wgpu::VertexAttribute const* getVertexAttributes(uint32_t i) const {
|
||||||
return mVertexBufferLayout.size();
|
return mVertexAttributes[i].data();
|
||||||
|
}
|
||||||
|
inline uint32_t getVertexAttributeCount(uint32_t i) const {
|
||||||
|
return static_cast<uint32_t>(mVertexAttributes[i].size());
|
||||||
}
|
}
|
||||||
|
|
||||||
inline wgpu::VertexAttribute const* getVertexAttributeForIndex(uint32_t index) const {
|
struct WebGPUSlotBindingInfo {
|
||||||
assert_invariant(index < mAttributes.size());
|
uint8_t sourceBuffer;
|
||||||
return mAttributes[index].data();
|
uint32_t slot;
|
||||||
}
|
uint64_t bufferOffset;
|
||||||
|
uint32_t stride;
|
||||||
inline uint32_t getVertexAttributeSize(uint32_t index) const {
|
};
|
||||||
assert_invariant(index < mAttributes.size());
|
inline const std::vector<WebGPUSlotBindingInfo>& getWebGPUSlotBindingInfos() const {
|
||||||
return mAttributes[index].size();
|
return mWebGPUSlotBindingInfos;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// TODO: can we do better in terms on heap management.
|
// This stores the final wgpu::VertexBufferLayout objects, one per WebGPU slot.
|
||||||
std::vector<wgpu::VertexBufferLayout> mVertexBufferLayout{};
|
std::vector<wgpu::VertexBufferLayout> mVertexBufferLayouts;
|
||||||
std::vector<std::vector<wgpu::VertexAttribute>> mAttributes{};
|
|
||||||
|
// This stores all wgpu::VertexAttribute structs, indexed by their original
|
||||||
|
// Filament attributeIndex.
|
||||||
|
std::vector<std::vector<wgpu::VertexAttribute>> mVertexAttributes;
|
||||||
|
|
||||||
|
// Stores information for the driver to perform setVertexBuffer calls
|
||||||
|
std::vector<WebGPUSlotBindingInfo> mWebGPUSlotBindingInfos;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WGPUVertexBuffer : public HwVertexBuffer {
|
struct WGPUVertexBuffer : public HwVertexBuffer {
|
||||||
@@ -145,6 +155,9 @@ public:
|
|||||||
[[nodiscard]] bool getIsLocked() const { return mBindGroup != nullptr; }
|
[[nodiscard]] bool getIsLocked() const { return mBindGroup != nullptr; }
|
||||||
[[nodiscard]] size_t countEntitiesWithDynamicOffsets() const;
|
[[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:
|
private:
|
||||||
wgpu::BindGroupLayout mLayout = nullptr;
|
wgpu::BindGroupLayout mLayout = nullptr;
|
||||||
static constexpr uint8_t INVALID_INDEX = MAX_DESCRIPTOR_COUNT + 1;
|
static constexpr uint8_t INVALID_INDEX = MAX_DESCRIPTOR_COUNT + 1;
|
||||||
@@ -166,7 +179,8 @@ public:
|
|||||||
size_t getBlockHeight() const { return mBlockHeight; }
|
size_t getBlockHeight() const { return mBlockHeight; }
|
||||||
|
|
||||||
[[nodiscard]] const wgpu::Texture& getTexture() const { return mTexture; }
|
[[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(
|
static wgpu::TextureFormat fToWGPUTextureFormat(
|
||||||
filament::backend::TextureFormat const& fFormat);
|
filament::backend::TextureFormat const& fFormat);
|
||||||
@@ -185,7 +199,7 @@ private:
|
|||||||
wgpu::TextureFormat mFormat = wgpu::TextureFormat::Undefined;
|
wgpu::TextureFormat mFormat = wgpu::TextureFormat::Undefined;
|
||||||
wgpu::TextureAspect mAspect = wgpu::TextureAspect::Undefined;
|
wgpu::TextureAspect mAspect = wgpu::TextureAspect::Undefined;
|
||||||
uint32_t mArrayLayerCount = 1;
|
uint32_t mArrayLayerCount = 1;
|
||||||
wgpu::TextureView mTexView = nullptr;
|
wgpu::TextureView mTexureView = nullptr;
|
||||||
wgpu::TextureUsage fToWGPUTextureUsage(filament::backend::TextureUsage const& fUsage);
|
wgpu::TextureUsage fToWGPUTextureUsage(filament::backend::TextureUsage const& fUsage);
|
||||||
size_t mBlockWidth;
|
size_t mBlockWidth;
|
||||||
size_t mBlockHeight;
|
size_t mBlockHeight;
|
||||||
@@ -199,55 +213,60 @@ struct WGPURenderPrimitive : public HwRenderPrimitive {
|
|||||||
|
|
||||||
class WGPURenderTarget : public HwRenderTarget {
|
class WGPURenderTarget : public HwRenderTarget {
|
||||||
public:
|
public:
|
||||||
class Attachment {
|
using Attachment = TargetBufferInfo; // Using TargetBufferInfo directly for attachments
|
||||||
public:
|
|
||||||
friend class WGPURenderTarget;
|
|
||||||
|
|
||||||
Attachment() = default;
|
|
||||||
Attachment(WGPUTexture* gpuTexture, uint8_t level = 0, uint16_t layer = 0)
|
|
||||||
: level(level),
|
|
||||||
layer(layer),
|
|
||||||
texture(gpuTexture->getTexture()),
|
|
||||||
mWGPUTexture(gpuTexture) {}
|
|
||||||
operator bool() const {
|
|
||||||
return mWGPUTexture != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t level = 0;
|
|
||||||
uint16_t layer = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
wgpu::Texture texture = nullptr;
|
|
||||||
WGPUTexture* mWGPUTexture = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples,
|
WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples,
|
||||||
Attachment colorAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT]);
|
const MRT& colorAttachments,
|
||||||
|
const Attachment& depthAttachment,
|
||||||
|
const Attachment& stencilAttachment);
|
||||||
|
|
||||||
|
// Default constructor for the default render target
|
||||||
WGPURenderTarget()
|
WGPURenderTarget()
|
||||||
: HwRenderTarget(0, 0),
|
: HwRenderTarget(0, 0),
|
||||||
defaultRenderTarget(true) {}
|
defaultRenderTarget(true),
|
||||||
|
samples(1) {}
|
||||||
|
|
||||||
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor,
|
// Updated signature: takes resolved views for custom RTs, and default views for default RT
|
||||||
wgpu::TextureView const& textureView, RenderPassParams const& params);
|
void setUpRenderPassAttachments(
|
||||||
|
wgpu::RenderPassDescriptor& descriptor,
|
||||||
|
RenderPassParams const& params,
|
||||||
|
// For default render target:
|
||||||
|
wgpu::TextureView const& defaultColorTextureView,
|
||||||
|
wgpu::TextureView const& defaultDepthStencilTextureView,
|
||||||
|
// For custom render targets:
|
||||||
|
wgpu::TextureView const* customColorTextureViews, // Array of views
|
||||||
|
uint32_t customColorTextureViewCount,
|
||||||
|
wgpu::TextureView const& customDepthTextureView,
|
||||||
|
wgpu::TextureView const& customStencilTextureView,
|
||||||
|
wgpu::TextureFormat customDepthFormat,
|
||||||
|
wgpu::TextureFormat customStencilFormat);
|
||||||
|
|
||||||
math::uint2 getAttachmentSize() noexcept;
|
|
||||||
|
|
||||||
bool isDefaultRenderTarget() const { return defaultRenderTarget; }
|
bool isDefaultRenderTarget() const { return defaultRenderTarget; }
|
||||||
uint8_t getSamples() const { return samples; }
|
uint8_t getSamples() const { return samples; }
|
||||||
|
|
||||||
Attachment getDrawColorAttachment(size_t index);
|
// Accessors for the driver to get stored attachment info
|
||||||
Attachment getReadColorAttachment(size_t index);
|
const MRT& getColorAttachmentInfos() const { return mColorAttachments; }
|
||||||
|
const Attachment& getDepthAttachmentInfo() const { return mDepthAttachment; }
|
||||||
|
const Attachment& getStencilAttachmentInfo() const { return mStencilAttachment; }
|
||||||
|
|
||||||
|
// Static helpers for load/store operations
|
||||||
static wgpu::LoadOp getLoadOperation(const RenderPassParams& params, TargetBufferFlags buffer);
|
static wgpu::LoadOp getLoadOperation(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||||
static wgpu::StoreOp getStoreOperation(const RenderPassParams& params, TargetBufferFlags buffer);
|
static wgpu::StoreOp getStoreOperation(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool defaultRenderTarget = false;
|
bool defaultRenderTarget = false;
|
||||||
uint8_t samples = 1;
|
uint8_t samples = 1;
|
||||||
|
|
||||||
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
|
MRT mColorAttachments{};
|
||||||
math::uint2 attachmentSize = {};
|
// TODO WebGPU only supports a DepthStencil attachment, should this be just mDepthStencilAttachment?
|
||||||
std::vector<wgpu::RenderPassColorAttachment> colorAttachments{};
|
Attachment mDepthAttachment{};
|
||||||
};
|
Attachment mStencilAttachment{};
|
||||||
|
|
||||||
|
// Cached descriptors for the render pass
|
||||||
|
std::vector<wgpu::RenderPassColorAttachment> mColorAttachmentDescriptors;
|
||||||
|
wgpu::RenderPassDepthStencilAttachment mDepthStencilAttachmentDescriptor{};
|
||||||
|
bool mHasDepthStencilAttachment = false;
|
||||||
|
};
|
||||||
}// namespace filament::backend
|
}// namespace filament::backend
|
||||||
#endif// TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
|
#endif// TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
|
||||||
|
|||||||
@@ -154,9 +154,9 @@ constexpr wgpu::BlendFactor toWebGPU(BlendFunction blendFunction) {
|
|||||||
wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
|
wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
|
||||||
WGPUProgram const& program, WGPUVertexBufferInfo const& vertexBufferInfo,
|
WGPUProgram const& program, WGPUVertexBufferInfo const& vertexBufferInfo,
|
||||||
wgpu::PipelineLayout const& layout, RasterState const& rasterState,
|
wgpu::PipelineLayout const& layout, RasterState const& rasterState,
|
||||||
StencilState const& stencilState, PolygonOffset const& polygonOffset,
|
StencilState const& stencilState, PolygonOffset const& polygonOffset, PrimitiveType primitiveType,
|
||||||
PrimitiveType primitiveType, wgpu::TextureFormat colorFormat,
|
std::vector<wgpu::TextureFormat> const& colorFormats,
|
||||||
wgpu::TextureFormat depthFormat) {
|
wgpu::TextureFormat depthFormat, uint8_t samplesCount) {
|
||||||
assert_invariant(program.vertexShaderModule);
|
assert_invariant(program.vertexShaderModule);
|
||||||
const wgpu::DepthStencilState depthStencilState {
|
const wgpu::DepthStencilState depthStencilState {
|
||||||
.format = depthFormat,
|
.format = depthFormat,
|
||||||
@@ -191,8 +191,8 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
|
|||||||
.entryPoint = "main",
|
.entryPoint = "main",
|
||||||
.constantCount = program.constants.size(),
|
.constantCount = program.constants.size(),
|
||||||
.constants = program.constants.data(),
|
.constants = program.constants.data(),
|
||||||
.bufferCount = vertexBufferInfo.getVertexBufferLayoutSize(),
|
.bufferCount = vertexBufferInfo.getVertexBufferLayoutCount(),
|
||||||
.buffers = vertexBufferInfo.getVertexBufferLayout()
|
.buffers = vertexBufferInfo.getVertexBufferLayouts()
|
||||||
},
|
},
|
||||||
.primitive = {
|
.primitive = {
|
||||||
.topology = toWebGPU(primitiveType),
|
.topology = toWebGPU(primitiveType),
|
||||||
@@ -207,9 +207,9 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
|
|||||||
.unclippedDepth = !rasterState.depthClamp &&
|
.unclippedDepth = !rasterState.depthClamp &&
|
||||||
device.HasFeature(wgpu::FeatureName::DepthClipControl)
|
device.HasFeature(wgpu::FeatureName::DepthClipControl)
|
||||||
},
|
},
|
||||||
.depthStencil = &depthStencilState,
|
.depthStencil = depthFormat != wgpu::TextureFormat::Undefined ? &depthStencilState: nullptr,
|
||||||
.multisample = {
|
.multisample = {
|
||||||
.count = 1, // TODO need to get this from the render target
|
.count = samplesCount,
|
||||||
.mask = 0xFFFFFFFF,
|
.mask = 0xFFFFFFFF,
|
||||||
.alphaToCoverageEnabled = rasterState.alphaToCoverage
|
.alphaToCoverageEnabled = rasterState.alphaToCoverage
|
||||||
},
|
},
|
||||||
@@ -234,12 +234,15 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
|
|||||||
fragmentState.entryPoint = "main";
|
fragmentState.entryPoint = "main";
|
||||||
fragmentState.constantCount = program.constants.size(),
|
fragmentState.constantCount = program.constants.size(),
|
||||||
fragmentState.constants = program.constants.data(),
|
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();
|
fragmentState.targets = colorTargets.data();
|
||||||
assert_invariant(fragmentState.targetCount <= MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
|
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++) {
|
for (size_t targetIndex = 0; targetIndex < fragmentState.targetCount; targetIndex++) {
|
||||||
auto& colorTarget = colorTargets[targetIndex];
|
auto& colorTarget = colorTargets[targetIndex];
|
||||||
colorTarget.format = colorFormat;
|
colorTarget.format = colorFormats[targetIndex];
|
||||||
colorTarget.blend = rasterState.hasBlending() ? &blendState : nullptr;
|
colorTarget.blend = rasterState.hasBlending() ? &blendState : nullptr;
|
||||||
colorTarget.writeMask =
|
colorTarget.writeMask =
|
||||||
rasterState.colorWrite ? wgpu::ColorWriteMask::All : wgpu::ColorWriteMask::None;
|
rasterState.colorWrite ? wgpu::ColorWriteMask::All : wgpu::ColorWriteMask::None;
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#define TNT_FILAMENT_BACKEND_WEBGPUPIPELINECREATION_H
|
#define TNT_FILAMENT_BACKEND_WEBGPUPIPELINECREATION_H
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace wgpu {
|
namespace wgpu {
|
||||||
class Device;
|
class Device;
|
||||||
@@ -38,8 +39,9 @@ class WGPUProgram;
|
|||||||
|
|
||||||
[[nodiscard]] wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const&,
|
[[nodiscard]] wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const&,
|
||||||
WGPUProgram const&, WGPUVertexBufferInfo const&, wgpu::PipelineLayout const&,
|
WGPUProgram const&, WGPUVertexBufferInfo const&, wgpu::PipelineLayout const&,
|
||||||
RasterState const&, StencilState const&, PolygonOffset const&, PrimitiveType,
|
RasterState const&, StencilState const&, PolygonOffset const&, PrimitiveType primitiveType,
|
||||||
wgpu::TextureFormat colorFormat, wgpu::TextureFormat depthFormat);
|
std::vector<wgpu::TextureFormat> const& colorFormats,
|
||||||
|
wgpu::TextureFormat depthFormat, uint8_t samplesCount);
|
||||||
|
|
||||||
}// namespace filament::backend
|
}// namespace filament::backend
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -17,34 +17,50 @@
|
|||||||
#include "webgpu/WebGPUSwapChain.h"
|
#include "webgpu/WebGPUSwapChain.h"
|
||||||
|
|
||||||
#include "webgpu/WebGPUConstants.h"
|
#include "webgpu/WebGPUConstants.h"
|
||||||
|
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||||
|
#include "webgpu/WebGPUStrings.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "backend/DriverEnums.h"
|
#include "backend/DriverEnums.h"
|
||||||
|
|
||||||
#include <utils/Panic.h>
|
#include <utils/Panic.h>
|
||||||
#include <utils/ostream.h>
|
#include <utils/ostream.h>
|
||||||
|
|
||||||
#include <dawn/webgpu_cpp_print.h>
|
|
||||||
#include <webgpu/webgpu_cpp.h>
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
#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) {
|
void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabilities) {
|
||||||
std::stringstream usagesStream{};
|
|
||||||
usagesStream << capabilities.usages;
|
|
||||||
FWGPU_LOGI << "WebGPU surface capabilities:" << utils::io::endl;
|
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;
|
FWGPU_LOGI << " surface formats (" << capabilities.formatCount << "):" << utils::io::endl;
|
||||||
if (capabilities.formatCount > 0 && capabilities.formats != nullptr) {
|
if (capabilities.formatCount > 0 && capabilities.formats != nullptr) {
|
||||||
std::for_each(capabilities.formats, capabilities.formats + capabilities.formatCount,
|
std::for_each(capabilities.formats, capabilities.formats + capabilities.formatCount,
|
||||||
[](wgpu::TextureFormat const format) {
|
[](wgpu::TextureFormat const format) {
|
||||||
std::stringstream formatStream{};
|
FWGPU_LOGI << " " << format << utils::io::endl;
|
||||||
formatStream << format;
|
|
||||||
FWGPU_LOGI << " " << formatStream.str().data() << utils::io::endl;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
FWGPU_LOGI << " surface present modes (" << capabilities.presentModeCount
|
FWGPU_LOGI << " surface present modes (" << capabilities.presentModeCount
|
||||||
@@ -53,9 +69,7 @@ void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabiliti
|
|||||||
std::for_each(capabilities.presentModes,
|
std::for_each(capabilities.presentModes,
|
||||||
capabilities.presentModes + capabilities.presentModeCount,
|
capabilities.presentModes + capabilities.presentModeCount,
|
||||||
[](wgpu::PresentMode const presentMode) {
|
[](wgpu::PresentMode const presentMode) {
|
||||||
std::stringstream presentModeStream{};
|
FWGPU_LOGI << " " << presentMode << utils::io::endl;
|
||||||
presentModeStream << presentMode;
|
|
||||||
FWGPU_LOGI << " " << presentModeStream.str().data() << utils::io::endl;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
FWGPU_LOGI << " surface alpha modes (" << capabilities.alphaModeCount
|
FWGPU_LOGI << " surface alpha modes (" << capabilities.alphaModeCount
|
||||||
@@ -64,46 +78,30 @@ void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabiliti
|
|||||||
std::for_each(capabilities.alphaModes,
|
std::for_each(capabilities.alphaModes,
|
||||||
capabilities.alphaModes + capabilities.alphaModeCount,
|
capabilities.alphaModes + capabilities.alphaModeCount,
|
||||||
[](wgpu::CompositeAlphaMode const alphaMode) {
|
[](wgpu::CompositeAlphaMode const alphaMode) {
|
||||||
std::stringstream alphaModeStream{};
|
FWGPU_LOGI << " " << alphaMode << utils::io::endl;
|
||||||
alphaModeStream << alphaMode;
|
|
||||||
FWGPU_LOGI << " " << alphaModeStream.str().data() << utils::io::endl;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
|
||||||
void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
|
void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
|
||||||
wgpu::TextureFormat depthFormat) {
|
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 << "WebGPU surface configuration:" << utils::io::endl;
|
||||||
FWGPU_LOGI << " surface format: " << formatStream.str() << utils::io::endl;
|
FWGPU_LOGI << " surface format: " << config.format << utils::io::endl;
|
||||||
FWGPU_LOGI << " surface usage: " << usageStream.str() << utils::io::endl;
|
FWGPU_LOGI << " surface usage: " << config.usage << utils::io::endl;
|
||||||
FWGPU_LOGI << " surface view formats (" << config.viewFormatCount << "):" << utils::io::endl;
|
FWGPU_LOGI << " surface view formats (" << config.viewFormatCount << "):" << utils::io::endl;
|
||||||
if (config.viewFormatCount > 0 && config.viewFormats != nullptr) {
|
if (config.viewFormatCount > 0 && config.viewFormats != nullptr) {
|
||||||
std::for_each(config.viewFormats, config.viewFormats + config.viewFormatCount,
|
std::for_each(config.viewFormats, config.viewFormats + config.viewFormatCount,
|
||||||
[](wgpu::TextureFormat const viewFormat) {
|
[](wgpu::TextureFormat const viewFormat) {
|
||||||
std::stringstream viewFormatStream{};
|
FWGPU_LOGI << " " << viewFormat << utils::io::endl;
|
||||||
viewFormatStream << viewFormat;
|
|
||||||
FWGPU_LOGI << " " << viewFormatStream.str().data() << 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 width: " << config.width << utils::io::endl;
|
||||||
FWGPU_LOGI << " surface height: " << config.height << utils::io::endl;
|
FWGPU_LOGI << " surface height: " << config.height << utils::io::endl;
|
||||||
FWGPU_LOGI << " surface present mode: " << presentModeStream.str() << utils::io::endl;
|
FWGPU_LOGI << " surface present mode: " << config.presentMode << utils::io::endl;
|
||||||
FWGPU_LOGI << "WebGPU selected depth format: " << depthFormatStream.str() << 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,
|
[[nodiscard]] constexpr wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
|
||||||
wgpu::TextureFormat const* availableFormats, bool useSRGBColorSpace) {
|
wgpu::TextureFormat const* availableFormats, bool useSRGBColorSpace) {
|
||||||
|
|||||||
@@ -18,16 +18,30 @@
|
|||||||
|
|
||||||
#include "webgpu/WebGPUConstants.h"
|
#include "webgpu/WebGPUConstants.h"
|
||||||
#include "webgpu/WebGPUDriver.h"
|
#include "webgpu/WebGPUDriver.h"
|
||||||
|
#include "webgpu/WebGPUStrings.h"
|
||||||
|
|
||||||
|
#include <backend/DriverEnums.h>
|
||||||
#include <backend/Platform.h>
|
#include <backend/Platform.h>
|
||||||
|
#include <utils/Hash.h>
|
||||||
#include <utils/Panic.h>
|
#include <utils/Panic.h>
|
||||||
#include <utils/ostream.h>
|
#include <utils/ostream.h>
|
||||||
|
|
||||||
#include <dawn/webgpu_cpp_print.h>
|
#include <dawn/webgpu_cpp_print.h>
|
||||||
#include <webgpu/webgpu_cpp.h>
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <cstdint>
|
#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).
|
* WebGPU Backend implementation common across platforms or operating systems (at least for now).
|
||||||
@@ -39,6 +53,213 @@ namespace filament::backend {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
constexpr std::array REQUIRED_FEATURES = {
|
||||||
|
wgpu::FeatureName::TransientAttachments };
|
||||||
|
|
||||||
|
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
|
//either returns a valid instance or panics
|
||||||
[[nodiscard]] wgpu::Instance createInstance() {
|
[[nodiscard]] wgpu::Instance createInstance() {
|
||||||
wgpu::DawnTogglesDescriptor dawnTogglesDescriptor{};
|
wgpu::DawnTogglesDescriptor dawnTogglesDescriptor{};
|
||||||
@@ -63,119 +284,409 @@ namespace {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
wgpu::Instance instance = wgpu::CreateInstance(&instanceDescriptor);
|
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;
|
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
|
}// namespace
|
||||||
|
|
||||||
wgpu::Adapter WebGPUPlatform::requestAdapter(wgpu::Surface const& surface) {
|
wgpu::Adapter WebGPUPlatform::requestAdapter(wgpu::Surface const& surface) {
|
||||||
// TODO consider power preference etc. (can be custom preferences passed to the platform or
|
assertLimitsAreExpressedInRequirementsStruct();
|
||||||
// based on whether this is a Mobile or Desktop system,
|
std::vector<wgpu::RequestAdapterOptions> requests = getAdapterOptions();
|
||||||
// etc...)
|
for (auto& request: requests) {
|
||||||
wgpu::RequestAdapterOptions adaptorOptions{ .compatibleSurface = surface };
|
request.compatibleSurface = surface;
|
||||||
// note this just gets the first adapter
|
}
|
||||||
wgpu::Adapter adapter = nullptr;
|
const std::unordered_set<AdapterDetails, AdapterDetailsHash> compatibleAdapters =
|
||||||
wgpu::WaitStatus status = mInstance.WaitAny(
|
requestCompatibleAdapters(mInstance, requests);
|
||||||
mInstance.RequestAdapter(&adaptorOptions, wgpu::CallbackMode::WaitAnyOnly,
|
return selectPreferredAdapter(compatibleAdapters);
|
||||||
[&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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
|
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;
|
std::vector<wgpu::FeatureName> enabledFeatures;
|
||||||
enabledFeatures.reserve(requiredFeatures.size() + optionalFeatures.size());
|
enabledFeatures.reserve(REQUIRED_FEATURES.size() + OPTIONAL_FEATURES.size());
|
||||||
|
for (auto const& requiredFeature : REQUIRED_FEATURES) {
|
||||||
std::set_intersection(supportedFeatures.features,
|
enabledFeatures.push_back(requiredFeature);
|
||||||
supportedFeatures.features + supportedFeatures.featureCount, requiredFeatures.begin(),
|
}
|
||||||
requiredFeatures.end(), std::back_inserter(enabledFeatures));
|
for (auto const& optionalFeature : OPTIONAL_FEATURES) {
|
||||||
|
if (adapter.HasFeature(optionalFeature)) {
|
||||||
if (enabledFeatures.size() != requiredFeatures.size()) {
|
enabledFeatures.push_back(optionalFeature);
|
||||||
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)) << " ";
|
|
||||||
}
|
}
|
||||||
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{};
|
wgpu::DeviceDescriptor deviceDescriptor{};
|
||||||
deviceDescriptor.label = "graphics_device";
|
deviceDescriptor.label = "graphics_device";
|
||||||
deviceDescriptor.defaultQueue.label = "default_queue";
|
deviceDescriptor.defaultQueue.label = "default_queue";
|
||||||
deviceDescriptor.requiredFeatureCount = enabledFeatures.size();
|
deviceDescriptor.requiredFeatureCount = enabledFeatures.size();
|
||||||
deviceDescriptor.requiredFeatures = enabledFeatures.data();
|
deviceDescriptor.requiredFeatures = enabledFeatures.data();
|
||||||
|
deviceDescriptor.requiredLimits = &REQUIRED_LIMITS;
|
||||||
deviceDescriptor.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous,
|
deviceDescriptor.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous,
|
||||||
[](wgpu::Device const&, wgpu::DeviceLostReason const& reason,
|
[](wgpu::Device const&, wgpu::DeviceLostReason const& reason,
|
||||||
wgpu::StringView message) {
|
wgpu::StringView message) {
|
||||||
if (reason == wgpu::DeviceLostReason::Destroyed) {
|
if (reason == wgpu::DeviceLostReason::Destroyed) {
|
||||||
|
#if FWGPU_ENABLED(FWGPU_DEBUG_VALIDATION)
|
||||||
FWGPU_LOGD << "WebGPU device lost due to being destroyed (expected)"
|
FWGPU_LOGD << "WebGPU device lost due to being destroyed (expected)"
|
||||||
<< utils::io::endl;
|
<< utils::io::endl;
|
||||||
|
#endif
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// TODO try recreating the device instead of just panicking
|
// TODO try recreating the device instead of just panicking
|
||||||
std::stringstream reasonStream{};
|
|
||||||
reasonStream << reason;
|
|
||||||
FILAMENT_CHECK_POSTCONDITION(reason != wgpu::DeviceLostReason::Destroyed)
|
FILAMENT_CHECK_POSTCONDITION(reason != wgpu::DeviceLostReason::Destroyed)
|
||||||
<< "WebGPU device lost: " << reasonStream.str() << " " << message.data;
|
<< "WebGPU device lost: " << deviceLostReasonToString(reason) << " "
|
||||||
|
<< message.data;
|
||||||
});
|
});
|
||||||
deviceDescriptor.SetUncapturedErrorCallback(
|
deviceDescriptor.SetUncapturedErrorCallback(
|
||||||
[](wgpu::Device const&, wgpu::ErrorType errorType, wgpu::StringView message) {
|
[](wgpu::Device const&, wgpu::ErrorType errorType, wgpu::StringView message) {
|
||||||
std::stringstream typeStream{};
|
FWGPU_LOGE << "WebGPU device error: " << errorTypeToString(errorType) << " "
|
||||||
typeStream << errorType;
|
<< message.data << utils::io::endl;
|
||||||
FWGPU_LOGE << "WebGPU device error: " << typeStream.str() << " " << message.data
|
|
||||||
<< utils::io::endl;
|
|
||||||
});
|
});
|
||||||
wgpu::Device device = nullptr;
|
wgpu::Device device = nullptr;
|
||||||
wgpu::WaitStatus status = mInstance.WaitAny(
|
wgpu::WaitStatus status = mInstance.WaitAny(
|
||||||
adapter.RequestDevice(&deviceDescriptor, wgpu::CallbackMode::WaitAnyOnly,
|
adapter.RequestDevice(&deviceDescriptor, wgpu::CallbackMode::WaitAnyOnly,
|
||||||
[&device](wgpu::RequestDeviceStatus const status,
|
[&device](wgpu::RequestDeviceStatus const status,
|
||||||
wgpu::Device const& readyDevice, wgpu::StringView const message) {
|
wgpu::Device const& readyDevice, wgpu::StringView const message) {
|
||||||
FILAMENT_CHECK_POSTCONDITION(status == wgpu::RequestDeviceStatus::Success)
|
FILAMENT_CHECK_POSTCONDITION(
|
||||||
<< "Unable to request a WebGPU device. Status: "
|
status != wgpu::RequestDeviceStatus::CallbackCancelled)
|
||||||
<< static_cast<uint32_t>(status)
|
<< "Failed to request a WebGPU device due to the callback being "
|
||||||
<< " with message: " << message.data;
|
"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;
|
device = readyDevice;
|
||||||
}),
|
}),
|
||||||
UINT64_MAX);// TODO define reasonable timeout (or do this asynchronously)
|
REQUEST_DEVICE_TIMEOUT_NANOSECONDS);
|
||||||
FILAMENT_CHECK_POSTCONDITION(status == wgpu::WaitStatus::Success)
|
FILAMENT_CHECK_POSTCONDITION(status != wgpu::WaitStatus::TimedOut)
|
||||||
<< "Non-successful wait status requesting a WebGPU device "
|
<< "Failed to request a WebGPU device due to a timeout.";
|
||||||
<< static_cast<uint32_t>(status);
|
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)
|
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;
|
return device;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,9 @@
|
|||||||
#include <android/native_window.h>
|
#include <android/native_window.h>
|
||||||
#include <webgpu/webgpu_cpp.h>
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Android OS specific implementation aspects of the WebGPU backend
|
* Android OS specific implementation aspects of the WebGPU backend
|
||||||
@@ -29,6 +31,30 @@
|
|||||||
|
|
||||||
namespace filament::backend {
|
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 {
|
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||||
ANativeWindow* window = static_cast<ANativeWindow*>(nativeWindow);
|
ANativeWindow* window = static_cast<ANativeWindow*>(nativeWindow);
|
||||||
return wgpu::Extent2D{
|
return wgpu::Extent2D{
|
||||||
|
|||||||
@@ -21,7 +21,9 @@
|
|||||||
|
|
||||||
#include <webgpu/webgpu_cpp.h>
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
// Platform specific includes and defines
|
// Platform specific includes and defines
|
||||||
#include <Cocoa/Cocoa.h>
|
#include <Cocoa/Cocoa.h>
|
||||||
@@ -33,6 +35,30 @@
|
|||||||
|
|
||||||
namespace filament::backend {
|
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 {
|
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||||
// Both IOS and MacOS expects CAMetalLayer.
|
// Both IOS and MacOS expects CAMetalLayer.
|
||||||
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*) nativeWindow;
|
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*) nativeWindow;
|
||||||
|
|||||||
@@ -21,8 +21,10 @@
|
|||||||
|
|
||||||
#include <webgpu/webgpu_cpp.h>
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#if defined(__linux__) || defined(__FreeBSD__)
|
#if defined(__linux__) || defined(__FreeBSD__)
|
||||||
#define LINUX_OR_FREEBSD 1
|
#define LINUX_OR_FREEBSD 1
|
||||||
@@ -79,6 +81,33 @@
|
|||||||
|
|
||||||
namespace filament::backend {
|
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 {
|
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||||
auto surfaceExtent = wgpu::Extent2D{};
|
auto surfaceExtent = wgpu::Extent2D{};
|
||||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||||
|
|||||||
@@ -20,7 +20,9 @@
|
|||||||
|
|
||||||
#include <webgpu/webgpu_cpp.h>
|
#include <webgpu/webgpu_cpp.h>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
@@ -30,6 +32,35 @@
|
|||||||
|
|
||||||
namespace filament::backend {
|
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 {
|
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||||
HWND window = static_cast<HWND>(nativeWindow);
|
HWND window = static_cast<HWND>(nativeWindow);
|
||||||
RECT windowRect;
|
RECT windowRect;
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ BackendTest::BackendTest() : commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE,
|
|||||||
CONFIG_COMMAND_BUFFERS_SIZE, /*mPaused=*/false) {
|
CONFIG_COMMAND_BUFFERS_SIZE, /*mPaused=*/false) {
|
||||||
initializeDriver();
|
initializeDriver();
|
||||||
mImageExpectations.emplace(getDriverApi());
|
mImageExpectations.emplace(getDriverApi());
|
||||||
|
NativeView nativeView = getNativeView();
|
||||||
|
mScreenSize = {nativeView.width, nativeView.height};
|
||||||
}
|
}
|
||||||
|
|
||||||
BackendTest::~BackendTest() {
|
BackendTest::~BackendTest() {
|
||||||
@@ -138,42 +140,12 @@ filament::backend::RenderPassParams BackendTest::getNoClearRenderPass() {
|
|||||||
return RenderPassParams{};
|
return RenderPassParams{};
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendTest::renderTriangle(
|
std::size_t BackendTest::screenWidth() const {
|
||||||
PipelineLayout const& pipelineLayout,
|
return mScreenSize[0];
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BackendTest::renderTriangle(
|
std::size_t BackendTest::screenHeight() const {
|
||||||
PipelineLayout const& pipelineLayout,
|
return mScreenSize[1];
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BackendTest::matchesEnvironment(Backend backend) {
|
bool BackendTest::matchesEnvironment(Backend backend) {
|
||||||
|
|||||||
@@ -61,24 +61,14 @@ protected:
|
|||||||
filament::math::float4 color = filament::math::float4(0, 1, 1, 1));
|
filament::math::float4 color = filament::math::float4(0, 1, 1, 1));
|
||||||
static filament::backend::RenderPassParams getNoClearRenderPass();
|
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::DriverApi& getDriverApi() { return *commandStream; }
|
||||||
filament::backend::Driver& getDriver() { return *driver; }
|
filament::backend::Driver& getDriver() { return *driver; }
|
||||||
|
|
||||||
ImageExpectations& getExpectations() { return *mImageExpectations; }
|
ImageExpectations& getExpectations() { return *mImageExpectations; }
|
||||||
|
|
||||||
|
std::size_t screenWidth() const;
|
||||||
|
std::size_t screenHeight() const;
|
||||||
|
|
||||||
static bool matchesEnvironment(Backend backend);
|
static bool matchesEnvironment(Backend backend);
|
||||||
static bool matchesEnvironment(OperatingSystem operatingSystem);
|
static bool matchesEnvironment(OperatingSystem operatingSystem);
|
||||||
private:
|
private:
|
||||||
@@ -98,6 +88,8 @@ private:
|
|||||||
// This isn't truly optional, it just needs to delay construction until after the driver has
|
// This isn't truly optional, it just needs to delay construction until after the driver has
|
||||||
// been initialized
|
// been initialized
|
||||||
std::optional<ImageExpectations> mImageExpectations;
|
std::optional<ImageExpectations> mImageExpectations;
|
||||||
|
|
||||||
|
std::array<size_t, 2> mScreenSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
|
|||||||
@@ -322,7 +322,11 @@ TEST_F(BlitTest, ColorResolve) {
|
|||||||
{
|
{
|
||||||
RenderFrame frame(api);
|
RenderFrame frame(api);
|
||||||
api.beginRenderPass(srcRenderTarget, params);
|
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();
|
api.endRenderPass();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -489,8 +493,8 @@ TEST_F(BlitTest, BlitRegionToSwapChain) {
|
|||||||
constexpr int kSrcTexWidth = 1024;
|
constexpr int kSrcTexWidth = 1024;
|
||||||
constexpr int kSrcTexHeight = 1024;
|
constexpr int kSrcTexHeight = 1024;
|
||||||
constexpr auto kSrcTexFormat = TextureFormat::RGBA8;
|
constexpr auto kSrcTexFormat = TextureFormat::RGBA8;
|
||||||
constexpr int kDstTexWidth = 512;
|
const uint32_t kDstTexWidth = screenWidth();
|
||||||
constexpr int kDstTexHeight = 512;
|
const uint32_t kDstTexHeight = screenHeight();
|
||||||
constexpr int kNumLevels = 3;
|
constexpr int kNumLevels = 3;
|
||||||
|
|
||||||
// Create a SwapChain and make it current.
|
// Create a SwapChain and make it current.
|
||||||
|
|||||||
@@ -132,7 +132,11 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
api.beginRenderPass(defaultRenderTarget, params);
|
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.endRenderPass();
|
||||||
|
|
||||||
triangleIndex++;
|
triangleIndex++;
|
||||||
@@ -153,12 +157,12 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
|
|||||||
TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
|
TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
|
||||||
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
|
||||||
"All values including alpha are written as 0, see b/417254943");
|
"All values including alpha are written as 0, see b/417254943");
|
||||||
constexpr int kTexWidth = 512;
|
|
||||||
constexpr int kTexHeight = 512;
|
|
||||||
|
|
||||||
auto& api = getDriverApi();
|
auto& api = getDriverApi();
|
||||||
Cleanup cleanup(api);
|
Cleanup cleanup(api);
|
||||||
|
|
||||||
|
const TrianglePrimitive triangle(api);
|
||||||
|
|
||||||
// Create a platform-specific SwapChain and make it current.
|
// Create a platform-specific SwapChain and make it current.
|
||||||
auto swapChain = cleanup.add(createSwapChain());
|
auto swapChain = cleanup.add(createSwapChain());
|
||||||
api.makeCurrent(swapChain, swapChain);
|
api.makeCurrent(swapChain, swapChain);
|
||||||
@@ -175,10 +179,11 @@ TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
|
|||||||
shader.bindUniform<SimpleMaterialParams>(api, ubuffer, kBindingConfig);
|
shader.bindUniform<SimpleMaterialParams>(api, ubuffer, kBindingConfig);
|
||||||
|
|
||||||
// Create a render target.
|
// Create a render target.
|
||||||
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
auto colorTexture =
|
||||||
TextureFormat::RGBA8, 1, kTexWidth, kTexHeight, 1, TextureUsage::COLOR_ATTACHMENT));
|
cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1, TextureFormat::RGBA8, 1,
|
||||||
auto renderTarget = cleanup.add(api.createRenderTarget(
|
screenWidth(), screenHeight(), 1, TextureUsage::COLOR_ATTACHMENT));
|
||||||
TargetBufferFlags::COLOR0, kTexWidth, kTexHeight, 1, 0, {{ colorTexture }}, {}, {}));
|
auto renderTarget = cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR0, screenWidth(),
|
||||||
|
screenHeight(), 1, 0, { { colorTexture } }, {}, {}));
|
||||||
|
|
||||||
// Upload uniforms for the first triangle.
|
// Upload uniforms for the first triangle.
|
||||||
// Upload the uniform, but with an offset to accommodate the padding in the shader's
|
// 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 }
|
.offset = { 0.0f, 0.0f, 0.0f, 0.0f }
|
||||||
});
|
});
|
||||||
|
|
||||||
RenderPassParams params = getClearColorRenderPass();
|
PipelineState state = getColorWritePipelineState();
|
||||||
params.viewport.height = kTexWidth;
|
shader.addProgramToPipelineState(state);
|
||||||
params.viewport.width = kTexHeight;
|
state.primitiveType = PrimitiveType::TRIANGLES;
|
||||||
renderTriangle({ { shader.getDescriptorSetLayout() } }, renderTarget, swapChain,
|
state.vertexBufferInfo = triangle.getVertexBufferInfo();
|
||||||
shader.getProgram(), params);
|
|
||||||
|
|
||||||
// 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.
|
RenderFrame frame(api);
|
||||||
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
|
|
||||||
});
|
|
||||||
|
|
||||||
params.flags.clear = TargetBufferFlags::NONE;
|
RenderPassParams clearParams = getClearColorRenderPass();
|
||||||
params.flags.discardStart = TargetBufferFlags::NONE;
|
clearParams.viewport.height = screenWidth();
|
||||||
renderTriangle({{ shader.getDescriptorSetLayout() }},
|
clearParams.viewport.width = screenHeight();
|
||||||
renderTarget, swapChain, shader.getProgram(), params);
|
|
||||||
|
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(),
|
EXPECT_IMAGE(renderTarget, getExpectations(),
|
||||||
ScreenshotParams(kTexWidth, kTexHeight, "BufferObjectUpdateWithOffset", 2320747245));
|
ScreenshotParams(screenWidth(), screenHeight(), "BufferObjectUpdateWithOffset",
|
||||||
|
2320747245));
|
||||||
|
|
||||||
api.flush();
|
api.flush();
|
||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
|
|||||||
@@ -197,7 +197,11 @@ TEST_F(BackendTest, FeedbackLoops) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
api.beginRenderPass(renderTargets[targetLevel], params);
|
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();
|
api.endRenderPass();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,7 +234,11 @@ TEST_F(BackendTest, FeedbackLoops) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
api.beginRenderPass(renderTargets[targetLevel], params);
|
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();
|
api.endRenderPass();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include <backend/DriverEnums.h>
|
#include <backend/DriverEnums.h>
|
||||||
#include <backend/Handle.h>
|
#include <backend/Handle.h>
|
||||||
|
|
||||||
|
#include "TrianglePrimitive.h"
|
||||||
#include "private/filament/SamplerInterfaceBlock.h"
|
#include "private/filament/SamplerInterfaceBlock.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -50,7 +51,7 @@ layout(location = 0) out vec4 fragColor;
|
|||||||
layout(location = 0, set = 0) uniform {samplerType} test_tex;
|
layout(location = 0, set = 0) uniform {samplerType} test_tex;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec2 fbsize = vec2(512);
|
vec2 fbsize = vec2({texSize});
|
||||||
vec2 uv = gl_FragCoord.xy / fbsize;
|
vec2 uv = gl_FragCoord.xy / fbsize;
|
||||||
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT) || defined(TARGET_WEBGPU_ENVIRONMENT)
|
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT) || defined(TARGET_WEBGPU_ENVIRONMENT)
|
||||||
uv.y = 1.0 - uv.y;
|
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; }
|
float getLayer(in sampler2DArray s) { return 2.0f; }
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec2 fbsize = vec2(512);
|
vec2 fbsize = vec2({texSize});
|
||||||
vec2 uv = gl_FragCoord.xy / fbsize;
|
vec2 uv = gl_FragCoord.xy / fbsize;
|
||||||
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT) || defined(TARGET_WEBGPU_ENVIRONMENT)
|
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT) || defined(TARGET_WEBGPU_ENVIRONMENT)
|
||||||
uv.y = 1.0 - uv.y;
|
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;
|
layout(location = 0, set = 0) uniform sampler2D test_tex;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec2 fbsize = vec2(512);
|
vec2 fbsize = vec2({texSize});
|
||||||
vec2 uv = gl_FragCoord.xy / fbsize;
|
vec2 uv = gl_FragCoord.xy / fbsize;
|
||||||
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT) || defined(TARGET_WEBGPU_ENVIRONMENT)
|
#if defined(TARGET_METAL_ENVIRONMENT) || defined(TARGET_VULKAN_ENVIRONMENT) || defined(TARGET_WEBGPU_ENVIRONMENT)
|
||||||
uv.y = 1.0 - uv.y;
|
uv.y = 1.0 - uv.y;
|
||||||
@@ -105,17 +106,6 @@ namespace test {
|
|||||||
|
|
||||||
template<typename componentType> inline componentType getMaxValue();
|
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,
|
inline std::string stringReplace(const std::string& find, const std::string& replace,
|
||||||
std::string source) {
|
std::string source) {
|
||||||
std::string::size_type pos = source.find(find);
|
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) {
|
TEST_F(LoadImageTest, UpdateImage2D) {
|
||||||
FAIL_IF(Backend::VULKAN, "Multiple test cases crash, see b/417481434");
|
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,
|
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||||
SamplerType::SAMPLER_2D, getSamplerFormat(t.textureFormat), Precision::HIGH, false };
|
SamplerType::SAMPLER_2D, getSamplerFormat(t.textureFormat), Precision::HIGH, false };
|
||||||
|
|
||||||
std::string const fragment = stringReplace("{samplerType}",
|
std::string const fragment = getFormattedFragment(fragmentTemplate, t.textureFormat);
|
||||||
getSamplerTypeName(t.textureFormat), fragmentTemplate);
|
|
||||||
Shader shader(api, cleanup, ShaderConfig{
|
Shader shader(api, cleanup, ShaderConfig{
|
||||||
.vertexShader = mVertexShader,
|
.vertexShader = mVertexShader,
|
||||||
.fragmentShader= fragment,
|
.fragmentShader= fragment,
|
||||||
@@ -314,21 +332,26 @@ TEST_F(LoadImageTest, UpdateImage2D) {
|
|||||||
// Create a Texture.
|
// Create a Texture.
|
||||||
auto usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
|
auto usage = TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE;
|
||||||
Handle<HwTexture> const texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
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.
|
// Upload some pixel data.
|
||||||
if (t.uploadSubregions) {
|
if (t.uploadSubregions) {
|
||||||
api.update3DImage(texture, 0, 0, 0, 0, 256, 256, 1,
|
api.update3DImage(texture, 0, 0, 0, 0, kHalfTexSize, kHalfTexSize, 1,
|
||||||
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 256, t.bufferPadding));
|
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, kHalfTexSize,
|
||||||
api.update3DImage(texture, 0, 256, 0, 0, 256, 256, 1,
|
t.bufferPadding));
|
||||||
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 256, t.bufferPadding));
|
api.update3DImage(texture, 0, kHalfTexSize, 0, 0, kHalfTexSize, kHalfTexSize, 1,
|
||||||
api.update3DImage(texture, 0, 0, 256, 0, 256, 256, 1,
|
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, kHalfTexSize,
|
||||||
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 256, t.bufferPadding));
|
t.bufferPadding));
|
||||||
api.update3DImage(texture, 0, 256, 256, 0, 256, 256, 1,
|
api.update3DImage(texture, 0, 0, kHalfTexSize, 0, kHalfTexSize, kHalfTexSize, 1,
|
||||||
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 256, t.bufferPadding));
|
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 {
|
} else {
|
||||||
api.update3DImage(texture, 0, 0, 0, 0, 512, 512, 1,
|
api.update3DImage(texture, 0, 0, 0, 0, kTexSize, kTexSize, 1,
|
||||||
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, 512, t.bufferPadding));
|
checkerboardPixelBuffer(t.pixelFormat, t.pixelType, kTexSize, t.bufferPadding));
|
||||||
}
|
}
|
||||||
|
|
||||||
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
DescriptorSetHandle descriptorSet = shader.createDescriptorSet(api);
|
||||||
@@ -338,11 +361,21 @@ TEST_F(LoadImageTest, UpdateImage2D) {
|
|||||||
|
|
||||||
api.bindDescriptorSet(descriptorSet, 0, {});
|
api.bindDescriptorSet(descriptorSet, 0, {});
|
||||||
|
|
||||||
renderTriangle({{ shader.getDescriptorSetLayout() }},
|
RenderPassParams params = getClearColorRenderPass();
|
||||||
defaultRenderTarget, swapChain, shader.getProgram());
|
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(),
|
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
|
||||||
ScreenshotParams(512, 512, t.name, expectedHash));
|
ScreenshotParams(kTexSize, kTexSize, t.name, expectedHash));
|
||||||
|
|
||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
api.endFrame(0);
|
api.endFrame(0);
|
||||||
@@ -368,39 +401,39 @@ TEST_F(LoadImageTest, UpdateImageSRGB) {
|
|||||||
// Create a program.
|
// Create a program.
|
||||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||||
SamplerType::SAMPLER_2D, getSamplerFormat(textureFormat), Precision::HIGH, false };
|
SamplerType::SAMPLER_2D, getSamplerFormat(textureFormat), Precision::HIGH, false };
|
||||||
std::string const fragment = stringReplace("{samplerType}",
|
std::string const fragment = getFormattedFragment(fragmentTemplate, textureFormat);
|
||||||
getSamplerTypeName(textureFormat), fragmentTemplate);
|
|
||||||
Shader shader(api, cleanup, ShaderConfig{
|
Shader shader(api, cleanup, ShaderConfig{
|
||||||
.vertexShader = mVertexShader, .fragmentShader = fragment, .uniforms = {{
|
.vertexShader = mVertexShader, .fragmentShader = fragment, .uniforms = {{
|
||||||
"test_tex", DescriptorType::SAMPLER_2D_FLOAT, samplerInfo
|
"test_tex", DescriptorType::SAMPLER_2D_FLOAT, samplerInfo
|
||||||
}}});
|
}}});
|
||||||
|
|
||||||
// Create a texture.
|
// Create a texture.
|
||||||
Handle<HwTexture> const texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
Handle<HwTexture> const texture =
|
||||||
textureFormat, 1, 512, 512, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE));
|
cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1, textureFormat, 1, kTexSize,
|
||||||
|
kTexSize, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE));
|
||||||
|
|
||||||
// Create image data.
|
// Create image data.
|
||||||
size_t components; int bpp;
|
size_t components; int bpp;
|
||||||
getPixelInfo(pixelFormat, pixelType, components, bpp);
|
getPixelInfo(pixelFormat, pixelType, components, bpp);
|
||||||
size_t bpl = 512 * 512 * bpp;
|
size_t bpl = kTexSize * kTexSize * bpp;
|
||||||
size_t bufferSize = bpl;
|
size_t bufferSize = bpl;
|
||||||
void* buffer = calloc(1, bufferSize);
|
void* buffer = calloc(1, bufferSize);
|
||||||
PixelBufferDescriptor descriptor(buffer, bufferSize, pixelFormat, pixelType,
|
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);
|
free(buffer);
|
||||||
}, nullptr);
|
}, nullptr);
|
||||||
|
|
||||||
// Add a gradient.
|
// Add a gradient.
|
||||||
uint8_t* pixel = (uint8_t*) buffer;
|
uint8_t* pixel = (uint8_t*) buffer;
|
||||||
for (int r = 0; r < 512; r++) {
|
for (int r = 0; r < kTexSize; r++) {
|
||||||
for (int c = 0; c < 512; c++) {
|
for (int c = 0; c < kTexSize; c++) {
|
||||||
for (int n = 0; n < components; n++) {
|
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);
|
api.beginFrame(0, 0, 0);
|
||||||
|
|
||||||
@@ -413,11 +446,21 @@ TEST_F(LoadImageTest, UpdateImageSRGB) {
|
|||||||
|
|
||||||
api.bindDescriptorSet(descriptorSet, 0, {});
|
api.bindDescriptorSet(descriptorSet, 0, {});
|
||||||
|
|
||||||
renderTriangle({{ shader.getDescriptorSetLayout() }},
|
RenderPassParams params = getClearColorRenderPass();
|
||||||
defaultRenderTarget, swapChain, shader.getProgram());
|
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(),
|
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
|
||||||
ScreenshotParams(512, 512, "UpdateImageSRGB", 3300305265));
|
ScreenshotParams(kTexSize, kTexSize, "UpdateImageSRGB", 3300305265));
|
||||||
|
|
||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
api.endFrame(0);
|
api.endFrame(0);
|
||||||
@@ -442,8 +485,7 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
|
|||||||
// Create a program.
|
// Create a program.
|
||||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||||
SamplerType::SAMPLER_2D, getSamplerFormat(textureFormat), Precision::HIGH, false };
|
SamplerType::SAMPLER_2D, getSamplerFormat(textureFormat), Precision::HIGH, false };
|
||||||
std::string const fragment = stringReplace("{samplerType}",
|
std::string const fragment = getFormattedFragment(fragmentUpdateImageMip, textureFormat);
|
||||||
getSamplerTypeName(textureFormat), fragmentUpdateImageMip);
|
|
||||||
Shader shader(api, cleanup, ShaderConfig {
|
Shader shader(api, cleanup, ShaderConfig {
|
||||||
.vertexShader = mVertexShader,
|
.vertexShader = mVertexShader,
|
||||||
.fragmentShader = fragment,
|
.fragmentShader = fragment,
|
||||||
@@ -454,12 +496,13 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
|
|||||||
// Base level: 1024
|
// Base level: 1024
|
||||||
// Level 1: 512 <-- upload data and sample from this level
|
// Level 1: 512 <-- upload data and sample from this level
|
||||||
// Level 2: 256
|
// Level 2: 256
|
||||||
Handle<HwTexture> texture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 3,
|
Handle<HwTexture> texture = cleanup.add(
|
||||||
textureFormat, 1, 1024, 1024, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE));
|
api.createTexture(SamplerType::SAMPLER_2D, 3, textureFormat, 1, kDoubleTexSize,
|
||||||
|
kDoubleTexSize, 1, TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE));
|
||||||
|
|
||||||
// Create image data.
|
// Create image data.
|
||||||
PixelBufferDescriptor descriptor = checkerboardPixelBuffer(pixelFormat, pixelType, 512);
|
PixelBufferDescriptor descriptor = checkerboardPixelBuffer(pixelFormat, pixelType, kTexSize);
|
||||||
api.update3DImage(texture, /* level*/ 1, 0, 0, 0, 512, 512, 1, std::move(descriptor));
|
api.update3DImage(texture, /* level*/ 1, 0, 0, 0, kTexSize, kTexSize, 1, std::move(descriptor));
|
||||||
|
|
||||||
api.beginFrame(0, 0, 0);
|
api.beginFrame(0, 0, 0);
|
||||||
|
|
||||||
@@ -472,11 +515,24 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
|
|||||||
|
|
||||||
api.bindDescriptorSet(descriptorSet, 0, {});
|
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(),
|
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
|
||||||
ScreenshotParams(512, 512, "UpdateImageMipLevel", 1875922935));
|
ScreenshotParams(kTexSize, kTexSize, "UpdateImageMipLevel", 1875922935));
|
||||||
|
|
||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
api.endFrame(0);
|
api.endFrame(0);
|
||||||
@@ -506,8 +562,7 @@ TEST_F(LoadImageTest, UpdateImage3D) {
|
|||||||
// Create a program.
|
// Create a program.
|
||||||
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
filament::SamplerInterfaceBlock::SamplerInfo samplerInfo { "test", "tex", 0,
|
||||||
SamplerType::SAMPLER_2D_ARRAY, getSamplerFormat(textureFormat), Precision::HIGH, false };
|
SamplerType::SAMPLER_2D_ARRAY, getSamplerFormat(textureFormat), Precision::HIGH, false };
|
||||||
std::string fragment = stringReplace("{samplerType}",
|
std::string fragment = getFormattedFragment(fragmentUpdateImage3DTemplate, samplerType);
|
||||||
getSamplerTypeName(samplerType), fragmentUpdateImage3DTemplate);
|
|
||||||
Shader shader(api, cleanup, ShaderConfig {
|
Shader shader(api, cleanup, ShaderConfig {
|
||||||
.vertexShader = mVertexShader,
|
.vertexShader = mVertexShader,
|
||||||
.fragmentShader = fragment,
|
.fragmentShader = fragment,
|
||||||
@@ -516,24 +571,24 @@ TEST_F(LoadImageTest, UpdateImage3D) {
|
|||||||
|
|
||||||
// Create a texture.
|
// Create a texture.
|
||||||
Handle<HwTexture> texture = cleanup.add(api.createTexture(samplerType, 1,
|
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.
|
// Create image data for all 4 layers.
|
||||||
size_t components; int bpp;
|
size_t components; int bpp;
|
||||||
getPixelInfo(pixelFormat, pixelType, components, bpp);
|
getPixelInfo(pixelFormat, pixelType, components, bpp);
|
||||||
size_t bpl = 512 * 512 * bpp;
|
size_t bpl = kTexSize * kTexSize * bpp;
|
||||||
size_t bufferSize = bpl * 4;
|
size_t bufferSize = bpl * 4;
|
||||||
void* buffer = calloc(1, bufferSize);
|
void* buffer = calloc(1, bufferSize);
|
||||||
PixelBufferDescriptor descriptor(buffer, bufferSize, pixelFormat, pixelType,
|
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);
|
free(buffer);
|
||||||
}, nullptr);
|
}, nullptr);
|
||||||
|
|
||||||
// Only add checkerboard data to the 3rd layer, which we'll sample from.
|
// Only add checkerboard data to the 3rd layer, which we'll sample from.
|
||||||
uint8_t* thirdLayer = (uint8_t*) buffer + (bpl * 2);
|
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);
|
RenderFrame frame(api);
|
||||||
@@ -546,11 +601,21 @@ TEST_F(LoadImageTest, UpdateImage3D) {
|
|||||||
|
|
||||||
api.bindDescriptorSet(descriptorSet, 0, {});
|
api.bindDescriptorSet(descriptorSet, 0, {});
|
||||||
|
|
||||||
renderTriangle({ { shader.getDescriptorSetLayout() } },
|
RenderPassParams params = getClearColorRenderPass();
|
||||||
defaultRenderTarget, swapChain, shader.getProgram());
|
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(),
|
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
|
||||||
ScreenshotParams(512, 512, "UpdateImage3D", 1875922935));
|
ScreenshotParams(kTexSize, kTexSize, "UpdateImage3D", 1875922935));
|
||||||
}
|
}
|
||||||
|
|
||||||
api.stopCapture();
|
api.stopCapture();
|
||||||
|
|||||||
@@ -75,32 +75,32 @@ TEST_F(BackendTest, MRT) {
|
|||||||
1, // levels
|
1, // levels
|
||||||
TextureFormat::RGBA8, // format
|
TextureFormat::RGBA8, // format
|
||||||
1, // samples
|
1, // samples
|
||||||
512, // width
|
screenWidth(), // width
|
||||||
512, // height
|
screenHeight(), // height
|
||||||
1, // depth
|
1, // depth
|
||||||
usage)); // usage
|
usage)); // usage
|
||||||
Handle<HwTexture> textureB = cleanup.add(api.createTexture(
|
Handle<HwTexture> textureB = cleanup.add(api.createTexture(
|
||||||
SamplerType::SAMPLER_2D, // target
|
SamplerType::SAMPLER_2D, // target
|
||||||
1, // levels
|
1, // levels
|
||||||
TextureFormat::RGBA8, // format
|
TextureFormat::RGBA8, // format
|
||||||
1, // samples
|
1, // samples
|
||||||
512, // width
|
screenWidth(), // width
|
||||||
512, // height
|
screenHeight(), // height
|
||||||
1, // depth
|
1, // depth
|
||||||
usage)); // usage
|
usage)); // usage
|
||||||
|
|
||||||
// Create a RenderTarget with two attachments.
|
// Create a RenderTarget with two attachments.
|
||||||
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
|
Handle<HwRenderTarget> renderTarget = cleanup.add(api.createRenderTarget(
|
||||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::COLOR1,
|
TargetBufferFlags::COLOR0 | TargetBufferFlags::COLOR1,
|
||||||
// The width and height must match the width and height of the respective mip
|
// The width and height must match the width and height of the respective mip
|
||||||
// level (at least for OpenGL).
|
// level (at least for OpenGL).
|
||||||
512, // width
|
screenWidth(), // width
|
||||||
512, // height
|
screenHeight(), // height
|
||||||
1, // samples
|
1, // samples
|
||||||
0, // layerCount
|
0, // layerCount
|
||||||
{{textureA },{textureB }}, // color
|
{{textureA },{textureB }}, // color
|
||||||
{}, // depth
|
{}, // depth
|
||||||
{})); // stencil
|
{})); // stencil
|
||||||
|
|
||||||
PipelineState state = getColorWritePipelineState();
|
PipelineState state = getColorWritePipelineState();
|
||||||
shader.addProgramToPipelineState(state);
|
shader.addProgramToPipelineState(state);
|
||||||
@@ -115,7 +115,11 @@ TEST_F(BackendTest, MRT) {
|
|||||||
|
|
||||||
// Draw a triangle.
|
// Draw a triangle.
|
||||||
api.beginRenderPass(renderTarget, params);
|
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.endRenderPass();
|
||||||
|
|
||||||
api.flush();
|
api.flush();
|
||||||
|
|||||||
@@ -145,7 +145,11 @@ TEST_F(BackendTest, TextureViewLod) {
|
|||||||
params.viewport = getFullViewport();
|
params.viewport = getFullViewport();
|
||||||
|
|
||||||
api.beginRenderPass(renderTarget, params);
|
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.endRenderPass();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +174,11 @@ TEST_F(BackendTest, TextureViewLod) {
|
|||||||
// previous pass.
|
// previous pass.
|
||||||
api.beginRenderPass(defaultRenderTarget, params);
|
api.beginRenderPass(defaultRenderTarget, params);
|
||||||
api.scissor(params.viewport);
|
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.endRenderPass();
|
||||||
|
|
||||||
// Adjust the base mip to 2.
|
// Adjust the base mip to 2.
|
||||||
@@ -195,7 +203,11 @@ TEST_F(BackendTest, TextureViewLod) {
|
|||||||
params.flags.discardStart = TargetBufferFlags::NONE;
|
params.flags.discardStart = TargetBufferFlags::NONE;
|
||||||
api.beginRenderPass(defaultRenderTarget, params);
|
api.beginRenderPass(defaultRenderTarget, params);
|
||||||
api.scissor(params.viewport);
|
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.endRenderPass();
|
||||||
|
|
||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
|
|||||||
@@ -92,7 +92,11 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
|
|||||||
|
|
||||||
// Render a triangle.
|
// Render a triangle.
|
||||||
api.beginRenderPass(defaultRenderTarget, params);
|
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.endRenderPass();
|
||||||
|
|
||||||
api.flush();
|
api.flush();
|
||||||
|
|||||||
@@ -71,6 +71,11 @@ namespace test {
|
|||||||
|
|
||||||
class ReadPixelsTest : public BackendTest {
|
class ReadPixelsTest : public BackendTest {
|
||||||
public:
|
public:
|
||||||
|
ReadPixelsTest() {
|
||||||
|
// Expect a square screen
|
||||||
|
EXPECT_THAT(screenWidth(), ::testing::Eq(screenHeight()));
|
||||||
|
}
|
||||||
|
|
||||||
bool readPixelsFinished = false;
|
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
|
// 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.
|
// 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 {
|
struct TestCase {
|
||||||
|
explicit TestCase(size_t renderTargetBaseSize)
|
||||||
|
: renderTargetBaseSize(renderTargetBaseSize) {}
|
||||||
|
|
||||||
|
size_t renderTargetBaseSize;
|
||||||
|
|
||||||
const char* testName = "readPixels_normal";
|
const char* testName = "readPixels_normal";
|
||||||
|
|
||||||
// The murmur3 hash of the read pixel buffer result, used to determine success.
|
// 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 normative read pixels test case. Render a white triangle over a blue background and read
|
||||||
// the full viewport into a pixel buffer.
|
// 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.
|
// 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.testName = "readPixels_subregion";
|
||||||
t2.readRect.x = 90;
|
t2.readRect.x = 90;
|
||||||
t2.readRect.y = 403;
|
t2.readRect.y = 403;
|
||||||
@@ -176,7 +186,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
|||||||
t2.hash = 0xcba7675a;
|
t2.hash = 0xcba7675a;
|
||||||
|
|
||||||
// Check that readPixels works when rendering into and reading from a mip level.
|
// Check that readPixels works when rendering into and reading from a mip level.
|
||||||
TestCase t3;
|
TestCase t3(renderTargetBaseSize);
|
||||||
t3.testName = "readPixels_mip";
|
t3.testName = "readPixels_mip";
|
||||||
t3.mipLevels = 4;
|
t3.mipLevels = 4;
|
||||||
t3.mipLevel = 2;
|
t3.mipLevel = 2;
|
||||||
@@ -186,7 +196,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
|||||||
t3.hash = 0xe6fa6c55;
|
t3.hash = 0xe6fa6c55;
|
||||||
|
|
||||||
// Check that readPixels can return pixels in floating point RGBA format.
|
// Check that readPixels can return pixels in floating point RGBA format.
|
||||||
TestCase t4;
|
TestCase t4(renderTargetBaseSize);
|
||||||
t4.testName = "readPixels_float";
|
t4.testName = "readPixels_float";
|
||||||
t4.format = PixelDataFormat::RGBA;
|
t4.format = PixelDataFormat::RGBA;
|
||||||
t4.type = PixelDataType::FLOAT;
|
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
|
// Check that readPixels can read a region of the render target into a subregion of a large
|
||||||
// buffer.
|
// buffer.
|
||||||
TestCase t5;
|
TestCase t5(renderTargetBaseSize);
|
||||||
t5.testName = "readPixels_subbuffer";
|
t5.testName = "readPixels_subbuffer";
|
||||||
t5.readRect.x = 90;
|
t5.readRect.x = 90;
|
||||||
t5.readRect.y = 403;
|
t5.readRect.y = 403;
|
||||||
@@ -206,7 +216,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
|||||||
t5.hash = 0xbaefdb54;
|
t5.hash = 0xbaefdb54;
|
||||||
|
|
||||||
// Check that readPixels works with integer formats.
|
// Check that readPixels works with integer formats.
|
||||||
TestCase t6;
|
TestCase t6(renderTargetBaseSize);
|
||||||
t6.testName = "readPixels_UINT";
|
t6.testName = "readPixels_UINT";
|
||||||
t6.format = PixelDataFormat::R_INTEGER;
|
t6.format = PixelDataFormat::R_INTEGER;
|
||||||
t6.type = PixelDataType::UINT;
|
t6.type = PixelDataType::UINT;
|
||||||
@@ -214,7 +224,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
|||||||
t6.hash = 0x9d91227;
|
t6.hash = 0x9d91227;
|
||||||
|
|
||||||
// Check that readPixels works with half formats.
|
// Check that readPixels works with half formats.
|
||||||
TestCase t7;
|
TestCase t7(renderTargetBaseSize);
|
||||||
t7.testName = "readPixels_half";
|
t7.testName = "readPixels_half";
|
||||||
t7.format = PixelDataFormat::RG;
|
t7.format = PixelDataFormat::RG;
|
||||||
t7.type = PixelDataType::HALF;
|
t7.type = PixelDataType::HALF;
|
||||||
@@ -223,7 +233,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
|||||||
|
|
||||||
// Check that readPixels works when rendering into the SwapChain.
|
// Check that readPixels works when rendering into the SwapChain.
|
||||||
// This requires that the test runner's native window size is 512x512.
|
// This requires that the test runner's native window size is 512x512.
|
||||||
TestCase t8;
|
TestCase t8(renderTargetBaseSize);
|
||||||
t8.testName = "readPixels_swapchain";
|
t8.testName = "readPixels_swapchain";
|
||||||
t8.useDefaultRT = true;
|
t8.useDefaultRT = true;
|
||||||
|
|
||||||
@@ -295,7 +305,11 @@ TEST_F(ReadPixelsTest, ReadPixels) {
|
|||||||
} else {
|
} else {
|
||||||
floatShader.addProgramToPipelineState(state);
|
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();
|
api.endRenderPass();
|
||||||
|
|
||||||
@@ -413,7 +427,11 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
|
|||||||
|
|
||||||
// Render some content, just so we don't read back uninitialized data.
|
// Render some content, just so we don't read back uninitialized data.
|
||||||
api.beginRenderPass(renderTarget, params);
|
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.endRenderPass();
|
||||||
|
|
||||||
PixelBufferDescriptor descriptor(buffer, renderTargetSize * renderTargetSize * 4,
|
PixelBufferDescriptor descriptor(buffer, renderTargetSize * renderTargetSize * 4,
|
||||||
|
|||||||
@@ -90,7 +90,11 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
|
|||||||
|
|
||||||
// Render a triangle.
|
// Render a triangle.
|
||||||
api.beginRenderPass(defaultRenderTarget, params);
|
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.endRenderPass();
|
||||||
|
|
||||||
api.flush();
|
api.flush();
|
||||||
@@ -185,14 +189,18 @@ TEST_F(BackendTest, RenderExternalImage) {
|
|||||||
|
|
||||||
// Render a triangle.
|
// Render a triangle.
|
||||||
api.beginRenderPass(defaultRenderTarget, params);
|
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.endRenderPass();
|
||||||
|
|
||||||
api.flush();
|
api.flush();
|
||||||
api.commit(swapChain);
|
api.commit(swapChain);
|
||||||
api.endFrame(0);
|
api.endFrame(0);
|
||||||
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
|
EXPECT_IMAGE(defaultRenderTarget, getExpectations(),
|
||||||
ScreenshotParams(512, 512, "RenderExternalImage", 1206264951));
|
ScreenshotParams(screenWidth(), screenHeight(), "RenderExternalImage", 1206264951));
|
||||||
|
|
||||||
api.stopCapture(0);
|
api.stopCapture(0);
|
||||||
api.finish();
|
api.finish();
|
||||||
|
|||||||
@@ -125,7 +125,11 @@ TEST_F(BackendTest, ScissorViewportRegion) {
|
|||||||
|
|
||||||
api.beginRenderPass(srcRenderTarget, params);
|
api.beginRenderPass(srcRenderTarget, params);
|
||||||
api.scissor(scissor);
|
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();
|
api.endRenderPass();
|
||||||
|
|
||||||
EXPECT_IMAGE(fullRenderTarget, getExpectations(),
|
EXPECT_IMAGE(fullRenderTarget, getExpectations(),
|
||||||
@@ -201,7 +205,11 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
|
|||||||
|
|
||||||
api.beginRenderPass(renderTarget, params);
|
api.beginRenderPass(renderTarget, params);
|
||||||
api.scissor(scissor);
|
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();
|
api.endRenderPass();
|
||||||
|
|
||||||
params.viewport = topLeftViewport;
|
params.viewport = topLeftViewport;
|
||||||
@@ -209,7 +217,11 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
|
|||||||
params.flags.discardStart = TargetBufferFlags::NONE;
|
params.flags.discardStart = TargetBufferFlags::NONE;
|
||||||
api.beginRenderPass(renderTarget, params);
|
api.beginRenderPass(renderTarget, params);
|
||||||
api.scissor(scissor);
|
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();
|
api.endRenderPass();
|
||||||
|
|
||||||
EXPECT_IMAGE(renderTarget, getExpectations(),
|
EXPECT_IMAGE(renderTarget, getExpectations(),
|
||||||
|
|||||||
@@ -74,7 +74,8 @@ public:
|
|||||||
// Render a small triangle only to the stencil buffer, increasing the stencil buffer to 1.
|
// Render a small triangle only to the stencil buffer, increasing the stencil buffer to 1.
|
||||||
RenderPassParams params = {};
|
RenderPassParams params = {};
|
||||||
params.flags.clear = TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL;
|
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.clearColor = math::float4(0.0f, 0.0f, 1.0f, 1.0f);
|
||||||
params.clearStencil = 0u;
|
params.clearStencil = 0u;
|
||||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||||
@@ -91,7 +92,11 @@ public:
|
|||||||
api.beginFrame(0, 0, 0);
|
api.beginFrame(0, 0, 0);
|
||||||
|
|
||||||
api.beginRenderPass(renderTarget, params);
|
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();
|
api.endRenderPass();
|
||||||
|
|
||||||
// Step 2: Render a larger triangle with the stencil test enabled.
|
// Step 2: Render a larger triangle with the stencil test enabled.
|
||||||
@@ -104,7 +109,11 @@ public:
|
|||||||
ps.stencilState.front.ref = 0u;
|
ps.stencilState.front.ref = 0u;
|
||||||
|
|
||||||
api.beginRenderPass(renderTarget, params);
|
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.endRenderPass();
|
||||||
|
|
||||||
api.commit(mSwapChain);
|
api.commit(mSwapChain);
|
||||||
@@ -123,18 +132,20 @@ TEST_F(BasicStencilBufferTest, StencilBuffer) {
|
|||||||
Cleanup cleanup(api);
|
Cleanup cleanup(api);
|
||||||
|
|
||||||
// Create two textures: a color and a stencil, and an associated RenderTarget.
|
// Create two textures: a color and a stencil, and an associated RenderTarget.
|
||||||
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
auto colorTexture =
|
||||||
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
|
cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1, TextureFormat::RGBA8, 1,
|
||||||
auto stencilTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
screenWidth(), screenHeight(), 1, TextureUsage::COLOR_ATTACHMENT));
|
||||||
TextureFormat::STENCIL8, 1, 512, 512, 1, TextureUsage::STENCIL_ATTACHMENT));
|
auto stencilTexture =
|
||||||
auto renderTarget = cleanup.add(api.createRenderTarget(
|
cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1, TextureFormat::STENCIL8, 1,
|
||||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
|
screenWidth(), screenHeight(), 1, TextureUsage::STENCIL_ATTACHMENT));
|
||||||
{{ colorTexture }}, {}, {{ stencilTexture }}));
|
auto renderTarget = cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR0 |
|
||||||
|
TargetBufferFlags::STENCIL,
|
||||||
|
screenWidth(), screenHeight(), 1, 0, { { colorTexture } }, {}, { { stencilTexture } }));
|
||||||
|
|
||||||
RunTest(renderTarget);
|
RunTest(renderTarget);
|
||||||
|
|
||||||
EXPECT_IMAGE(renderTarget, getExpectations(),
|
EXPECT_IMAGE(renderTarget, getExpectations(),
|
||||||
ScreenshotParams(512, 512, "StencilBuffer", 0x3B1AEF0F));
|
ScreenshotParams(screenWidth(), screenHeight(), "StencilBuffer", 0x3B1AEF0F));
|
||||||
|
|
||||||
flushAndWait();
|
flushAndWait();
|
||||||
getDriver().purge();
|
getDriver().purge();
|
||||||
@@ -147,19 +158,20 @@ TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
|
|||||||
Cleanup cleanup(api);
|
Cleanup cleanup(api);
|
||||||
|
|
||||||
// Create two textures: a color and a stencil, and an associated RenderTarget.
|
// Create two textures: a color and a stencil, and an associated RenderTarget.
|
||||||
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
auto colorTexture =
|
||||||
TextureFormat::RGBA8, 1, 512, 512, 1, TextureUsage::COLOR_ATTACHMENT));
|
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,
|
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));
|
TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
|
||||||
auto renderTarget = cleanup.add(api.createRenderTarget(
|
auto renderTarget = cleanup.add(api.createRenderTarget(
|
||||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, 512, 512, 1, 0,
|
TargetBufferFlags::COLOR0 | TargetBufferFlags::STENCIL, screenWidth(), screenHeight(),
|
||||||
{{ colorTexture }}, { depthStencilTexture }, {{ depthStencilTexture }}));
|
1, 0, { { colorTexture } }, { depthStencilTexture }, { { depthStencilTexture } }));
|
||||||
|
|
||||||
RunTest(renderTarget);
|
RunTest(renderTarget);
|
||||||
|
|
||||||
EXPECT_IMAGE(renderTarget, getExpectations(),
|
EXPECT_IMAGE(renderTarget, getExpectations(),
|
||||||
ScreenshotParams(512, 512, "DepthAndStencilBuffer", 0x3B1AEF0F));
|
ScreenshotParams(screenWidth(), screenHeight(), "DepthAndStencilBuffer", 0x3B1AEF0F));
|
||||||
|
|
||||||
flushAndWait();
|
flushAndWait();
|
||||||
getDriver().purge();
|
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.
|
// Pass 1: Render a triangle into (an auto-created) MSAA color buffer using the stencil test.
|
||||||
// Performs an auto-resolve on the color.
|
// Performs an auto-resolve on the color.
|
||||||
auto colorTexture = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
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));
|
TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE));
|
||||||
auto depthStencilTextureMSAA = cleanup.add(api.createTexture(SamplerType::SAMPLER_2D, 1,
|
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));
|
TextureUsage::STENCIL_ATTACHMENT | TextureUsage::DEPTH_ATTACHMENT));
|
||||||
auto renderTarget0 = cleanup.add(api.createRenderTarget(
|
auto renderTarget0 = cleanup.add(api.createRenderTarget(
|
||||||
TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
|
TargetBufferFlags::DEPTH_AND_STENCIL, screenWidth(), screenHeight(), 4, 0,
|
||||||
{{}}, { depthStencilTextureMSAA }, { depthStencilTextureMSAA }));
|
{{}}, { depthStencilTextureMSAA }, { depthStencilTextureMSAA }));
|
||||||
auto renderTarget1 = cleanup.add(api.createRenderTarget(
|
auto renderTarget1 = cleanup.add(
|
||||||
TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH_AND_STENCIL, 512, 512, 4, 0,
|
api.createRenderTarget(TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH_AND_STENCIL,
|
||||||
{{ colorTexture }}, { depthStencilTextureMSAA }, { depthStencilTextureMSAA }));
|
screenWidth(), screenHeight(), 4, 0, { { colorTexture } },
|
||||||
|
{ depthStencilTextureMSAA }, { depthStencilTextureMSAA }));
|
||||||
|
|
||||||
api.startCapture(0);
|
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.
|
// Render a small triangle only to the stencil buffer, increasing the stencil buffer to 1.
|
||||||
RenderPassParams params = {};
|
RenderPassParams params = {};
|
||||||
params.flags.clear = TargetBufferFlags::STENCIL;
|
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.clearStencil = 0u;
|
||||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||||
@@ -222,7 +236,11 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
|||||||
api.beginFrame(0, 0, 0);
|
api.beginFrame(0, 0, 0);
|
||||||
|
|
||||||
api.beginRenderPass(renderTarget0, params);
|
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();
|
api.endRenderPass();
|
||||||
|
|
||||||
// Step 2: Render a larger triangle with the stencil test enabled.
|
// Step 2: Render a larger triangle with the stencil test enabled.
|
||||||
@@ -237,7 +255,11 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
|||||||
ps.stencilState.front.ref = 0u;
|
ps.stencilState.front.ref = 0u;
|
||||||
|
|
||||||
api.beginRenderPass(renderTarget1, params);
|
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.endRenderPass();
|
||||||
|
|
||||||
api.commit(mSwapChain);
|
api.commit(mSwapChain);
|
||||||
@@ -245,7 +267,8 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
|
|||||||
api.endFrame(0);
|
api.endFrame(0);
|
||||||
|
|
||||||
EXPECT_IMAGE(renderTarget1, getExpectations(),
|
EXPECT_IMAGE(renderTarget1, getExpectations(),
|
||||||
ScreenshotParams(512, 512, "StencilBufferAutoResolve", 3353562179));
|
ScreenshotParams(screenWidth(), screenHeight(), "StencilBufferAutoResolve",
|
||||||
|
3353562179));
|
||||||
|
|
||||||
flushAndWait();
|
flushAndWait();
|
||||||
getDriver().purge();
|
getDriver().purge();
|
||||||
|
|||||||
@@ -1017,7 +1017,7 @@ public:
|
|||||||
*
|
*
|
||||||
* @see setPaused
|
* @see setPaused
|
||||||
*/
|
*/
|
||||||
bool isPaused() const noexcept;
|
bool isPaused() const noexcept(UTILS_HAS_THREADING);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pause or resume rendering thread.
|
* Pause or resume rendering thread.
|
||||||
|
|||||||
@@ -153,6 +153,14 @@ public:
|
|||||||
*/
|
*/
|
||||||
Builder& multiview(AttachmentPoint attachment, uint8_t layerCount, uint8_t baseLayer = 0) noexcept;
|
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.
|
* Creates the RenderTarget object and returns a pointer to it.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -172,6 +172,20 @@ public:
|
|||||||
*/
|
*/
|
||||||
Builder& levels(uint8_t levels) noexcept;
|
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.
|
* Specifies the type of sampler to use.
|
||||||
* @param target Sampler type
|
* @param target Sampler type
|
||||||
|
|||||||
@@ -387,7 +387,7 @@ JobSystem& Engine::getJobSystem() noexcept {
|
|||||||
return downcast(this)->getJobSystem();
|
return downcast(this)->getJobSystem();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Engine::isPaused() const noexcept {
|
bool Engine::isPaused() const noexcept(UTILS_HAS_THREADING) {
|
||||||
FILAMENT_CHECK_PRECONDITION(UTILS_HAS_THREADING)
|
FILAMENT_CHECK_PRECONDITION(UTILS_HAS_THREADING)
|
||||||
<< "Pause is meant for multi-threaded platforms.";
|
<< "Pause is meant for multi-threaded platforms.";
|
||||||
return downcast(this)->isPaused();
|
return downcast(this)->isPaused();
|
||||||
|
|||||||
@@ -304,8 +304,10 @@ static const PostProcessManager::StaticMaterialInfo sMaterialList[] = {
|
|||||||
{ "mipmapDepth", MATERIAL(MIPMAPDEPTH) },
|
{ "mipmapDepth", MATERIAL(MIPMAPDEPTH) },
|
||||||
{ "sao", MATERIAL(SAO) },
|
{ "sao", MATERIAL(SAO) },
|
||||||
{ "saoBentNormals", MATERIAL(SAOBENTNORMALS) },
|
{ "saoBentNormals", MATERIAL(SAOBENTNORMALS) },
|
||||||
|
#ifndef FILAMENT_DISABLE_GTAO
|
||||||
{ "gtao", MATERIAL(GTAO) },
|
{ "gtao", MATERIAL(GTAO) },
|
||||||
{ "gtaoBentNormals", MATERIAL(GTAOBENTNORMALS) },
|
{ "gtaoBentNormals", MATERIAL(GTAOBENTNORMALS) },
|
||||||
|
#endif
|
||||||
{ "separableGaussianBlur1", MATERIAL(SEPARABLEGAUSSIANBLUR),
|
{ "separableGaussianBlur1", MATERIAL(SEPARABLEGAUSSIANBLUR),
|
||||||
{ {"arraySampler", false}, {"componentCount", 1} } },
|
{ {"arraySampler", false}, {"componentCount", 1} } },
|
||||||
{ "separableGaussianBlur1L", MATERIAL(SEPARABLEGAUSSIANBLUR),
|
{ "separableGaussianBlur1L", MATERIAL(SEPARABLEGAUSSIANBLUR),
|
||||||
@@ -874,20 +876,25 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
|||||||
}};
|
}};
|
||||||
|
|
||||||
std::string_view materialName;
|
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) {
|
AmbientOcclusionOptions::AmbientOcclusionType::GTAO) {
|
||||||
materialName = computeBentNormals ? "gtaoBentNormals" : "gtao";
|
materialName = computeBentNormals ? "gtaoBentNormals" : "gtao";
|
||||||
} else {
|
} else {
|
||||||
materialName = computeBentNormals ? "saoBentNormals" : "sao";
|
materialName = computeBentNormals ? "saoBentNormals" : "sao";
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
auto& material = getPostProcessMaterial(materialName);
|
auto& material = getPostProcessMaterial(materialName);
|
||||||
|
|
||||||
FMaterial const * const ma = material.getMaterial(mEngine);
|
FMaterial const * const ma = material.getMaterial(mEngine);
|
||||||
FMaterialInstance* const mi = PostProcessMaterial::getMaterialInstance(ma);
|
FMaterialInstance* const mi = PostProcessMaterial::getMaterialInstance(ma);
|
||||||
|
|
||||||
// Set AO type specific material parameters
|
// Set AO type specific material parameters
|
||||||
switch (options.aoType) {
|
switch (aoType) {
|
||||||
case AmbientOcclusionOptions::AmbientOcclusionType::SAO: {
|
case AmbientOcclusionOptions::AmbientOcclusionType::SAO: {
|
||||||
// Where the falloff function peaks
|
// Where the falloff function peaks
|
||||||
const float peak = 0.1f * options.radius;
|
const float peak = 0.1f * options.radius;
|
||||||
|
|||||||
@@ -67,6 +67,7 @@
|
|||||||
#include <math/vec4.h>
|
#include <math/vec4.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -369,9 +370,16 @@ void FEngine::init() {
|
|||||||
.sampler(Texture::Sampler::SAMPLER_CUBEMAP)
|
.sampler(Texture::Sampler::SAMPLER_CUBEMAP)
|
||||||
.build(*this));
|
.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,
|
driverApi.update3DImage(mDefaultIblTexture->getHwHandle(), 0, 0, 0, 0, 1, 1, 6,
|
||||||
{ std::array<uint8_t, 6>{}.data(), 6,
|
{ zeroCubemap.data(), size(zeroCubemap), Texture::Format::RGBA, Texture::Type::UBYTE });
|
||||||
Texture::Format::RGBA, Texture::Type::UBYTE });
|
|
||||||
|
|
||||||
// 3 bands = 9 float3
|
// 3 bands = 9 float3
|
||||||
constexpr float sh[9 * 3] = { 0.0f };
|
constexpr float sh[9 * 3] = { 0.0f };
|
||||||
@@ -393,12 +401,10 @@ void FEngine::init() {
|
|||||||
TextureFormat::RGBA8, 1, 1, 1, 1, TextureUsage::DEFAULT);
|
TextureFormat::RGBA8, 1, 1, 1, 1, TextureUsage::DEFAULT);
|
||||||
|
|
||||||
driverApi.update3DImage(mDummyOneTexture, 0, 0, 0, 0, 1, 1, 1,
|
driverApi.update3DImage(mDummyOneTexture, 0, 0, 0, 0, 1, 1, 1,
|
||||||
{ std::array<uint8_t, 4>{0xff, 0xff, 0xff, 0xff}.data(), 4,
|
{ oneRGBA.data(), size(oneRGBA), Texture::Format::RGBA, Texture::Type::UBYTE });
|
||||||
Texture::Format::RGBA, Texture::Type::UBYTE });
|
|
||||||
|
|
||||||
driverApi.update3DImage(mDummyZeroTexture, 0, 0, 0, 0, 1, 1, 1,
|
driverApi.update3DImage(mDummyZeroTexture, 0, 0, 0, 0, 1, 1, 1,
|
||||||
{ std::array<uint8_t, 4>{}.data(), 4,
|
{ zeroRGBA.data(), size(zeroRGBA), Texture::Format::RGBA, Texture::Type::UBYTE });
|
||||||
Texture::Format::RGBA, Texture::Type::UBYTE });
|
|
||||||
|
|
||||||
|
|
||||||
mPerViewDescriptorSetLayoutSsrVariant = {
|
mPerViewDescriptorSetLayoutSsrVariant = {
|
||||||
@@ -463,16 +469,13 @@ void FEngine::init() {
|
|||||||
TextureFormat::RGBA8, 1, 1, 1, 1, TextureUsage::DEFAULT);
|
TextureFormat::RGBA8, 1, 1, 1, 1, TextureUsage::DEFAULT);
|
||||||
|
|
||||||
driverApi.update3DImage(mDummyOneTextureArray, 0, 0, 0, 0, 1, 1, 1,
|
driverApi.update3DImage(mDummyOneTextureArray, 0, 0, 0, 0, 1, 1, 1,
|
||||||
{ std::array<uint8_t, 4>{0xff, 0xff, 0xff, 0xff}.data(), 4,
|
{ oneRGBA.data(), size(oneRGBA), Texture::Format::RGBA, Texture::Type::UBYTE });
|
||||||
Texture::Format::RGBA, Texture::Type::UBYTE });
|
|
||||||
|
|
||||||
driverApi.update3DImage(mDummyOneTextureArrayDepth, 0, 0, 0, 0, 1, 1, 1,
|
driverApi.update3DImage(mDummyOneTextureArrayDepth, 0, 0, 0, 0, 1, 1, 1,
|
||||||
{ std::array<float, 1>{1.0f}.data(), 4,
|
{ oneFloat.data(), size(oneFloat), Texture::Format::DEPTH_COMPONENT, Texture::Type::FLOAT });
|
||||||
Texture::Format::DEPTH_COMPONENT, Texture::Type::FLOAT });
|
|
||||||
|
|
||||||
driverApi.update3DImage(mDummyZeroTextureArray, 0, 0, 0, 0, 1, 1, 1,
|
driverApi.update3DImage(mDummyZeroTextureArray, 0, 0, 0, 0, 1, 1, 1,
|
||||||
{ std::array<uint8_t, 4>{}.data(), 4,
|
{ zeroRGBA.data(), size(zeroRGBA), Texture::Format::RGBA, Texture::Type::UBYTE });
|
||||||
Texture::Format::RGBA, Texture::Type::UBYTE });
|
|
||||||
|
|
||||||
mDummyUniformBuffer = driverApi.createBufferObject(CONFIG_MINSPEC_UBO_SIZE,
|
mDummyUniformBuffer = driverApi.createBufferObject(CONFIG_MINSPEC_UBO_SIZE,
|
||||||
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
|
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
|
||||||
@@ -624,6 +627,7 @@ void FEngine::shutdown() {
|
|||||||
driver.destroyTexture(std::move(mDummyOneTextureArray));
|
driver.destroyTexture(std::move(mDummyOneTextureArray));
|
||||||
driver.destroyTexture(std::move(mDummyZeroTexture));
|
driver.destroyTexture(std::move(mDummyZeroTexture));
|
||||||
driver.destroyTexture(std::move(mDummyZeroTextureArray));
|
driver.destroyTexture(std::move(mDummyZeroTextureArray));
|
||||||
|
driver.destroyTexture(std::move(mDummyOneTextureArrayDepth));
|
||||||
|
|
||||||
driver.destroyBufferObject(std::move(mDummyUniformBuffer));
|
driver.destroyBufferObject(std::move(mDummyUniformBuffer));
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,19 @@
|
|||||||
|
|
||||||
#include <filament/Fence.h>
|
#include <filament/Fence.h>
|
||||||
|
|
||||||
|
#include <backend/DriverEnums.h>
|
||||||
|
|
||||||
|
#include <utils/compiler.h>
|
||||||
#include <utils/Panic.h>
|
#include <utils/Panic.h>
|
||||||
#include <utils/debug.h>
|
#include <utils/debug.h>
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <chrono>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace filament {
|
namespace filament {
|
||||||
|
|
||||||
using namespace backend;
|
using namespace backend;
|
||||||
@@ -30,7 +40,7 @@ using namespace backend;
|
|||||||
utils::Mutex FFence::sLock;
|
utils::Mutex FFence::sLock;
|
||||||
utils::Condition FFence::sCondition;
|
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 ms = std::chrono::milliseconds;
|
||||||
using ns = std::chrono::nanoseconds;
|
using ns = std::chrono::nanoseconds;
|
||||||
@@ -60,7 +70,7 @@ FenceStatus FFence::waitAndDestroy(FFence* fence, Mode const mode) noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
UTILS_NOINLINE
|
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)
|
FILAMENT_CHECK_PRECONDITION(UTILS_HAS_THREADING || timeout == 0)
|
||||||
<< "Non-zero timeout requires threads.";
|
<< "Non-zero timeout requires threads.";
|
||||||
|
|
||||||
@@ -103,18 +113,15 @@ FenceStatus FFence::wait(Mode const mode, uint64_t const timeout) noexcept {
|
|||||||
|
|
||||||
UTILS_NOINLINE
|
UTILS_NOINLINE
|
||||||
void FFence::FenceSignal::signal(State const s) noexcept {
|
void FFence::FenceSignal::signal(State const s) noexcept {
|
||||||
std::lock_guard<utils::Mutex> const lock(sLock);
|
std::lock_guard const lock(sLock);
|
||||||
mState = s;
|
mState = s;
|
||||||
sCondition.notify_all();
|
sCondition.notify_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
UTILS_NOINLINE
|
UTILS_NOINLINE
|
||||||
Fence::FenceStatus FFence::FenceSignal::wait(uint64_t const timeout) noexcept {
|
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) {
|
while (mState == UNSIGNALED) {
|
||||||
if (mState == DESTROYED) {
|
|
||||||
return FenceStatus::ERROR;
|
|
||||||
}
|
|
||||||
if (timeout == FENCE_WAIT_FOR_EVER) {
|
if (timeout == FENCE_WAIT_FOR_EVER) {
|
||||||
sCondition.wait(lock);
|
sCondition.wait(lock);
|
||||||
} else {
|
} 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;
|
return FenceStatus::CONDITION_SATISFIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public:
|
|||||||
|
|
||||||
void terminate(FEngine& engine) noexcept;
|
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;
|
static FenceStatus waitAndDestroy(FFence* fence, Mode mode) noexcept;
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
materialResult != MaterialParser::ParseResult::ERROR_MISSING_BACKEND)
|
||||||
<< "the material was not built for any of the " << to_string(backend)
|
<< "the material was not built for any of the " << to_string(backend)
|
||||||
<< " backend's supported shader languages (" << languageNames.c_str() << ")\n";
|
<< " backend's supported shader languages (" << languageNames.c_str() << ")\n";
|
||||||
@@ -104,12 +104,12 @@ static std::unique_ptr<MaterialParser> createParser(Backend const backend,
|
|||||||
return materialParser;
|
return materialParser;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILAMENT_CHECK_PRECONDITION(materialResult == MaterialParser::ParseResult::SUCCESS)
|
FILAMENT_CHECK_POSTCONDITION(materialResult == MaterialParser::ParseResult::SUCCESS)
|
||||||
<< "could not parse the material package";
|
<< "could not parse the material package";
|
||||||
|
|
||||||
uint32_t version = 0;
|
uint32_t version = 0;
|
||||||
materialParser->getMaterialVersion(&version);
|
materialParser->getMaterialVersion(&version);
|
||||||
FILAMENT_CHECK_PRECONDITION(version == MATERIAL_VERSION)
|
FILAMENT_CHECK_POSTCONDITION(version == MATERIAL_VERSION)
|
||||||
<< "Material version mismatch. Expected " << MATERIAL_VERSION << " but received "
|
<< "Material version mismatch. Expected " << MATERIAL_VERSION << " but received "
|
||||||
<< version << ".";
|
<< version << ".";
|
||||||
|
|
||||||
@@ -573,7 +573,7 @@ void FMaterial::getPostProcessProgramSlow(Variant const variant,
|
|||||||
Program FMaterial::getProgramWithVariants(
|
Program FMaterial::getProgramWithVariants(
|
||||||
Variant variant,
|
Variant variant,
|
||||||
Variant vertexVariant,
|
Variant vertexVariant,
|
||||||
Variant fragmentVariant) const noexcept {
|
Variant fragmentVariant) const {
|
||||||
FEngine const& engine = mEngine;
|
FEngine const& engine = mEngine;
|
||||||
const ShaderModel sm = engine.getShaderModel();
|
const ShaderModel sm = engine.getShaderModel();
|
||||||
const bool isNoop = engine.getBackend() == Backend::NOOP;
|
const bool isNoop = engine.getBackend() == Backend::NOOP;
|
||||||
|
|||||||
@@ -267,7 +267,7 @@ private:
|
|||||||
void getPostProcessProgramSlow(Variant variant,
|
void getPostProcessProgramSlow(Variant variant,
|
||||||
CompilerPriorityQueue priorityQueue) const noexcept;
|
CompilerPriorityQueue priorityQueue) const noexcept;
|
||||||
backend::Program getProgramWithVariants(Variant variant,
|
backend::Program getProgramWithVariants(Variant variant,
|
||||||
Variant vertexVariant, Variant fragmentVariant) const noexcept;
|
Variant vertexVariant, Variant fragmentVariant) const;
|
||||||
|
|
||||||
void processBlendingMode(MaterialParser const* parser);
|
void processBlendingMode(MaterialParser const* parser);
|
||||||
|
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t const targetInde
|
|||||||
void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t const targetIndex,
|
void FMorphTargetBuffer::setPositionsAt(FEngine& engine, size_t const targetIndex,
|
||||||
float4 const* positions, size_t const count, size_t const offset) {
|
float4 const* positions, size_t const count, size_t const offset) {
|
||||||
FILAMENT_CHECK_PRECONDITION(offset + count <= mVertexCount)
|
FILAMENT_CHECK_PRECONDITION(offset + count <= mVertexCount)
|
||||||
<< "MorphTargetBuffer (size=" << (unsigned)mVertexCount
|
<< "MorphTargetBuffer (size=" << mVertexCount
|
||||||
<< ") overflow (count=" << (unsigned)count << ", offset=" << (unsigned)offset << ")";
|
<< ") overflow (count=" << (unsigned)count << ", offset=" << (unsigned)offset << ")";
|
||||||
|
|
||||||
auto size = getSize<POSITION>(count);
|
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,
|
void FMorphTargetBuffer::setTangentsAt(FEngine& engine, size_t const targetIndex,
|
||||||
short4 const* tangents, size_t const count, size_t const offset) {
|
short4 const* tangents, size_t const count, size_t const offset) {
|
||||||
FILAMENT_CHECK_PRECONDITION(offset + count <= mVertexCount)
|
FILAMENT_CHECK_PRECONDITION(offset + count <= mVertexCount)
|
||||||
<< "MorphTargetBuffer (size=" << (unsigned)mVertexCount
|
<< "MorphTargetBuffer (size=" << mVertexCount
|
||||||
<< ") overflow (count=" << (unsigned)count << ", offset=" << (unsigned)offset << ")";
|
<< ") overflow (count=" << (unsigned)count << ", offset=" << (unsigned)offset << ")";
|
||||||
|
|
||||||
const auto size = getSize<TANGENTS>(count);
|
const auto size = getSize<TANGENTS>(count);
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ struct RenderTarget::BuilderDetails {
|
|||||||
FRenderTarget::Attachment mAttachments[FRenderTarget::ATTACHMENT_COUNT] = {};
|
FRenderTarget::Attachment mAttachments[FRenderTarget::ATTACHMENT_COUNT] = {};
|
||||||
uint32_t mWidth{};
|
uint32_t mWidth{};
|
||||||
uint32_t mHeight{};
|
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.
|
// 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
|
// If multiview is enabled, this value is appropriately updated based on the layerCount value
|
||||||
// from each attachment. Hence, #>1 means using multiview
|
// from each attachment. Hence, #>1 means using multiview
|
||||||
@@ -85,6 +85,10 @@ RenderTarget::Builder& RenderTarget::Builder::multiview(AttachmentPoint pt, uint
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RenderTarget::Builder& RenderTarget::Builder::samples(uint8_t samples) noexcept {
|
||||||
|
mImpl->mSamples = samples;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
RenderTarget* RenderTarget::Builder::build(Engine& engine) {
|
RenderTarget* RenderTarget::Builder::build(Engine& engine) {
|
||||||
using backend::TextureUsage;
|
using backend::TextureUsage;
|
||||||
|
|||||||
@@ -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,
|
void FRenderer::readPixels(uint32_t const xoffset, uint32_t const yoffset, uint32_t const width, uint32_t const height,
|
||||||
PixelBufferDescriptor&& buffer) {
|
PixelBufferDescriptor&& buffer) {
|
||||||
#ifndef NDEBUG
|
|
||||||
const bool withinFrame = mSwapChain != nullptr;
|
const bool withinFrame = mSwapChain != nullptr;
|
||||||
FILAMENT_CHECK_PRECONDITION(withinFrame) << "readPixels() on a SwapChain must be called after"
|
FILAMENT_CHECK_PRECONDITION(withinFrame)
|
||||||
" beginFrame() and before endFrame().";
|
<< "readPixels() on a SwapChain must be called after "
|
||||||
#endif
|
"beginFrame() and before endFrame().";
|
||||||
|
|
||||||
RendererUtils::readPixels(mEngine.getDriverApi(), mRenderTargetHandle,
|
RendererUtils::readPixels(mEngine.getDriverApi(), mRenderTargetHandle,
|
||||||
xoffset, yoffset, width, height, std::move(buffer));
|
xoffset, yoffset, width, height, std::move(buffer));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ struct Texture::BuilderDetails {
|
|||||||
uint32_t mHeight = 1;
|
uint32_t mHeight = 1;
|
||||||
uint32_t mDepth = 1;
|
uint32_t mDepth = 1;
|
||||||
uint8_t mLevels = 1;
|
uint8_t mLevels = 1;
|
||||||
|
uint8_t mSamples = 1;
|
||||||
Sampler mTarget = Sampler::SAMPLER_2D;
|
Sampler mTarget = Sampler::SAMPLER_2D;
|
||||||
InternalFormat mFormat = InternalFormat::RGBA8;
|
InternalFormat mFormat = InternalFormat::RGBA8;
|
||||||
Usage mUsage = Usage::NONE;
|
Usage mUsage = Usage::NONE;
|
||||||
@@ -118,6 +119,11 @@ Texture::Builder& Texture::Builder::levels(uint8_t const levels) noexcept {
|
|||||||
return *this;
|
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 {
|
Texture::Builder& Texture::Builder::sampler(Sampler const target) noexcept {
|
||||||
mImpl->mTarget = target;
|
mImpl->mTarget = target;
|
||||||
return *this;
|
return *this;
|
||||||
@@ -169,6 +175,13 @@ Texture* Texture::Builder::build(Engine& engine) {
|
|||||||
<< "Texture has invalid dimensions: (" << mImpl->mWidth << ", " << mImpl->mHeight
|
<< "Texture has invalid dimensions: (" << mImpl->mWidth << ", " << mImpl->mHeight
|
||||||
<< "), texture name=" << getNameOrDefault().c_str_safe();
|
<< "), 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 =
|
const bool isProtectedTexturesSupported =
|
||||||
downcast(engine).getDriverApi().isProtectedTexturesSupported();
|
downcast(engine).getDriverApi().isProtectedTexturesSupported();
|
||||||
const bool useProtectedMemory = bool(mImpl->mUsage & TextureUsage::PROTECTED);
|
const bool useProtectedMemory = bool(mImpl->mUsage & TextureUsage::PROTECTED);
|
||||||
@@ -286,6 +299,7 @@ FTexture::FTexture(FEngine& engine, const Builder& builder)
|
|||||||
mUsage = builder->mUsage;
|
mUsage = builder->mUsage;
|
||||||
mTarget = builder->mTarget;
|
mTarget = builder->mTarget;
|
||||||
mLevelCount = builder->mLevels;
|
mLevelCount = builder->mLevels;
|
||||||
|
mSampleCount = builder->mSamples;
|
||||||
mSwizzle = builder->mSwizzle;
|
mSwizzle = builder->mSwizzle;
|
||||||
mTextureIsSwizzled = builder->mTextureIsSwizzled;
|
mTextureIsSwizzled = builder->mTextureIsSwizzled;
|
||||||
mHasBlitSrc = builder->mHasBlitSrc;
|
mHasBlitSrc = builder->mHasBlitSrc;
|
||||||
@@ -369,6 +383,9 @@ void FTexture::setImage(FEngine& engine, size_t const level,
|
|||||||
FILAMENT_CHECK_PRECONDITION(!mExternal)
|
FILAMENT_CHECK_PRECONDITION(!mExternal)
|
||||||
<< "External Texture not supported for this operation.";
|
<< "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 ("
|
FILAMENT_CHECK_PRECONDITION(mSampleCount <= 1) << "Operation not supported with multisample ("
|
||||||
<< unsigned(mSampleCount) << ") texture.";
|
<< 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.";
|
FILAMENT_CHECK_PRECONDITION(p.buffer) << "Data buffer is nullptr.";
|
||||||
|
|
||||||
uint32_t effectiveTextureDepthOrLayers;
|
uint32_t effectiveTextureDepthOrLayers = 0;
|
||||||
switch (mTarget) {
|
switch (mTarget) {
|
||||||
case SamplerType::SAMPLER_EXTERNAL:
|
case SamplerType::SAMPLER_EXTERNAL:
|
||||||
// can't happen by construction, fallthrough...
|
// can't happen by construction, fallthrough...
|
||||||
@@ -447,6 +464,7 @@ void FTexture::setImage(FEngine& engine, size_t const level,
|
|||||||
case SamplerType::SAMPLER_EXTERNAL:
|
case SamplerType::SAMPLER_EXTERNAL:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// this should have been validated already
|
// this should have been validated already
|
||||||
|
|||||||
@@ -76,9 +76,6 @@ public:
|
|||||||
|
|
||||||
void generateMipmaps(FEngine& engine) const noexcept;
|
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 isCompressed() const noexcept { return isCompressedFormat(mFormat); }
|
||||||
|
|
||||||
bool isCubemap() const noexcept { return mTarget == Sampler::SAMPLER_CUBEMAP; }
|
bool isCubemap() const noexcept { return mTarget == Sampler::SAMPLER_CUBEMAP; }
|
||||||
|
|||||||
@@ -100,19 +100,7 @@ VertexBuffer::Builder& VertexBuffer::Builder::attribute(VertexAttribute const at
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (size_t(attribute) < MAX_VERTEX_ATTRIBUTE_COUNT &&
|
if (size_t(attribute) < MAX_VERTEX_ATTRIBUTE_COUNT &&
|
||||||
size_t(bufferIndex) < 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
|
|
||||||
|
|
||||||
auto& entry = mImpl->mAttributes[attribute];
|
auto& entry = mImpl->mAttributes[attribute];
|
||||||
entry.buffer = bufferIndex;
|
entry.buffer = bufferIndex;
|
||||||
entry.offset = byteOffset;
|
entry.offset = byteOffset;
|
||||||
@@ -169,9 +157,15 @@ VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
|
|||||||
auto const& attributes = mImpl->mAttributes;
|
auto const& attributes = mImpl->mAttributes;
|
||||||
utils::bitset32 attributedBuffers;
|
utils::bitset32 attributedBuffers;
|
||||||
|
|
||||||
declaredAttributes.forEachSetBit([&](size_t const j){
|
declaredAttributes.forEachSetBit([&](size_t const j) {
|
||||||
// update set of used buffers
|
|
||||||
attributedBuffers.set(attributes[j].buffer);
|
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) {
|
if (engine.getActiveFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0) {
|
||||||
FILAMENT_CHECK_PRECONDITION(!(attributes[j].flags & Attribute::FLAG_INTEGER_TARGET))
|
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::HALF2)) |
|
||||||
(1 << int(ET::HALF3)) |
|
(1 << int(ET::HALF3)) |
|
||||||
(1 << int(ET::HALF4));
|
(1 << int(ET::HALF4));
|
||||||
|
|
||||||
FILAMENT_CHECK_PRECONDITION(!(invalidIntegerTypes & (1 << int(attributes[j].type))))
|
FILAMENT_CHECK_PRECONDITION(!(invalidIntegerTypes & (1 << int(attributes[j].type))))
|
||||||
<< "invalid integer vertex attribute type " << 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)
|
FILAMENT_CHECK_PRECONDITION(attributedBuffers.count() == mImpl->mBufferCount)
|
||||||
|
|||||||
@@ -241,6 +241,8 @@ bool DescriptorSet::isTextureCompatibleWithDescriptor(
|
|||||||
return t == TextureType::DEPTH || t == TextureType::DEPTH_STENCIL;
|
return t == TextureType::DEPTH || t == TextureType::DEPTH_STENCIL;
|
||||||
|
|
||||||
case DescriptorType::SAMPLER_EXTERNAL:
|
case DescriptorType::SAMPLER_EXTERNAL:
|
||||||
|
return true;
|
||||||
|
|
||||||
case DescriptorType::UNIFORM_BUFFER:
|
case DescriptorType::UNIFORM_BUFFER:
|
||||||
case DescriptorType::SHADER_STORAGE_BUFFER:
|
case DescriptorType::SHADER_STORAGE_BUFFER:
|
||||||
case DescriptorType::INPUT_ATTACHMENT:
|
case DescriptorType::INPUT_ATTACHMENT:
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#define TNT_FILAFLAT_UNFLATTENER_H
|
#define TNT_FILAFLAT_UNFLATTENER_H
|
||||||
|
|
||||||
#include <utils/compiler.h>
|
#include <utils/compiler.h>
|
||||||
|
#include <utils/debug.h>
|
||||||
#include <utils/CString.h>
|
#include <utils/CString.h>
|
||||||
|
|
||||||
#include <private/filament/Variant.h>
|
#include <private/filament/Variant.h>
|
||||||
|
|||||||
@@ -305,7 +305,7 @@ public:
|
|||||||
|
|
||||||
//! Add a parameter array to this material.
|
//! Add a parameter array to this material.
|
||||||
MaterialBuilder& parameter(const char* name, size_t size, UniformType type,
|
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.
|
//! Add a constant parameter to this material.
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@@ -325,9 +325,9 @@ public:
|
|||||||
SamplerFormat format = SamplerFormat::FLOAT,
|
SamplerFormat format = SamplerFormat::FLOAT,
|
||||||
ParameterPrecision precision = ParameterPrecision::DEFAULT, bool multisample = false,
|
ParameterPrecision precision = ParameterPrecision::DEFAULT, bool multisample = false,
|
||||||
const char* transformName = "",
|
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).
|
//! Custom variables (all float4).
|
||||||
MaterialBuilder& variable(Variable v, const char* name) noexcept;
|
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.
|
//! Add a new fragment shader output variable. Only valid for materials in the POST_PROCESS domain.
|
||||||
MaterialBuilder& output(VariableQualifier qualifier, OutputTarget target, Precision precision,
|
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;
|
MaterialBuilder& enableFramebufferFetch() noexcept;
|
||||||
|
|
||||||
@@ -646,12 +646,12 @@ public:
|
|||||||
* Add a subpass parameter to this material.
|
* Add a subpass parameter to this material.
|
||||||
*/
|
*/
|
||||||
MaterialBuilder& subpass(SubpassType subpassType,
|
MaterialBuilder& subpass(SubpassType subpassType,
|
||||||
SamplerFormat format, ParameterPrecision precision, const char* name) noexcept;
|
SamplerFormat format, ParameterPrecision precision, const char* name);
|
||||||
MaterialBuilder& subpass(SubpassType subpassType,
|
MaterialBuilder& subpass(SubpassType subpassType,
|
||||||
SamplerFormat format, const char* name) noexcept;
|
SamplerFormat format, const char* name);
|
||||||
MaterialBuilder& subpass(SubpassType subpassType,
|
MaterialBuilder& subpass(SubpassType subpassType,
|
||||||
ParameterPrecision precision, const char* name) noexcept;
|
ParameterPrecision precision, const char* name);
|
||||||
MaterialBuilder& subpass(SubpassType subpassType, const char* name) noexcept;
|
MaterialBuilder& subpass(SubpassType subpassType, const char* name);
|
||||||
|
|
||||||
struct Parameter {
|
struct Parameter {
|
||||||
Parameter() noexcept: parameterType(INVALID) {}
|
Parameter() noexcept: parameterType(INVALID) {}
|
||||||
|
|||||||
@@ -610,6 +610,14 @@ bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
|
|||||||
slog.e << "Tint writer error: " << wgslOut.Failure().reason << io::endl;
|
slog.e << "Tint writer error: " << wgslOut.Failure().reason << io::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// 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;
|
*outWsl = wgslOut->wgsl;
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
|
|||||||
@@ -289,7 +289,7 @@ MaterialBuilder& MaterialBuilder::variable(Variable v,
|
|||||||
}
|
}
|
||||||
|
|
||||||
MaterialBuilder& MaterialBuilder::parameter(const char* name, size_t size, UniformType type,
|
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";
|
FILAMENT_CHECK_POSTCONDITION(mParameterCount < MAX_PARAMETERS_COUNT) << "Too many parameters";
|
||||||
mParameters[mParameterCount++] = { name, type, size, precision };
|
mParameters[mParameterCount++] = { name, type, size, precision };
|
||||||
return *this;
|
return *this;
|
||||||
@@ -303,7 +303,7 @@ MaterialBuilder& MaterialBuilder::parameter(const char* name, UniformType const
|
|||||||
|
|
||||||
MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType samplerType,
|
MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType samplerType,
|
||||||
SamplerFormat format, ParameterPrecision precision, bool multisample,
|
SamplerFormat format, ParameterPrecision precision, bool multisample,
|
||||||
const char* transformName, ShaderStageFlags stages) noexcept {
|
const char* transformName, ShaderStageFlags stages) {
|
||||||
FILAMENT_CHECK_PRECONDITION(!multisample ||
|
FILAMENT_CHECK_PRECONDITION(!multisample ||
|
||||||
(format != SamplerFormat::SHADOW &&
|
(format != SamplerFormat::SHADOW &&
|
||||||
(samplerType == SamplerType::SAMPLER_2D ||
|
(samplerType == SamplerType::SAMPLER_2D ||
|
||||||
@@ -364,14 +364,14 @@ template MaterialBuilder& MaterialBuilder::constant<float>(
|
|||||||
template MaterialBuilder& MaterialBuilder::constant<bool>(
|
template MaterialBuilder& MaterialBuilder::constant<bool>(
|
||||||
const char* name, ConstantType type, bool defaultValue);
|
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";
|
FILAMENT_CHECK_POSTCONDITION(mBuffers.size() < MAX_BUFFERS_COUNT) << "Too many buffers";
|
||||||
mBuffers.emplace_back(std::make_unique<BufferInterfaceBlock>(std::move(bib)));
|
mBuffers.emplace_back(std::make_unique<BufferInterfaceBlock>(std::move(bib)));
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialBuilder& MaterialBuilder::subpass(SubpassType subpassType, SamplerFormat format,
|
MaterialBuilder& MaterialBuilder::subpass(SubpassType subpassType, SamplerFormat format,
|
||||||
ParameterPrecision precision, const char* name) noexcept {
|
ParameterPrecision precision, const char* name) {
|
||||||
FILAMENT_CHECK_PRECONDITION(format == SamplerFormat::FLOAT)
|
FILAMENT_CHECK_PRECONDITION(format == SamplerFormat::FLOAT)
|
||||||
<< "Subpass parameters must have FLOAT format.";
|
<< "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,
|
MaterialBuilder& MaterialBuilder::subpass(SubpassType const subpassType, SamplerFormat const format,
|
||||||
const char* name) noexcept {
|
const char* name) {
|
||||||
return subpass(subpassType, format, ParameterPrecision::DEFAULT, name);
|
return subpass(subpassType, format, ParameterPrecision::DEFAULT, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialBuilder& MaterialBuilder::subpass(SubpassType const subpassType, ParameterPrecision const precision,
|
MaterialBuilder& MaterialBuilder::subpass(SubpassType const subpassType, ParameterPrecision const precision,
|
||||||
const char* name) noexcept {
|
const char* name) {
|
||||||
return subpass(subpassType, SamplerFormat::FLOAT, precision, 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);
|
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,
|
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)
|
FILAMENT_CHECK_PRECONDITION(target != OutputTarget::DEPTH || type == OutputType::FLOAT)
|
||||||
<< "Depth outputs must be of type FLOAT.";
|
<< "Depth outputs must be of type FLOAT.";
|
||||||
FILAMENT_CHECK_PRECONDITION(
|
FILAMENT_CHECK_PRECONDITION(
|
||||||
|
|||||||
@@ -939,9 +939,9 @@ utils::io::sstream& CodeGenerator::generateSpecializationConstant(utils::io::sst
|
|||||||
// Spec constants aren't fully supported in Tint,
|
// Spec constants aren't fully supported in Tint,
|
||||||
// workaround until https://issues.chromium.org/issues/42250586 is resolved
|
// workaround until https://issues.chromium.org/issues/42250586 is resolved
|
||||||
if (mTargetApi == TargetApi::WEBGPU) {
|
if (mTargetApi == TargetApi::WEBGPU) {
|
||||||
out << "layout (constant_id = " << id << ") const "
|
std::string const variableName = "FILAMENT_SPEC_CONST_" + std::to_string(id) + "_" + name;
|
||||||
<< types[value.index()] << " " << name << "_hack = " << constantString << ";\n"
|
out << " " << types[value.index()] << " " << variableName << " = " << constantString << ";\n";
|
||||||
<< types[value.index()] << " " << name << " = " << name << "_hack;\n";
|
out << types[value.index()] << " " << name << " = " << variableName << ";\n";
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
if (mTargetLanguage == MaterialBuilderBase::TargetLanguage::SPIRV) {
|
if (mTargetLanguage == MaterialBuilderBase::TargetLanguage::SPIRV) {
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ struct Config {
|
|||||||
bool resizeable = true;
|
bool resizeable = true;
|
||||||
bool headless = false;
|
bool headless = false;
|
||||||
int stereoscopicEyeCount = 2;
|
int stereoscopicEyeCount = 2;
|
||||||
|
uint8_t samples = 1;
|
||||||
|
|
||||||
// Provided to indicate GPU preference for vulkan
|
// Provided to indicate GPU preference for vulkan
|
||||||
std::string vulkanGPUHint;
|
std::string vulkanGPUHint;
|
||||||
|
|||||||
@@ -340,7 +340,7 @@ public:
|
|||||||
std::is_same<filament::math::ushort3, T>::value ||
|
std::is_same<filament::math::ushort3, T>::value ||
|
||||||
std::is_same<filament::math::ushort4, T>::value>::type;
|
std::is_same<filament::math::ushort4, T>::value>::type;
|
||||||
template<typename T, typename = is_supported_aux_t<T>>
|
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.
|
* Get number of output triangles.
|
||||||
|
|||||||
@@ -747,22 +747,22 @@ void TangentSpaceMesh::getQuats(quath* out, size_t stride) const noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template void TangentSpaceMesh::getAux<float2>(AuxAttribute attribute, float2* out,
|
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,
|
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,
|
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,
|
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,
|
template void TangentSpaceMesh::getAux<ushort4>(AuxAttribute attribute, ushort4* out,
|
||||||
size_t stride) const noexcept;
|
size_t stride) const;
|
||||||
|
|
||||||
template<typename T, typename>
|
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);
|
AttributeImpl attrib = static_cast<AttributeImpl>(attribute);
|
||||||
auto inAux = mInput->data<T>(attrib);
|
auto inAux = mInput->data<T>(attrib);
|
||||||
FILAMENT_CHECK_PRECONDITION(inAux) << "Must provide input auxilliary attribute";
|
FILAMENT_CHECK_PRECONDITION(inAux) << "Must provide input auxilliary attribute";
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <math/norm.h>
|
#include <math/norm.h>
|
||||||
#include <math/quat.h>
|
#include <math/quat.h>
|
||||||
|
|
||||||
|
#include <utils/debug.h>
|
||||||
#include <utils/Panic.h>
|
#include <utils/Panic.h>
|
||||||
|
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|||||||
@@ -263,7 +263,7 @@ struct FAssetLoader : public AssetLoader {
|
|||||||
mEngine(*config.engine),
|
mEngine(*config.engine),
|
||||||
mDefaultNodeName(config.defaultNodeName) {
|
mDefaultNodeName(config.defaultNodeName) {
|
||||||
if (config.ext) {
|
if (config.ext) {
|
||||||
FILAMENT_CHECK_PRECONDITION(AssetConfigurationExtended::isSupported())
|
FILAMENT_CHECK_POSTCONDITION(AssetConfigurationExtended::isSupported())
|
||||||
<< "Extend asset loading is not supported on this platform";
|
<< "Extend asset loading is not supported on this platform";
|
||||||
mLoaderExtended = std::make_unique<AssetLoaderExtended>(
|
mLoaderExtended = std::make_unique<AssetLoaderExtended>(
|
||||||
*config.ext, config.engine, mMaterials);
|
*config.ext, config.engine, mMaterials);
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "DependencyGraph.h"
|
#include "DependencyGraph.h"
|
||||||
|
|
||||||
|
#include <utils/debug.h>
|
||||||
#include <utils/Panic.h>
|
#include <utils/Panic.h>
|
||||||
|
|
||||||
using namespace filament;
|
using namespace filament;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
#include <gltfio/NodeManager.h>
|
#include <gltfio/NodeManager.h>
|
||||||
|
|
||||||
#include <utils/compiler.h>
|
#include <utils/compiler.h>
|
||||||
|
#include <utils/debug.h>
|
||||||
#include <utils/SingleInstanceComponentManager.h>
|
#include <utils/SingleInstanceComponentManager.h>
|
||||||
#include <utils/Entity.h>
|
#include <utils/Entity.h>
|
||||||
#include <utils/Slice.h>
|
#include <utils/Slice.h>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#include "TangentSpaceMeshWrapper.h"
|
#include "TangentSpaceMeshWrapper.h"
|
||||||
|
|
||||||
|
#include <utils/debug.h>
|
||||||
#include <utils/Panic.h>
|
#include <utils/Panic.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ IBLPrefilterContext::IBLPrefilterContext(Engine& engine)
|
|||||||
mVertexBuffer = VertexBuffer::Builder()
|
mVertexBuffer = VertexBuffer::Builder()
|
||||||
.vertexCount(3)
|
.vertexCount(3)
|
||||||
.bufferCount(1)
|
.bufferCount(1)
|
||||||
.attribute(VertexAttribute::POSITION, 0,
|
.attribute(POSITION, 0,
|
||||||
VertexBuffer::AttributeType::FLOAT4, 0)
|
VertexBuffer::AttributeType::FLOAT4, 0)
|
||||||
.build(engine);
|
.build(engine);
|
||||||
|
|
||||||
@@ -180,7 +180,7 @@ IBLPrefilterContext& IBLPrefilterContext::operator=(IBLPrefilterContext&& rhs) n
|
|||||||
|
|
||||||
IBLPrefilterContext::EquirectangularToCubemap::EquirectangularToCubemap(
|
IBLPrefilterContext::EquirectangularToCubemap::EquirectangularToCubemap(
|
||||||
IBLPrefilterContext& context,
|
IBLPrefilterContext& context,
|
||||||
IBLPrefilterContext::EquirectangularToCubemap::Config const& config)
|
Config const& config)
|
||||||
: mContext(context), mConfig(config) {
|
: mContext(context), mConfig(config) {
|
||||||
Engine& engine = mContext.mEngine;
|
Engine& engine = mContext.mEngine;
|
||||||
mEquirectMaterial = Material::Builder().package(
|
mEquirectMaterial = Material::Builder().package(
|
||||||
@@ -198,7 +198,7 @@ IBLPrefilterContext::EquirectangularToCubemap::~EquirectangularToCubemap() noexc
|
|||||||
}
|
}
|
||||||
|
|
||||||
IBLPrefilterContext::EquirectangularToCubemap::EquirectangularToCubemap(
|
IBLPrefilterContext::EquirectangularToCubemap::EquirectangularToCubemap(
|
||||||
IBLPrefilterContext::EquirectangularToCubemap&& rhs) noexcept
|
EquirectangularToCubemap&& rhs) noexcept
|
||||||
: mContext(rhs.mContext) {
|
: mContext(rhs.mContext) {
|
||||||
using std::swap;
|
using std::swap;
|
||||||
swap(mEquirectMaterial, rhs.mEquirectMaterial);
|
swap(mEquirectMaterial, rhs.mEquirectMaterial);
|
||||||
@@ -206,7 +206,7 @@ IBLPrefilterContext::EquirectangularToCubemap::EquirectangularToCubemap(
|
|||||||
|
|
||||||
IBLPrefilterContext::EquirectangularToCubemap&
|
IBLPrefilterContext::EquirectangularToCubemap&
|
||||||
IBLPrefilterContext::EquirectangularToCubemap::operator=(
|
IBLPrefilterContext::EquirectangularToCubemap::operator=(
|
||||||
IBLPrefilterContext::EquirectangularToCubemap&& rhs) noexcept {
|
EquirectangularToCubemap&& rhs) noexcept {
|
||||||
using std::swap;
|
using std::swap;
|
||||||
if (this != &rhs) {
|
if (this != &rhs) {
|
||||||
swap(mEquirectMaterial, rhs.mEquirectMaterial);
|
swap(mEquirectMaterial, rhs.mEquirectMaterial);
|
||||||
@@ -300,7 +300,7 @@ Texture* IBLPrefilterContext::EquirectangularToCubemap::operator()(
|
|||||||
// ------------------------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
IBLPrefilterContext::IrradianceFilter::IrradianceFilter(IBLPrefilterContext& context,
|
IBLPrefilterContext::IrradianceFilter::IrradianceFilter(IBLPrefilterContext& context,
|
||||||
IBLPrefilterContext::IrradianceFilter::Config config)
|
Config config)
|
||||||
: mContext(context),
|
: mContext(context),
|
||||||
mSampleCount(std::min(config.sampleCount, uint16_t(2048))) {
|
mSampleCount(std::min(config.sampleCount, uint16_t(2048))) {
|
||||||
|
|
||||||
@@ -362,13 +362,13 @@ IBLPrefilterContext::IrradianceFilter::~IrradianceFilter() noexcept {
|
|||||||
}
|
}
|
||||||
|
|
||||||
IBLPrefilterContext::IrradianceFilter::IrradianceFilter(
|
IBLPrefilterContext::IrradianceFilter::IrradianceFilter(
|
||||||
IBLPrefilterContext::IrradianceFilter&& rhs) noexcept
|
IrradianceFilter&& rhs) noexcept
|
||||||
: mContext(rhs.mContext) {
|
: mContext(rhs.mContext) {
|
||||||
this->operator=(std::move(rhs));
|
this->operator=(std::move(rhs));
|
||||||
}
|
}
|
||||||
|
|
||||||
IBLPrefilterContext::IrradianceFilter& IBLPrefilterContext::IrradianceFilter::operator=(
|
IBLPrefilterContext::IrradianceFilter& IBLPrefilterContext::IrradianceFilter::operator=(
|
||||||
IBLPrefilterContext::IrradianceFilter&& rhs) noexcept {
|
IrradianceFilter&& rhs) noexcept {
|
||||||
using std::swap;
|
using std::swap;
|
||||||
if (this != & rhs) {
|
if (this != & rhs) {
|
||||||
swap(mKernelMaterial, rhs.mKernelMaterial);
|
swap(mKernelMaterial, rhs.mKernelMaterial);
|
||||||
@@ -378,9 +378,8 @@ IBLPrefilterContext::IrradianceFilter& IBLPrefilterContext::IrradianceFilter::op
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
filament::Texture* IBLPrefilterContext::IrradianceFilter::operator()(
|
Texture* IBLPrefilterContext::IrradianceFilter::operator()(Options options,
|
||||||
IBLPrefilterContext::IrradianceFilter::Options options,
|
Texture const* environmentCubemap, Texture* outIrradianceTexture) {
|
||||||
filament::Texture const* environmentCubemap, filament::Texture* outIrradianceTexture) {
|
|
||||||
|
|
||||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||||
using namespace backend;
|
using namespace backend;
|
||||||
@@ -470,12 +469,12 @@ filament::Texture* IBLPrefilterContext::IrradianceFilter::operator()(
|
|||||||
}
|
}
|
||||||
|
|
||||||
UTILS_NOINLINE
|
UTILS_NOINLINE
|
||||||
filament::Texture* IBLPrefilterContext::IrradianceFilter::operator()(
|
Texture* IBLPrefilterContext::IrradianceFilter::operator()(
|
||||||
filament::Texture const* environmentCubemap, filament::Texture* outIrradianceTexture) {
|
Texture const* environmentCubemap, Texture* outIrradianceTexture) {
|
||||||
return operator()({}, environmentCubemap, outIrradianceTexture);
|
return operator()({}, environmentCubemap, outIrradianceTexture);
|
||||||
}
|
}
|
||||||
|
|
||||||
filament::Texture* IBLPrefilterContext::IrradianceFilter::createIrradianceTexture() {
|
Texture* IBLPrefilterContext::IrradianceFilter::createIrradianceTexture() {
|
||||||
Engine& engine = mContext.mEngine;
|
Engine& engine = mContext.mEngine;
|
||||||
|
|
||||||
Texture* const outCubemap = Texture::Builder()
|
Texture* const outCubemap = Texture::Builder()
|
||||||
@@ -614,7 +613,7 @@ Texture* IBLPrefilterContext::SpecularFilter::operator()(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Texture* IBLPrefilterContext::SpecularFilter::operator()(
|
Texture* IBLPrefilterContext::SpecularFilter::operator()(
|
||||||
IBLPrefilterContext::SpecularFilter::Options options,
|
Options options,
|
||||||
Texture const* environmentCubemap, Texture* outReflectionsTexture) {
|
Texture const* environmentCubemap, Texture* outReflectionsTexture) {
|
||||||
|
|
||||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
|
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
|
#include <utils/compiler.h>
|
||||||
|
#include <utils/debug.h>
|
||||||
#include <utils/Log.h>
|
#include <utils/Log.h>
|
||||||
#include <utils/Panic.h>
|
#include <utils/Panic.h>
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
#define TNT_UTILS_ALLOCATOR_H
|
#define TNT_UTILS_ALLOCATOR_H
|
||||||
|
|
||||||
#include <utils/compiler.h>
|
#include <utils/compiler.h>
|
||||||
#include <utils/debug.h>
|
|
||||||
#include <utils/memalign.h>
|
#include <utils/memalign.h>
|
||||||
#include <utils/Mutex.h>
|
#include <utils/Mutex.h>
|
||||||
|
|
||||||
@@ -36,22 +35,22 @@ namespace utils {
|
|||||||
|
|
||||||
namespace pointermath {
|
namespace pointermath {
|
||||||
|
|
||||||
template <typename P, typename T>
|
template<typename P, typename T>
|
||||||
static inline P* add(P* a, T b) noexcept {
|
static P* add(P* a, T b) noexcept {
|
||||||
return (P*)(uintptr_t(a) + uintptr_t(b));
|
return (P*) (uintptr_t(a) + uintptr_t(b));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename P>
|
template<typename P>
|
||||||
static inline P* align(P* p, size_t alignment) noexcept {
|
static P* align(P* p, size_t alignment) noexcept {
|
||||||
// alignment must be a power-of-two
|
// alignment must be a power-of-two
|
||||||
assert_invariant(alignment && !(alignment & alignment-1));
|
assert(alignment && !(alignment & alignment-1));
|
||||||
return (P*)((uintptr_t(p) + alignment - 1) & ~(alignment - 1));
|
return (P*) ((uintptr_t(p) + alignment - 1) & ~(alignment - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename P>
|
template<typename P>
|
||||||
static inline P* align(P* p, size_t alignment, size_t offset) noexcept {
|
static P* align(P* p, size_t alignment, size_t offset) noexcept {
|
||||||
P* const r = align(add(p, offset), alignment);
|
P* const r = align(add(p, offset), alignment);
|
||||||
assert_invariant(r >= add(p, offset));
|
assert(r >= add(p, offset));
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,7 +101,7 @@ public:
|
|||||||
|
|
||||||
// free memory back to the specified point
|
// free memory back to the specified point
|
||||||
void rewind(void* p) UTILS_RESTRICT noexcept {
|
void rewind(void* p) UTILS_RESTRICT noexcept {
|
||||||
assert_invariant(p >= mBegin && p < end());
|
assert(p >= mBegin && p < end());
|
||||||
set_current(p);
|
set_current(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,13 +232,13 @@ public:
|
|||||||
Node* const head = mHead;
|
Node* const head = mHead;
|
||||||
mHead = head ? head->next : nullptr;
|
mHead = head ? head->next : nullptr;
|
||||||
// this could indicate a use after free
|
// this could indicate a use after free
|
||||||
assert_invariant(!mHead || mHead >= mBegin && mHead < mEnd);
|
assert(!mHead || mHead >= mBegin && mHead < mEnd);
|
||||||
return head;
|
return head;
|
||||||
}
|
}
|
||||||
|
|
||||||
void push(void* p) noexcept {
|
void push(void* p) noexcept {
|
||||||
assert_invariant(p);
|
assert(p);
|
||||||
assert_invariant(p >= mBegin && p < mEnd);
|
assert(p >= mBegin && p < mEnd);
|
||||||
Node* const head = static_cast<Node*>(p);
|
Node* const head = static_cast<Node*>(p);
|
||||||
head->next = mHead;
|
head->next = mHead;
|
||||||
mHead = head;
|
mHead = head;
|
||||||
@@ -297,18 +296,18 @@ public:
|
|||||||
// This assert needs to occur after we have validated that there was no race condition
|
// This assert needs to occur after we have validated that there was no race condition
|
||||||
// Otherwise, next might already contain application data, if another thread
|
// Otherwise, next might already contain application data, if another thread
|
||||||
// raced ahead of us after we loaded mHead, but before we loaded mHead->next.
|
// raced ahead of us after we loaded mHead, but before we loaded mHead->next.
|
||||||
assert_invariant(!pNext || pNext >= pStorage);
|
assert(!pNext || pNext >= pStorage);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void* p = (currentHead.offset >= 0) ? (pStorage + currentHead.offset) : nullptr;
|
void* p = (currentHead.offset >= 0) ? (pStorage + currentHead.offset) : nullptr;
|
||||||
assert_invariant(!p || p >= pStorage);
|
assert(!p || p >= pStorage);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void push(void* p) noexcept {
|
void push(void* p) noexcept {
|
||||||
Node* const storage = mStorage;
|
Node* const storage = mStorage;
|
||||||
assert_invariant(p && p >= storage);
|
assert(p && p >= storage);
|
||||||
Node* const node = static_cast<Node*>(p);
|
Node* const node = static_cast<Node*>(p);
|
||||||
HeadPtr currentHead = mHead.load(std::memory_order_relaxed);
|
HeadPtr currentHead = mHead.load(std::memory_order_relaxed);
|
||||||
HeadPtr newHead = { int32_t(node - storage), currentHead.tag + 1 };
|
HeadPtr newHead = { int32_t(node - storage), currentHead.tag + 1 };
|
||||||
@@ -381,9 +380,9 @@ public:
|
|||||||
// our allocator concept
|
// our allocator concept
|
||||||
void* alloc(size_t size = ELEMENT_SIZE,
|
void* alloc(size_t size = ELEMENT_SIZE,
|
||||||
size_t alignment = ALIGNMENT, size_t offset = OFFSET) noexcept {
|
size_t alignment = ALIGNMENT, size_t offset = OFFSET) noexcept {
|
||||||
assert_invariant(size <= ELEMENT_SIZE);
|
assert(size <= ELEMENT_SIZE);
|
||||||
assert_invariant(alignment <= ALIGNMENT);
|
assert(alignment <= ALIGNMENT);
|
||||||
assert_invariant(offset == OFFSET);
|
assert(offset == OFFSET);
|
||||||
return mFreeList.pop();
|
return mFreeList.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -461,7 +460,7 @@ public:
|
|||||||
if (UTILS_UNLIKELY(!p)) {
|
if (UTILS_UNLIKELY(!p)) {
|
||||||
p = HeapAllocator::alloc(size, alignment);
|
p = HeapAllocator::alloc(size, alignment);
|
||||||
}
|
}
|
||||||
assert_invariant(p);
|
assert(p);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -942,7 +941,7 @@ public:
|
|||||||
|
|
||||||
TYPE* allocate(std::size_t n) {
|
TYPE* allocate(std::size_t n) {
|
||||||
auto p = static_cast<TYPE *>(mArena.alloc(n * sizeof(TYPE), alignof(TYPE)));
|
auto p = static_cast<TYPE *>(mArena.alloc(n * sizeof(TYPE), alignof(TYPE)));
|
||||||
assert_invariant(p);
|
assert(p);
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -40,7 +40,7 @@
|
|||||||
|
|
||||||
namespace utils {
|
namespace utils {
|
||||||
|
|
||||||
class FixedCapacityVectorBase {
|
class UTILS_PUBLIC FixedCapacityVectorBase {
|
||||||
protected:
|
protected:
|
||||||
UTILS_NORETURN static void capacityCheckFailed(size_t capacity, size_t size);
|
UTILS_NORETURN static void capacityCheckFailed(size_t capacity, size_t size);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,12 +18,11 @@
|
|||||||
#ifndef TNT_UTILS_FIXEDCIRCULARBUFFER_H
|
#ifndef TNT_UTILS_FIXEDCIRCULARBUFFER_H
|
||||||
#define TNT_UTILS_FIXEDCIRCULARBUFFER_H
|
#define TNT_UTILS_FIXEDCIRCULARBUFFER_H
|
||||||
|
|
||||||
#include <utils/debug.h>
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
namespace utils {
|
namespace utils {
|
||||||
@@ -31,7 +30,7 @@ namespace utils {
|
|||||||
template<typename T>
|
template<typename T>
|
||||||
class FixedCircularBuffer {
|
class FixedCircularBuffer {
|
||||||
public:
|
public:
|
||||||
explicit FixedCircularBuffer(size_t capacity)
|
explicit FixedCircularBuffer(size_t const capacity)
|
||||||
: mData(std::make_unique<T[]>(capacity)), mCapacity(capacity) {}
|
: mData(std::make_unique<T[]>(capacity)), mCapacity(capacity) {}
|
||||||
|
|
||||||
size_t size() const noexcept { return mSize; }
|
size_t size() const noexcept { return mSize; }
|
||||||
@@ -57,7 +56,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
T pop() noexcept {
|
T pop() noexcept {
|
||||||
assert_invariant(mSize > 0);
|
assert(mSize > 0);
|
||||||
T result = mData[mBegin];
|
T result = mData[mBegin];
|
||||||
mBegin = (mBegin + 1) % mCapacity;
|
mBegin = (mBegin + 1) % mCapacity;
|
||||||
mSize--;
|
mSize--;
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user