Compare 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 | ||
|
|
1305303f2e | ||
|
|
8532511107 | ||
|
|
b762ccb78c | ||
|
|
3296eb153c | ||
|
|
ae6d1028f7 | ||
|
|
92579b6307 | ||
|
|
315b53c339 | ||
|
|
c55d459986 | ||
|
|
7876695545 | ||
|
|
e1a1464c5d | ||
|
|
67ed5141bf | ||
|
|
b9d7a25b91 | ||
|
|
ba63ac63a0 | ||
|
|
1e77947758 | ||
|
|
53df4012fe | ||
|
|
d880fab93f | ||
|
|
fb8030b80a | ||
|
|
e2d009ff2a | ||
|
|
0a67ef4d80 | ||
|
|
79d6f46f1a | ||
|
|
6481fa2acf | ||
|
|
7638b3ddd9 | ||
|
|
fe474a9d7e | ||
|
|
81008abe06 | ||
|
|
ef1d079c53 | ||
|
|
5286dec70b | ||
|
|
d748dc590e | ||
|
|
93a0aec246 | ||
|
|
f3fa734793 | ||
|
|
33d3c3a897 | ||
|
|
bbabf3480b | ||
|
|
e7a8f471a1 | ||
|
|
d45a5cc926 | ||
|
|
361ba2afea | ||
|
|
78419cd992 |
@@ -97,6 +97,10 @@ Make sure you've installed the following dependencies:
|
||||
- `libxcomposite-dev` (`libXcomposite-devel` on Fedora)
|
||||
- `libxxf86vm-dev` (`libXxf86vm-devel` on Fedora)
|
||||
|
||||
```shell
|
||||
sudo apt install clang-14 libglu1-mesa-dev libc++-14-dev libc++abi-14-dev ninja-build libxi-dev libxcomposite-dev libxxf86vm-dev -y
|
||||
```
|
||||
|
||||
After dependencies have been installed, we highly recommend using the [easy build](#easy-build)
|
||||
script.
|
||||
|
||||
|
||||
@@ -49,6 +49,10 @@ option(FILAMENT_SUPPORTS_OSMESA "Enable OSMesa (headless GL context) for Filamen
|
||||
|
||||
option(FILAMENT_ENABLE_FGVIEWER "Enable the frame graph viewer" OFF)
|
||||
|
||||
# This is to disable GTAO for the short-term while we investigate a way to better manage size increases.
|
||||
# On the regular filament build (where size is of less concern), we enable GTAO by default.
|
||||
option(FILAMENT_DISABLE_GTAO "Disable GTAO" OFF)
|
||||
|
||||
set(FILAMENT_NDK_VERSION "" CACHE STRING
|
||||
"Android NDK version or version prefix to be used when building for Android."
|
||||
)
|
||||
|
||||
@@ -7,6 +7,12 @@ A new header is inserted each time a *tag* is created.
|
||||
Instead, if you are authoring a PR for the main branch, add your release note to
|
||||
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
|
||||
|
||||
## v1.61.0
|
||||
|
||||
- materials: sampler now export their type in the material binary [⚠️ **New Material Version**]
|
||||
- samples/texturedquad.cpp now has CLI to select backend api
|
||||
- samples/hellopbr.cpp CLI now allows for selecting webgpu
|
||||
|
||||
## v1.60.1
|
||||
|
||||
|
||||
|
||||
@@ -1801,6 +1801,22 @@ public class View {
|
||||
* @see setAmbientOcclusionOptions()
|
||||
*/
|
||||
public static class AmbientOcclusionOptions {
|
||||
public enum AmbientOcclusionType {
|
||||
/**
|
||||
* use Scalable Ambient Occlusion
|
||||
*/
|
||||
SAO,
|
||||
/**
|
||||
* use Ground Truth-Based Ambient Occlusion
|
||||
*/
|
||||
GTAO,
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of ambient occlusion algorithm.
|
||||
*/
|
||||
@NonNull
|
||||
public AmbientOcclusionOptions.AmbientOcclusionType aoType = AmbientOcclusionOptions.AmbientOcclusionType.SAO;
|
||||
/**
|
||||
* Ambient Occlusion radius in meters, between 0 and ~10.
|
||||
*/
|
||||
@@ -1810,7 +1826,8 @@ public class View {
|
||||
*/
|
||||
public float power = 1.0f;
|
||||
/**
|
||||
* Self-occlusion bias in meters. Use to avoid self-occlusion. Between 0 and a few mm.
|
||||
* Self-occlusion bias in meters. Use to avoid self-occlusion.
|
||||
* Between 0 and a few mm. No effect when aoType set to GTAO
|
||||
*/
|
||||
public float bias = 0.0005f;
|
||||
/**
|
||||
@@ -1826,12 +1843,12 @@ public class View {
|
||||
*/
|
||||
public float bilateralThreshold = 0.05f;
|
||||
/**
|
||||
* affects # of samples used for AO.
|
||||
* affects # of samples used for AO and params for filtering
|
||||
*/
|
||||
@NonNull
|
||||
public QualityLevel quality = QualityLevel.LOW;
|
||||
/**
|
||||
* affects AO smoothness
|
||||
* affects AO smoothness. Recommend setting to HIGH when aoType set to GTAO.
|
||||
*/
|
||||
@NonNull
|
||||
public QualityLevel lowPassFilter = QualityLevel.MEDIUM;
|
||||
@@ -1849,7 +1866,7 @@ public class View {
|
||||
*/
|
||||
public boolean bentNormals = false;
|
||||
/**
|
||||
* min angle in radian to consider
|
||||
* min angle in radian to consider. No effect when aoType set to GTAO.
|
||||
*/
|
||||
public float minHorizonAngleRad = 0.0f;
|
||||
/**
|
||||
@@ -1904,6 +1921,19 @@ public class View {
|
||||
*/
|
||||
public boolean ssctEnabled = false;
|
||||
|
||||
/**
|
||||
* Ground Truth-base Ambient Occlusion (GTAO) options
|
||||
*/
|
||||
public int gtaoSampleSliceCount = 4;
|
||||
/**
|
||||
* Ground Truth-base Ambient Occlusion (GTAO) options
|
||||
*/
|
||||
public int gtaoSampleStepsPerSlice = 3;
|
||||
/**
|
||||
* Ground Truth-base Ambient Occlusion (GTAO) options
|
||||
*/
|
||||
public float gtaoThicknessHeuristic = 0.004f;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
- [Vulkan](./notes/vulkan_debugging.md)
|
||||
- [SPIR-V](./notes/spirv_debugging.md)
|
||||
- [Running with ASAN and UBSAN](./notes/asan_ubsan.md)
|
||||
- [Using Instruments on macOS](./notes/instruments.md)
|
||||
- [Libraries](./notes/libs.md)
|
||||
- [bluegl](./dup/bluegl.md)
|
||||
- [bluevk](./dup/bluevk.md)
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# Using Instruments on macOS
|
||||
|
||||
When running a binary under Instruments on macOS, you may run into the following issue when
|
||||
launching or attaching to an executable:
|
||||
|
||||
```
|
||||
Failed to gain authorization
|
||||
Recovery Suggestion: Target binary needs to be debuggable and signed with 'get-task-allow'
|
||||
```
|
||||
|
||||
This is a security precaution; the solution is to code sign the binary with the
|
||||
`com.apple.security.get-task-allow` entitlement.
|
||||
|
||||
1. Create an `entitlements.plist` file with the following contents:
|
||||
|
||||
```
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.get-task-allow</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
2. Run the following command:
|
||||
|
||||
```
|
||||
codesign -s - --entitlements entitlements.plist <binary>
|
||||
```
|
||||
|
||||
Replace `<binary>` with the name of the binary, for example: `out/cmake-debug/samples/gltf_viewer`.
|
||||
|
||||
Afterwards, you should be able to successfully launch and attach to the executable using
|
||||
Instruments.
|
||||
@@ -265,6 +265,13 @@ set(MATERIAL_SRCS
|
||||
src/materials/vsmMipmap.mat
|
||||
)
|
||||
|
||||
if (NOT FILAMENT_DISABLE_GTAO)
|
||||
list(APPEND MATERIAL_SRCS
|
||||
src/materials/ssao/gtao.mat
|
||||
src/materials/ssao/gtaoBentNormals.mat
|
||||
)
|
||||
endif()
|
||||
|
||||
set(MATERIAL_FL0_SRCS
|
||||
src/materials/defaultMaterial.mat
|
||||
src/materials/skybox.mat
|
||||
@@ -314,12 +321,14 @@ if (FILAMENT_FORCE_PROFILING_MODE)
|
||||
add_definitions(-DFILAMENT_FORCE_PROFILING_MODE)
|
||||
endif()
|
||||
|
||||
if (FILAMENT_DISABLE_GTAO)
|
||||
add_definitions(-DFILAMENT_DISABLE_GTAO)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Definitions
|
||||
# ==================================================================================================
|
||||
|
||||
# "2" corresponds to SYSTRACE_TAG_FILEMENT (See: utils/Systrace.h)
|
||||
add_definitions(-DSYSTRACE_TAG=2)
|
||||
add_definitions(-DFILAMENT_DFG_LUT_SIZE=${DFG_LUT_SIZE})
|
||||
add_definitions(
|
||||
-DFILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB=${FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB}
|
||||
@@ -463,6 +472,30 @@ add_custom_command(
|
||||
APPEND
|
||||
)
|
||||
|
||||
if (NOT FILAMENT_DISABLE_GTAO)
|
||||
add_custom_command(
|
||||
OUTPUT "${MATERIAL_DIR}/gtao.filamat"
|
||||
DEPENDS src/materials/ssao/ssaoUtils.fs
|
||||
DEPENDS src/materials/ssao/ssct.fs
|
||||
DEPENDS src/materials/utils/depthUtils.fs
|
||||
DEPENDS src/materials/utils/geometry.fs
|
||||
DEPENDS src/materials/ssao/gtaoImpl.fs
|
||||
DEPENDS src/materials/ssao/ssctImpl.fs
|
||||
APPEND
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${MATERIAL_DIR}/gtaoBentNormals.filamat"
|
||||
DEPENDS src/materials/ssao/ssaoUtils.fs
|
||||
DEPENDS src/materials/ssao/ssct.fs
|
||||
DEPENDS src/materials/utils/depthUtils.fs
|
||||
DEPENDS src/materials/utils/geometry.fs
|
||||
DEPENDS src/materials/ssao/gtaoImpl.fs
|
||||
DEPENDS src/materials/ssao/ssctImpl.fs
|
||||
APPEND
|
||||
)
|
||||
endif()
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT "${MATERIAL_DIR}/bilateralBlur.filamat"
|
||||
DEPENDS src/materials/ssao/ssaoUtils.fs
|
||||
|
||||
@@ -261,6 +261,7 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
src/webgpu/WebGPUHandles.h
|
||||
src/webgpu/WebGPUPipelineCreation.cpp
|
||||
src/webgpu/WebGPUPipelineCreation.h
|
||||
src/webgpu/WebGPUStrings.h
|
||||
src/webgpu/WebGPUSwapChain.cpp
|
||||
src/webgpu/WebGPUSwapChain.h
|
||||
src/webgpu/WGPUProgram.cpp
|
||||
@@ -290,12 +291,6 @@ if (ANDROID)
|
||||
list(APPEND SRCS src/BackendUtilsAndroid.cpp)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Definitions
|
||||
# ==================================================================================================
|
||||
# "2" corresponds to SYSTRACE_TAG_FILEMENT (See: utils/Systrace.h)
|
||||
add_definitions(-DSYSTRACE_TAG=2 )
|
||||
|
||||
# ==================================================================================================
|
||||
# Includes & target definition
|
||||
# ==================================================================================================
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
#include <variant>
|
||||
#include <string_view>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
@@ -162,7 +163,7 @@ enum class TimerQueryResult : int8_t {
|
||||
AVAILABLE = 1, // result is available
|
||||
};
|
||||
|
||||
static constexpr const char* backendToString(Backend backend) {
|
||||
constexpr std::string_view to_string(Backend const backend) noexcept {
|
||||
switch (backend) {
|
||||
case Backend::NOOP:
|
||||
return "Noop";
|
||||
@@ -174,9 +175,10 @@ static constexpr const char* backendToString(Backend backend) {
|
||||
return "Metal";
|
||||
case Backend::WEBGPU:
|
||||
return "WebGPU";
|
||||
default:
|
||||
return "Unknown";
|
||||
case Backend::DEFAULT:
|
||||
return "Default";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -193,7 +195,7 @@ enum class ShaderLanguage {
|
||||
WGSL = 5,
|
||||
};
|
||||
|
||||
static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) {
|
||||
constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) noexcept {
|
||||
switch (shaderLanguage) {
|
||||
case ShaderLanguage::ESSL1:
|
||||
return "ESSL 1.0";
|
||||
@@ -208,6 +210,7 @@ static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguag
|
||||
case ShaderLanguage::WGSL:
|
||||
return "WGSL";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
enum class ShaderStage : uint8_t {
|
||||
@@ -225,7 +228,7 @@ enum class ShaderStageFlags : uint8_t {
|
||||
ALL_SHADER_STAGE_FLAGS = VERTEX | FRAGMENT | COMPUTE
|
||||
};
|
||||
|
||||
static inline constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage type) noexcept {
|
||||
constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage type) noexcept {
|
||||
switch (type) {
|
||||
case ShaderStage::VERTEX:
|
||||
return bool(uint8_t(flags) & uint8_t(ShaderStageFlags::VERTEX));
|
||||
@@ -236,14 +239,239 @@ static inline constexpr bool hasShaderType(ShaderStageFlags flags, ShaderStage t
|
||||
}
|
||||
}
|
||||
|
||||
enum class DescriptorType : uint8_t {
|
||||
UNIFORM_BUFFER,
|
||||
SHADER_STORAGE_BUFFER,
|
||||
SAMPLER,
|
||||
INPUT_ATTACHMENT,
|
||||
SAMPLER_EXTERNAL
|
||||
enum class TextureType : uint8_t {
|
||||
FLOAT,
|
||||
INT,
|
||||
UINT,
|
||||
DEPTH,
|
||||
STENCIL,
|
||||
DEPTH_STENCIL
|
||||
};
|
||||
|
||||
constexpr std::string_view to_string(TextureType type) noexcept {
|
||||
switch (type) {
|
||||
case TextureType::FLOAT: return "FLOAT";
|
||||
case TextureType::INT: return "INT";
|
||||
case TextureType::UINT: return "UINT";
|
||||
case TextureType::DEPTH: return "DEPTH";
|
||||
case TextureType::STENCIL: return "STENCIL";
|
||||
case TextureType::DEPTH_STENCIL: return "DEPTH_STENCIL";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
enum class DescriptorType : uint8_t {
|
||||
SAMPLER_2D_FLOAT,
|
||||
SAMPLER_2D_INT,
|
||||
SAMPLER_2D_UINT,
|
||||
SAMPLER_2D_DEPTH,
|
||||
|
||||
SAMPLER_2D_ARRAY_FLOAT,
|
||||
SAMPLER_2D_ARRAY_INT,
|
||||
SAMPLER_2D_ARRAY_UINT,
|
||||
SAMPLER_2D_ARRAY_DEPTH,
|
||||
|
||||
SAMPLER_CUBE_FLOAT,
|
||||
SAMPLER_CUBE_INT,
|
||||
SAMPLER_CUBE_UINT,
|
||||
SAMPLER_CUBE_DEPTH,
|
||||
|
||||
SAMPLER_CUBE_ARRAY_FLOAT,
|
||||
SAMPLER_CUBE_ARRAY_INT,
|
||||
SAMPLER_CUBE_ARRAY_UINT,
|
||||
SAMPLER_CUBE_ARRAY_DEPTH,
|
||||
|
||||
SAMPLER_3D_FLOAT,
|
||||
SAMPLER_3D_INT,
|
||||
SAMPLER_3D_UINT,
|
||||
|
||||
SAMPLER_2D_MS_FLOAT,
|
||||
SAMPLER_2D_MS_INT,
|
||||
SAMPLER_2D_MS_UINT,
|
||||
|
||||
SAMPLER_2D_MS_ARRAY_FLOAT,
|
||||
SAMPLER_2D_MS_ARRAY_INT,
|
||||
SAMPLER_2D_MS_ARRAY_UINT,
|
||||
|
||||
SAMPLER_EXTERNAL,
|
||||
UNIFORM_BUFFER,
|
||||
SHADER_STORAGE_BUFFER,
|
||||
INPUT_ATTACHMENT,
|
||||
};
|
||||
|
||||
constexpr bool isDepthDescriptor(DescriptorType const type) noexcept {
|
||||
switch (type) {
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
return true;
|
||||
default: ;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool isFloatDescriptor(DescriptorType const type) noexcept {
|
||||
switch (type) {
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
|
||||
return true;
|
||||
default: ;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool isIntDescriptor(DescriptorType const type) noexcept {
|
||||
switch (type) {
|
||||
case DescriptorType::SAMPLER_2D_INT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_3D_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
|
||||
return true;
|
||||
default: ;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool isUnsignedIntDescriptor(DescriptorType const type) noexcept {
|
||||
switch (type) {
|
||||
case DescriptorType::SAMPLER_2D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_3D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_MS_UINT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT:
|
||||
return true;
|
||||
default: ;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool is3dTypeDescriptor(DescriptorType const type) noexcept {
|
||||
switch (type) {
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_INT:
|
||||
case DescriptorType::SAMPLER_3D_UINT:
|
||||
return true;
|
||||
default: ;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool is2dTypeDescriptor(DescriptorType const type) noexcept {
|
||||
switch (type) {
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_INT:
|
||||
case DescriptorType::SAMPLER_2D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_MS_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_UINT:
|
||||
return true;
|
||||
default: ;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool is2dArrayTypeDescriptor(DescriptorType const type) noexcept {
|
||||
switch (type) {
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT:
|
||||
return true;
|
||||
default: ;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool isCubeTypeDescriptor(DescriptorType const type) noexcept {
|
||||
switch (type) {
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
return true;
|
||||
default: ;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool isCubeArrayTypeDescriptor(DescriptorType const type) noexcept {
|
||||
switch (type) {
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
return true;
|
||||
default: ;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr bool isMultiSampledTypeDescriptor(DescriptorType const type) noexcept {
|
||||
switch (type) {
|
||||
case DescriptorType::SAMPLER_2D_MS_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_UINT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT:
|
||||
return true;
|
||||
default: ;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr std::string_view to_string(DescriptorType type) noexcept {
|
||||
#define DESCRIPTOR_TYPE_CASE(TYPE) case DescriptorType::TYPE: return #TYPE;
|
||||
switch (type) {
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_FLOAT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_INT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_UINT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_DEPTH)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_FLOAT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_INT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_UINT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_ARRAY_DEPTH)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_FLOAT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_INT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_UINT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_DEPTH)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_FLOAT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_INT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_UINT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_CUBE_ARRAY_DEPTH)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_3D_FLOAT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_3D_INT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_3D_UINT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_FLOAT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_INT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_UINT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_ARRAY_FLOAT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_ARRAY_INT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_2D_MS_ARRAY_UINT)
|
||||
DESCRIPTOR_TYPE_CASE(SAMPLER_EXTERNAL)
|
||||
DESCRIPTOR_TYPE_CASE(UNIFORM_BUFFER)
|
||||
DESCRIPTOR_TYPE_CASE(SHADER_STORAGE_BUFFER)
|
||||
DESCRIPTOR_TYPE_CASE(INPUT_ATTACHMENT)
|
||||
}
|
||||
return "UNKNOWN";
|
||||
#undef DESCRIPTOR_TYPE_CASE
|
||||
}
|
||||
|
||||
enum class DescriptorFlags : uint8_t {
|
||||
NONE = 0x00,
|
||||
DYNAMIC_OFFSET = 0x01
|
||||
@@ -254,6 +482,13 @@ using descriptor_set_t = uint8_t;
|
||||
using descriptor_binding_t = uint8_t;
|
||||
|
||||
struct DescriptorSetLayoutBinding {
|
||||
static bool isSampler(DescriptorType type) noexcept {
|
||||
return int(type) <= int(DescriptorType::SAMPLER_EXTERNAL);
|
||||
}
|
||||
static bool isBuffer(DescriptorType type) noexcept {
|
||||
return type == DescriptorType::UNIFORM_BUFFER ||
|
||||
type == DescriptorType::SHADER_STORAGE_BUFFER;
|
||||
}
|
||||
DescriptorType type;
|
||||
ShaderStageFlags stageFlags;
|
||||
descriptor_binding_t binding;
|
||||
@@ -264,7 +499,7 @@ struct DescriptorSetLayoutBinding {
|
||||
// no uninitialized padding bytes.
|
||||
// uint8_t externalSamplerDataIndex = EXTERNAL_SAMPLER_DATA_INDEX_UNUSED;
|
||||
|
||||
friend inline bool operator==(DescriptorSetLayoutBinding const& lhs,
|
||||
friend bool operator==(DescriptorSetLayoutBinding const& lhs,
|
||||
DescriptorSetLayoutBinding const& rhs) noexcept {
|
||||
return lhs.type == rhs.type &&
|
||||
lhs.flags == rhs.flags &&
|
||||
@@ -297,7 +532,7 @@ enum class TargetBufferFlags : uint32_t {
|
||||
ALL = COLOR_ALL | DEPTH | STENCIL //!< Color, depth and stencil buffer selected.
|
||||
};
|
||||
|
||||
inline constexpr TargetBufferFlags getTargetBufferFlagsAt(size_t index) noexcept {
|
||||
constexpr TargetBufferFlags getTargetBufferFlagsAt(size_t index) noexcept {
|
||||
if (index == 0u) return TargetBufferFlags::COLOR0;
|
||||
if (index == 1u) return TargetBufferFlags::COLOR1;
|
||||
if (index == 2u) return TargetBufferFlags::COLOR2;
|
||||
@@ -454,6 +689,24 @@ enum class SamplerType : uint8_t {
|
||||
SAMPLER_CUBEMAP_ARRAY, //!< Cube map array texture (feature level 2)
|
||||
};
|
||||
|
||||
constexpr std::string_view to_string(SamplerType const type) noexcept {
|
||||
switch (type) {
|
||||
case SamplerType::SAMPLER_2D:
|
||||
return "SAMPLER_2D";
|
||||
case SamplerType::SAMPLER_2D_ARRAY:
|
||||
return "SAMPLER_2D_ARRAY";
|
||||
case SamplerType::SAMPLER_CUBEMAP:
|
||||
return "SAMPLER_CUBEMAP";
|
||||
case SamplerType::SAMPLER_EXTERNAL:
|
||||
return "SAMPLER_EXTERNAL";
|
||||
case SamplerType::SAMPLER_3D:
|
||||
return "SAMPLER_3D";
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
return "SAMPLER_CUBEMAP_ARRAY";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
//! Subpass type
|
||||
enum class SubpassType : uint8_t {
|
||||
SUBPASS_INPUT
|
||||
@@ -467,6 +720,20 @@ enum class SamplerFormat : uint8_t {
|
||||
SHADOW = 3 //!< shadow sampler (PCF)
|
||||
};
|
||||
|
||||
constexpr std::string_view to_string(SamplerFormat const format) noexcept {
|
||||
switch (format) {
|
||||
case SamplerFormat::INT:
|
||||
return "INT";
|
||||
case SamplerFormat::UINT:
|
||||
return "UINT";
|
||||
case SamplerFormat::FLOAT:
|
||||
return "FLOAT";
|
||||
case SamplerFormat::SHADOW:
|
||||
return "SHADOW";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* Supported element types
|
||||
*/
|
||||
@@ -506,6 +773,15 @@ enum class BufferObjectBinding : uint8_t {
|
||||
SHADER_STORAGE
|
||||
};
|
||||
|
||||
constexpr std::string_view to_string(BufferObjectBinding type) noexcept {
|
||||
switch (type) {
|
||||
case BufferObjectBinding::VERTEX: return "VERTEX";
|
||||
case BufferObjectBinding::UNIFORM: return "UNIFORM";
|
||||
case BufferObjectBinding::SHADER_STORAGE: return "SHADER_STORAGE";
|
||||
}
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
//! Face culling Mode
|
||||
enum class CullingMode : uint8_t {
|
||||
NONE, //!< No culling, front and back faces are visible
|
||||
@@ -767,6 +1043,8 @@ enum class TextureFormat : uint16_t {
|
||||
SRGB_ALPHA_BPTC_UNORM, // BC7 sRGB
|
||||
};
|
||||
|
||||
TextureType getTextureType(TextureFormat format) noexcept;
|
||||
|
||||
//! Bitmask describing the intended Texture Usage
|
||||
enum class TextureUsage : uint16_t {
|
||||
NONE = 0x0000,
|
||||
@@ -794,7 +1072,7 @@ enum class TextureSwizzle : uint8_t {
|
||||
};
|
||||
|
||||
//! returns whether this format a depth format
|
||||
static constexpr bool isDepthFormat(TextureFormat format) noexcept {
|
||||
constexpr bool isDepthFormat(TextureFormat format) noexcept {
|
||||
switch (format) {
|
||||
case TextureFormat::DEPTH32F:
|
||||
case TextureFormat::DEPTH24:
|
||||
@@ -807,7 +1085,7 @@ static constexpr bool isDepthFormat(TextureFormat format) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr bool isStencilFormat(TextureFormat format) noexcept {
|
||||
constexpr bool isStencilFormat(TextureFormat format) noexcept {
|
||||
switch (format) {
|
||||
case TextureFormat::STENCIL8:
|
||||
case TextureFormat::DEPTH24_STENCIL8:
|
||||
@@ -818,7 +1096,7 @@ static constexpr bool isStencilFormat(TextureFormat format) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
inline constexpr bool isColorFormat(TextureFormat format) noexcept {
|
||||
constexpr bool isColorFormat(TextureFormat format) noexcept {
|
||||
switch (format) {
|
||||
// Standard color formats
|
||||
case TextureFormat::R8:
|
||||
@@ -845,7 +1123,7 @@ inline constexpr bool isColorFormat(TextureFormat format) noexcept {
|
||||
return false;
|
||||
}
|
||||
|
||||
static constexpr bool isUnsignedIntFormat(TextureFormat format) {
|
||||
constexpr bool isUnsignedIntFormat(TextureFormat format) {
|
||||
switch (format) {
|
||||
case TextureFormat::R8UI:
|
||||
case TextureFormat::R16UI:
|
||||
@@ -866,7 +1144,7 @@ static constexpr bool isUnsignedIntFormat(TextureFormat format) {
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr bool isSignedIntFormat(TextureFormat format) {
|
||||
constexpr bool isSignedIntFormat(TextureFormat format) {
|
||||
switch (format) {
|
||||
case TextureFormat::R8I:
|
||||
case TextureFormat::R16I:
|
||||
@@ -888,35 +1166,35 @@ static constexpr bool isSignedIntFormat(TextureFormat format) {
|
||||
}
|
||||
|
||||
//! returns whether this format is a compressed format
|
||||
static constexpr bool isCompressedFormat(TextureFormat format) noexcept {
|
||||
constexpr bool isCompressedFormat(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::EAC_R11;
|
||||
}
|
||||
|
||||
//! returns whether this format is an ETC2 compressed format
|
||||
static constexpr bool isETC2Compression(TextureFormat format) noexcept {
|
||||
constexpr bool isETC2Compression(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::EAC_R11 && format <= TextureFormat::ETC2_EAC_SRGBA8;
|
||||
}
|
||||
|
||||
//! returns whether this format is an S3TC compressed format
|
||||
static constexpr bool isS3TCCompression(TextureFormat format) noexcept {
|
||||
constexpr bool isS3TCCompression(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::DXT1_RGB && format <= TextureFormat::DXT5_SRGBA;
|
||||
}
|
||||
|
||||
static constexpr bool isS3TCSRGBCompression(TextureFormat format) noexcept {
|
||||
constexpr bool isS3TCSRGBCompression(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::DXT1_SRGB && format <= TextureFormat::DXT5_SRGBA;
|
||||
}
|
||||
|
||||
//! returns whether this format is an RGTC compressed format
|
||||
static constexpr bool isRGTCCompression(TextureFormat format) noexcept {
|
||||
constexpr bool isRGTCCompression(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::RED_RGTC1 && format <= TextureFormat::SIGNED_RED_GREEN_RGTC2;
|
||||
}
|
||||
|
||||
//! returns whether this format is an BPTC compressed format
|
||||
static constexpr bool isBPTCCompression(TextureFormat format) noexcept {
|
||||
constexpr bool isBPTCCompression(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::RGB_BPTC_SIGNED_FLOAT && format <= TextureFormat::SRGB_ALPHA_BPTC_UNORM;
|
||||
}
|
||||
|
||||
static constexpr bool isASTCCompression(TextureFormat format) noexcept {
|
||||
constexpr bool isASTCCompression(TextureFormat format) noexcept {
|
||||
return format >= TextureFormat::RGBA_ASTC_4x4 && format <= TextureFormat::SRGB8_ALPHA8_ASTC_12x12;
|
||||
}
|
||||
|
||||
@@ -1037,19 +1315,23 @@ struct SamplerParams { // NOLINT
|
||||
assert_invariant(lhs.padding2 == 0);
|
||||
auto* pLhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&lhs));
|
||||
auto* pRhs = reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&rhs));
|
||||
return *pLhs == *pRhs;
|
||||
return *pLhs < *pRhs;
|
||||
}
|
||||
};
|
||||
|
||||
bool isFiltered() const noexcept {
|
||||
return filterMag != SamplerMagFilter::NEAREST || filterMin != SamplerMinFilter::NEAREST;
|
||||
}
|
||||
|
||||
private:
|
||||
friend inline bool operator == (SamplerParams lhs, SamplerParams rhs) noexcept {
|
||||
return SamplerParams::EqualTo{}(lhs, rhs);
|
||||
friend bool operator == (SamplerParams lhs, SamplerParams rhs) noexcept {
|
||||
return EqualTo{}(lhs, rhs);
|
||||
}
|
||||
friend inline bool operator != (SamplerParams lhs, SamplerParams rhs) noexcept {
|
||||
return !SamplerParams::EqualTo{}(lhs, rhs);
|
||||
friend bool operator != (SamplerParams lhs, SamplerParams rhs) noexcept {
|
||||
return !EqualTo{}(lhs, rhs);
|
||||
}
|
||||
friend inline bool operator < (SamplerParams lhs, SamplerParams rhs) noexcept {
|
||||
return SamplerParams::LessThan{}(lhs, rhs);
|
||||
friend bool operator < (SamplerParams lhs, SamplerParams rhs) noexcept {
|
||||
return LessThan{}(lhs, rhs);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1099,15 +1381,15 @@ struct SamplerYcbcrConversion {// NOLINT
|
||||
};
|
||||
|
||||
private:
|
||||
friend inline bool operator == (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
friend bool operator == (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
noexcept {
|
||||
return SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
|
||||
}
|
||||
friend inline bool operator != (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
friend bool operator != (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
noexcept {
|
||||
return !SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
|
||||
}
|
||||
friend inline bool operator < (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
friend bool operator < (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
|
||||
noexcept {
|
||||
return SamplerYcbcrConversion::LessThan{}(lhs, rhs);
|
||||
}
|
||||
|
||||
@@ -204,23 +204,15 @@ public:
|
||||
}, new T(std::forward<T>(functor))
|
||||
};
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Computes the size in bytes needed to fit an image of given dimensions and format
|
||||
* Computes the size in bytes for a pixel of given dimensions and format
|
||||
*
|
||||
* @param format Format of the image pixels
|
||||
* @param type Type of the image pixels
|
||||
* @param stride Stride of a row in pixels
|
||||
* @param height Height of the image in rows
|
||||
* @param alignment Alignment in bytes of pixel rows
|
||||
* @return The buffer size needed to fit this image in bytes
|
||||
* @return The size of the specified pixel in bytes
|
||||
*/
|
||||
static constexpr size_t computeDataSize(PixelDataFormat format, PixelDataType type,
|
||||
size_t stride, size_t height, size_t alignment) noexcept {
|
||||
assert_invariant(alignment);
|
||||
|
||||
static constexpr size_t computePixelSize(PixelDataFormat format, PixelDataType type) noexcept {
|
||||
if (type == PixelDataType::COMPRESSED) {
|
||||
return 0;
|
||||
}
|
||||
@@ -242,7 +234,7 @@ public:
|
||||
case PixelDataFormat::RGB_INTEGER:
|
||||
n = 3;
|
||||
break;
|
||||
case PixelDataFormat::UNUSED: // shouldn't happen (used to be rgbm)
|
||||
case PixelDataFormat::UNUSED:// shouldn't happen (used to be rgbm)
|
||||
case PixelDataFormat::RGBA:
|
||||
case PixelDataFormat::RGBA_INTEGER:
|
||||
n = 4;
|
||||
@@ -251,7 +243,7 @@ public:
|
||||
|
||||
size_t bpp = n;
|
||||
switch (type) {
|
||||
case PixelDataType::COMPRESSED: // Impossible -- to squash the IDE warnings
|
||||
case PixelDataType::COMPRESSED:// Impossible -- to squash the IDE warnings
|
||||
case PixelDataType::UBYTE:
|
||||
case PixelDataType::BYTE:
|
||||
// nothing to do
|
||||
@@ -282,16 +274,35 @@ public:
|
||||
bpp = 2;
|
||||
break;
|
||||
}
|
||||
return bpp;
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Computes the size in bytes needed to fit an image of given dimensions and format
|
||||
*
|
||||
* @param format Format of the image pixels
|
||||
* @param type Type of the image pixels
|
||||
* @param stride Stride of a row in pixels
|
||||
* @param height Height of the image in rows
|
||||
* @param alignment Alignment in bytes of pixel rows
|
||||
* @return The buffer size needed to fit this image in bytes
|
||||
*/
|
||||
static constexpr size_t computeDataSize(PixelDataFormat format, PixelDataType type,
|
||||
size_t stride, size_t height, size_t alignment) noexcept {
|
||||
assert_invariant(alignment);
|
||||
|
||||
size_t bpp = computePixelSize(format, type);
|
||||
size_t const bpr = bpp * stride;
|
||||
size_t const bprAligned = (bpr + (alignment - 1)) & (~alignment + 1);
|
||||
return bprAligned * height;
|
||||
}
|
||||
|
||||
//! left coordinate in pixels
|
||||
uint32_t left = 0;
|
||||
uint32_t left = 0;
|
||||
//! top coordinate in pixels
|
||||
uint32_t top = 0;
|
||||
uint32_t top = 0;
|
||||
union {
|
||||
struct {
|
||||
//! stride in pixels
|
||||
|
||||
@@ -199,7 +199,7 @@ public:
|
||||
*/
|
||||
virtual bool makeCurrent(ContextType type,
|
||||
SwapChain* UTILS_NONNULL drawSwapChain,
|
||||
SwapChain* UTILS_NONNULL readSwapChain) noexcept = 0;
|
||||
SwapChain* UTILS_NONNULL readSwapChain) = 0;
|
||||
|
||||
/**
|
||||
* Called by the driver to make the OpenGL context active on the calling thread and bind
|
||||
@@ -219,7 +219,7 @@ public:
|
||||
SwapChain* UTILS_NONNULL drawSwapChain,
|
||||
SwapChain* UTILS_NONNULL readSwapChain,
|
||||
utils::Invocable<void()> preContextChange,
|
||||
utils::Invocable<void(size_t index)> postContextChange) noexcept;
|
||||
utils::Invocable<void(size_t index)> postContextChange);
|
||||
|
||||
/**
|
||||
* Called by the backend just before calling commit()
|
||||
|
||||
@@ -58,7 +58,7 @@ protected:
|
||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override;
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
ExternalTexture* createExternalImageTexture() noexcept override;
|
||||
void destroyExternalImageTexture(ExternalTexture* texture) noexcept override;
|
||||
|
||||
@@ -55,7 +55,7 @@ public:
|
||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override;
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
|
||||
ExternalTexture* createExternalImageTexture() noexcept override;
|
||||
|
||||
@@ -109,11 +109,11 @@ protected:
|
||||
|
||||
bool makeCurrent(ContextType type,
|
||||
SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept override;
|
||||
SwapChain* readSwapChain) override;
|
||||
|
||||
void makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain,
|
||||
utils::Invocable<void()> preContextChange,
|
||||
utils::Invocable<void(size_t index)> postContextChange) noexcept override;
|
||||
utils::Invocable<void(size_t index)> postContextChange) override;
|
||||
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
|
||||
@@ -148,12 +148,12 @@ protected:
|
||||
EGLContext getContextForType(ContextType type) const noexcept;
|
||||
|
||||
// makes the draw and read surface current without changing the current context
|
||||
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept {
|
||||
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) {
|
||||
return egl.makeCurrent(drawSurface, readSurface);
|
||||
}
|
||||
|
||||
// makes context current and set draw and read surfaces to EGL_NO_SURFACE
|
||||
EGLBoolean makeCurrent(EGLContext context) noexcept {
|
||||
EGLBoolean makeCurrent(EGLContext context) {
|
||||
return egl.makeCurrent(context, mEGLDummySurface, mEGLDummySurface);
|
||||
}
|
||||
|
||||
@@ -211,9 +211,9 @@ private:
|
||||
public:
|
||||
explicit EGL(EGLDisplay& dpy) : mEGLDisplay(dpy) {}
|
||||
EGLBoolean makeCurrent(EGLContext context,
|
||||
EGLSurface drawSurface, EGLSurface readSurface) noexcept;
|
||||
EGLSurface drawSurface, EGLSurface readSurface);
|
||||
|
||||
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) noexcept {
|
||||
EGLBoolean makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) {
|
||||
return makeCurrent(mCurrentContext, drawSurface, readSurface);
|
||||
}
|
||||
} egl{ mEGLDisplay };
|
||||
|
||||
@@ -127,7 +127,7 @@ protected:
|
||||
protected:
|
||||
bool makeCurrent(ContextType type,
|
||||
SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept override;
|
||||
SwapChain* readSwapChain) override;
|
||||
|
||||
private:
|
||||
struct InitializeJvmForPerformanceManagerIfNeeded {
|
||||
|
||||
@@ -51,7 +51,7 @@ protected:
|
||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override;
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -39,18 +39,40 @@ public:
|
||||
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
|
||||
int getOSVersion() const noexcept override { return 0; }
|
||||
|
||||
/**
|
||||
* Optionally initializes the Metal platform by acquiring resources necessary for rendering.
|
||||
*
|
||||
* This method attempts to acquire a Metal device and command queue, returning true if both are
|
||||
* successfully obtained, or false otherwise. Typically, these objects are acquired when
|
||||
* the Metal backend is initialized. This method allows clients to check for their availability
|
||||
* earlier.
|
||||
*
|
||||
* Calling initialize() is optional and safe to do so multiple times. After initialize() returns
|
||||
* true, subsequent calls will continue to return true but have no effect.
|
||||
*
|
||||
* initialize() must be called from the main thread.
|
||||
*
|
||||
* @returns true if the device and command queue have been successfully obtained; false
|
||||
* otherwise.
|
||||
*/
|
||||
bool initialize() noexcept;
|
||||
|
||||
/**
|
||||
* Obtain the preferred Metal device object for the backend to use.
|
||||
*
|
||||
* On desktop platforms, there may be multiple GPUs suitable for rendering, and this method is
|
||||
* free to decide which one to use. On mobile systems with a single GPU, implementations should
|
||||
* simply return the result of MTLCreateSystemDefaultDevice();
|
||||
*
|
||||
* createDevice is called by the Metal backend from the backend thread.
|
||||
*/
|
||||
virtual void createDevice(MetalDevice& outDevice) noexcept;
|
||||
|
||||
/**
|
||||
* Create a command submission queue on the Metal device object.
|
||||
*
|
||||
* createCommandQueue is called by the Metal backend from the backend thread.
|
||||
*
|
||||
* @param device The device which was returned from createDevice()
|
||||
*/
|
||||
virtual void createCommandQueue(
|
||||
@@ -60,6 +82,8 @@ public:
|
||||
* Obtain a MTLCommandBuffer enqueued on this Platform's MTLCommandQueue. The command buffer is
|
||||
* guaranteed to execute before all subsequent command buffers created either by Filament, or
|
||||
* further calls to this method.
|
||||
*
|
||||
* createAndEnqueueCommandBuffer must be called from the main thread.
|
||||
*/
|
||||
void createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept;
|
||||
|
||||
@@ -68,6 +92,8 @@ public:
|
||||
*
|
||||
* Each frame rendered requires a CAMetalDrawable texture, which is presented on-screen at the
|
||||
* completion of each frame. These are limited and provided round-robin style by the system.
|
||||
*
|
||||
* setDrawableFailureBehavior must be called from the main thread.
|
||||
*/
|
||||
enum class DrawableFailureBehavior : uint8_t {
|
||||
/**
|
||||
|
||||
@@ -56,7 +56,7 @@ protected:
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept override;
|
||||
SwapChain* readSwapChain) override;
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -53,7 +53,7 @@ protected:
|
||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override;
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -46,7 +46,7 @@ protected:
|
||||
SwapChain* createSwapChain(void* nativewindow, uint64_t flags) noexcept override;
|
||||
SwapChain* createSwapChain(uint32_t width, uint32_t height, uint64_t flags) noexcept override;
|
||||
void destroySwapChain(SwapChain* swapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept override;
|
||||
bool makeCurrent(ContextType type, SwapChain* drawSwapChain, SwapChain* readSwapChain) override;
|
||||
void commit(SwapChain* swapChain) noexcept override;
|
||||
};
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -56,6 +57,10 @@ protected:
|
||||
const Platform::DriverConfig& driverConfig) noexcept override;
|
||||
|
||||
private:
|
||||
// returns adapter request option variations applicable for the particular
|
||||
// platform
|
||||
[[nodiscard]] static std::vector<wgpu::RequestAdapterOptions> getAdapterOptions();
|
||||
|
||||
// we may consider having the driver own this in the future
|
||||
wgpu::Instance mInstance;
|
||||
};
|
||||
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
Range getBuffer() noexcept;
|
||||
|
||||
private:
|
||||
void* alloc(size_t size) noexcept;
|
||||
void* alloc(size_t size);
|
||||
void dealloc() noexcept;
|
||||
|
||||
// pointer to the beginning of the circular buffer (constant)
|
||||
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
|
||||
// all commands buffers (Slices) written to this point are returned by waitForCommand(). This
|
||||
// call blocks until the CircularBuffer has at least mRequiredSize bytes available.
|
||||
void flush() noexcept;
|
||||
void flush();
|
||||
|
||||
// returns from waitForCommands() immediately.
|
||||
void requestExit();
|
||||
|
||||
@@ -27,13 +27,13 @@ namespace filament {
|
||||
class VirtualMachineEnv {
|
||||
public:
|
||||
// must be called before VirtualMachineEnv::get() from a thread that is attached to the JavaVM
|
||||
static jint JNI_OnLoad(JavaVM* vm) noexcept;
|
||||
static jint JNI_OnLoad(JavaVM* vm);
|
||||
|
||||
// must be called on backend thread
|
||||
static VirtualMachineEnv& get() noexcept;
|
||||
|
||||
// can be called from any thread that already has a JniEnv
|
||||
static JNIEnv* getThreadEnvironment() noexcept;
|
||||
static JNIEnv* getThreadEnvironment();
|
||||
|
||||
// must be called from the backend thread
|
||||
JNIEnv* getEnvironment() noexcept {
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
private:
|
||||
explicit VirtualMachineEnv(JavaVM* vm) noexcept;
|
||||
~VirtualMachineEnv() noexcept;
|
||||
JNIEnv* getEnvironmentSlow() noexcept;
|
||||
JNIEnv* getEnvironmentSlow();
|
||||
|
||||
static utils::Mutex sLock;
|
||||
static JavaVM* sVirtualMachine;
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
|
||||
#include "DataReshaper.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <utils/CString.h>
|
||||
|
||||
#include <string_view>
|
||||
@@ -223,6 +225,143 @@ size_t getFormatSize(TextureFormat format) noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
TextureType getTextureType(TextureFormat const format) noexcept {
|
||||
switch (format) {
|
||||
case TextureFormat::UNUSED:
|
||||
// should not happen
|
||||
return TextureType::FLOAT;
|
||||
|
||||
case TextureFormat::R8:
|
||||
case TextureFormat::R16F:
|
||||
case TextureFormat::RG8:
|
||||
case TextureFormat::RGB565:
|
||||
case TextureFormat::RGB5_A1:
|
||||
case TextureFormat::RGBA4:
|
||||
case TextureFormat::R32F:
|
||||
case TextureFormat::RGB8:
|
||||
case TextureFormat::SRGB8:
|
||||
case TextureFormat::RG16F:
|
||||
case TextureFormat::R11F_G11F_B10F:
|
||||
case TextureFormat::RGB9_E5:
|
||||
case TextureFormat::RGBA8:
|
||||
case TextureFormat::SRGB8_A8:
|
||||
case TextureFormat::RGB10_A2:
|
||||
case TextureFormat::RGB16F:
|
||||
case TextureFormat::RG32F:
|
||||
case TextureFormat::RGBA16F:
|
||||
case TextureFormat::RGB32F:
|
||||
case TextureFormat::RGBA32F:
|
||||
return TextureType::FLOAT;
|
||||
|
||||
case TextureFormat::R8_SNORM:
|
||||
case TextureFormat::RG8_SNORM:
|
||||
case TextureFormat::RGB8_SNORM:
|
||||
case TextureFormat::RGBA8_SNORM:
|
||||
// SNORM are treated as float
|
||||
return TextureType::FLOAT;
|
||||
|
||||
case TextureFormat::R8UI:
|
||||
case TextureFormat::R16UI:
|
||||
case TextureFormat::RG8UI:
|
||||
case TextureFormat::RGB8UI:
|
||||
case TextureFormat::R32UI:
|
||||
case TextureFormat::RG16UI:
|
||||
case TextureFormat::RGBA8UI:
|
||||
case TextureFormat::RGB16UI:
|
||||
case TextureFormat::RG32UI:
|
||||
case TextureFormat::RGBA16UI:
|
||||
case TextureFormat::RGB32UI:
|
||||
case TextureFormat::RGBA32UI:
|
||||
return TextureType::UINT;
|
||||
|
||||
case TextureFormat::R8I:
|
||||
case TextureFormat::R16I:
|
||||
case TextureFormat::RG8I:
|
||||
case TextureFormat::RGB8I:
|
||||
case TextureFormat::R32I:
|
||||
case TextureFormat::RG16I:
|
||||
case TextureFormat::RGBA8I:
|
||||
case TextureFormat::RGB16I:
|
||||
case TextureFormat::RG32I:
|
||||
case TextureFormat::RGBA16I:
|
||||
case TextureFormat::RGB32I:
|
||||
case TextureFormat::RGBA32I:
|
||||
return TextureType::INT;
|
||||
|
||||
case TextureFormat::DEPTH16:
|
||||
case TextureFormat::DEPTH24:
|
||||
case TextureFormat::DEPTH32F:
|
||||
return TextureType::DEPTH;
|
||||
|
||||
case TextureFormat::STENCIL8:
|
||||
return TextureType::STENCIL;
|
||||
|
||||
case TextureFormat::DEPTH24_STENCIL8:
|
||||
case TextureFormat::DEPTH32F_STENCIL8:
|
||||
return TextureType::DEPTH_STENCIL;
|
||||
|
||||
// Compressed formats ---------------------------------------------------------------------
|
||||
|
||||
case TextureFormat::EAC_RG11:
|
||||
case TextureFormat::EAC_RG11_SIGNED:
|
||||
case TextureFormat::ETC2_EAC_RGBA8:
|
||||
case TextureFormat::ETC2_EAC_SRGBA8:
|
||||
case TextureFormat::EAC_R11:
|
||||
case TextureFormat::EAC_R11_SIGNED:
|
||||
case TextureFormat::ETC2_RGB8:
|
||||
case TextureFormat::ETC2_SRGB8:
|
||||
case TextureFormat::ETC2_RGB8_A1:
|
||||
case TextureFormat::ETC2_SRGB8_A1:
|
||||
case TextureFormat::DXT1_RGB:
|
||||
case TextureFormat::DXT1_RGBA:
|
||||
case TextureFormat::DXT1_SRGB:
|
||||
case TextureFormat::DXT1_SRGBA:
|
||||
case TextureFormat::DXT3_RGBA:
|
||||
case TextureFormat::DXT3_SRGBA:
|
||||
case TextureFormat::DXT5_RGBA:
|
||||
case TextureFormat::DXT5_SRGBA:
|
||||
case TextureFormat::RED_RGTC1:
|
||||
case TextureFormat::SIGNED_RED_RGTC1:
|
||||
case TextureFormat::RED_GREEN_RGTC2:
|
||||
case TextureFormat::SIGNED_RED_GREEN_RGTC2:
|
||||
case TextureFormat::RGB_BPTC_SIGNED_FLOAT:
|
||||
case TextureFormat::RGB_BPTC_UNSIGNED_FLOAT:
|
||||
case TextureFormat::RGBA_BPTC_UNORM:
|
||||
case TextureFormat::SRGB_ALPHA_BPTC_UNORM:
|
||||
case TextureFormat::RGBA_ASTC_4x4:
|
||||
case TextureFormat::RGBA_ASTC_5x4:
|
||||
case TextureFormat::RGBA_ASTC_5x5:
|
||||
case TextureFormat::RGBA_ASTC_6x5:
|
||||
case TextureFormat::RGBA_ASTC_6x6:
|
||||
case TextureFormat::RGBA_ASTC_8x5:
|
||||
case TextureFormat::RGBA_ASTC_8x6:
|
||||
case TextureFormat::RGBA_ASTC_8x8:
|
||||
case TextureFormat::RGBA_ASTC_10x5:
|
||||
case TextureFormat::RGBA_ASTC_10x6:
|
||||
case TextureFormat::RGBA_ASTC_10x8:
|
||||
case TextureFormat::RGBA_ASTC_10x10:
|
||||
case TextureFormat::RGBA_ASTC_12x10:
|
||||
case TextureFormat::RGBA_ASTC_12x12:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_4x4:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_5x4:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_5x5:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_6x5:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_6x6:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_8x5:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_8x6:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_8x8:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_10x5:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_10x6:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_10x8:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_10x10:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_12x10:
|
||||
case TextureFormat::SRGB8_ALPHA8_ASTC_12x12:
|
||||
return TextureType::FLOAT;
|
||||
}
|
||||
|
||||
return TextureType::FLOAT;
|
||||
}
|
||||
|
||||
size_t getFormatComponentCount(TextureFormat format) noexcept {
|
||||
switch (format) {
|
||||
case TextureFormat::R8:
|
||||
|
||||
@@ -65,7 +65,7 @@ CircularBuffer::~CircularBuffer() noexcept {
|
||||
// to each others and a special case in circularize()
|
||||
|
||||
UTILS_NOINLINE
|
||||
void* CircularBuffer::alloc(size_t size) noexcept {
|
||||
void* CircularBuffer::alloc(size_t size) {
|
||||
#if HAS_MMAP
|
||||
void* data = nullptr;
|
||||
void* vaddr = MAP_FAILED;
|
||||
|
||||
@@ -18,12 +18,13 @@
|
||||
#include "private/backend/CircularBuffer.h"
|
||||
#include "private/backend/CommandStream.h"
|
||||
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Systrace.h>
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -52,18 +53,18 @@ CommandBufferQueue::~CommandBufferQueue() {
|
||||
}
|
||||
|
||||
void CommandBufferQueue::requestExit() {
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
std::lock_guard const lock(mLock);
|
||||
mExitRequested = EXIT_REQUESTED;
|
||||
mCondition.notify_one();
|
||||
}
|
||||
|
||||
bool CommandBufferQueue::isPaused() const noexcept {
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
std::lock_guard const lock(mLock);
|
||||
return mPaused;
|
||||
}
|
||||
|
||||
void CommandBufferQueue::setPaused(bool paused) {
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
std::lock_guard const lock(mLock);
|
||||
if (paused) {
|
||||
mPaused = true;
|
||||
} else {
|
||||
@@ -73,13 +74,13 @@ void CommandBufferQueue::setPaused(bool paused) {
|
||||
}
|
||||
|
||||
bool CommandBufferQueue::isExitRequested() const {
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
return (bool)mExitRequested;
|
||||
std::lock_guard const lock(mLock);
|
||||
return bool(mExitRequested);
|
||||
}
|
||||
|
||||
|
||||
void CommandBufferQueue::flush() noexcept {
|
||||
SYSTRACE_CALL();
|
||||
void CommandBufferQueue::flush() {
|
||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
|
||||
CircularBuffer& circularBuffer = mCircularBuffer;
|
||||
if (circularBuffer.empty()) {
|
||||
@@ -102,7 +103,7 @@ void CommandBufferQueue::flush() noexcept {
|
||||
static_cast<char const*>(begin), static_cast<char const*>(end));
|
||||
|
||||
|
||||
std::unique_lock<utils::Mutex> lock(mLock);
|
||||
std::unique_lock lock(mLock);
|
||||
|
||||
// circular buffer is too small, we corrupted the stream
|
||||
FILAMENT_CHECK_POSTCONDITION(used <= mFreeSpace) <<
|
||||
@@ -129,7 +130,7 @@ void CommandBufferQueue::flush() noexcept {
|
||||
mHighWatermark = std::max(mHighWatermark, totalUsed);
|
||||
#endif
|
||||
|
||||
SYSTRACE_NAME("waiting: CircularBuffer::flush()");
|
||||
FILAMENT_TRACING_NAME(FILAMENT_TRACING_CATEGORY_FILAMENT, "waiting: CircularBuffer::flush()");
|
||||
|
||||
FILAMENT_CHECK_POSTCONDITION(!mPaused) <<
|
||||
"CommandStream is full, but since the rendering thread is paused, "
|
||||
@@ -146,7 +147,7 @@ std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() con
|
||||
if (!UTILS_HAS_THREADING) {
|
||||
return std::move(mCommandBuffersToExecute);
|
||||
}
|
||||
std::unique_lock<utils::Mutex> lock(mLock);
|
||||
std::unique_lock lock(mLock);
|
||||
while ((mCommandBuffersToExecute.empty() || mPaused) && !mExitRequested) {
|
||||
mCondition.wait(lock);
|
||||
}
|
||||
@@ -156,7 +157,7 @@ std::vector<CommandBufferQueue::Range> CommandBufferQueue::waitForCommands() con
|
||||
void CommandBufferQueue::releaseBuffer(CommandBufferQueue::Range const& buffer) {
|
||||
size_t const used = std::distance(
|
||||
static_cast<char const*>(buffer.begin), static_cast<char const*>(buffer.end));
|
||||
std::lock_guard<utils::Mutex> const lock(mLock);
|
||||
std::lock_guard const lock(mLock);
|
||||
mFreeSpace += used;
|
||||
mCondition.notify_one();
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
#include "private/backend/CommandStream.h"
|
||||
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#if DEBUG_COMMAND_STREAM
|
||||
#include <utils/CallStack.h>
|
||||
#endif
|
||||
@@ -24,7 +26,6 @@
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Profiler.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
@@ -79,12 +80,13 @@ CommandStream::CommandStream(Driver& driver, CircularBuffer& buffer) noexcept
|
||||
}
|
||||
|
||||
void CommandStream::execute(void* buffer) {
|
||||
// NOTE: we can't use SYSTRACE_CALL() or similar here because, execute() below, also
|
||||
// NOTE: we can't use FILAMENT_TRACING_CALL() or similar here because, execute() below, also
|
||||
// uses systrace BEGIN/END and the END is not guaranteed to be happening in this scope.
|
||||
|
||||
Profiler profiler;
|
||||
|
||||
if (SYSTRACE_TAG) {
|
||||
|
||||
if constexpr (FILAMENT_TRACING_ENABLED) {
|
||||
if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
|
||||
// we want to remove all this when tracing is completely disabled
|
||||
profiler.resetEvents(Profiler::EV_CPU_CYCLES | Profiler::EV_BPU_MISSES);
|
||||
@@ -100,17 +102,17 @@ void CommandStream::execute(void* buffer) {
|
||||
}
|
||||
});
|
||||
|
||||
if (SYSTRACE_TAG) {
|
||||
if constexpr (FILAMENT_TRACING_ENABLED) {
|
||||
if (UTILS_UNLIKELY(mUsePerformanceCounter)) {
|
||||
// we want to remove all this when tracing is completely disabled
|
||||
profiler.stop();
|
||||
UTILS_UNUSED Profiler::Counters const counters = profiler.readCounters();
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_VALUE32("GLThread (I)", counters.getInstructions());
|
||||
SYSTRACE_VALUE32("GLThread (C)", counters.getCpuCycles());
|
||||
SYSTRACE_VALUE32("GLThread (CPI x10)", counters.getCPI() * 10);
|
||||
SYSTRACE_VALUE32("GLThread (BPU miss)", counters.getBranchMisses());
|
||||
SYSTRACE_VALUE32("GLThread (I / BPU miss)",
|
||||
FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
FILAMENT_TRACING_VALUE(FILAMENT_TRACING_CATEGORY_FILAMENT, "GLThread (I)", counters.getInstructions());
|
||||
FILAMENT_TRACING_VALUE(FILAMENT_TRACING_CATEGORY_FILAMENT, "GLThread (C)", counters.getCpuCycles());
|
||||
FILAMENT_TRACING_VALUE(FILAMENT_TRACING_CATEGORY_FILAMENT, "GLThread (CPI x10)", counters.getCPI() * 10);
|
||||
FILAMENT_TRACING_VALUE(FILAMENT_TRACING_CATEGORY_FILAMENT, "GLThread (BPU miss)", counters.getBranchMisses());
|
||||
FILAMENT_TRACING_VALUE(FILAMENT_TRACING_CATEGORY_FILAMENT, "GLThread (I / BPU miss)",
|
||||
counters.getInstructions() / counters.getBranchMisses());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
#include "private/backend/Driver.h"
|
||||
#include "private/backend/CommandStream.h"
|
||||
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
@@ -38,7 +39,7 @@
|
||||
#if DEBUG_LEVEL == DEBUG_LEVEL_NONE
|
||||
# define SYSTRACE()
|
||||
#elif DEBUG_LEVEL == DEBUG_LEVEL_SYSTRACE
|
||||
# define SYSTRACE() SYSTRACE_CALL();
|
||||
# define SYSTRACE() FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
#else
|
||||
# error "invalid debug level"
|
||||
#endif
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
#include "CompilerThreadPool.h"
|
||||
|
||||
#include <utils/Systrace.h>
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
@@ -41,7 +41,7 @@ void CompilerThreadPool::init(uint32_t threadCount,
|
||||
|
||||
for (size_t i = 0; i < threadCount; i++) {
|
||||
mCompilerThreads.emplace_back([this, setup, cleanup]() {
|
||||
SYSTRACE_CONTEXT();
|
||||
FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
|
||||
(*setup)();
|
||||
|
||||
@@ -54,7 +54,7 @@ void CompilerThreadPool::init(uint32_t threadCount,
|
||||
[](auto&& q) { return q.empty(); }));
|
||||
});
|
||||
|
||||
SYSTRACE_VALUE32("CompilerThreadPool Jobs",
|
||||
FILAMENT_TRACING_VALUE(FILAMENT_TRACING_CATEGORY_FILAMENT, "CompilerThreadPool Jobs",
|
||||
mQueues[0].size() + mQueues[1].size());
|
||||
|
||||
if (UTILS_LIKELY(!mExitRequested)) {
|
||||
|
||||
@@ -23,11 +23,12 @@
|
||||
#include <backend/BufferDescriptor.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <math/half.h>
|
||||
#include <math/vec2.h>
|
||||
@@ -151,30 +152,31 @@ void DriverBase::debugCommandBegin(CommandStream* cmds, bool synchronous, const
|
||||
utils::slog.d << methodName << utils::io::endl;
|
||||
}
|
||||
if constexpr (bool(FILAMENT_DEBUG_COMMANDS & FILAMENT_DEBUG_COMMANDS_SYSTRACE)) {
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_BEGIN(methodName);
|
||||
FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
FILAMENT_TRACING_NAME_BEGIN(FILAMENT_TRACING_CATEGORY_FILAMENT, methodName);
|
||||
|
||||
if (!synchronous) {
|
||||
cmds->queueCommand([=]() {
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_BEGIN(methodName);
|
||||
FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
FILAMENT_TRACING_NAME_BEGIN(FILAMENT_TRACING_CATEGORY_FILAMENT, methodName);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DriverBase::debugCommandEnd(CommandStream* cmds, bool synchronous, const char* methodName) noexcept {
|
||||
void DriverBase::debugCommandEnd(CommandStream* cmds, bool synchronous,
|
||||
const char* methodName) noexcept {
|
||||
if constexpr (bool(FILAMENT_DEBUG_COMMANDS > FILAMENT_DEBUG_COMMANDS_NONE)) {
|
||||
if constexpr (bool(FILAMENT_DEBUG_COMMANDS & FILAMENT_DEBUG_COMMANDS_SYSTRACE)) {
|
||||
if (!synchronous) {
|
||||
cmds->queueCommand([]() {
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_END();
|
||||
FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
FILAMENT_TRACING_NAME_END(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
});
|
||||
}
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_END();
|
||||
FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
FILAMENT_TRACING_NAME_END(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
|
||||
#include <private/backend/PlatformFactory.h>
|
||||
|
||||
#include <utils/Systrace.h>
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
|
||||
// We need to keep this up top for the linux (X11) name collisions.
|
||||
@@ -85,7 +86,7 @@ namespace filament::backend {
|
||||
// responsible for destroying it. Initialization of the backend API is deferred until
|
||||
// createDriver(). The passed-in backend hint is replaced with the resolved backend.
|
||||
Platform* PlatformFactory::create(Backend* backend) noexcept {
|
||||
SYSTRACE_CALL();
|
||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
assert_invariant(backend);
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
#ifndef TNT_FILAMENT_BACKEND_SYSTRACEPROFILE_H
|
||||
#define TNT_FILAMENT_BACKEND_SYSTRACEPROFILE_H
|
||||
|
||||
#include <utils/Systrace.h>
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#define PROFILE_SCOPE(marker) SYSTRACE_NAME(marker)
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ JavaVM* VirtualMachineEnv::getVirtualMachine() {
|
||||
*/
|
||||
UTILS_PUBLIC
|
||||
UTILS_NOINLINE
|
||||
jint VirtualMachineEnv::JNI_OnLoad(JavaVM* vm) noexcept {
|
||||
jint VirtualMachineEnv::JNI_OnLoad(JavaVM* vm) {
|
||||
std::lock_guard const lock(sLock);
|
||||
if (sVirtualMachine) {
|
||||
// It doesn't make sense for JNI_OnLoad() to be called more than once
|
||||
@@ -77,7 +77,7 @@ VirtualMachineEnv& VirtualMachineEnv::get() noexcept {
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
JNIEnv* VirtualMachineEnv::getThreadEnvironment() noexcept {
|
||||
JNIEnv* VirtualMachineEnv::getThreadEnvironment() {
|
||||
JavaVM* const vm = getVirtualMachine();
|
||||
JNIEnv* env = nullptr;
|
||||
jint const result = vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6);
|
||||
@@ -101,7 +101,7 @@ VirtualMachineEnv::~VirtualMachineEnv() noexcept {
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
JNIEnv* VirtualMachineEnv::getEnvironmentSlow() noexcept {
|
||||
JNIEnv* VirtualMachineEnv::getEnvironmentSlow() {
|
||||
FILAMENT_CHECK_PRECONDITION(mVirtualMachine)
|
||||
<< "JNI_OnLoad() has not been called";
|
||||
|
||||
|
||||
@@ -218,11 +218,11 @@ void MetalBlitter::blitDepthPlane(id<MTLCommandBuffer> cmdBuffer, const BlitArgs
|
||||
}
|
||||
[encoder setFragmentTexture:srcTextureColor atIndex:0];
|
||||
|
||||
SamplerMinFilter filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST;
|
||||
SamplerMinFilter filterMin = SamplerMinFilter::LINEAR;
|
||||
if (args.filter == SamplerMagFilter::NEAREST) {
|
||||
filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST;
|
||||
filterMin = SamplerMinFilter::LINEAR;
|
||||
} else if (args.filter == SamplerMagFilter::LINEAR) {
|
||||
filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST;
|
||||
filterMin = SamplerMinFilter::LINEAR;
|
||||
}
|
||||
|
||||
SamplerState const s {
|
||||
|
||||
@@ -672,22 +672,8 @@ void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
|
||||
// nothing to do, timer query was constructed in createTimerQueryS
|
||||
}
|
||||
|
||||
const char* toString(DescriptorType type) {
|
||||
switch (type) {
|
||||
case DescriptorType::UNIFORM_BUFFER:
|
||||
return "UNIFORM_BUFFER";
|
||||
case DescriptorType::SHADER_STORAGE_BUFFER:
|
||||
return "SHADER_STORAGE_BUFFER";
|
||||
case DescriptorType::SAMPLER:
|
||||
return "SAMPLER";
|
||||
case DescriptorType::INPUT_ATTACHMENT:
|
||||
return "INPUT_ATTACHMENT";
|
||||
case DescriptorType::SAMPLER_EXTERNAL:
|
||||
return "SAMPLER_EXTERNAL";
|
||||
}
|
||||
}
|
||||
|
||||
const char* toString(ShaderStageFlags flags) {
|
||||
UTILS_UNUSED
|
||||
static const char* toString(ShaderStageFlags flags) {
|
||||
std::vector<const char*> stages;
|
||||
if (any(flags & ShaderStageFlags::VERTEX)) {
|
||||
stages.push_back("VERTEX");
|
||||
@@ -1547,7 +1533,7 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
|
||||
id<MTLTexture> readPixelsTexture = [mContext->device newTextureWithDescriptor:textureDescriptor];
|
||||
|
||||
MetalBlitter::BlitArgs args{};
|
||||
args.filter = SamplerMagFilter::NEAREST;
|
||||
args.filter = SamplerMagFilter::LINEAR;
|
||||
args.source.level = miplevel;
|
||||
args.source.region = MTLRegionMake2D(0, 0, srcTexture.width >> miplevel, srcTexture.height >> miplevel);
|
||||
args.source.texture = srcTexture;
|
||||
@@ -1664,7 +1650,7 @@ void MetalDriver::blit(
|
||||
<< "Metal does not support blitting to/from non-2D textures.";
|
||||
|
||||
MetalBlitter::BlitArgs args{};
|
||||
args.filter = SamplerMagFilter::NEAREST;
|
||||
args.filter = SamplerMagFilter::LINEAR;
|
||||
args.source.region = MTLRegionMake2D(
|
||||
(NSUInteger)srcOrigin.x,
|
||||
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_MIPMAP_NEAREST:
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
|
||||
return MTLSamplerMinMagFilterNearest;
|
||||
return MTLSamplerMinMagFilterLinear;
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
|
||||
case SamplerMinFilter::LINEAR:
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
|
||||
@@ -359,7 +359,7 @@ constexpr inline MTLSamplerMinMagFilter getFilter(SamplerMinFilter filter) noexc
|
||||
constexpr inline MTLSamplerMinMagFilter getFilter(SamplerMagFilter filter) noexcept {
|
||||
switch (filter) {
|
||||
case SamplerMagFilter::NEAREST:
|
||||
return MTLSamplerMinMagFilterNearest;
|
||||
return MTLSamplerMinMagFilterLinear;
|
||||
case SamplerMagFilter::LINEAR:
|
||||
return MTLSamplerMinMagFilterLinear;
|
||||
}
|
||||
@@ -369,10 +369,10 @@ constexpr inline MTLSamplerMipFilter getMipFilter(SamplerMinFilter filter) noexc
|
||||
switch (filter) {
|
||||
case SamplerMinFilter::NEAREST:
|
||||
case SamplerMinFilter::LINEAR:
|
||||
return MTLSamplerMipFilterNotMipmapped;
|
||||
return MTLSamplerMipFilterLinear;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
|
||||
return MTLSamplerMipFilterNearest;
|
||||
return MTLSamplerMipFilterLinear;
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
|
||||
return MTLSamplerMipFilterLinear;
|
||||
|
||||
@@ -943,7 +943,7 @@ void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region
|
||||
}
|
||||
|
||||
MetalBlitter::BlitArgs args{};
|
||||
args.filter = SamplerMagFilter::NEAREST;
|
||||
args.filter = SamplerMagFilter::LINEAR;
|
||||
args.source.level = 0;
|
||||
args.source.slice = 0;
|
||||
args.source.region = sourceRegion;
|
||||
@@ -1356,7 +1356,31 @@ id<MTLArgumentEncoder> MetalDescriptorSetLayout::getArgumentEncoderSlow(id<MTLDe
|
||||
[arguments addObject:bufferArgument];
|
||||
break;
|
||||
}
|
||||
case DescriptorType::SAMPLER:
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_INT:
|
||||
case DescriptorType::SAMPLER_2D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_INT:
|
||||
case DescriptorType::SAMPLER_3D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_MS_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_UINT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_EXTERNAL: {
|
||||
MTLArgumentDescriptor* textureArgument = [MTLArgumentDescriptor argumentDescriptor];
|
||||
textureArgument.index = binding.binding * 2;
|
||||
@@ -1478,7 +1502,31 @@ id<MTLBuffer> MetalDescriptorSet::finalizeAndGetBuffer(MetalDriver* driver, Shad
|
||||
atIndex:binding.binding * 2];
|
||||
break;
|
||||
}
|
||||
case DescriptorType::SAMPLER:
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_INT:
|
||||
case DescriptorType::SAMPLER_2D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_INT:
|
||||
case DescriptorType::SAMPLER_3D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_MS_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_UINT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_EXTERNAL: {
|
||||
auto found = textures.find(binding.binding);
|
||||
if (found == textures.end()) {
|
||||
|
||||
@@ -24,14 +24,22 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
struct PlatformMetalImpl {
|
||||
std::mutex mLock; // locks mDevice and mCommandQueue
|
||||
id<MTLDevice> mDevice = nil;
|
||||
id<MTLCommandQueue> mCommandQueue = nil;
|
||||
|
||||
// read form driver thread, read/written to from client thread
|
||||
std::atomic<PlatformMetal::DrawableFailureBehavior> mDrawableFailureBehavior =
|
||||
PlatformMetal::DrawableFailureBehavior::PANIC;
|
||||
|
||||
// These methods must be called with mLock held
|
||||
void createDeviceImpl(MetalDevice& outDevice);
|
||||
void createCommandQueueImpl(MetalDevice& device, MetalCommandQueue& outCommandQueue);
|
||||
};
|
||||
|
||||
Platform* createDefaultMetalPlatform() {
|
||||
@@ -48,7 +56,59 @@ Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::Dri
|
||||
return MetalDriverFactory::create(this, driverConfig);
|
||||
}
|
||||
|
||||
|
||||
bool PlatformMetal::initialize() noexcept {
|
||||
std::lock_guard<std::mutex> lock(pImpl->mLock);
|
||||
|
||||
MetalDevice device{};
|
||||
pImpl->createDeviceImpl(device);
|
||||
if (device.device == nil) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MetalCommandQueue commandQueue{};
|
||||
pImpl->createCommandQueueImpl(device, commandQueue);
|
||||
if (commandQueue.commandQueue == nil) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PlatformMetal::createDevice(MetalDevice& outDevice) noexcept {
|
||||
std::lock_guard<std::mutex> lock(pImpl->mLock);
|
||||
pImpl->createDeviceImpl(outDevice);
|
||||
}
|
||||
|
||||
void PlatformMetal::createCommandQueue(
|
||||
MetalDevice& device, MetalCommandQueue& outCommandQueue) noexcept {
|
||||
std::lock_guard<std::mutex> lock(pImpl->mLock);
|
||||
pImpl->createCommandQueueImpl(device, outCommandQueue);
|
||||
}
|
||||
|
||||
void PlatformMetal::createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept {
|
||||
std::lock_guard<std::mutex> lock(pImpl->mLock);
|
||||
id<MTLCommandBuffer> commandBuffer = [pImpl->mCommandQueue commandBuffer];
|
||||
[commandBuffer enqueue];
|
||||
outCommandBuffer.commandBuffer = commandBuffer;
|
||||
}
|
||||
|
||||
void PlatformMetal::setDrawableFailureBehavior(DrawableFailureBehavior behavior) noexcept {
|
||||
pImpl->mDrawableFailureBehavior = behavior;
|
||||
}
|
||||
|
||||
PlatformMetal::DrawableFailureBehavior PlatformMetal::getDrawableFailureBehavior() const noexcept {
|
||||
return pImpl->mDrawableFailureBehavior;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------
|
||||
|
||||
void PlatformMetalImpl::createDeviceImpl(MetalDevice& outDevice) {
|
||||
if (mDevice) {
|
||||
outDevice.device = mDevice;
|
||||
return;
|
||||
}
|
||||
|
||||
id<MTLDevice> result;
|
||||
|
||||
#if !defined(FILAMENT_IOS)
|
||||
@@ -74,27 +134,17 @@ void PlatformMetal::createDevice(MetalDevice& outDevice) noexcept {
|
||||
<< utils::io::endl;
|
||||
|
||||
outDevice.device = result;
|
||||
mDevice = result;
|
||||
}
|
||||
|
||||
void PlatformMetal::createCommandQueue(
|
||||
MetalDevice& device, MetalCommandQueue& outCommandQueue) noexcept {
|
||||
pImpl->mCommandQueue = [device.device newCommandQueue];
|
||||
pImpl->mCommandQueue.label = @"Filament";
|
||||
outCommandQueue.commandQueue = pImpl->mCommandQueue;
|
||||
}
|
||||
|
||||
void PlatformMetal::createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept {
|
||||
id<MTLCommandBuffer> commandBuffer = [pImpl->mCommandQueue commandBuffer];
|
||||
[commandBuffer enqueue];
|
||||
outCommandBuffer.commandBuffer = commandBuffer;
|
||||
}
|
||||
|
||||
void PlatformMetal::setDrawableFailureBehavior(DrawableFailureBehavior behavior) noexcept {
|
||||
pImpl->mDrawableFailureBehavior = behavior;
|
||||
}
|
||||
|
||||
PlatformMetal::DrawableFailureBehavior PlatformMetal::getDrawableFailureBehavior() const noexcept {
|
||||
return pImpl->mDrawableFailureBehavior;
|
||||
void PlatformMetalImpl::createCommandQueueImpl(MetalDevice& device, MetalCommandQueue& outCommandQueue) {
|
||||
if (mCommandQueue) {
|
||||
outCommandQueue.commandQueue = mCommandQueue;
|
||||
return;
|
||||
}
|
||||
mCommandQueue = [device.device newCommandQueue];
|
||||
mCommandQueue.label = @"Filament";
|
||||
outCommandQueue.commandQueue = mCommandQueue;
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -69,9 +69,8 @@ public:
|
||||
assert_invariant(set < MAX_DESCRIPTOR_SET_COUNT);
|
||||
assert_invariant(binding < MAX_DESCRIPTOR_COUNT);
|
||||
assert_invariant(entry.binding < 128); // we reserve 1 bit for the type right now
|
||||
mStorage[set][binding] = { (uint8_t)entry.binding,
|
||||
entry.type == DescriptorType::SAMPLER ||
|
||||
entry.type == DescriptorType::SAMPLER_EXTERNAL };
|
||||
mStorage[set][binding] = { uint8_t(entry.binding),
|
||||
DescriptorSetLayoutBinding::isSampler(entry.type) };
|
||||
mActiveDescriptors[set].set(binding);
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,32 @@ GLDescriptorSet::GLDescriptorSet(OpenGLContext& gl, DescriptorSetLayoutHandle ds
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DescriptorType::SAMPLER:
|
||||
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_INT:
|
||||
case DescriptorType::SAMPLER_2D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_INT:
|
||||
case DescriptorType::SAMPLER_3D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_MS_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_UINT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_EXTERNAL:
|
||||
if (UTILS_UNLIKELY(gl.isES2())) {
|
||||
desc.emplace<SamplerGLES2>();
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
#include <backend/Platform.h>
|
||||
#include <backend/Program.h>
|
||||
|
||||
#include <utils/Systrace.h>
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -36,7 +36,7 @@ OpenGLBlobCache::OpenGLBlobCache(OpenGLContext& gl) noexcept
|
||||
|
||||
GLuint OpenGLBlobCache::retrieve(BlobCacheKey* outKey, Platform& platform,
|
||||
Program const& program) const noexcept {
|
||||
SYSTRACE_CALL();
|
||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
if (!mCachingSupported || !platform.hasRetrieveBlobFunc()) {
|
||||
// the key is never updated in that case
|
||||
return 0;
|
||||
@@ -68,7 +68,7 @@ GLuint OpenGLBlobCache::retrieve(BlobCacheKey* outKey, Platform& platform,
|
||||
programId = glCreateProgram();
|
||||
|
||||
{ // scope for systrace
|
||||
SYSTRACE_NAME("glProgramBinary");
|
||||
FILAMENT_TRACING_NAME(FILAMENT_TRACING_CATEGORY_FILAMENT, "glProgramBinary");
|
||||
glProgramBinary(programId, blob->format, blob->data, programBinarySize);
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ GLuint OpenGLBlobCache::retrieve(BlobCacheKey* outKey, Platform& platform,
|
||||
|
||||
void OpenGLBlobCache::insert(Platform& platform,
|
||||
BlobCacheKey const& key, GLuint program) noexcept {
|
||||
SYSTRACE_CALL();
|
||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
if (!mCachingSupported || !platform.hasInsertBlobFunc()) {
|
||||
// the key is never updated in that case
|
||||
return;
|
||||
@@ -100,7 +100,7 @@ void OpenGLBlobCache::insert(Platform& platform,
|
||||
GLenum format;
|
||||
GLint programBinarySize = 0;
|
||||
{ // scope for systrace
|
||||
SYSTRACE_NAME("glGetProgramiv");
|
||||
FILAMENT_TRACING_NAME(FILAMENT_TRACING_CATEGORY_FILAMENT, "glGetProgramiv");
|
||||
glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &programBinarySize);
|
||||
}
|
||||
if (programBinarySize) {
|
||||
@@ -108,7 +108,7 @@ void OpenGLBlobCache::insert(Platform& platform,
|
||||
std::unique_ptr<Blob, decltype(&::free)> blob{ (Blob*)malloc(size), &::free };
|
||||
if (UTILS_LIKELY(blob)) {
|
||||
{ // scope for systrace
|
||||
SYSTRACE_NAME("glGetProgramBinary");
|
||||
FILAMENT_TRACING_NAME(FILAMENT_TRACING_CATEGORY_FILAMENT, "glGetProgramBinary");
|
||||
glGetProgramBinary(program, programBinarySize,
|
||||
&programBinarySize, &format, blob->data);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
#include "private/backend/Dispatcher.h"
|
||||
#include "private/backend/DriverApi.h"
|
||||
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#include <type_traits>
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
@@ -51,7 +53,6 @@
|
||||
#include <utils/Invocable.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Systrace.h>
|
||||
#include <utils/Slice.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
@@ -98,13 +99,13 @@
|
||||
|
||||
#define DEBUG_GROUP_MARKER_NONE 0x00 // no debug marker
|
||||
#define DEBUG_GROUP_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
|
||||
#define DEBUG_GROUP_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
|
||||
#define DEBUG_GROUP_MARKER_BACKEND 0x02 // markers on the backend side (perfetto)
|
||||
#define DEBUG_GROUP_MARKER_ALL 0xFF // all markers
|
||||
|
||||
#define DEBUG_MARKER_NONE 0x00 // no debug marker
|
||||
#define DEBUG_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
|
||||
#define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
|
||||
#define DEBUG_MARKER_PROFILE 0x04 // profiling on the backend side (systrace)
|
||||
#define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (perfetto)
|
||||
#define DEBUG_MARKER_PROFILE 0x04 // profiling on the backend side (perfetto)
|
||||
#define DEBUG_MARKER_ALL (0xFF & ~DEBUG_MARKER_PROFILE) // all markers
|
||||
|
||||
// set to the desired debug marker level (for user markers [default: All])
|
||||
@@ -248,8 +249,8 @@ OpenGLDriver::DebugMarker::DebugMarker(OpenGLDriver& driver, const char* string)
|
||||
#endif
|
||||
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_BEGIN(string);
|
||||
FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
FILAMENT_TRACING_NAME_BEGIN(FILAMENT_TRACING_CATEGORY_FILAMENT, string);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
@@ -265,8 +266,8 @@ OpenGLDriver::DebugMarker::~DebugMarker() noexcept {
|
||||
#endif
|
||||
|
||||
#if DEBUG_MARKER_LEVEL & DEBUG_MARKER_BACKEND
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_END();
|
||||
FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
FILAMENT_TRACING_NAME_END(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
@@ -931,7 +932,12 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
|
||||
if (gl.features.multisample_texture) {
|
||||
// multi-sample texture on GL 3.2 / GLES 3.1 and above
|
||||
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
|
||||
if (depth <= 1) {
|
||||
// We forcibly change the target to 2D-multisample only for flat texture.
|
||||
// A depth value greater than 1 may indicate multiview usage, which requires
|
||||
// GL_TEXTURE_2D_ARRAY. Also 2D MSAA won't work with non-flat texture anyway.
|
||||
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
|
||||
}
|
||||
} else {
|
||||
// Turn off multi-sampling for that texture. It's just not supported.
|
||||
}
|
||||
@@ -1207,7 +1213,12 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> th, intptr_t id,
|
||||
#if defined(BACKEND_OPENGL_LEVEL_GLES31)
|
||||
if (gl.features.multisample_texture) {
|
||||
// multi-sample texture on GL 3.2 / GLES 3.1 and above
|
||||
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
|
||||
if (depth <= 1) {
|
||||
// We forcibly change the target to 2D-multisample only for flat texture.
|
||||
// A depth value greater than 1 may indicate multiview usage, which requires
|
||||
// GL_TEXTURE_2D_ARRAY. Also 2D MSAA won't work with non-flat texture anyway.
|
||||
t->gl.target = GL_TEXTURE_2D_MULTISAMPLE;
|
||||
}
|
||||
} else {
|
||||
// Turn off multi-sampling for that texture. It's just not supported.
|
||||
}
|
||||
@@ -1452,8 +1463,15 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
|
||||
#if !defined(__EMSCRIPTEN__) && !defined(FILAMENT_IOS)
|
||||
if (layerCount > 1) {
|
||||
// if layerCount > 1, it means we use the multiview extension.
|
||||
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, attachment,
|
||||
t->gl.id, 0, binfo.layer, layerCount);
|
||||
if (rt->gl.samples > 1) {
|
||||
// For MSAA
|
||||
glFramebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, attachment,
|
||||
t->gl.id, 0, rt->gl.samples, binfo.layer, layerCount);
|
||||
}
|
||||
else {
|
||||
glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, attachment, t->gl.id, 0,
|
||||
binfo.layer, layerCount);
|
||||
}
|
||||
} else
|
||||
#endif // !defined(__EMSCRIPTEN__) && !defined(FILAMENT_IOS)
|
||||
{
|
||||
@@ -3348,8 +3366,8 @@ void OpenGLDriver::pushGroupMarker(char const* string) {
|
||||
#endif
|
||||
|
||||
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_BACKEND
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_BEGIN(string);
|
||||
FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
FILAMENT_TRACING_NAME_BEGIN(FILAMENT_TRACING_CATEGORY_FILAMENT, string);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
@@ -3365,8 +3383,8 @@ void OpenGLDriver::popGroupMarker(int) {
|
||||
#endif
|
||||
|
||||
#if DEBUG_GROUP_MARKER_LEVEL & DEBUG_GROUP_MARKER_BACKEND
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_NAME_END();
|
||||
FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
FILAMENT_TRACING_NAME_END(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
@@ -3685,7 +3703,7 @@ void OpenGLDriver::endFrame(UTILS_UNUSED uint32_t frameId) {
|
||||
gl.depthFunc(GL_LESS);
|
||||
gl.disable(GL_SCISSOR_TEST);
|
||||
#endif
|
||||
//SYSTRACE_NAME("glFinish");
|
||||
//FILAMENT_TRACING_NAME(FILAMENT_TRACING_CATEGORY_FILAMENT, "glFinish");
|
||||
//glFinish();
|
||||
mPlatform.endFrame(frameId);
|
||||
insertEventMarker("endFrame");
|
||||
|
||||
@@ -64,7 +64,7 @@ utils::CString OpenGLPlatform::getRendererString(Driver const* driver) {
|
||||
}
|
||||
|
||||
void OpenGLPlatform::makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain,
|
||||
utils::Invocable<void()>, utils::Invocable<void(size_t)>) noexcept {
|
||||
utils::Invocable<void()>, utils::Invocable<void(size_t)>) {
|
||||
makeCurrent(getCurrentContextType(), drawSwapChain, readSwapChain);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,12 +25,13 @@
|
||||
#include <backend/Program.h>
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/FixedCapacityVector.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -96,8 +97,7 @@ OpenGLProgram::~OpenGLProgram() noexcept {
|
||||
}
|
||||
|
||||
void OpenGLProgram::initialize(OpenGLDriver& gld) {
|
||||
|
||||
SYSTRACE_CALL();
|
||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
|
||||
assert_invariant(gl.program == 0);
|
||||
assert_invariant(mToken);
|
||||
@@ -122,8 +122,7 @@ void OpenGLProgram::initialize(OpenGLDriver& gld) {
|
||||
*/
|
||||
void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint program,
|
||||
LazyInitializationData& lazyInitializationData) noexcept {
|
||||
|
||||
SYSTRACE_CALL();
|
||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
|
||||
// from the pipeline layout we compute a mapping from {set, binding} to {binding}
|
||||
// for both buffers and textures
|
||||
@@ -175,7 +174,31 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DescriptorType::SAMPLER:
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_INT:
|
||||
case DescriptorType::SAMPLER_2D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_INT:
|
||||
case DescriptorType::SAMPLER_3D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_MS_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_UINT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_EXTERNAL: {
|
||||
if (!entry.name.empty()) {
|
||||
GLint const loc = glGetUniformLocation(program, entry.name.c_str());
|
||||
|
||||
@@ -23,12 +23,13 @@
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
#include <backend/DriverEnums.h>
|
||||
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/debug.h>
|
||||
#include <utils/JobSystem.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
@@ -240,8 +241,8 @@ void TimerQueryFenceFactory::beginTimeElapsedQuery(GLTimerQuery* tq) {
|
||||
if (state) {
|
||||
platform.waitFence(fence, FENCE_WAIT_FOR_EVER);
|
||||
state->then = clock::now().time_since_epoch().count();
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_ASYNC_BEGIN("OpenGLTimerQueryFence", intptr_t(state.get()));
|
||||
FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
FILAMENT_TRACING_ASYNC_BEGIN(FILAMENT_TRACING_CATEGORY_FILAMENT, "OpenGLTimerQueryFence", intptr_t(state.get()));
|
||||
}
|
||||
platform.destroyFence(fence);
|
||||
});
|
||||
@@ -257,8 +258,8 @@ void TimerQueryFenceFactory::endTimeElapsedQuery(OpenGLDriver&, GLTimerQuery* tq
|
||||
platform.waitFence(fence, FENCE_WAIT_FOR_EVER);
|
||||
int64_t const now = clock::now().time_since_epoch().count();
|
||||
state->elapsed.store(now - state->then, std::memory_order_relaxed);
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_ASYNC_END("OpenGLTimerQueryFence", intptr_t(state.get()));
|
||||
FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
FILAMENT_TRACING_ASYNC_END(FILAMENT_TRACING_CATEGORY_FILAMENT, "OpenGLTimerQueryFence", intptr_t(state.get()));
|
||||
}
|
||||
platform.destroyFence(fence);
|
||||
});
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Program.h>
|
||||
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/debug.h>
|
||||
@@ -36,7 +38,6 @@
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -367,7 +368,7 @@ void ShaderCompilerService::notifyWhenAllProgramsAreReady(
|
||||
|
||||
GLuint ShaderCompilerService::initialize(program_token_t& token) {
|
||||
|
||||
SYSTRACE_CALL();
|
||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
|
||||
assert_invariant(token);// This function should be called when the token is still alive.
|
||||
|
||||
@@ -454,8 +455,8 @@ void ShaderCompilerService::runAtNextTick(CompilerPriorityQueue priority,
|
||||
});
|
||||
ops.emplace(pos, priority, token, std::move(job));
|
||||
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_VALUE32("ShaderCompilerService Jobs", mRunAtNextTickOps.size());
|
||||
FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
FILAMENT_TRACING_VALUE(FILAMENT_TRACING_CATEGORY_FILAMENT, "ShaderCompilerService Jobs", mRunAtNextTickOps.size());
|
||||
}
|
||||
|
||||
bool ShaderCompilerService::cancelTickOp(program_token_t const& token) noexcept {
|
||||
@@ -468,8 +469,8 @@ bool ShaderCompilerService::cancelTickOp(program_token_t const& token) noexcept
|
||||
ops.erase(pos);
|
||||
return true;
|
||||
}
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_VALUE32("ShaderCompilerService Jobs", ops.size());
|
||||
FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
FILAMENT_TRACING_VALUE(FILAMENT_TRACING_CATEGORY_FILAMENT, "ShaderCompilerService Jobs", ops.size());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -485,15 +486,15 @@ void ShaderCompilerService::executeTickOps() noexcept {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
SYSTRACE_CONTEXT();
|
||||
SYSTRACE_VALUE32("ShaderCompilerService Jobs", ops.size());
|
||||
FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
FILAMENT_TRACING_VALUE(FILAMENT_TRACING_CATEGORY_FILAMENT, "ShaderCompilerService Jobs", ops.size());
|
||||
}
|
||||
|
||||
/* static */ void ShaderCompilerService::compileShaders(OpenGLContext& context,
|
||||
Program::ShaderSource shadersSource,
|
||||
FixedCapacityVector<Program::SpecializationConstant> const& specializationConstants,
|
||||
bool multiview, program_token_t const& token) noexcept {
|
||||
SYSTRACE_CALL();
|
||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
|
||||
auto const appendSpecConstantString = +[](std::string& s, Program::SpecializationConstant const& sc) {
|
||||
s += "#define SPIRV_CROSS_CONSTANT_ID_" + std::to_string(sc.id) + ' ';
|
||||
@@ -616,7 +617,7 @@ void ShaderCompilerService::executeTickOps() noexcept {
|
||||
}
|
||||
|
||||
/* static */ void ShaderCompilerService::checkCompileStatus(program_token_t const& token) noexcept {
|
||||
SYSTRACE_CALL();
|
||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
|
||||
UTILS_NOUNROLL
|
||||
for (size_t i = 0; i < Program::SHADER_TYPE_COUNT; i++) {
|
||||
@@ -639,7 +640,7 @@ void ShaderCompilerService::executeTickOps() noexcept {
|
||||
|
||||
/* static */ void ShaderCompilerService::linkProgram(OpenGLContext const& context,
|
||||
program_token_t const& token) noexcept {
|
||||
SYSTRACE_CALL();
|
||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
|
||||
// Shader compilation should be completed by now. Check the status and log errors on failure.
|
||||
checkCompileStatus(token);
|
||||
@@ -675,7 +676,7 @@ void ShaderCompilerService::executeTickOps() noexcept {
|
||||
|
||||
/* static */ bool ShaderCompilerService::checkLinkStatusAndCleanupShaders(
|
||||
program_token_t const& token) noexcept {
|
||||
SYSTRACE_CALL();
|
||||
FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT);
|
||||
assert_invariant(token->gl.program);
|
||||
|
||||
bool linked = true;
|
||||
|
||||
@@ -70,6 +70,9 @@ PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
|
||||
#ifdef GL_OVR_multiview
|
||||
PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
|
||||
#endif
|
||||
#ifdef GL_OVR_multiview_multisampled_render_to_texture
|
||||
PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC glFramebufferTextureMultisampleMultiviewOVR;
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
|
||||
// On Android, If we want to support a build system less than ANDROID_API 21, we need to
|
||||
@@ -123,6 +126,9 @@ void importGLESExtensionsEntryPoints() {
|
||||
#ifdef GL_OVR_multiview
|
||||
getProcAddress(glFramebufferTextureMultiviewOVR, "glFramebufferTextureMultiviewOVR");
|
||||
#endif
|
||||
#ifdef GL_OVR_multiview_multisampled_render_to_texture
|
||||
getProcAddress(glFramebufferTextureMultisampleMultiviewOVR, "glFramebufferTextureMultisampleMultiviewOVR");
|
||||
#endif
|
||||
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
|
||||
getProcAddress(glDispatchCompute, "glDispatchCompute");
|
||||
#endif
|
||||
|
||||
@@ -154,6 +154,9 @@ extern PFNGLMAXSHADERCOMPILERTHREADSKHRPROC glMaxShaderCompilerThreadsKHR;
|
||||
#ifdef GL_OVR_multiview
|
||||
extern PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC glFramebufferTextureMultiviewOVR;
|
||||
#endif
|
||||
#ifdef GL_OVR_multiview_multisampled_render_to_texture
|
||||
extern PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC glFramebufferTextureMultisampleMultiviewOVR;
|
||||
#endif
|
||||
#if defined(__ANDROID__) && !defined(FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2)
|
||||
extern PFNGLDISPATCHCOMPUTEPROC glDispatchCompute;
|
||||
#endif
|
||||
|
||||
@@ -255,7 +255,7 @@ void PlatformCocoaGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept
|
||||
}
|
||||
|
||||
bool PlatformCocoaGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept {
|
||||
SwapChain* readSwapChain) {
|
||||
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
|
||||
"ContextManagerCocoa does not support using distinct draw/read swap chains.");
|
||||
CocoaGLSwapChain* swapChain = (CocoaGLSwapChain*)drawSwapChain;
|
||||
|
||||
@@ -155,7 +155,7 @@ uint32_t PlatformCocoaTouchGL::getDefaultFramebufferObject() noexcept {
|
||||
}
|
||||
|
||||
bool PlatformCocoaTouchGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept {
|
||||
SwapChain* readSwapChain) {
|
||||
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
|
||||
"PlatformCocoaTouchGL does not support using distinct draw/read swap chains.");
|
||||
CAEAGLLayer* const glLayer = (__bridge CAEAGLLayer*) drawSwapChain;
|
||||
|
||||
@@ -577,18 +577,18 @@ OpenGLPlatform::ContextType PlatformEGL::getCurrentContextType() const noexcept
|
||||
}
|
||||
|
||||
bool PlatformEGL::makeCurrent(ContextType type,
|
||||
SwapChain* drawSwapChain, SwapChain* readSwapChain) noexcept {
|
||||
SwapChain* drawSwapChain, SwapChain* readSwapChain) {
|
||||
SwapChainEGL const* const dsc = static_cast<SwapChainEGL const*>(drawSwapChain);
|
||||
SwapChainEGL const* const rsc = static_cast<SwapChainEGL const*>(readSwapChain);
|
||||
EGLContext context = getContextForType(type);
|
||||
EGLBoolean const success = egl.makeCurrent(context, dsc->sur, rsc->sur);
|
||||
return success == EGL_TRUE ? true : false;
|
||||
return success == EGL_TRUE;
|
||||
}
|
||||
|
||||
void PlatformEGL::makeCurrent(SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain,
|
||||
Invocable<void()> preContextChange,
|
||||
Invocable<void(size_t index)> postContextChange) noexcept {
|
||||
Invocable<void(size_t index)> postContextChange) {
|
||||
|
||||
assert_invariant(drawSwapChain);
|
||||
assert_invariant(readSwapChain);
|
||||
@@ -796,7 +796,7 @@ void PlatformEGL::Config::erase(EGLint name) noexcept {
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
EGLBoolean PlatformEGL::EGL::makeCurrent(EGLContext context, EGLSurface drawSurface,
|
||||
EGLSurface readSurface) noexcept {
|
||||
EGLSurface readSurface) {
|
||||
if (UTILS_UNLIKELY((
|
||||
mCurrentContext != context ||
|
||||
drawSurface != mCurrentDrawSurface || readSurface != mCurrentReadSurface))) {
|
||||
|
||||
@@ -135,7 +135,7 @@ static constexpr const std::string_view kNativeWindowInvalidMsg =
|
||||
|
||||
bool PlatformEGLAndroid::makeCurrent(ContextType type,
|
||||
SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept {
|
||||
SwapChain* readSwapChain) {
|
||||
|
||||
// fast & safe path
|
||||
if (UTILS_LIKELY(!mAssertNativeWindowIsValid)) {
|
||||
@@ -255,6 +255,7 @@ Platform::ExternalImageHandle PlatformEGLAndroid::createExternalImage(AHardwareB
|
||||
p->height = hardwareBufferDescription.height;
|
||||
p->width = hardwareBufferDescription.width;
|
||||
auto textureFormat = mapToFilamentFormat(hardwareBufferDescription.format, sRGB);
|
||||
p->format = textureFormat;
|
||||
p->usage = mapToFilamentUsage(hardwareBufferDescription.usage, textureFormat);
|
||||
return ExternalImageHandle{ p };
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ void PlatformGLX::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
||||
}
|
||||
|
||||
bool PlatformGLX::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept {
|
||||
SwapChain* readSwapChain) {
|
||||
g_glx.setCurrentContext(mGLXDisplay,
|
||||
(GLXDrawable)drawSwapChain, (GLXDrawable)readSwapChain, mGLXContext);
|
||||
return true;
|
||||
|
||||
@@ -169,7 +169,7 @@ void PlatformOSMesa::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
||||
}
|
||||
|
||||
bool PlatformOSMesa::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept {
|
||||
SwapChain* readSwapChain) {
|
||||
OSMesaAPI* api = (OSMesaAPI*) mOsMesaApi;
|
||||
OSMesaSwapchain* impl = (OSMesaSwapchain*) drawSwapChain;
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ void PlatformWGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
||||
}
|
||||
|
||||
bool PlatformWGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept {
|
||||
SwapChain* readSwapChain) {
|
||||
ASSERT_PRECONDITION_NON_FATAL(drawSwapChain == readSwapChain,
|
||||
"PlatformWGL does not support distinct draw/read swap chains.");
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ void PlatformWebGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
||||
}
|
||||
|
||||
bool PlatformWebGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
|
||||
SwapChain* readSwapChain) noexcept {
|
||||
SwapChain* readSwapChain) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ VulkanBuffer::VulkanBuffer(VmaAllocator allocator, VulkanStagePool& stagePool,
|
||||
: mAllocator(allocator),
|
||||
mStagePool(stagePool),
|
||||
mUsage(usage),
|
||||
mUpdatedOffset(0),
|
||||
mUpdatedOffset(0),
|
||||
mUpdatedBytes(0) {
|
||||
// for now make sure that only 1 bit is set in usage
|
||||
// (because loadFromCpu() assumes that somewhat)
|
||||
@@ -56,11 +56,12 @@ void VulkanBuffer::loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint
|
||||
vmaMapMemory(mAllocator, stage->memory, &mapped);
|
||||
memcpy(mapped, cpuData, numBytes);
|
||||
vmaUnmapMemory(mAllocator, stage->memory);
|
||||
vmaFlushAllocation(mAllocator, stage->memory, byteOffset, numBytes);
|
||||
vmaFlushAllocation(mAllocator, stage->memory, 0, numBytes);
|
||||
|
||||
// If there was a previous update, then we need to make sure the following write is properly
|
||||
// synced with the previous read.
|
||||
if (mUpdatedBytes > 0) {
|
||||
if (mUpdatedBytes > 0 &&
|
||||
(byteOffset >= mUpdatedOffset && byteOffset <= (mUpdatedOffset + mUpdatedBytes))) {
|
||||
VkAccessFlags srcAccess = 0;
|
||||
VkPipelineStageFlags srcStage = 0;
|
||||
if (mUsage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) {
|
||||
@@ -74,28 +75,28 @@ void VulkanBuffer::loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint
|
||||
srcStage = VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
|
||||
}
|
||||
|
||||
VkBufferMemoryBarrier barrier{
|
||||
VkBufferMemoryBarrier barrier = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
||||
.srcAccessMask = srcAccess,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.buffer = mGpuBuffer,
|
||||
.offset = mUpdatedOffset,
|
||||
.size = mUpdatedBytes,
|
||||
.offset = byteOffset,
|
||||
.size = numBytes,
|
||||
};
|
||||
vkCmdPipelineBarrier(cmdbuf, srcStage, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 1,
|
||||
&barrier, 0, nullptr);
|
||||
}
|
||||
|
||||
VkBufferCopy region {
|
||||
.srcOffset = 0,
|
||||
.dstOffset = byteOffset,
|
||||
.size = numBytes,
|
||||
VkBufferCopy region = {
|
||||
.srcOffset = 0,
|
||||
.dstOffset = byteOffset,
|
||||
.size = numBytes,
|
||||
};
|
||||
vkCmdCopyBuffer(cmdbuf, stage->buffer, mGpuBuffer, 1, ®ion);
|
||||
|
||||
mUpdatedOffset = byteOffset;
|
||||
mUpdatedOffset = byteOffset;
|
||||
mUpdatedBytes = numBytes;
|
||||
|
||||
// Firstly, ensure that the copy finishes before the next draw call.
|
||||
@@ -112,21 +113,21 @@ void VulkanBuffer::loadFromCpu(VkCommandBuffer cmdbuf, const void* cpuData, uint
|
||||
dstAccessMask |= VK_ACCESS_INDEX_READ_BIT;
|
||||
dstStageMask |= VK_PIPELINE_STAGE_VERTEX_INPUT_BIT;
|
||||
} else if (mUsage & VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT) {
|
||||
dstAccessMask |= VK_ACCESS_UNIFORM_READ_BIT;
|
||||
dstStageMask |=
|
||||
(VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
|
||||
dstAccessMask |= VK_ACCESS_SHADER_READ_BIT;
|
||||
dstStageMask |= VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
|
||||
} else if (mUsage & VK_BUFFER_USAGE_STORAGE_BUFFER_BIT) {
|
||||
// TODO: implement me
|
||||
}
|
||||
|
||||
VkBufferMemoryBarrier barrier{
|
||||
VkBufferMemoryBarrier barrier = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = dstAccessMask,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.buffer = mGpuBuffer,
|
||||
.size = VK_WHOLE_SIZE,
|
||||
.offset = byteOffset,
|
||||
.size = numBytes,
|
||||
};
|
||||
|
||||
vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_TRANSFER_BIT, dstStageMask, 0, 0, nullptr, 1,
|
||||
|
||||
@@ -151,13 +151,13 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
|
||||
|
||||
#elif FVK_ENABLED(FVK_DEBUG_SYSTRACE)
|
||||
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#define FVK_SYSTRACE_CONTEXT() SYSTRACE_CONTEXT()
|
||||
#define FVK_SYSTRACE_START(marker) SYSTRACE_NAME_BEGIN(marker)
|
||||
#define FVK_SYSTRACE_END() SYSTRACE_NAME_END()
|
||||
#define FVK_SYSTRACE_SCOPE() SYSTRACE_NAME(__func__)
|
||||
#define FVK_PROFILE_MARKER(marker) FVK_SYSTRACE_SCOPE()
|
||||
#include <private/utils/Tracing.h>
|
||||
|
||||
#define FVK_SYSTRACE_CONTEXT() FILAMENT_TRACING_CONTEXT(FILAMENT_TRACING_CATEGORY_FILAMENT)
|
||||
#define FVK_SYSTRACE_START(marker) FILAMENT_TRACING_NAME_BEGIN(FILAMENT_TRACING_CATEGORY_FILAMENT, marker)
|
||||
#define FVK_SYSTRACE_END() FILAMENT_TRACING_NAME_END(FILAMENT_TRACING_CATEGORY_FILAMENT)
|
||||
#define FVK_SYSTRACE_SCOPE() FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT)
|
||||
#define FVK_PROFILE_MARKER(marker) FILAMENT_TRACING_CALL(FILAMENT_TRACING_CATEGORY_FILAMENT)
|
||||
|
||||
#else
|
||||
#define FVK_SYSTRACE_CONTEXT()
|
||||
|
||||
@@ -358,12 +358,11 @@ void VulkanDescriptorSetCache::updateSamplerImpl(VkDescriptorSet vkset, uint8_t
|
||||
range.levelCount = 1;
|
||||
range.layerCount = 1;
|
||||
}
|
||||
VkDescriptorImageInfo info{
|
||||
VkDescriptorImageInfo info = {
|
||||
.sampler = sampler,
|
||||
.imageView = texture->getView(range),
|
||||
.imageLayout = fvkutils::getVkLayout(texture->getDefaultLayout()),
|
||||
.imageLayout = fvkutils::getVkLayout(texture->getSamplerLayout()),
|
||||
};
|
||||
|
||||
VkWriteDescriptorSet descriptorWrite = {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.pNext = nullptr,
|
||||
|
||||
@@ -622,7 +622,7 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::S
|
||||
auto& commands = mCommands.get();
|
||||
// Unlike uploaded textures or swapchains, we need to explicit transition this
|
||||
// texture into the read layout.
|
||||
texture->transitionLayout(&commands, texture->getPrimaryViewRange(), VulkanLayout::READ_ONLY);
|
||||
texture->transitionLayout(&commands, texture->getPrimaryViewRange(), VulkanLayout::FRAG_READ);
|
||||
|
||||
if (imgData.external.valid()) {
|
||||
mExternalImageManager.addExternallySampledTexture(texture, externalImage);
|
||||
|
||||
@@ -93,7 +93,32 @@ BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) {
|
||||
case DescriptorType::SAMPLER_EXTERNAL:
|
||||
fromStageFlags(binding.stageFlags, binding.binding, mask.externalSampler);
|
||||
UTILS_FALLTHROUGH;
|
||||
case DescriptorType::SAMPLER: {
|
||||
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_INT:
|
||||
case DescriptorType::SAMPLER_2D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_INT:
|
||||
case DescriptorType::SAMPLER_3D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_MS_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_UINT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT: {
|
||||
fromStageFlags(binding.stageFlags, binding.binding, mask.sampler);
|
||||
break;
|
||||
}
|
||||
@@ -495,7 +520,8 @@ void VulkanRenderTarget::emitBarriersEndRenderPass(VulkanCommandBuffer& commands
|
||||
}
|
||||
} else {
|
||||
texture->setLayout(range, VulkanFboCache::FINAL_COLOR_ATTACHMENT_LAYOUT);
|
||||
if (!texture->transitionLayout(&commands, range, VulkanLayout::READ_WRITE)) {
|
||||
if (texture->isSampleable() &&
|
||||
!texture->transitionLayout(&commands, range, VulkanLayout::FRAG_READ)) {
|
||||
texture->attachmentToSamplerBarrier(&commands, range);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,6 +234,7 @@ void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget
|
||||
|
||||
VulkanAttachment const srcAttachment = srcTarget->getColor0();
|
||||
VkImageSubresourceRange const srcRange = srcAttachment.getSubresourceRange();
|
||||
VulkanLayout const srcLayout = srcAttachment.getLayout();
|
||||
srcTexture->transitionLayout(cmdbuffer, srcRange, VulkanLayout::TRANSFER_SRC);
|
||||
|
||||
VkImageCopy const imageCopyRegion = {
|
||||
@@ -270,7 +271,7 @@ void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget
|
||||
fvkutils::getVkLayout(VulkanLayout::TRANSFER_DST), 1, &imageCopyRegion);
|
||||
|
||||
// Restore the source image layout.
|
||||
srcTexture->transitionLayout(cmdbuffer, srcRange, srcTexture->getDefaultLayout());
|
||||
srcTexture->transitionLayout(cmdbuffer, srcRange, srcLayout);
|
||||
|
||||
vkEndCommandBuffer(cmdbuffer);
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi
|
||||
fvkutils::transitionLayout(cmdbuffer, {
|
||||
.image = image->image,
|
||||
.oldLayout = VulkanLayout::UNDEFINED,
|
||||
.newLayout = VulkanLayout::READ_WRITE, // (= VK_IMAGE_LAYOUT_GENERAL)
|
||||
.newLayout = VulkanLayout::STAGING, // (= VK_IMAGE_LAYOUT_GENERAL)
|
||||
.subresources = { aspectFlags, 0, 1, 0, 1 },
|
||||
});
|
||||
return image;
|
||||
|
||||
@@ -125,7 +125,7 @@ inline VulkanLayout getDefaultLayoutImpl(TextureUsage usage) {
|
||||
return VulkanLayout::COLOR_ATTACHMENT;
|
||||
}
|
||||
// Finally, the layout for an immutable texture is optimal read-only.
|
||||
return VulkanLayout::READ_ONLY;
|
||||
return VulkanLayout::FRAG_READ;
|
||||
}
|
||||
|
||||
inline VulkanLayout getDefaultLayoutImpl(VkImageUsageFlags vkusage) {
|
||||
@@ -187,7 +187,7 @@ VkImageUsageFlags getUsage(VulkanContext const& context, uint8_t samples,
|
||||
VkFormatProperties props;
|
||||
vkGetPhysicalDeviceFormatProperties(physicalDevice, vkFormat, &props);
|
||||
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
|
||||
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << mState->mVkFormat << " is not "
|
||||
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << vkFormat << " is not "
|
||||
"sampleable with optimal tiling." << utils::io::endl;
|
||||
}
|
||||
}
|
||||
@@ -691,6 +691,7 @@ bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceR
|
||||
<< " is skipped because of no change in layout" << utils::io::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
return hasTransitions;
|
||||
}
|
||||
|
||||
@@ -700,16 +701,15 @@ void VulkanTexture::samplerToAttachmentBarrier(VulkanCommandBuffer* commands,
|
||||
VkImageLayout const layout =
|
||||
fvkutils::getVkLayout(getLayout(range.baseArrayLayer, range.baseMipLevel));
|
||||
VkImageMemoryBarrier barrier = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
.dstAccessMask =
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.oldLayout = layout,
|
||||
.newLayout = layout,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = mState->mTextureImage,
|
||||
.subresourceRange = range,
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.oldLayout = layout,
|
||||
.newLayout = layout,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = mState->mTextureImage,
|
||||
.subresourceRange = range,
|
||||
};
|
||||
vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
|
||||
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT |
|
||||
@@ -723,15 +723,15 @@ void VulkanTexture::attachmentToSamplerBarrier(VulkanCommandBuffer* commands,
|
||||
VkImageLayout const layout
|
||||
= fvkutils::getVkLayout(getLayout(range.baseArrayLayer, range.baseMipLevel));
|
||||
VkImageMemoryBarrier barrier = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
.oldLayout = layout,
|
||||
.newLayout = layout,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = mState->mTextureImage,
|
||||
.subresourceRange = range,
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_SHADER_READ_BIT,
|
||||
.oldLayout = layout,
|
||||
.newLayout = layout,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.image = mState->mTextureImage,
|
||||
.subresourceRange = range,
|
||||
};
|
||||
vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
|
||||
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
|
||||
@@ -145,8 +145,14 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
||||
|
||||
VkImageSubresourceRange const& getPrimaryViewRange() const { return mPrimaryViewRange; }
|
||||
|
||||
VulkanLayout getPrimaryImageLayout() const {
|
||||
return getLayout(mPrimaryViewRange.baseArrayLayer, mPrimaryViewRange.baseMipLevel);
|
||||
VulkanLayout getSamplerLayout() const {
|
||||
if (!isSampleable()) {
|
||||
return VulkanLayout::UNDEFINED;
|
||||
}
|
||||
if (mState->mUsage & VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) {
|
||||
return VulkanLayout::DEPTH_SAMPLER;
|
||||
}
|
||||
return VulkanLayout::FRAG_READ;
|
||||
}
|
||||
|
||||
// Returns the layout for the intended use of this texture (and not the expected layout at the
|
||||
@@ -180,6 +186,10 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
|
||||
return mState->mUsage & VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT;
|
||||
}
|
||||
|
||||
bool isSampleable() const {
|
||||
return mState->mUsage & VK_IMAGE_USAGE_SAMPLED_BIT;
|
||||
}
|
||||
|
||||
bool getIsProtected() const {
|
||||
return mState->mIsProtected;
|
||||
}
|
||||
|
||||
@@ -42,20 +42,22 @@ getVkTransition(const VulkanLayoutTransition& transition) {
|
||||
srcStage = VK_PIPELINE_STAGE_ALL_GRAPHICS_BIT;
|
||||
break;
|
||||
case VulkanLayout::COLOR_ATTACHMENT:
|
||||
srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
||||
srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT |
|
||||
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
srcStage = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
srcStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
break;
|
||||
case VulkanLayout::READ_WRITE:
|
||||
case VulkanLayout::STAGING:
|
||||
srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
|
||||
srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
break;
|
||||
case VulkanLayout::READ_ONLY:
|
||||
case VulkanLayout::FRAG_READ:
|
||||
srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
break;
|
||||
case VulkanLayout::VERT_READ:
|
||||
srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
srcStage = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
|
||||
break;
|
||||
case VulkanLayout::TRANSFER_SRC:
|
||||
srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
srcStage = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
@@ -69,7 +71,7 @@ getVkTransition(const VulkanLayoutTransition& transition) {
|
||||
srcStage = VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||
break;
|
||||
case VulkanLayout::DEPTH_SAMPLER:
|
||||
srcAccessMask = VK_ACCESS_MEMORY_READ_BIT;
|
||||
srcAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
srcStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
break;
|
||||
case VulkanLayout::COLOR_ATTACHMENT_RESOLVE:
|
||||
@@ -84,19 +86,21 @@ getVkTransition(const VulkanLayoutTransition& transition) {
|
||||
|
||||
switch (transition.newLayout) {
|
||||
case VulkanLayout::COLOR_ATTACHMENT:
|
||||
dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT
|
||||
| VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
|
||||
dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT
|
||||
| VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
dstStage = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT
|
||||
| VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
dstStage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
break;
|
||||
case VulkanLayout::READ_WRITE:
|
||||
case VulkanLayout::STAGING:
|
||||
dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT;
|
||||
dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
break;
|
||||
case VulkanLayout::READ_ONLY:
|
||||
case VulkanLayout::FRAG_READ:
|
||||
dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
|
||||
dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
break;
|
||||
case VulkanLayout::VERT_READ:
|
||||
dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
dstStage = VK_PIPELINE_STAGE_VERTEX_SHADER_BIT;
|
||||
break;
|
||||
case VulkanLayout::TRANSFER_SRC:
|
||||
dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
@@ -112,10 +116,8 @@ getVkTransition(const VulkanLayoutTransition& transition) {
|
||||
dstStage = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
|
||||
break;
|
||||
case VulkanLayout::DEPTH_SAMPLER:
|
||||
dstAccessMask =
|
||||
VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT |
|
||||
VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT;
|
||||
dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
|
||||
dstStage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
break;
|
||||
case VulkanLayout::PRESENT:
|
||||
case VulkanLayout::COLOR_ATTACHMENT_RESOLVE:
|
||||
@@ -138,7 +140,6 @@ bool transitionLayout(VkCommandBuffer cmdbuffer,
|
||||
}
|
||||
auto [srcAccessMask, dstAccessMask, srcStage, dstStage, oldLayout, newLayout]
|
||||
= getVkTransition(transition);
|
||||
|
||||
if (oldLayout == newLayout) {
|
||||
return false;
|
||||
}
|
||||
@@ -155,7 +156,7 @@ bool transitionLayout(VkCommandBuffer cmdbuffer,
|
||||
.image = transition.image,
|
||||
.subresourceRange = transition.subresources,
|
||||
};
|
||||
vkCmdPipelineBarrier(cmdbuffer, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
vkCmdPipelineBarrier(cmdbuffer, srcStage, dstStage, 0, 0, nullptr, 0, nullptr, 1, &barrier);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -264,8 +265,9 @@ utils::io::ostream& operator<<(utils::io::ostream& out,
|
||||
const filament::backend::VulkanLayout& layout) {
|
||||
switch (layout) {
|
||||
CASE(UNDEFINED)
|
||||
CASE(READ_ONLY)
|
||||
CASE(READ_WRITE)
|
||||
CASE(FRAG_READ)
|
||||
CASE(VERT_READ)
|
||||
CASE(STAGING)
|
||||
CASE(TRANSFER_SRC)
|
||||
CASE(TRANSFER_DST)
|
||||
CASE(DEPTH_ATTACHMENT)
|
||||
|
||||
@@ -32,9 +32,11 @@ enum class VulkanLayout : uint8_t {
|
||||
// any transition.
|
||||
UNDEFINED,
|
||||
// Fragment/vertex shader accessible layout for reading and writing.
|
||||
READ_WRITE,
|
||||
// Fragment/vertex shader accessible layout for reading only.
|
||||
READ_ONLY,
|
||||
STAGING,
|
||||
// Fragment shader accessible layout for reading only.
|
||||
FRAG_READ,
|
||||
// Vertex shader accessible layout for reading only.
|
||||
VERT_READ,
|
||||
// For the source of a copy operation.
|
||||
TRANSFER_SRC,
|
||||
// For the destination of a copy operation.
|
||||
@@ -65,9 +67,10 @@ constexpr inline VkImageLayout getVkLayout(VulkanLayout layout) {
|
||||
switch (layout) {
|
||||
case VulkanLayout::UNDEFINED:
|
||||
return VK_IMAGE_LAYOUT_UNDEFINED;
|
||||
case VulkanLayout::READ_WRITE:
|
||||
case VulkanLayout::STAGING:
|
||||
return VK_IMAGE_LAYOUT_GENERAL;
|
||||
case VulkanLayout::READ_ONLY:
|
||||
case VulkanLayout::FRAG_READ:
|
||||
case VulkanLayout::VERT_READ:
|
||||
return VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
case VulkanLayout::TRANSFER_SRC:
|
||||
return VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
@@ -80,9 +83,8 @@ constexpr inline VkImageLayout getVkLayout(VulkanLayout layout) {
|
||||
case VulkanLayout::PRESENT:
|
||||
return VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
// Filament sometimes samples from one miplevel while writing to another level in the
|
||||
// same texture (e.g. bloom does this). Moreover we'd like to avoid lots of expensive
|
||||
// layout transitions. So, keep it simple and use GENERAL for all color-attachable
|
||||
// textures.
|
||||
// same texture (e.g. bloom does this). So, keep it simple and use GENERAL for all
|
||||
// color-attachable textures.
|
||||
case VulkanLayout::COLOR_ATTACHMENT:
|
||||
return VK_IMAGE_LAYOUT_GENERAL;
|
||||
case VulkanLayout::COLOR_ATTACHMENT_RESOLVE:
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "WebGPUHandles.h"
|
||||
|
||||
#include "WebGPUConstants.h"
|
||||
#include "WebGPUStrings.h"
|
||||
|
||||
#include "DriverBase.h"
|
||||
#include <backend/DriverEnums.h>
|
||||
@@ -28,39 +29,116 @@
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] constexpr std::string_view toString(ShaderStage stage) {
|
||||
switch (stage) {
|
||||
case ShaderStage::VERTEX:
|
||||
return "vertex";
|
||||
case ShaderStage::FRAGMENT:
|
||||
return "fragment";
|
||||
case ShaderStage::COMPUTE:
|
||||
return "compute";
|
||||
[[nodiscard]] std::string replaceSpecConstants(std::string_view shaderLabel,
|
||||
std::string_view shaderSource,
|
||||
std::unordered_map<uint32_t, std::variant<int32_t, float, bool>> const&
|
||||
specConstants) {
|
||||
assert_invariant(!specConstants.empty());
|
||||
constexpr std::string_view specConstantPrefix = "FILAMENT_SPEC_CONST_";
|
||||
constexpr size_t specConstantPrefixSize = specConstantPrefix.size();
|
||||
const char* const sourceData = shaderSource.data();
|
||||
std::stringstream processedShaderSourceStr{};
|
||||
size_t pos = 0;
|
||||
while (pos < shaderSource.size()) {
|
||||
const size_t posOfNextSpecConstant = shaderSource.find(specConstantPrefix, pos);
|
||||
if (posOfNextSpecConstant == std::string::npos) {
|
||||
// no more spec constants, so just stream the rest of the source code string
|
||||
processedShaderSourceStr
|
||||
<< std::string_view(sourceData + pos, shaderSource.size() - pos);
|
||||
break;
|
||||
}
|
||||
const size_t posOfId = posOfNextSpecConstant + specConstantPrefixSize;
|
||||
const size_t posAfterId = shaderSource.find('_', posOfId);
|
||||
FILAMENT_CHECK_POSTCONDITION(posAfterId != std::string::npos)
|
||||
<< "malformed " << shaderLabel << ". Found spec constant prefix '"
|
||||
<< specConstantPrefix << "' without an id or '_' after it.";
|
||||
const std::string_view idStr =
|
||||
std::string_view(sourceData + posOfId, posAfterId - posOfId);
|
||||
const size_t posEndOfStatement = shaderSource.find(';', posAfterId);
|
||||
FILAMENT_CHECK_POSTCONDITION(posEndOfStatement != std::string::npos)
|
||||
<< "malformed " << shaderLabel << ". Found spec constant assignment with id "
|
||||
<< idStr << " without a terminating ';' character?";
|
||||
const std::string_view statementSegment =
|
||||
std::string_view(sourceData + posAfterId, posEndOfStatement - posAfterId);
|
||||
size_t posOfEqual = statementSegment.find('=');
|
||||
if (posOfEqual == std::string::npos) {
|
||||
// not an assignment statement, so stream to the end of the statement and continue...
|
||||
processedShaderSourceStr
|
||||
<< std::string_view(sourceData + pos, posEndOfStatement + 1 - pos);
|
||||
pos = posEndOfStatement + 1;
|
||||
continue;
|
||||
}
|
||||
posOfEqual += posAfterId; // position in original source, not just the segment
|
||||
int id = 0;
|
||||
try {
|
||||
id = std::stoi(idStr.data());
|
||||
} catch (const std::invalid_argument& e) {
|
||||
PANIC_POSTCONDITION("Invalid spec constant id '%s' in %s (not a valid integer?): %s",
|
||||
idStr.data(), shaderLabel.data(), e.what());
|
||||
} catch (const std::out_of_range& e) {
|
||||
PANIC_POSTCONDITION(
|
||||
"Invalid spec constant id '%s' in %s (not an integer? out of range?): %s",
|
||||
idStr.data(), shaderLabel.data(), e.what());
|
||||
}
|
||||
const auto newValueItr = specConstants.find(static_cast<uint32_t>(id));
|
||||
if (newValueItr == specConstants.end()) {
|
||||
// not going to override the constant (stream to the end of the statement)...
|
||||
processedShaderSourceStr
|
||||
<< std::string_view(sourceData + pos, posEndOfStatement + 1 - pos);
|
||||
pos = posEndOfStatement + 1;
|
||||
continue;
|
||||
}
|
||||
// need to override the constant...
|
||||
const std::variant<int32_t, float, bool> newValue = newValueItr->second;
|
||||
// stream up to the equal sign...
|
||||
processedShaderSourceStr << std::string_view(sourceData + pos, posOfEqual + 1 - pos);
|
||||
// stream the new value...
|
||||
if (auto* v = std::get_if<int32_t>(&newValue)) {
|
||||
processedShaderSourceStr << " " << *v << "i";
|
||||
} else if (auto* f = std::get_if<float>(&newValue)) {
|
||||
processedShaderSourceStr << " " << *f << "f";
|
||||
} else if (auto* b = std::get_if<bool>(&newValue)) {
|
||||
processedShaderSourceStr << " " << ((*b) ? "true" : "false");
|
||||
}
|
||||
processedShaderSourceStr << ";";
|
||||
// and skip to after the end of the statement in the original source...
|
||||
pos = posEndOfStatement + 1;
|
||||
}
|
||||
return processedShaderSourceStr.str();
|
||||
}
|
||||
|
||||
[[nodiscard]] wgpu::ShaderModule createShaderModule(wgpu::Device& device, const char* programName,
|
||||
std::array<utils::FixedCapacityVector<uint8_t>, Program::SHADER_TYPE_COUNT> const&
|
||||
shaderSource,
|
||||
ShaderStage stage) {
|
||||
[[nodiscard]] wgpu::ShaderModule createShaderModule(wgpu::Device& device, Program& program,
|
||||
ShaderStage stage,
|
||||
std::unordered_map<uint32_t, std::variant<int32_t, float, bool>> const& specConstants) {
|
||||
const char* const programName = program.getName().c_str_safe();
|
||||
std::array<utils::FixedCapacityVector<uint8_t>, Program::SHADER_TYPE_COUNT> const&
|
||||
shaderSource = program.getShadersSource();
|
||||
utils::FixedCapacityVector<uint8_t> const& sourceBytes =
|
||||
shaderSource[static_cast<size_t>(stage)];
|
||||
if (sourceBytes.empty()) {
|
||||
return nullptr;// nothing to compile, the shader was not provided
|
||||
}
|
||||
wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{};
|
||||
wgslDescriptor.code = wgpu::StringView(reinterpret_cast<const char*>(sourceBytes.data()));
|
||||
std::stringstream labelStream;
|
||||
labelStream << programName << " " << toString(stage) << " shader";
|
||||
labelStream << programName << " " << filamentShaderStageToString(stage) << " shader";
|
||||
auto label = labelStream.str();
|
||||
const std::string processedShaderSource =
|
||||
specConstants.empty()
|
||||
? reinterpret_cast<const char*>(sourceBytes.data())
|
||||
: replaceSpecConstants(label, reinterpret_cast<const char*>(sourceBytes.data()),
|
||||
specConstants);
|
||||
wgpu::ShaderModuleWGSLDescriptor wgslDescriptor{};
|
||||
wgslDescriptor.code = wgpu::StringView(processedShaderSource);
|
||||
wgpu::ShaderModuleDescriptor descriptor{
|
||||
.nextInChain = &wgslDescriptor,
|
||||
.label = label.data()
|
||||
@@ -119,85 +197,33 @@ namespace {
|
||||
<< ":\n"
|
||||
<< errorStream.str();
|
||||
}
|
||||
#if FWGPU_ENABLED(FWGPU_DEBUG_VALIDATION)
|
||||
FWGPU_LOGD << descriptor.label << " compiled successfully"
|
||||
<< utils::io::endl;
|
||||
#endif
|
||||
}),
|
||||
UINT16_MAX);
|
||||
SHADER_COMPILATION_TIMEOUT_NANOSECONDS);
|
||||
return module;
|
||||
}
|
||||
|
||||
// This is a 1 to 1 mapping of the ReservedSpecializationConstants enum in EngineEnums.h
|
||||
// The _hack is a workaround until https://issues.chromium.org/issues/42250586 is resolved
|
||||
// This workaround is the same one being used on the generateSpecializationConstant() function
|
||||
wgpu::StringView getSpecConstantStringId(uint32_t id) {
|
||||
switch (id) {
|
||||
case 0:
|
||||
return "0";// BACKEND_FEATURE_LEVEL_hack
|
||||
case 1:
|
||||
return "1";// CONFIG_MAX_INSTANCES_hack
|
||||
case 2:
|
||||
return "2";// ONFIG_STATIC_TEXTURE_TARGET_WORKAROUND_hack
|
||||
case 3:
|
||||
return "3";// CONFIG_SRGB_SWAPCHAIN_EMULATION_hack
|
||||
case 4:
|
||||
return "4";// CONFIG_FROXEL_BUFFER_HEIGHT_hack
|
||||
case 5:
|
||||
return "5";// CONFIG_POWER_VR_SHADER_WORKAROUNDS_hack
|
||||
case 6:
|
||||
return "6";// CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP_hack
|
||||
case 7:
|
||||
return "7";// CONFIG_DEBUG_FROXEL_VISUALIZATION_hack
|
||||
case 8:
|
||||
return "8";// CONFIG_STEREO_EYE_COUNT_hack
|
||||
case 9:
|
||||
return "9";// CONFIG_SH_BANDS_COUNT_hack
|
||||
case 10:
|
||||
return "10";// CONFIG_SHADOW_SAMPLING_METHOD_hack
|
||||
default:
|
||||
PANIC_POSTCONDITION("Unknown/unhandled spec constant key/id: %d", id);
|
||||
void toMap(utils::FixedCapacityVector<Program::SpecializationConstant> const& specConstants,
|
||||
std::unordered_map<uint32_t, std::variant<int32_t, float, bool>>& constantById) {
|
||||
constantById.reserve(specConstants.size());
|
||||
for (auto const& specConstant: specConstants) {
|
||||
constantById.emplace(specConstant.id, specConstant.value);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<wgpu::ConstantEntry> convertConstants(
|
||||
utils::FixedCapacityVector<filament::backend::Program::SpecializationConstant> const&
|
||||
constantsInfo) {
|
||||
std::vector<wgpu::ConstantEntry> constants;
|
||||
constants.reserve(constantsInfo.size());
|
||||
for (filament::backend::Program::SpecializationConstant const& constant: constantsInfo) {
|
||||
// CONFIG_MAX_INSTANCES (1) and CONFIG_FROXEL_BUFFER_HEIGHT (4) will not be present
|
||||
// as constant overrides in the generated WGSL, because WGSL doesn't support specialization
|
||||
// constants as an array length
|
||||
// More information at https://github.com/gpuweb/gpuweb/issues/572#issuecomment-649760005
|
||||
// CONFIG_SRGB_SWAPCHAIN_EMULATION (3) is being skipped all together since it's only
|
||||
// included for the case of mFeatureLevel == FeatureLevel::FEATURE_LEVEL_0, which should
|
||||
// not be possible for WebGPU
|
||||
if (constant.id == 1 || constant.id == 3 || constant.id == 4) {
|
||||
continue;
|
||||
}
|
||||
double value = 0.0;
|
||||
if (auto* v = std::get_if<int32_t>(&constant.value)) {
|
||||
value = static_cast<double>(*v);
|
||||
} else if (auto* f = std::get_if<float>(&constant.value)) {
|
||||
value = static_cast<double>(*f);
|
||||
} else if (auto* b = std::get_if<bool>(&constant.value)) {
|
||||
value = *b ? 0.0 : 1.0;
|
||||
}
|
||||
constants.push_back(
|
||||
wgpu::ConstantEntry{ .key = getSpecConstantStringId(constant.id), .value = value });
|
||||
}
|
||||
return constants;
|
||||
}
|
||||
|
||||
}// namespace
|
||||
|
||||
WGPUProgram::WGPUProgram(wgpu::Device& device, Program& program)
|
||||
: HwProgram(program.getName()),
|
||||
vertexShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
|
||||
ShaderStage::VERTEX)),
|
||||
fragmentShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
|
||||
ShaderStage::FRAGMENT)),
|
||||
computeShaderModule(createShaderModule(device, name.c_str_safe(), program.getShadersSource(),
|
||||
ShaderStage::COMPUTE)),
|
||||
constants(convertConstants(program.getSpecializationConstants())) {}
|
||||
: HwProgram(program.getName()) {
|
||||
std::unordered_map<uint32_t, std::variant<int32_t, float, bool>> specConstants;
|
||||
toMap(program.getSpecializationConstants(), specConstants);
|
||||
vertexShaderModule = createShaderModule(device, program, ShaderStage::VERTEX, specConstants);
|
||||
fragmentShaderModule =
|
||||
createShaderModule(device, program, ShaderStage::FRAGMENT, specConstants);
|
||||
computeShaderModule = createShaderModule(device, program, ShaderStage::COMPUTE, specConstants);
|
||||
}
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
@@ -63,4 +63,13 @@
|
||||
#define FWGPU_LOGI (utils::slog.i)
|
||||
#endif
|
||||
|
||||
constexpr uint64_t REQUEST_ADAPTER_TIMEOUT_NANOSECONDS =
|
||||
/* milliseconds */ 1000u * /* converted to ns */ 1000000u;
|
||||
|
||||
constexpr uint64_t REQUEST_DEVICE_TIMEOUT_NANOSECONDS =
|
||||
/* milliseconds */ 1000u * /* converted to ns */ 1000000u;
|
||||
|
||||
constexpr uint64_t SHADER_COMPILATION_TIMEOUT_NANOSECONDS =
|
||||
/* milliseconds */ 1000u * /* converted to ns */ 1000000u;
|
||||
|
||||
#endif// TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
|
||||
|
||||
@@ -26,193 +26,24 @@
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Handle.h>
|
||||
#include <backend/TargetBufferInfo.h>
|
||||
|
||||
#include <private/backend/BackendUtils.h>
|
||||
#include <math/mat3.h>
|
||||
#include <utils/CString.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <dawn/webgpu_cpp_print.h>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
namespace {
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printInstanceDetails(wgpu::Instance const& instance) {
|
||||
wgpu::SupportedWGSLLanguageFeatures supportedWGSLLanguageFeatures{};
|
||||
if (!instance.GetWGSLLanguageFeatures(&supportedWGSLLanguageFeatures)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU instance supported WGSL language features"
|
||||
<< utils::io::endl;
|
||||
} else {
|
||||
FWGPU_LOGI << "WebGPU instance supported WGSL language features ("
|
||||
<< supportedWGSLLanguageFeatures.featureCount << "):" << utils::io::endl;
|
||||
if (supportedWGSLLanguageFeatures.featureCount > 0 &&
|
||||
supportedWGSLLanguageFeatures.features != nullptr) {
|
||||
std::for_each(supportedWGSLLanguageFeatures.features,
|
||||
supportedWGSLLanguageFeatures.features +
|
||||
supportedWGSLLanguageFeatures.featureCount,
|
||||
[](wgpu::WGSLLanguageFeatureName const featureName) {
|
||||
std::stringstream nameStream{};
|
||||
nameStream << featureName;
|
||||
FWGPU_LOGI << " " << nameStream.str() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printLimit(std::string_view name, const std::variant<uint32_t, uint64_t> value) {
|
||||
FWGPU_LOGI << " " << name.data() << ": ";
|
||||
bool undefined = true;
|
||||
if (std::holds_alternative<uint32_t>(value)) {
|
||||
if (std::get<uint32_t>(value) != WGPU_LIMIT_U32_UNDEFINED) {
|
||||
undefined = false;
|
||||
FWGPU_LOGI << std::get<uint32_t>(value);
|
||||
}
|
||||
} else if (std::holds_alternative<uint64_t>(value)) {
|
||||
if (std::get<uint64_t>(value) != WGPU_LIMIT_U64_UNDEFINED) {
|
||||
undefined = false;
|
||||
FWGPU_LOGI << std::get<uint64_t>(value);
|
||||
}
|
||||
}
|
||||
if (undefined) {
|
||||
FWGPU_LOGI << "UNDEFINED";
|
||||
}
|
||||
FWGPU_LOGI << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printLimits(wgpu::Limits const& limits) {
|
||||
printLimit("maxTextureDimension1D", limits.maxTextureDimension1D);
|
||||
printLimit("maxTextureDimension2D", limits.maxTextureDimension2D);
|
||||
printLimit("maxTextureDimension3D", limits.maxTextureDimension3D);
|
||||
printLimit("maxTextureArrayLayers", limits.maxTextureArrayLayers);
|
||||
printLimit("maxBindGroups", limits.maxBindGroups);
|
||||
printLimit("maxBindGroupsPlusVertexBuffers", limits.maxBindGroupsPlusVertexBuffers);
|
||||
printLimit("maxBindingsPerBindGroup", limits.maxBindingsPerBindGroup);
|
||||
printLimit("maxDynamicUniformBuffersPerPipelineLayout",
|
||||
limits.maxDynamicUniformBuffersPerPipelineLayout);
|
||||
printLimit("maxDynamicStorageBuffersPerPipelineLayout",
|
||||
limits.maxDynamicStorageBuffersPerPipelineLayout);
|
||||
printLimit("maxSampledTexturesPerShaderStage", limits.maxSampledTexturesPerShaderStage);
|
||||
printLimit("maxSamplersPerShaderStage", limits.maxSamplersPerShaderStage);
|
||||
printLimit("maxStorageBuffersPerShaderStage", limits.maxStorageBuffersPerShaderStage);
|
||||
printLimit("maxStorageTexturesPerShaderStage", limits.maxStorageTexturesPerShaderStage);
|
||||
printLimit("maxUniformBuffersPerShaderStage", limits.maxUniformBuffersPerShaderStage);
|
||||
printLimit("maxUniformBufferBindingSize", limits.maxUniformBufferBindingSize);
|
||||
printLimit("maxStorageBufferBindingSize", limits.maxStorageBufferBindingSize);
|
||||
printLimit("minUniformBufferOffsetAlignment", limits.minUniformBufferOffsetAlignment);
|
||||
printLimit("minStorageBufferOffsetAlignment", limits.minStorageBufferOffsetAlignment);
|
||||
printLimit("maxVertexBuffers", limits.maxVertexBuffers);
|
||||
printLimit("maxBufferSize", limits.maxBufferSize);
|
||||
printLimit("maxVertexAttributes", limits.maxVertexAttributes);
|
||||
printLimit("maxVertexBufferArrayStride", limits.maxVertexBufferArrayStride);
|
||||
printLimit("maxInterStageShaderVariables", limits.maxInterStageShaderVariables);
|
||||
printLimit("maxColorAttachments", limits.maxColorAttachments);
|
||||
printLimit("maxColorAttachmentBytesPerSample", limits.maxColorAttachmentBytesPerSample);
|
||||
printLimit("maxComputeWorkgroupStorageSize", limits.maxComputeWorkgroupStorageSize);
|
||||
printLimit("maxComputeInvocationsPerWorkgroup", limits.maxComputeInvocationsPerWorkgroup);
|
||||
printLimit("maxComputeWorkgroupSizeX", limits.maxComputeWorkgroupSizeX);
|
||||
printLimit("maxComputeWorkgroupSizeY", limits.maxComputeWorkgroupSizeY);
|
||||
printLimit("maxComputeWorkgroupSizeZ", limits.maxComputeWorkgroupSizeZ);
|
||||
printLimit("maxComputeWorkgroupsPerDimension", limits.maxComputeWorkgroupsPerDimension);
|
||||
printLimit("maxStorageBuffersInVertexStage", limits.maxStorageBuffersInVertexStage);
|
||||
printLimit("maxStorageTexturesInVertexStage", limits.maxStorageTexturesInVertexStage);
|
||||
printLimit("maxStorageBuffersInFragmentStage", limits.maxStorageBuffersInFragmentStage);
|
||||
printLimit("maxStorageTexturesInFragmentStage", limits.maxStorageTexturesInFragmentStage);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printAdapterDetails(wgpu::Adapter const& adapter) {
|
||||
wgpu::DawnAdapterPropertiesPowerPreference powerPreferenceProperties{};
|
||||
wgpu::AdapterInfo adapterInfo{};
|
||||
adapterInfo.nextInChain = &powerPreferenceProperties;
|
||||
if (!adapter.GetInfo(&adapterInfo)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU adapter info" << utils::io::endl;
|
||||
} else {
|
||||
std::stringstream backendTypeStream{};
|
||||
backendTypeStream << adapterInfo.backendType;
|
||||
std::stringstream adapterTypeStream{};
|
||||
adapterTypeStream << adapterInfo.adapterType;
|
||||
std::stringstream powerPreferenceStream{};
|
||||
powerPreferenceStream << powerPreferenceProperties.powerPreference;
|
||||
FWGPU_LOGI << "WebGPU adapter info:" << utils::io::endl;
|
||||
FWGPU_LOGI << " vendor: " << adapterInfo.vendor.data << utils::io::endl;
|
||||
FWGPU_LOGI << " architecture: " << adapterInfo.architecture.data << utils::io::endl;
|
||||
FWGPU_LOGI << " device: " << adapterInfo.device.data << utils::io::endl;
|
||||
FWGPU_LOGI << " description: " << adapterInfo.description.data << utils::io::endl;
|
||||
FWGPU_LOGI << " backend type: " << backendTypeStream.str().data() << utils::io::endl;
|
||||
FWGPU_LOGI << " adapter type: " << adapterTypeStream.str().data() << utils::io::endl;
|
||||
FWGPU_LOGI << " device ID: " << adapterInfo.deviceID << utils::io::endl;
|
||||
FWGPU_LOGI << " vendor ID: " << adapterInfo.vendorID << utils::io::endl;
|
||||
FWGPU_LOGI << " subgroup min size: " << adapterInfo.subgroupMinSize << utils::io::endl;
|
||||
FWGPU_LOGI << " subgroup max size: " << adapterInfo.subgroupMaxSize << utils::io::endl;
|
||||
FWGPU_LOGI << " power preference: " << powerPreferenceStream.str() << utils::io::endl;
|
||||
}
|
||||
wgpu::SupportedFeatures supportedFeatures{};
|
||||
adapter.GetFeatures(&supportedFeatures);
|
||||
FWGPU_LOGI << "WebGPU adapter supported features (" << supportedFeatures.featureCount
|
||||
<< "):" << utils::io::endl;
|
||||
if (supportedFeatures.featureCount > 0 && supportedFeatures.features != nullptr) {
|
||||
std::for_each(supportedFeatures.features,
|
||||
supportedFeatures.features + supportedFeatures.featureCount,
|
||||
[](wgpu::FeatureName const name) {
|
||||
std::stringstream nameStream{};
|
||||
nameStream << name;
|
||||
FWGPU_LOGI << " " << nameStream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
wgpu::Limits supportedLimits{};
|
||||
if (!adapter.GetLimits(&supportedLimits)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU adapter supported limits" << utils::io::endl;
|
||||
} else {
|
||||
FWGPU_LOGI << "WebGPU adapter supported limits:" << utils::io::endl;
|
||||
printLimits(supportedLimits);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printDeviceDetails(wgpu::Device const& device) {
|
||||
wgpu::SupportedFeatures supportedFeatures{};
|
||||
device.GetFeatures(&supportedFeatures);
|
||||
FWGPU_LOGI << "WebGPU device supported features (" << supportedFeatures.featureCount
|
||||
<< "):" << utils::io::endl;
|
||||
if (supportedFeatures.featureCount > 0 && supportedFeatures.features != nullptr) {
|
||||
std::for_each(supportedFeatures.features,
|
||||
supportedFeatures.features + supportedFeatures.featureCount,
|
||||
[](wgpu::FeatureName const name) {
|
||||
std::stringstream nameStream{};
|
||||
nameStream << name;
|
||||
FWGPU_LOGI << " " << nameStream.str().data() << utils::io::endl;
|
||||
});
|
||||
}
|
||||
wgpu::Limits supportedLimits{};
|
||||
if (!device.GetLimits(&supportedLimits)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU supported device limits" << utils::io::endl;
|
||||
} else {
|
||||
FWGPU_LOGI << "WebGPU device supported limits:" << utils::io::endl;
|
||||
printLimits(supportedLimits);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}// namespace
|
||||
|
||||
Driver* WebGPUDriver::create(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept {
|
||||
constexpr size_t defaultSize = FILAMENT_WEBGPU_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
|
||||
Platform::DriverConfig validConfig {driverConfig};
|
||||
@@ -220,23 +51,16 @@ Driver* WebGPUDriver::create(WebGPUPlatform& platform, const Platform::DriverCon
|
||||
return new WebGPUDriver(platform, validConfig);
|
||||
}
|
||||
|
||||
WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform, const Platform::DriverConfig& driverConfig) noexcept
|
||||
WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform,
|
||||
const Platform::DriverConfig& driverConfig) noexcept
|
||||
: mPlatform(platform),
|
||||
mHandleAllocator("Handles",
|
||||
driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck,
|
||||
driverConfig.disableHeapHandleTags) {
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printInstanceDetails(mPlatform.getInstance());
|
||||
#endif
|
||||
mHandleAllocator("Handles", driverConfig.handleArenaSize,
|
||||
driverConfig.disableHandleUseAfterFreeCheck, driverConfig.disableHeapHandleTags) {
|
||||
mAdapter = mPlatform.requestAdapter(nullptr);
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printAdapterDetails(mAdapter);
|
||||
#endif
|
||||
mDevice = mPlatform.requestDevice(mAdapter);
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printDeviceDetails(mDevice);
|
||||
#endif
|
||||
wgpu::Limits supportedLimits{};
|
||||
mDevice.GetLimits(&supportedLimits);
|
||||
mMinUniformBufferOffsetAlignment = supportedLimits.minUniformBufferOffsetAlignment;
|
||||
mQueue = mDevice.GetQueue();
|
||||
}
|
||||
|
||||
@@ -350,6 +174,20 @@ void WebGPUDriver::destroyProgram(Handle<HwProgram> ph) {
|
||||
}
|
||||
|
||||
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) {
|
||||
@@ -372,6 +210,19 @@ void WebGPUDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh)
|
||||
}
|
||||
|
||||
void WebGPUDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) {
|
||||
auto* bindGroup = handleCast<WebGPUDescriptorSet>(tqh);
|
||||
assert_invariant(bindGroup);
|
||||
if (bindGroup->getBindGroup() != nullptr) {
|
||||
for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) {
|
||||
if (mCurrentDescriptorSets[i].bindGroup != nullptr &&
|
||||
bindGroup->getBindGroup().Get() == mCurrentDescriptorSets[i].bindGroup.Get()) {
|
||||
// Clear this from our current entries
|
||||
mCurrentDescriptorSets[i].bindGroup = nullptr;
|
||||
mCurrentDescriptorSets[i].offsets.clear();
|
||||
mCurrentDescriptorSets[i].offsetCount = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tqh) {
|
||||
destructHandle<WebGPUDescriptorSet>(tqh);
|
||||
}
|
||||
@@ -468,11 +319,10 @@ void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
mSwapChain = constructHandle<WebGPUSwapChain>(sch, std::move(surface), surfaceSize, mAdapter,
|
||||
mDevice, flags);
|
||||
assert_invariant(mSwapChain);
|
||||
WebGPUDescriptorSet::initializeDummyResourcesIfNotAlready(mDevice,
|
||||
mSwapChain->getColorFormat());
|
||||
FWGPU_LOGW << "WebGPU support is still essentially a no-op at this point in development (only "
|
||||
"background components have been instantiated/selected, such as surface/screen, "
|
||||
"graphics device/GPU, etc.), thus nothing is being drawn to the screen."
|
||||
|
||||
FWGPU_LOGW << "WebGPU support is highly experimental, in development, and tested for only a "
|
||||
"small set of simple samples (e.g. hellotriangle and texturedquad), thus issues "
|
||||
"are likely to be encountered at this stage."
|
||||
<< utils::io::endl;
|
||||
#if !FWGPU_ENABLED(FWGPU_PRINT_SYSTEM) && !defined(NDEBUG)
|
||||
FWGPU_LOGI << "If the FILAMENT_BACKEND_DEBUG_FLAG variable were set with the " << utils::io::hex
|
||||
@@ -577,7 +427,21 @@ void WebGPUDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
|
||||
|
||||
void WebGPUDriver::createRenderTargetR(Handle<HwRenderTarget> rth, TargetBufferFlags targets,
|
||||
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) {}
|
||||
|
||||
@@ -660,7 +524,7 @@ bool WebGPUDriver::isAutoDepthResolveSupported() {
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isSRGBSwapChainSupported() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WebGPUDriver::isProtectedContentSupported() {
|
||||
@@ -721,17 +585,20 @@ size_t WebGPUDriver::getMaxArrayTextureLayers() {
|
||||
|
||||
void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
|
||||
uint32_t byteOffset) {
|
||||
updateGPUBuffer(handleCast<WGPUIndexBuffer>(ibh), std::move(p), byteOffset);
|
||||
handleCast<WGPUIndexBuffer>(ibh)->updateGPUBuffer(p, byteOffset, mQueue);
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void WebGPUDriver::updateBufferObject(Handle<HwBufferObject> ibh, BufferDescriptor&& p,
|
||||
uint32_t byteOffset) {
|
||||
updateGPUBuffer(handleCast<WGPUBufferObject>(ibh), std::move(p), byteOffset);
|
||||
handleCast<WGPUBufferObject>(ibh)->updateGPUBuffer(p, byteOffset, mQueue);
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void WebGPUDriver::updateBufferObjectUnsynchronized(Handle<HwBufferObject> ibh,
|
||||
BufferDescriptor&& p, uint32_t byteOffset) {
|
||||
updateGPUBuffer(handleCast<WGPUBufferObject>(ibh), std::move(p), byteOffset);
|
||||
handleCast<WGPUBufferObject>(ibh)->updateGPUBuffer(p, byteOffset, mQueue);
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void WebGPUDriver::resetBufferObject(Handle<HwBufferObject> boh) {
|
||||
@@ -743,15 +610,76 @@ void WebGPUDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t in
|
||||
auto* vertexBuffer = handleCast<WGPUVertexBuffer>(vbh);
|
||||
auto* bufferObject = handleCast<WGPUBufferObject>(boh);
|
||||
assert_invariant(index < vertexBuffer->buffers.size());
|
||||
assert_invariant(bufferObject->buffer.GetUsage() & wgpu::BufferUsage::Vertex);
|
||||
vertexBuffer->buffers[index] = bufferObject->buffer;
|
||||
assert_invariant(bufferObject->getBuffer().GetUsage() & wgpu::BufferUsage::Vertex);
|
||||
vertexBuffer->buffers[index] = bufferObject->getBuffer();
|
||||
}
|
||||
|
||||
void WebGPUDriver::update3DImage(Handle<HwTexture> th,
|
||||
uint32_t level, uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
|
||||
uint32_t width, uint32_t height, uint32_t depth,
|
||||
PixelBufferDescriptor&& data) {
|
||||
scheduleDestroy(std::move(data));
|
||||
void WebGPUDriver::update3DImage(Handle<HwTexture> th, uint32_t level, uint32_t xoffset,
|
||||
uint32_t yoffset, uint32_t zoffset, uint32_t width, uint32_t height, uint32_t depth,
|
||||
PixelBufferDescriptor&& p) {
|
||||
PixelBufferDescriptor* data = &p;
|
||||
PixelBufferDescriptor reshapedData;
|
||||
if (reshape(p, reshapedData)) {
|
||||
data = &reshapedData;
|
||||
}
|
||||
auto texture = handleCast<WGPUTexture>(th);
|
||||
|
||||
// TODO: Writing to a depth texture is illegal and errors. I'm not sure why Filament is trying
|
||||
// to do so, but early returning is working?
|
||||
if(texture->getAspect() == wgpu::TextureAspect::DepthOnly){
|
||||
scheduleDestroy(std::move(p));
|
||||
return;
|
||||
}
|
||||
size_t blockWidth = texture->getBlockWidth();
|
||||
size_t blockHeight = texture->getBlockHeight();
|
||||
// WebGPU specification requires that for compressed textures, the x and y offsets
|
||||
// must be a multiple of the compressed texture format's block width and height.
|
||||
// See: https://www.w3.org/TR/webgpu/#abstract-opdef-validating-gputexelcopytextureinfo
|
||||
if (blockWidth > 1 || blockHeight > 1) {
|
||||
FILAMENT_CHECK_PRECONDITION(xoffset % blockWidth == 0)
|
||||
<< "xoffset must be aligned to blockwidth, but offset is " << blockWidth
|
||||
<< "and offset is " << xoffset;
|
||||
FILAMENT_CHECK_PRECONDITION(yoffset % blockHeight == 0)
|
||||
<< "yoffset must be aligned to blockHeight, but offset is " << blockHeight
|
||||
<< "and offset is " << yoffset;
|
||||
}
|
||||
|
||||
auto copyInfo = wgpu::TexelCopyTextureInfo{ .texture = texture->getTexture(),
|
||||
.mipLevel = level,
|
||||
.origin = { .x = xoffset, .y = yoffset, .z = zoffset },
|
||||
.aspect = texture->getAspect() };
|
||||
uint32_t bytesPerRow = static_cast<uint32_t>(
|
||||
PixelBufferDescriptor::computePixelSize(data->format, data->type) * width);
|
||||
auto extent = wgpu::Extent3D{ .width = width, .height = height, .depthOrArrayLayers = depth };
|
||||
|
||||
const uint8_t* dataBuff = static_cast<const uint8_t*>(data->buffer);
|
||||
size_t dataSize = data->size;
|
||||
std::unique_ptr<uint8_t[]> paddedBuffer;
|
||||
|
||||
if (bytesPerRow % 256 != 0) {
|
||||
uint32_t padding = 256 - (bytesPerRow % 256);
|
||||
uint32_t paddedBytesPerRow = bytesPerRow + padding;
|
||||
|
||||
size_t paddedBufferSize = static_cast<size_t>(paddedBytesPerRow) * height * depth;
|
||||
paddedBuffer = std::make_unique<uint8_t[]>(paddedBufferSize);
|
||||
uint8_t* dest = paddedBuffer.get();
|
||||
|
||||
for (uint32_t z = 0; z < depth; ++z) {
|
||||
for (uint32_t y = 0; y < height; ++y) {
|
||||
std::memcpy(dest, dataBuff, bytesPerRow);
|
||||
dest += paddedBytesPerRow;
|
||||
dataBuff += bytesPerRow;
|
||||
}
|
||||
}
|
||||
dataBuff = paddedBuffer.get();
|
||||
dataSize = paddedBufferSize;
|
||||
bytesPerRow = paddedBytesPerRow;
|
||||
}
|
||||
|
||||
auto layout = wgpu::TexelCopyBufferLayout{ .bytesPerRow = bytesPerRow, .rowsPerImage = height };
|
||||
|
||||
mQueue.WriteTexture(©Info, dataBuff, dataSize, &layout, &extent);
|
||||
scheduleDestroy(std::move(p));
|
||||
}
|
||||
|
||||
void WebGPUDriver::setupExternalImage(void* image) {
|
||||
@@ -778,56 +706,81 @@ void WebGPUDriver::compilePrograms(CompilerPriorityQueue priority,
|
||||
|
||||
void WebGPUDriver::beginRenderPass(Handle<HwRenderTarget> rth, RenderPassParams const& params) {
|
||||
assert_invariant(mCommandEncoder);
|
||||
|
||||
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 renderPassDescriptor2;
|
||||
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(renderPassDescriptor2, mTextureView, params);
|
||||
renderPassDescriptor2.depthStencilAttachment = &depthStencilAttachment;
|
||||
// TODO: Remove this code once WebGPU pipeline is implemented
|
||||
static float red = 1.0f;
|
||||
if (red - 0.01 > 0) {
|
||||
red -= 0.01;
|
||||
|
||||
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 {
|
||||
red = 1.0f;
|
||||
// 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
assert_invariant(mTextureView);
|
||||
wgpu::RenderPassColorAttachment renderPassColorAttachment = {
|
||||
.view = mTextureView,
|
||||
// TODO: remove this code once WebGPU Pipeline is implemented with render targets, pipeline and buffers.
|
||||
.depthSlice = wgpu::kDepthSliceUndefined,
|
||||
.loadOp = wgpu::LoadOp::Clear,
|
||||
.storeOp = wgpu::StoreOp::Store,
|
||||
.clearValue = wgpu::Color{red, 0 , 0 , 1},
|
||||
};
|
||||
wgpu::RenderPassDescriptor renderPassDescriptor{};
|
||||
renderTarget->setUpRenderPassAttachments(renderPassDescriptor,
|
||||
params,
|
||||
defaultColorView,
|
||||
defaultDepthStencilView,
|
||||
customColorViews.data(),
|
||||
customColorViewCount,
|
||||
customDepthView,
|
||||
customStencilView,
|
||||
customDepthFormat,
|
||||
customStencilFormat);
|
||||
|
||||
wgpu::RenderPassDescriptor renderPassDescriptor = {
|
||||
.colorAttachmentCount = 1,
|
||||
.colorAttachments = &renderPassColorAttachment,
|
||||
.depthStencilAttachment = nullptr,
|
||||
.timestampWrites = nullptr,
|
||||
};
|
||||
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor);
|
||||
|
||||
mRenderPassEncoder = mCommandEncoder.BeginRenderPass(&renderPassDescriptor2);
|
||||
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 */) {
|
||||
@@ -920,7 +873,7 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
// VulkanPipelineCache to handle this, may be missing nuance
|
||||
static auto pipleineStateHasher = utils::hash::MurmurHashFn<filament::backend::PipelineState>();
|
||||
auto hash = pipleineStateHasher(pipelineState);
|
||||
if(mPipelineMap.find(hash) != mPipelineMap.end()){
|
||||
if (mPipelineMap.find(hash) != mPipelineMap.end()) {
|
||||
mRenderPassEncoder.SetPipeline(mPipelineMap[hash]);
|
||||
return;
|
||||
}
|
||||
@@ -956,42 +909,95 @@ void WebGPUDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
<< layoutDescriptor.label;
|
||||
auto const* vertexBufferInfo = handleCast<WGPUVertexBufferInfo>(pipelineState.vertexBufferInfo);
|
||||
assert_invariant(vertexBufferInfo);
|
||||
const wgpu::RenderPipeline pipeline = createWebGPURenderPipeline(mDevice, *program,
|
||||
*vertexBufferInfo, layout, pipelineState.rasterState, pipelineState.stencilState,
|
||||
pipelineState.polygonOffset, pipelineState.primitiveType, mSwapChain->getColorFormat(),
|
||||
mSwapChain->getDepthFormat());
|
||||
|
||||
std::vector<wgpu::TextureFormat> pipelineColorFormats;
|
||||
wgpu::TextureFormat pipelineDepthFormat = wgpu::TextureFormat::Undefined;
|
||||
uint8_t pipelineSamples = 1;
|
||||
|
||||
if (mCurrentRenderTarget->isDefaultRenderTarget()) {
|
||||
pipelineColorFormats.push_back(mSwapChain->getColorFormat());
|
||||
pipelineDepthFormat = mSwapChain->getDepthFormat();
|
||||
pipelineSamples =
|
||||
mCurrentRenderTarget->getSamples();// Default RT should have samples (usually 1)
|
||||
} else {
|
||||
const auto& mrtColorAttachments = mCurrentRenderTarget->getColorAttachmentInfos();
|
||||
for (size_t i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; ++i) {
|
||||
if (mrtColorAttachments[i].handle) {
|
||||
const auto* colorTexture = handleCast<WGPUTexture>(mrtColorAttachments[i].handle);
|
||||
if (colorTexture) {
|
||||
pipelineColorFormats.push_back(colorTexture->getTexture().GetFormat());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto& depthInfo = mCurrentRenderTarget->getDepthAttachmentInfo();
|
||||
const auto& stencilInfo = mCurrentRenderTarget->getStencilAttachmentInfo();
|
||||
if (depthInfo.handle) {
|
||||
FILAMENT_CHECK_POSTCONDITION(!stencilInfo.handle)
|
||||
<< "depth and stencil attachments cannot both be provided for WebGPU";
|
||||
const auto* dfTexture = handleCast<WGPUTexture>(depthInfo.handle);
|
||||
if (dfTexture) pipelineDepthFormat = dfTexture->getTexture().GetFormat();
|
||||
} else {
|
||||
if (stencilInfo.handle) {
|
||||
const auto* sfTexture = handleCast<WGPUTexture>(stencilInfo.handle);
|
||||
// Assuming combined depth/stencil format if only stencil is present
|
||||
if (sfTexture) {
|
||||
pipelineDepthFormat = sfTexture->getTexture().GetFormat();
|
||||
}
|
||||
}
|
||||
}
|
||||
pipelineSamples = mCurrentRenderTarget->getSamples();
|
||||
}
|
||||
|
||||
if (program->fragmentShaderModule != nullptr) {
|
||||
FILAMENT_CHECK_POSTCONDITION(!pipelineColorFormats.empty())
|
||||
<< "Render pipeline with fragment shader must have at least one color target "
|
||||
"format.";
|
||||
}
|
||||
wgpu::RenderPipeline pipeline = createWebGPURenderPipeline(mDevice, *program, *vertexBufferInfo,
|
||||
layout, pipelineState.rasterState, pipelineState.stencilState,
|
||||
pipelineState.polygonOffset, pipelineState.primitiveType, pipelineColorFormats,
|
||||
pipelineDepthFormat, pipelineSamples);
|
||||
assert_invariant(pipeline);
|
||||
mPipelineMap[hash] = pipeline;
|
||||
mRenderPassEncoder.SetPipeline(pipeline);
|
||||
}
|
||||
|
||||
void WebGPUDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
auto* renderPrimitive = handleCast<WGPURenderPrimitive>(rph);
|
||||
|
||||
// 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);
|
||||
assert_invariant(
|
||||
vbi->getVertexBufferLayoutSize() == renderPrimitive->vertexBuffer->buffers.size());
|
||||
for (uint32_t i = 0; i < vbi->getVertexBufferLayoutSize(); i++) {
|
||||
mRenderPassEncoder.SetVertexBuffer(i, renderPrimitive->vertexBuffer->buffers[i]);
|
||||
for (const auto& webGPUSlotBindings: vbi->getWebGPUSlotBindingInfos()) {
|
||||
mRenderPassEncoder.SetVertexBuffer(webGPUSlotBindings.slot,
|
||||
renderPrimitive->vertexBuffer->buffers[webGPUSlotBindings.sourceBuffer],
|
||||
webGPUSlotBindings.bufferOffset);
|
||||
}
|
||||
|
||||
mRenderPassEncoder.SetIndexBuffer(renderPrimitive->indexBuffer->buffer,
|
||||
mRenderPassEncoder.SetIndexBuffer(renderPrimitive->indexBuffer->getBuffer(),
|
||||
renderPrimitive->indexBuffer->indexFormat);
|
||||
}
|
||||
|
||||
void WebGPUDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
// Calling DrawIndexed with "firstInstance = 0" results in a NON spinning triangle
|
||||
// mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 0);
|
||||
// Calling DrawIndexed with "firstInstance = 1" results in a spinning triangle
|
||||
mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 1);
|
||||
// Calling Draw with "firstInstance = 0" results in a NON spinning triangle
|
||||
// Calling Draw with "firstInstance = 1" results in a spinning triangle
|
||||
// mRenderPassEncoder.Draw(indexCount, instanceCount, 0, 1);
|
||||
// We defer actually binding until we actually draw
|
||||
for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) {
|
||||
auto& binding = mCurrentDescriptorSets[i];
|
||||
if (binding.bindGroup) {
|
||||
mRenderPassEncoder.SetBindGroup(i, binding.bindGroup, binding.offsetCount,
|
||||
binding.offsets.data());
|
||||
}
|
||||
}
|
||||
|
||||
mRenderPassEncoder.DrawIndexed(indexCount, instanceCount, indexOffset, 0, 0);
|
||||
}
|
||||
|
||||
void WebGPUDriver::draw(PipelineState, Handle<HwRenderPrimitive>, uint32_t indexOffset,
|
||||
uint32_t indexCount, uint32_t instanceCount) {
|
||||
void WebGPUDriver::draw(PipelineState pipelineState,
|
||||
Handle<HwRenderPrimitive> renderPrimitiveHandle, uint32_t indexOffset, uint32_t indexCount,
|
||||
uint32_t instanceCount) {
|
||||
WGPURenderPrimitive const* const renderPrimitive =
|
||||
handleCast<WGPURenderPrimitive>(renderPrimitiveHandle);
|
||||
pipelineState.primitiveType = renderPrimitive->type;
|
||||
pipelineState.vertexBufferInfo = renderPrimitive->vertexBuffer->vbih;
|
||||
bindPipeline(pipelineState);
|
||||
bindRenderPrimitive(renderPrimitiveHandle);
|
||||
draw2(indexOffset, indexCount, instanceCount);
|
||||
}
|
||||
|
||||
@@ -1018,8 +1024,11 @@ void WebGPUDriver::updateDescriptorSetBuffer(Handle<HwDescriptorSet> dsh,
|
||||
auto buffer = handleCast<WGPUBufferObject>(boh);
|
||||
if (!bindGroup->getIsLocked()) {
|
||||
// TODO making assumptions that size and offset mean the same thing here.
|
||||
FILAMENT_CHECK_PRECONDITION(offset % mMinUniformBufferOffsetAlignment == 0)
|
||||
<< "Binding offset must be multiple of " << mMinUniformBufferOffsetAlignment
|
||||
<< "But requested offset is " << offset;
|
||||
wgpu::BindGroupEntry entry{ .binding = static_cast<uint32_t>(binding * 2),
|
||||
.buffer = buffer->buffer,
|
||||
.buffer = buffer->getBuffer(),
|
||||
.offset = offset,
|
||||
.size = size };
|
||||
bindGroup->addEntry(entry.binding, std::move(entry));
|
||||
@@ -1037,7 +1046,7 @@ void WebGPUDriver::updateDescriptorSetTexture(Handle<HwDescriptorSet> dsh,
|
||||
auto sampler = makeSampler(params);
|
||||
// TODO making assumptions that size and offset mean the same thing here.
|
||||
wgpu::BindGroupEntry tEntry{ .binding = static_cast<uint32_t>(binding * 2),
|
||||
.textureView = texture->getTexView() };
|
||||
.textureView = texture->getTextureView() };
|
||||
bindGroup->addEntry(tEntry.binding, std::move(tEntry));
|
||||
|
||||
wgpu::BindGroupEntry sEntry{ .binding = static_cast<uint32_t>(binding * 2 + 1),
|
||||
@@ -1048,118 +1057,111 @@ void WebGPUDriver::updateDescriptorSetTexture(Handle<HwDescriptorSet> dsh,
|
||||
|
||||
void WebGPUDriver::bindDescriptorSet(Handle<HwDescriptorSet> dsh,
|
||||
backend::descriptor_set_t setIndex, backend::DescriptorSetOffsetArray&& offsets) {
|
||||
assert_invariant(setIndex < MAX_DESCRIPTOR_SET_COUNT);
|
||||
const auto bindGroup = handleCast<WebGPUDescriptorSet>(dsh);
|
||||
const auto wbg = bindGroup->lockAndReturn(mDevice);
|
||||
assert_invariant(mRenderPassEncoder);
|
||||
// TODO is this how we should be getting the dynamic offsets?
|
||||
// should we add offsets for unused entries or is the input already have them?
|
||||
// this implementation assumes unused entries are not provided, and adds dummy values.
|
||||
// The count also includes unused entities, as not doing so produces errors
|
||||
const size_t dynamicOffsetCount = bindGroup->countEntitiesWithDynamicOffsets();
|
||||
uint32_t const* const dynamicOffsetsWithUnused = bindGroup->setDynamicOffsets(offsets.data());
|
||||
mRenderPassEncoder.SetBindGroup(setIndex, wbg, dynamicOffsetCount, dynamicOffsetsWithUnused);
|
||||
|
||||
mCurrentDescriptorSets[setIndex] = { .bindGroup = wbg,
|
||||
.offsetCount = bindGroup->countEntitiesWithDynamicOffsets(),
|
||||
.offsets = std::move(offsets) };
|
||||
}
|
||||
|
||||
void WebGPUDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
|
||||
}
|
||||
|
||||
wgpu::Sampler WebGPUDriver::makeSampler(SamplerParams const& params) {
|
||||
wgpu::SamplerDescriptor desc;
|
||||
wgpu::SamplerDescriptor desc{};
|
||||
|
||||
desc.label = "TODO";
|
||||
desc.addressModeU = fWrapModeToWAddressMode(params.wrapS);
|
||||
desc.addressModeV = fWrapModeToWAddressMode(params.wrapR);
|
||||
desc.addressModeW = fWrapModeToWAddressMode(params.wrapT);
|
||||
switch (params.filterMag) {
|
||||
case SamplerMagFilter::NEAREST: {
|
||||
desc.magFilter = wgpu::FilterMode::Nearest;
|
||||
break;
|
||||
if (params.compareMode == SamplerCompareMode::COMPARE_TO_TEXTURE) {
|
||||
switch (params.filterMag) {
|
||||
case SamplerMagFilter::NEAREST: {
|
||||
desc.magFilter = wgpu::FilterMode::Nearest;
|
||||
break;
|
||||
}
|
||||
case SamplerMagFilter::LINEAR: {
|
||||
desc.magFilter = wgpu::FilterMode::Linear;
|
||||
break;
|
||||
}
|
||||
}
|
||||
case SamplerMagFilter::LINEAR: {
|
||||
desc.magFilter = wgpu::FilterMode::Linear;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (params.filterMin) {
|
||||
case SamplerMinFilter::NEAREST: {
|
||||
desc.minFilter = wgpu::FilterMode::Nearest;
|
||||
// Metal Driver uses an explicit not-mipmapped value webgpu lacks. Nearest should
|
||||
// suffice
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::LINEAR: {
|
||||
desc.minFilter = wgpu::FilterMode::Linear;
|
||||
// Metal Driver uses an explicit not-mipmapped value webgpu lacks. Nearest should
|
||||
// suffice
|
||||
switch (params.filterMin) {
|
||||
case SamplerMinFilter::NEAREST: {
|
||||
desc.minFilter = wgpu::FilterMode::Nearest;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Undefined;
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::LINEAR: {
|
||||
desc.minFilter = wgpu::FilterMode::Linear;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Undefined;
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST: {
|
||||
desc.minFilter = wgpu::FilterMode::Nearest;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST: {
|
||||
desc.minFilter = wgpu::FilterMode::Linear;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
|
||||
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST: {
|
||||
desc.minFilter = wgpu::FilterMode::Nearest;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST: {
|
||||
desc.minFilter = wgpu::FilterMode::Linear;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Nearest;
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR: {
|
||||
desc.minFilter = wgpu::FilterMode::Nearest;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
|
||||
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR: {
|
||||
desc.minFilter = wgpu::FilterMode::Linear;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
|
||||
break;
|
||||
}
|
||||
}
|
||||
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR: {
|
||||
desc.minFilter = wgpu::FilterMode::Nearest;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
|
||||
|
||||
break;
|
||||
}
|
||||
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR: {
|
||||
desc.minFilter = wgpu::FilterMode::Linear;
|
||||
desc.mipmapFilter = wgpu::MipmapFilterMode::Linear;
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (params.compareFunc) {
|
||||
case SamplerCompareFunc::LE: {
|
||||
desc.compare = wgpu::CompareFunction::LessEqual;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::GE: {
|
||||
desc.compare = wgpu::CompareFunction::GreaterEqual;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::L: {
|
||||
desc.compare = wgpu::CompareFunction::Less;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::G: {
|
||||
desc.compare = wgpu::CompareFunction::Greater;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::E: {
|
||||
desc.compare = wgpu::CompareFunction::Equal;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::NE: {
|
||||
desc.compare = wgpu::CompareFunction::NotEqual;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::A: {
|
||||
desc.compare = wgpu::CompareFunction::Always;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::N: {
|
||||
desc.compare = wgpu::CompareFunction::Never;
|
||||
break;
|
||||
switch (params.compareFunc) {
|
||||
case SamplerCompareFunc::LE: {
|
||||
desc.compare = wgpu::CompareFunction::LessEqual;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::GE: {
|
||||
desc.compare = wgpu::CompareFunction::GreaterEqual;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::L: {
|
||||
desc.compare = wgpu::CompareFunction::Less;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::G: {
|
||||
desc.compare = wgpu::CompareFunction::Greater;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::E: {
|
||||
desc.compare = wgpu::CompareFunction::Equal;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::NE: {
|
||||
desc.compare = wgpu::CompareFunction::NotEqual;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::A: {
|
||||
desc.compare = wgpu::CompareFunction::Always;
|
||||
break;
|
||||
}
|
||||
case SamplerCompareFunc::N: {
|
||||
desc.compare = wgpu::CompareFunction::Never;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
desc.maxAnisotropy = 1u << params.anisotropyLog2;
|
||||
|
||||
// Unused: WGPU lodMinClamp/lodMaxClamp unnecessary
|
||||
|
||||
// Unused: Filament's compareMode, WGPU lodMinClamp/lodMaxClamp
|
||||
|
||||
//TODO Once we can properly map to descriptorsetlayout use the sampler.
|
||||
return mDevice.CreateSampler(/*&desc*/);
|
||||
return mDevice.CreateSampler(&desc);
|
||||
}
|
||||
wgpu::AddressMode WebGPUDriver::fWrapModeToWAddressMode(const SamplerWrapMode& fWrapMode) {
|
||||
switch (fWrapMode) {
|
||||
@@ -1179,5 +1181,4 @@ wgpu::AddressMode WebGPUDriver::fWrapModeToWAddressMode(const SamplerWrapMode& f
|
||||
return wgpu::AddressMode::Undefined;
|
||||
}
|
||||
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -58,32 +58,13 @@ private:
|
||||
[[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final;
|
||||
[[nodiscard]] wgpu::Sampler makeSampler(SamplerParams const& params);
|
||||
[[nodiscard]] static wgpu::AddressMode fWrapModeToWAddressMode(const filament::backend::SamplerWrapMode& fUsage);
|
||||
template<typename GPUBufferObject>
|
||||
void updateGPUBuffer(GPUBufferObject* gpuBufferObject, BufferDescriptor&& bufferDescriptor,
|
||||
uint32_t byteOffset) {
|
||||
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.buffer)
|
||||
<< "copyIntoBuffer called with a null buffer";
|
||||
FILAMENT_CHECK_PRECONDITION(
|
||||
bufferDescriptor.size + byteOffset <= gpuBufferObject->buffer.GetSize())
|
||||
<< "Attempting to copy " << bufferDescriptor.size << " bytes into a buffer of size "
|
||||
<< gpuBufferObject->buffer.GetSize() << " at offset " << byteOffset;
|
||||
|
||||
// TODO: All buffer objects are created with CopyDst usage.
|
||||
// This may have some performance implications. That should be investigated later.
|
||||
assert_invariant(gpuBufferObject->buffer.GetUsage() & wgpu::BufferUsage::CopyDst);
|
||||
|
||||
// WriteBuffer is an async call. But cpu buffer data is already written to the staging
|
||||
// buffer on return from the WriteBuffer.
|
||||
mQueue.WriteBuffer(gpuBufferObject->buffer, byteOffset, bufferDescriptor.buffer,
|
||||
bufferDescriptor.size);
|
||||
scheduleDestroy(std::move(bufferDescriptor));
|
||||
}
|
||||
|
||||
// the platform (e.g. OS) specific aspects of the WebGPU backend are strictly only
|
||||
// handled in the WebGPUPlatform
|
||||
WebGPUPlatform& mPlatform;
|
||||
wgpu::Adapter mAdapter = nullptr;
|
||||
wgpu::Device mDevice = nullptr;
|
||||
uint32_t mMinUniformBufferOffsetAlignment;
|
||||
wgpu::Queue mQueue = nullptr;
|
||||
void* mNativeWindow = nullptr;
|
||||
WebGPUSwapChain* mSwapChain = nullptr;
|
||||
@@ -93,8 +74,16 @@ private:
|
||||
wgpu::RenderPassEncoder mRenderPassEncoder = nullptr;
|
||||
wgpu::CommandBuffer mCommandBuffer = nullptr;
|
||||
WGPURenderTarget* mDefaultRenderTarget = nullptr;
|
||||
WGPURenderTarget* mCurrentRenderTarget = nullptr;
|
||||
|
||||
tsl::robin_map<uint32_t, wgpu::RenderPipeline> mPipelineMap;
|
||||
|
||||
struct DescriptorSetBindingInfo{
|
||||
wgpu::BindGroup bindGroup;
|
||||
size_t offsetCount;
|
||||
backend::DescriptorSetOffsetArray offsets;
|
||||
};
|
||||
std::array<DescriptorSetBindingInfo,MAX_DESCRIPTOR_SET_COUNT> mCurrentDescriptorSets;
|
||||
/*
|
||||
* Driver interface
|
||||
*/
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
#include <utils/BitmaskEnum.h>
|
||||
#include <utils/Panic.h>
|
||||
|
||||
#include <private/backend/BackendUtils.h>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <algorithm>
|
||||
@@ -41,15 +41,6 @@ constexpr wgpu::BufferUsage getBufferObjectUsage(
|
||||
}
|
||||
}
|
||||
|
||||
wgpu::Buffer createBuffer(wgpu::Device const& device, wgpu::BufferUsage usage, uint32_t size,
|
||||
char const* label) {
|
||||
wgpu::BufferDescriptor descriptor{ .label = label,
|
||||
.usage = usage,
|
||||
.size = size,
|
||||
.mappedAtCreation = false };
|
||||
return device.CreateBuffer(&descriptor);
|
||||
}
|
||||
|
||||
wgpu::VertexFormat getVertexFormat(filament::backend::ElementType type, bool normalized, bool integer) {
|
||||
using ElementType = filament::backend::ElementType;
|
||||
using VertexFormat = wgpu::VertexFormat;
|
||||
@@ -77,7 +68,7 @@ wgpu::VertexFormat getVertexFormat(filament::backend::ElementType type, bool nor
|
||||
case ElementType::BYTE4: return VertexFormat::Snorm8x4;
|
||||
case ElementType::UBYTE4: return VertexFormat::Unorm8x4;
|
||||
case ElementType::SHORT4: return VertexFormat::Snorm16x4;
|
||||
case ElementType::USHORT4: return VertexFormat::Unorm8x4;
|
||||
case ElementType::USHORT4: return VertexFormat::Unorm16x4;
|
||||
default:
|
||||
FILAMENT_CHECK_POSTCONDITION(false) << "Normalized format does not exist.";
|
||||
return VertexFormat::Float32x3;
|
||||
@@ -135,7 +126,7 @@ wgpu::StringView getUserTextureLabel(filament::backend::SamplerType target) {
|
||||
case SamplerType::SAMPLER_3D:
|
||||
return "a_3D_user_texture";
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
return "a_cube_mape_array_user_texture";
|
||||
return "a_cube_map_array_user_texture";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,7 +145,7 @@ wgpu::StringView getUserTextureViewLabel(filament::backend::SamplerType target)
|
||||
case SamplerType::SAMPLER_3D:
|
||||
return "a_3D_user_texture_view";
|
||||
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
|
||||
return "a_cube_mape_array_user_texture_view";
|
||||
return "a_cube_map_array_user_texture_view";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,51 +153,142 @@ wgpu::StringView getUserTextureViewLabel(filament::backend::SamplerType target)
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
void WGPUBufferBase::createBuffer(const wgpu::Device& device, wgpu::BufferUsage usage,
|
||||
uint32_t size, const char* label) {
|
||||
// Write size must be divisible by 4. If the whole buffer is written to as is common, so must
|
||||
// the buffer size.
|
||||
size += (4 - (size % 4)) % 4;
|
||||
wgpu::BufferDescriptor descriptor{ .label = label,
|
||||
.usage = usage,
|
||||
.size = size,
|
||||
.mappedAtCreation = false };
|
||||
buffer = device.CreateBuffer(&descriptor);
|
||||
}
|
||||
|
||||
void WGPUBufferBase::updateGPUBuffer(BufferDescriptor& bufferDescriptor, uint32_t byteOffset,
|
||||
wgpu::Queue queue) {
|
||||
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.buffer)
|
||||
<< "copyIntoBuffer called with a null buffer";
|
||||
FILAMENT_CHECK_PRECONDITION(bufferDescriptor.size + byteOffset <= buffer.GetSize())
|
||||
<< "Attempting to copy " << bufferDescriptor.size << " bytes into a buffer of size "
|
||||
<< buffer.GetSize() << " at offset " << byteOffset;
|
||||
FILAMENT_CHECK_PRECONDITION(byteOffset % 4 == 0)
|
||||
<< "Byte offset must be a multiple of 4 but is " << byteOffset;
|
||||
|
||||
// TODO: All buffer objects are created with CopyDst usage.
|
||||
// This may have some performance implications. That should be investigated later.
|
||||
assert_invariant(buffer.GetUsage() & wgpu::BufferUsage::CopyDst);
|
||||
|
||||
size_t remainder = bufferDescriptor.size % 4;
|
||||
|
||||
// WriteBuffer is an async call. But cpu buffer data is already written to the staging
|
||||
// buffer on return from the WriteBuffer.
|
||||
auto legalSize = bufferDescriptor.size - remainder;
|
||||
queue.WriteBuffer(buffer, byteOffset, bufferDescriptor.buffer, legalSize);
|
||||
if (remainder != 0) {
|
||||
const uint8_t* remainderStart =
|
||||
static_cast<const uint8_t*>(bufferDescriptor.buffer) + legalSize;
|
||||
memcpy(mRemainderChunk.data(), remainderStart, remainder);
|
||||
// Pad the remainder with zeros to ensure deterministic behavior, though GPU shouldn't
|
||||
// access this
|
||||
std::memset(mRemainderChunk.data() + remainder, 0, 4 - remainder);
|
||||
|
||||
queue.WriteBuffer(buffer, byteOffset + legalSize, &mRemainderChunk, 4);
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr uint32_t DUMMY_WEBGPU_SLOT = 0;
|
||||
|
||||
WGPUVertexBufferInfo::WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
|
||||
AttributeArray const& attributes)
|
||||
: HwVertexBufferInfo(bufferCount, attributeCount),
|
||||
mVertexBufferLayout(bufferCount),
|
||||
mAttributes(bufferCount) {
|
||||
assert_invariant(attributeCount > 0);
|
||||
assert_invariant(bufferCount > 0);
|
||||
for (uint32_t attribIndex = 0; attribIndex < attributes.size(); attribIndex++) {
|
||||
Attribute const& attrib = attributes[attribIndex];
|
||||
// Ignore the attributes which are not bind to vertex buffers.
|
||||
if (attrib.buffer == Attribute::BUFFER_UNUSED) {
|
||||
// TODO: max limits may not be supported by webgpu driver. This should be addressed in the
|
||||
// hardening part.
|
||||
mVertexBufferLayouts(MAX_VERTEX_BUFFER_COUNT),
|
||||
mVertexAttributes(MAX_VERTEX_BUFFER_COUNT) {
|
||||
|
||||
// It starts from 1 because slot 0 is now reserved for the dummy.
|
||||
uint32_t currentWebGPUSlotIndex = 1;
|
||||
// A reasonable dummy stride (e.g., for vec4)
|
||||
const uint32_t DUMMY_STRIDE = 16;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
assert_invariant(attrib.buffer < bufferCount);
|
||||
bool const isInteger = attrib.flags & Attribute::FLAG_INTEGER_TARGET;
|
||||
bool const isNormalized = attrib.flags & Attribute::FLAG_NORMALIZED;
|
||||
wgpu::VertexFormat vertexFormat = getVertexFormat(attrib.type, isNormalized, isInteger);
|
||||
auto it = std::find_if(mWebGPUSlotBindingInfos.begin(), mWebGPUSlotBindingInfos.end(),
|
||||
[&](const auto& info) {
|
||||
return info.sourceBuffer == attribute.buffer && info.stride == attribute.stride;
|
||||
});
|
||||
|
||||
// Attributes are sequential per buffer
|
||||
mAttributes[attrib.buffer].push_back({
|
||||
.format = vertexFormat,
|
||||
.offset = attrib.offset,
|
||||
.shaderLocation = attribIndex,
|
||||
});
|
||||
|
||||
mVertexBufferLayout[attrib.buffer].stepMode = wgpu::VertexStepMode::Vertex;
|
||||
if (mVertexBufferLayout[attrib.buffer].arrayStride == 0) {
|
||||
mVertexBufferLayout[attrib.buffer].arrayStride = attrib.stride;
|
||||
uint32_t assignedSlot;
|
||||
if (it != mWebGPUSlotBindingInfos.end()) {
|
||||
assignedSlot = it->slot;
|
||||
} 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++) {
|
||||
mVertexBufferLayout[bufferIndex].attributeCount = mAttributes[bufferIndex].size();
|
||||
mVertexBufferLayout[bufferIndex].attributes = mAttributes[bufferIndex].data();
|
||||
mVertexBufferLayouts.resize(currentWebGPUSlotIndex);
|
||||
|
||||
for (const auto& info: mWebGPUSlotBindingInfos) {
|
||||
mVertexBufferLayouts[info.slot].attributes = mVertexAttributes[info.slot].data();
|
||||
}
|
||||
}
|
||||
|
||||
WGPUIndexBuffer::WGPUIndexBuffer(wgpu::Device const& device, uint8_t elementSize,
|
||||
uint32_t indexCount)
|
||||
: buffer(createBuffer(device, wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Index,
|
||||
elementSize * indexCount, "index_buffer")),
|
||||
indexFormat(elementSize == 2 ? wgpu::IndexFormat::Uint16 : wgpu::IndexFormat::Uint32) {}
|
||||
: indexFormat(elementSize == 2 ? wgpu::IndexFormat::Uint16 : wgpu::IndexFormat::Uint32) {
|
||||
createBuffer(device, wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::Index,
|
||||
elementSize * indexCount, "index_buffer");
|
||||
}
|
||||
|
||||
|
||||
WGPUVertexBuffer::WGPUVertexBuffer(wgpu::Device const& device, uint32_t vertexCount,
|
||||
@@ -217,10 +299,10 @@ WGPUVertexBuffer::WGPUVertexBuffer(wgpu::Device const& device, uint32_t vertexCo
|
||||
|
||||
WGPUBufferObject::WGPUBufferObject(wgpu::Device const& device, BufferObjectBinding bindingType,
|
||||
uint32_t byteCount)
|
||||
: HwBufferObject(byteCount),
|
||||
buffer(createBuffer(device, wgpu::BufferUsage::CopyDst | getBufferObjectUsage(bindingType),
|
||||
byteCount, "buffer_object")),
|
||||
bufferObjectBinding(bindingType) {}
|
||||
: HwBufferObject(byteCount) {
|
||||
createBuffer(device, wgpu::BufferUsage::CopyDst | getBufferObjectUsage(bindingType), byteCount,
|
||||
"buffer_object");
|
||||
}
|
||||
|
||||
wgpu::ShaderStage WebGPUDescriptorSetLayout::filamentStageToWGPUStage(ShaderStageFlags fFlags) {
|
||||
wgpu::ShaderStage retStages = wgpu::ShaderStage::None;
|
||||
@@ -255,8 +337,7 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
|
||||
|
||||
unsigned int samplerCount =
|
||||
std::count_if(layout.bindings.begin(), layout.bindings.end(), [](auto& fEntry) {
|
||||
return fEntry.type == DescriptorType::SAMPLER ||
|
||||
fEntry.type == DescriptorType::SAMPLER_EXTERNAL;
|
||||
return DescriptorSetLayoutBinding::isSampler(fEntry.type);
|
||||
});
|
||||
|
||||
|
||||
@@ -272,26 +353,44 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
|
||||
entryInfo.binding = wEntry.binding;
|
||||
|
||||
switch (fEntry.type) {
|
||||
case DescriptorType::SAMPLER_EXTERNAL:
|
||||
case DescriptorType::SAMPLER: {
|
||||
case DescriptorType::SAMPLER_2D_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_INT:
|
||||
case DescriptorType::SAMPLER_2D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_DEPTH:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_2D_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_DEPTH:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_UINT:
|
||||
case DescriptorType::SAMPLER_CUBE_ARRAY_DEPTH:
|
||||
case DescriptorType::SAMPLER_3D_FLOAT:
|
||||
case DescriptorType::SAMPLER_3D_INT:
|
||||
case DescriptorType::SAMPLER_3D_UINT:
|
||||
case DescriptorType::SAMPLER_2D_MS_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_UINT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_FLOAT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_INT:
|
||||
case DescriptorType::SAMPLER_2D_MS_ARRAY_UINT: {
|
||||
auto& samplerEntry = wEntries.emplace_back();
|
||||
auto& samplerEntryInfo = mBindGroupEntries.emplace_back();
|
||||
samplerEntry.binding = fEntry.binding * 2 + 1;
|
||||
samplerEntryInfo.binding = samplerEntry.binding;
|
||||
samplerEntryInfo.type = WebGPUDescriptorSetLayout::BindGroupEntryType::SAMPLER;
|
||||
samplerEntry.visibility = wEntry.visibility;
|
||||
// We are simply hoping that undefined and defaults suffices here.
|
||||
samplerEntry.sampler.type = wgpu::SamplerBindingType::NonFiltering; // Example default
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::Float; // Example default
|
||||
// TODO: FIX! THIS IS HACK FOR HELLO-TRIANGLE!
|
||||
if (baseLabel.find("Skybox") != std::string::npos ||
|
||||
(baseLabel == "Filament Default Material_perView" && wEntry.binding == 22)) {
|
||||
wEntry.texture.viewDimension = wgpu::TextureViewDimension::Cube;
|
||||
wEntry.texture.multisampled = isMultiSampledTypeDescriptor(fEntry.type);
|
||||
// TODO: Set once we have the filtering values
|
||||
if (isDepthDescriptor(fEntry.type)) {
|
||||
samplerEntry.sampler.type = wgpu::SamplerBindingType::Comparison;
|
||||
} else {
|
||||
wEntry.texture.viewDimension =
|
||||
wgpu::TextureViewDimension::e2D;// Example default
|
||||
samplerEntry.sampler.type =
|
||||
wgpu::SamplerBindingType::NonFiltering;
|
||||
}
|
||||
entryInfo.type = WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW;
|
||||
break;
|
||||
}
|
||||
case DescriptorType::UNIFORM_BUFFER: {
|
||||
@@ -302,7 +401,6 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
|
||||
// TODO: Ideally we fill minBindingSize
|
||||
break;
|
||||
}
|
||||
|
||||
case DescriptorType::INPUT_ATTACHMENT: {
|
||||
PANIC_POSTCONDITION("Input Attachment is not supported");
|
||||
break;
|
||||
@@ -311,6 +409,48 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
|
||||
PANIC_POSTCONDITION("Shader storage is not supported");
|
||||
break;
|
||||
}
|
||||
case DescriptorType::SAMPLER_EXTERNAL: {
|
||||
PANIC_POSTCONDITION("External Sampler is not supported");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isDepthDescriptor(fEntry.type))
|
||||
{
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::Depth;
|
||||
}
|
||||
else if (isFloatDescriptor(fEntry.type))
|
||||
{
|
||||
// TODO: Set once we have the filtering values
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::UnfilterableFloat;
|
||||
}
|
||||
else if (isIntDescriptor(fEntry.type))
|
||||
{
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::Sint;
|
||||
}
|
||||
else if (isUnsignedIntDescriptor(fEntry.type))
|
||||
{
|
||||
wEntry.texture.sampleType = wgpu::TextureSampleType::Uint;
|
||||
}
|
||||
|
||||
if (is3dTypeDescriptor(fEntry.type))
|
||||
{
|
||||
wEntry.texture.viewDimension = wgpu::TextureViewDimension::e3D;
|
||||
}
|
||||
else if (is2dTypeDescriptor(fEntry.type))
|
||||
{
|
||||
wEntry.texture.viewDimension = wgpu::TextureViewDimension::e2D;
|
||||
}
|
||||
else if (is2dArrayTypeDescriptor(fEntry.type))
|
||||
{
|
||||
wEntry.texture.viewDimension = wgpu::TextureViewDimension::e2DArray;
|
||||
}
|
||||
else if (isCubeTypeDescriptor(fEntry.type))
|
||||
{
|
||||
wEntry.texture.viewDimension = wgpu::TextureViewDimension::Cube;
|
||||
}
|
||||
else if (isCubeArrayTypeDescriptor(fEntry.type))
|
||||
{
|
||||
wEntry.texture.viewDimension = wgpu::TextureViewDimension::CubeArray;
|
||||
}
|
||||
// fEntry.count is unused currently
|
||||
}
|
||||
@@ -325,114 +465,26 @@ WebGPUDescriptorSetLayout::WebGPUDescriptorSetLayout(DescriptorSetLayout const&
|
||||
|
||||
WebGPUDescriptorSetLayout::~WebGPUDescriptorSetLayout() {}
|
||||
|
||||
wgpu::Buffer WebGPUDescriptorSet::sDummyUniformBuffer = nullptr;
|
||||
wgpu::Texture WebGPUDescriptorSet::sDummyTexture = nullptr;
|
||||
wgpu::TextureView WebGPUDescriptorSet::sDummyTextureView = nullptr;
|
||||
wgpu::Sampler WebGPUDescriptorSet::sDummySampler = nullptr;
|
||||
|
||||
void WebGPUDescriptorSet::initializeDummyResourcesIfNotAlready(wgpu::Device const& device,
|
||||
wgpu::TextureFormat aColorFormat) {
|
||||
if (!sDummyUniformBuffer) {
|
||||
wgpu::BufferDescriptor bufferDescriptor{
|
||||
.label = "dummy_uniform_not_to_be_used",
|
||||
.usage = wgpu::BufferUsage::Uniform,
|
||||
.size = 4
|
||||
};
|
||||
sDummyUniformBuffer = device.CreateBuffer(&bufferDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyUniformBuffer)
|
||||
<< "Failed to create dummy uniform buffer?";
|
||||
}
|
||||
if (!sDummyTexture || !sDummyTextureView) {
|
||||
wgpu::TextureDescriptor textureDescriptor{
|
||||
.label = "dummy_texture_not_to_be_used",
|
||||
.usage = wgpu::TextureUsage::TextureBinding,
|
||||
.dimension = wgpu::TextureDimension::e2D,
|
||||
.size = wgpu::Extent3D{ .width = 4, .height = 4, .depthOrArrayLayers = 1 },
|
||||
.format = aColorFormat,
|
||||
};
|
||||
if (!sDummyTexture) {
|
||||
sDummyTexture = device.CreateTexture(&textureDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyUniformBuffer) << "Failed to create dummy texture?";
|
||||
}
|
||||
if (!sDummyTextureView) {
|
||||
wgpu::TextureViewDescriptor textureViewDescriptor{
|
||||
.label = "dummy_texture_view_not_to_be_used"
|
||||
};
|
||||
sDummyTextureView = sDummyTexture.CreateView(&textureViewDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyUniformBuffer)
|
||||
<< "Failed to create dummy texture view?";
|
||||
}
|
||||
}
|
||||
if (!sDummySampler) {
|
||||
wgpu::SamplerDescriptor samplerDescriptor{
|
||||
.label = "dummy_sampler_not_to_be_used"
|
||||
};
|
||||
sDummySampler = device.CreateSampler(&samplerDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(sDummyUniformBuffer) << "Failed to create dummy sampler?";
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<wgpu::BindGroupEntry> WebGPUDescriptorSet::createDummyEntriesSortedByBinding(
|
||||
std::vector<filament::backend::WebGPUDescriptorSetLayout::BindGroupEntryInfo> const&
|
||||
bindGroupEntries) {
|
||||
assert_invariant(WebGPUDescriptorSet::sDummyUniformBuffer &&
|
||||
"Dummy uniform buffer must have been created before "
|
||||
"creating dummy bind group entries.");
|
||||
assert_invariant(
|
||||
WebGPUDescriptorSet::sDummyTexture &&
|
||||
"Dummy texture must have been created before creating dummy bind group entries.");
|
||||
assert_invariant(
|
||||
WebGPUDescriptorSet::sDummyTextureView &&
|
||||
"Dummy texture view must have been created before creating dummy bind group entries.");
|
||||
assert_invariant(
|
||||
WebGPUDescriptorSet::sDummySampler &&
|
||||
"Dummy sampler must have been created before creating dummy bind group entries.");
|
||||
using filament::backend::WebGPUDescriptorSetLayout;
|
||||
std::vector<wgpu::BindGroupEntry> entries;
|
||||
entries.reserve(bindGroupEntries.size());
|
||||
for (auto const& entryInfo: bindGroupEntries) {
|
||||
auto& entry = entries.emplace_back();
|
||||
entry.binding = entryInfo.binding;
|
||||
switch (entryInfo.type) {
|
||||
case WebGPUDescriptorSetLayout::BindGroupEntryType::UNIFORM_BUFFER:
|
||||
entry.buffer = WebGPUDescriptorSet::sDummyUniformBuffer;
|
||||
break;
|
||||
case WebGPUDescriptorSetLayout::BindGroupEntryType::TEXTURE_VIEW:
|
||||
entry.textureView = WebGPUDescriptorSet::sDummyTextureView;
|
||||
break;
|
||||
case WebGPUDescriptorSetLayout::BindGroupEntryType::SAMPLER:
|
||||
entry.sampler = WebGPUDescriptorSet::sDummySampler;
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::sort(entries.begin(), entries.end(),
|
||||
[](wgpu::BindGroupEntry const& a, wgpu::BindGroupEntry const& b) {
|
||||
return a.binding < b.binding;
|
||||
});
|
||||
return entries;
|
||||
}
|
||||
|
||||
WebGPUDescriptorSet::WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,
|
||||
std::vector<WebGPUDescriptorSetLayout::BindGroupEntryInfo> const& bindGroupEntries)
|
||||
: mLayout(layout),
|
||||
mEntriesSortedByBinding(createDummyEntriesSortedByBinding(bindGroupEntries)) {
|
||||
mEntriesWithDynamicOffsetsCount(std::count_if(bindGroupEntries.begin(),
|
||||
bindGroupEntries.end(), [](auto const& entry) { return entry.hasDynamicOffset; })) {
|
||||
|
||||
mEntries.resize(bindGroupEntries.size());
|
||||
for (size_t i = 0; i < bindGroupEntries.size(); ++i) {
|
||||
mEntries[i].binding = bindGroupEntries[i].binding;
|
||||
}
|
||||
// Establish the size of entries based on the layout. This should be reliable and efficient.
|
||||
assert_invariant(INVALID_INDEX > mEntryIndexByBinding.size());
|
||||
for (size_t i = 0; i < mEntryIndexByBinding.size(); i++) {
|
||||
mEntryIndexByBinding[i] = INVALID_INDEX;
|
||||
}
|
||||
for (size_t index = 0; index < mEntriesSortedByBinding.size(); index++) {
|
||||
wgpu::BindGroupEntry const& entry = mEntriesSortedByBinding[index];
|
||||
for (size_t index = 0; index < mEntries.size(); index++) {
|
||||
wgpu::BindGroupEntry const& entry = mEntries[index];
|
||||
assert_invariant(entry.binding < mEntryIndexByBinding.size());
|
||||
mEntryIndexByBinding[entry.binding] = static_cast<uint8_t>(index);
|
||||
}
|
||||
for (auto const& entry : bindGroupEntries) {
|
||||
if (entry.hasDynamicOffset) {
|
||||
assert_invariant(entry.binding < mEntriesByBindingWithDynamicOffsets.size());
|
||||
mEntriesByBindingWithDynamicOffsets[entry.binding] = true;
|
||||
}
|
||||
}
|
||||
mDynamicOffsets.reserve(mEntriesSortedByBinding.size());
|
||||
}
|
||||
|
||||
WebGPUDescriptorSet::~WebGPUDescriptorSet() {
|
||||
@@ -447,15 +499,15 @@ wgpu::BindGroup WebGPUDescriptorSet::lockAndReturn(const wgpu::Device& device) {
|
||||
// TODO label? Should we just copy layout label?
|
||||
wgpu::BindGroupDescriptor desc{
|
||||
.layout = mLayout,
|
||||
.entryCount = mEntriesSortedByBinding.size(),
|
||||
.entries = mEntriesSortedByBinding.data()
|
||||
.entryCount = mEntries.size(),
|
||||
.entries = mEntries.data()
|
||||
};
|
||||
mBindGroup = device.CreateBindGroup(&desc);
|
||||
FILAMENT_CHECK_POSTCONDITION(mBindGroup) << "Failed to create bind group?";
|
||||
// once we have created the bind group itself we should no longer need any other state
|
||||
mLayout = nullptr;
|
||||
mEntriesSortedByBinding.clear();
|
||||
mEntriesSortedByBinding.shrink_to_fit();
|
||||
mEntries.clear();
|
||||
mEntries.shrink_to_fit();
|
||||
return mBindGroup;
|
||||
}
|
||||
|
||||
@@ -473,34 +525,14 @@ void WebGPUDescriptorSet::addEntry(unsigned int index, wgpu::BindGroupEntry&& en
|
||||
<< index;
|
||||
uint8_t entryIndex = mEntryIndexByBinding[index];
|
||||
FILAMENT_CHECK_POSTCONDITION(
|
||||
entryIndex != INVALID_INDEX && entryIndex < mEntriesSortedByBinding.size())
|
||||
entryIndex != INVALID_INDEX && entryIndex < mEntries.size())
|
||||
<< "Invalid binding " << index;
|
||||
entry.binding = index;
|
||||
mEntriesSortedByBinding[entryIndex] = std::move(entry);
|
||||
mEntriesByBindingAdded[index] = true;
|
||||
}
|
||||
|
||||
uint32_t const* WebGPUDescriptorSet::setDynamicOffsets(uint32_t const* offsets) {
|
||||
// mDynamicOffsets already reserves enough memory for the number of entries in the set
|
||||
mDynamicOffsets.clear();
|
||||
// this implementation copies the offsets to mDynamicOffsets, but also adds values for
|
||||
// unused entries TODO: is this necessary?
|
||||
size_t inputIndex = 0;
|
||||
size_t outputIndex = 0;
|
||||
for (auto const& entry : mEntriesSortedByBinding) {
|
||||
if (mEntriesByBindingWithDynamicOffsets[entry.binding]) {
|
||||
if (mEntriesByBindingAdded[entry.binding]) {
|
||||
mDynamicOffsets[outputIndex++] = offsets[inputIndex++];
|
||||
} else {
|
||||
mDynamicOffsets[outputIndex++] = 0; // dummy offset, as it was never added
|
||||
}
|
||||
}
|
||||
}
|
||||
return mDynamicOffsets.data();
|
||||
mEntries[entryIndex] = std::move(entry);
|
||||
}
|
||||
|
||||
size_t WebGPUDescriptorSet::countEntitiesWithDynamicOffsets() const {
|
||||
return mEntriesByBindingWithDynamicOffsets.count();
|
||||
return mEntriesWithDynamicOffsetsCount;
|
||||
}
|
||||
|
||||
WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
|
||||
@@ -517,6 +549,8 @@ WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat forma
|
||||
mUsage = fToWGPUTextureUsage(usage);
|
||||
mFormat = fToWGPUTextureFormat(format);
|
||||
mAspect = fToWGPUTextureViewAspect(usage, format);
|
||||
mBlockWidth = filament::backend::getBlockWidth(format);
|
||||
mBlockHeight = filament::backend::getBlockHeight(format);
|
||||
wgpu::TextureDescriptor textureDescriptor{
|
||||
.label = getUserTextureLabel(target),
|
||||
.usage = mUsage,
|
||||
@@ -558,12 +592,16 @@ WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat forma
|
||||
FILAMENT_CHECK_POSTCONDITION(mTexture)
|
||||
<< "Failed to create texture for " << textureDescriptor.label;
|
||||
// Second, the texture view aspect
|
||||
mTexView = makeTextureView(0, levels, target);
|
||||
mTexureView = makeTextureView(0, levels, target);
|
||||
}
|
||||
|
||||
WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept {
|
||||
mTexture = src->mTexture;
|
||||
mTexView = makeTextureView(baseLevel, levelCount, target);
|
||||
mAspect = src->mAspect;
|
||||
mBlockWidth = src->mBlockWidth;
|
||||
mBlockHeight = src->mBlockHeight;
|
||||
|
||||
mTexureView = makeTextureView(baseLevel, levelCount, target);
|
||||
}
|
||||
|
||||
wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(TextureUsage const& fUsage) {
|
||||
@@ -932,58 +970,153 @@ wgpu::TextureView WGPUTexture::makeTextureView(const uint8_t& baseLevel, const u
|
||||
return textureView;
|
||||
}
|
||||
|
||||
WGPURenderTarget::Attachment WGPURenderTarget::getDrawColorAttachment(size_t index) {
|
||||
assert_invariant( index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
|
||||
auto result = color[index];
|
||||
if (index == 0 && defaultRenderTarget) {
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
WGPURenderTarget::WGPURenderTarget(uint32_t width, uint32_t height, uint8_t samples,
|
||||
const MRT& colorAttachmentsMRT,
|
||||
const Attachment& depthAttachmentInfo,
|
||||
const Attachment& stencilAttachmentInfo)
|
||||
: HwRenderTarget(width, height),
|
||||
defaultRenderTarget(false),
|
||||
samples(samples),
|
||||
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,
|
||||
TargetBufferFlags buffer) {
|
||||
auto clearFlags = params.flags.clear;
|
||||
auto discardStartFlags = params.flags.discardStart;
|
||||
if (any(clearFlags & buffer)) {
|
||||
return wgpu::LoadOp::Clear;
|
||||
} else if (any(discardStartFlags & buffer)) {
|
||||
TargetBufferFlags bufferToOperateOn) {
|
||||
if (any(params.flags.clear & bufferToOperateOn)) {
|
||||
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;
|
||||
}
|
||||
|
||||
wgpu::StoreOp WGPURenderTarget::getStoreOperation(RenderPassParams const& params,
|
||||
TargetBufferFlags buffer) {
|
||||
const auto discardEndFlags = params.flags.discardEnd;
|
||||
if (any(discardEndFlags & buffer)) {
|
||||
TargetBufferFlags bufferToOperateOn) {
|
||||
if (any(params.flags.discardEnd & bufferToOperateOn)) {
|
||||
return wgpu::StoreOp::Discard;
|
||||
}
|
||||
return wgpu::StoreOp::Store;
|
||||
}
|
||||
|
||||
void WGPURenderTarget::setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor,
|
||||
wgpu::TextureView const& textureView, RenderPassParams const& params) {
|
||||
// auto discardFlags = params.flags.discardEnd;
|
||||
// (void) discardFlags;
|
||||
// std::vector<wgpu::RenderPassColorAttachment> colorAttachments;
|
||||
colorAttachments.clear();
|
||||
for (size_t i = 0; i < 1/*MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT*/; i++) {
|
||||
// auto attachment = getDrawColorAttachment(i);
|
||||
// if (attachment) {
|
||||
wgpu::RenderPassColorAttachment colorAttachment;
|
||||
colorAttachment.view = textureView;
|
||||
colorAttachment.loadOp = getLoadOperation(params, getTargetBufferFlagsAt(i));
|
||||
colorAttachment.storeOp = getStoreOperation(params, getTargetBufferFlagsAt(i));
|
||||
colorAttachment.clearValue = { params.clearColor.r, params.clearColor.g, params.clearColor.b, params.clearColor.a };
|
||||
colorAttachments.emplace_back(colorAttachment);
|
||||
// }
|
||||
RenderPassParams const& params, wgpu::TextureView const& defaultColorTextureView,
|
||||
wgpu::TextureView const& defaultDepthStencilTextureView,
|
||||
wgpu::TextureView const* customColorTextureViews, uint32_t customColorTextureViewCount,
|
||||
wgpu::TextureView const& customDepthTextureView,
|
||||
wgpu::TextureView const& customStencilTextureView, wgpu::TextureFormat customDepthFormat,
|
||||
wgpu::TextureFormat customStencilFormat) {
|
||||
mColorAttachmentDescriptors.clear();
|
||||
mHasDepthStencilAttachment = false;
|
||||
|
||||
if (defaultRenderTarget) {
|
||||
assert_invariant(defaultColorTextureView);
|
||||
mColorAttachmentDescriptors.push_back({ .view = defaultColorTextureView,
|
||||
.resolveTarget = nullptr,
|
||||
.loadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::COLOR0),
|
||||
.storeOp = WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::COLOR0),
|
||||
.clearValue = { params.clearColor.r, params.clearColor.g, params.clearColor.b,
|
||||
params.clearColor.a } });
|
||||
|
||||
if (defaultDepthStencilTextureView) {
|
||||
mDepthStencilAttachmentDescriptor = {
|
||||
.view = defaultDepthStencilTextureView,
|
||||
.depthLoadOp = WGPURenderTarget::getLoadOperation(params, TargetBufferFlags::DEPTH),
|
||||
.depthStoreOp =
|
||||
WGPURenderTarget::getStoreOperation(params, TargetBufferFlags::DEPTH),
|
||||
.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.depthStencilAttachment = nullptr;
|
||||
descriptor.timestampWrites = nullptr;
|
||||
|
||||
descriptor.colorAttachmentCount = mColorAttachmentDescriptors.size();
|
||||
descriptor.colorAttachments = mColorAttachmentDescriptors.data();
|
||||
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
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
@@ -44,38 +43,47 @@ public:
|
||||
std::vector<wgpu::ConstantEntry> constants;
|
||||
};
|
||||
|
||||
struct WGPUBufferObject;
|
||||
|
||||
// VertexBufferInfo contains layout info for Vertex Buffer based on WebGPU structs. In WebGPU each
|
||||
// VertexBufferLayout is associated with a single vertex buffer. So number of mVertexBufferLayout
|
||||
// is equal to bufferCount. Each VertexBufferLayout can contain multiple VertexAttribute. Bind index
|
||||
// of vertex buffer is implicitly calculated by the position of VertexBufferLayout in an array.
|
||||
// WGPUVertexBufferInfo maps Filament vertex attributes to WebGPU buffer binding model.
|
||||
class WGPUVertexBufferInfo : public HwVertexBufferInfo {
|
||||
public:
|
||||
WGPUVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
|
||||
AttributeArray const& attributes);
|
||||
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 {
|
||||
return mVertexBufferLayout.size();
|
||||
inline wgpu::VertexAttribute const* getVertexAttributes(uint32_t i) const {
|
||||
return mVertexAttributes[i].data();
|
||||
}
|
||||
inline uint32_t getVertexAttributeCount(uint32_t i) const {
|
||||
return static_cast<uint32_t>(mVertexAttributes[i].size());
|
||||
}
|
||||
|
||||
inline wgpu::VertexAttribute const* getVertexAttributeForIndex(uint32_t index) const {
|
||||
assert_invariant(index < mAttributes.size());
|
||||
return mAttributes[index].data();
|
||||
}
|
||||
|
||||
inline uint32_t getVertexAttributeSize(uint32_t index) const {
|
||||
assert_invariant(index < mAttributes.size());
|
||||
return mAttributes[index].size();
|
||||
struct WebGPUSlotBindingInfo {
|
||||
uint8_t sourceBuffer;
|
||||
uint32_t slot;
|
||||
uint64_t bufferOffset;
|
||||
uint32_t stride;
|
||||
};
|
||||
inline const std::vector<WebGPUSlotBindingInfo>& getWebGPUSlotBindingInfos() const {
|
||||
return mWebGPUSlotBindingInfos;
|
||||
}
|
||||
|
||||
private:
|
||||
// TODO: can we do better in terms on heap management.
|
||||
std::vector<wgpu::VertexBufferLayout> mVertexBufferLayout{};
|
||||
std::vector<std::vector<wgpu::VertexAttribute>> mAttributes{};
|
||||
// This stores the final wgpu::VertexBufferLayout objects, one per WebGPU slot.
|
||||
std::vector<wgpu::VertexBufferLayout> mVertexBufferLayouts;
|
||||
|
||||
// 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 {
|
||||
@@ -86,33 +94,37 @@ struct WGPUVertexBuffer : public HwVertexBuffer {
|
||||
utils::FixedCapacityVector<wgpu::Buffer> buffers;
|
||||
};
|
||||
|
||||
struct WGPUIndexBuffer : public HwIndexBuffer {
|
||||
class WGPUBufferBase {
|
||||
public:
|
||||
void createBuffer(wgpu::Device const& device, wgpu::BufferUsage usage, uint32_t size,
|
||||
char const* label);
|
||||
void updateGPUBuffer(BufferDescriptor& bufferDescriptor, uint32_t byteOffset,
|
||||
wgpu::Queue queue);
|
||||
const wgpu::Buffer& getBuffer() const { return buffer; }
|
||||
protected:
|
||||
wgpu::Buffer buffer;
|
||||
private:
|
||||
// 4 bytes to hold any extra chunk we need.
|
||||
std::array<uint8_t,4> mRemainderChunk;
|
||||
};
|
||||
|
||||
class WGPUIndexBuffer : public HwIndexBuffer, public WGPUBufferBase {
|
||||
public:
|
||||
WGPUIndexBuffer(wgpu::Device const &device, uint8_t elementSize,
|
||||
uint32_t indexCount);
|
||||
|
||||
wgpu::Buffer buffer;
|
||||
wgpu::IndexFormat indexFormat;
|
||||
};
|
||||
|
||||
struct WGPUBufferObject : HwBufferObject {
|
||||
class WGPUBufferObject : public HwBufferObject, public WGPUBufferBase {
|
||||
public:
|
||||
WGPUBufferObject(wgpu::Device const &device, BufferObjectBinding bindingType, uint32_t byteCount);
|
||||
|
||||
wgpu::Buffer buffer = nullptr;
|
||||
const BufferObjectBinding bufferObjectBinding;
|
||||
};
|
||||
|
||||
class WebGPUDescriptorSetLayout final : public HwDescriptorSetLayout {
|
||||
public:
|
||||
|
||||
enum class BindGroupEntryType : uint8_t {
|
||||
UNIFORM_BUFFER,
|
||||
TEXTURE_VIEW,
|
||||
SAMPLER
|
||||
};
|
||||
|
||||
struct BindGroupEntryInfo final {
|
||||
uint8_t binding = 0;
|
||||
BindGroupEntryType type = BindGroupEntryType::UNIFORM_BUFFER;
|
||||
bool hasDynamicOffset = false;
|
||||
};
|
||||
|
||||
@@ -125,7 +137,7 @@ public:
|
||||
|
||||
private:
|
||||
// TODO: If this is useful elsewhere, remove it from this class
|
||||
// Convert Filament Shader Stage Flags bitmask to webgpu equivilant
|
||||
// Convert Filament Shader Stage Flags bitmask to webgpu equivalent
|
||||
static wgpu::ShaderStage filamentStageToWGPUStage(ShaderStageFlags fFlags);
|
||||
std::vector<BindGroupEntryInfo> mBindGroupEntries;
|
||||
wgpu::BindGroupLayout mLayout;
|
||||
@@ -133,8 +145,6 @@ private:
|
||||
|
||||
class WebGPUDescriptorSet final : public HwDescriptorSet {
|
||||
public:
|
||||
static void initializeDummyResourcesIfNotAlready(wgpu::Device const&,
|
||||
wgpu::TextureFormat aColorFormat);
|
||||
|
||||
WebGPUDescriptorSet(wgpu::BindGroupLayout const& layout,
|
||||
std::vector<WebGPUDescriptorSetLayout::BindGroupEntryInfo> const& bindGroupEntries);
|
||||
@@ -142,30 +152,18 @@ public:
|
||||
|
||||
wgpu::BindGroup lockAndReturn(wgpu::Device const&);
|
||||
void addEntry(unsigned int index, wgpu::BindGroupEntry&& entry);
|
||||
[[nodiscard]] uint32_t const* setDynamicOffsets(uint32_t const* offsets);
|
||||
[[nodiscard]] bool getIsLocked() const { return mBindGroup != nullptr; }
|
||||
[[nodiscard]] size_t countEntitiesWithDynamicOffsets() const;
|
||||
|
||||
// May be nullptr. Use lockAndReturn to create the bind group when appropriate
|
||||
[[nodiscard]] const wgpu::BindGroup& getBindGroup() const { return mBindGroup; }
|
||||
|
||||
private:
|
||||
static wgpu::Buffer sDummyUniformBuffer;
|
||||
static wgpu::Texture sDummyTexture;
|
||||
static wgpu::TextureView sDummyTextureView;
|
||||
static wgpu::Sampler sDummySampler;
|
||||
|
||||
static std::vector<wgpu::BindGroupEntry> createDummyEntriesSortedByBinding(
|
||||
std::vector<filament::backend::WebGPUDescriptorSetLayout::BindGroupEntryInfo> const&);
|
||||
|
||||
// TODO: Consider storing what we used to make the layout. However we need to essentially
|
||||
// Recreate some of the info (Sampler in slot X with the actual sampler) so letting Dawn confirm
|
||||
// there isn't a mismatch may be easiest.
|
||||
// Also storing the wgpu ObjectBase takes care of ownership challenges in theory
|
||||
wgpu::BindGroupLayout mLayout = nullptr;
|
||||
static constexpr uint8_t INVALID_INDEX = MAX_DESCRIPTOR_COUNT + 1;
|
||||
std::array<uint8_t, MAX_DESCRIPTOR_COUNT> mEntryIndexByBinding{};
|
||||
std::vector<wgpu::BindGroupEntry> mEntriesSortedByBinding;
|
||||
std::bitset<MAX_DESCRIPTOR_COUNT> mEntriesByBindingWithDynamicOffsets{};
|
||||
std::bitset<MAX_DESCRIPTOR_COUNT> mEntriesByBindingAdded{};
|
||||
std::vector<uint32_t> mDynamicOffsets;
|
||||
std::vector<wgpu::BindGroupEntry> mEntries;
|
||||
const size_t mEntriesWithDynamicOffsetsCount;
|
||||
wgpu::BindGroup mBindGroup = nullptr;
|
||||
};
|
||||
|
||||
@@ -176,9 +174,13 @@ public:
|
||||
wgpu::Device const& device) noexcept;
|
||||
|
||||
WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
|
||||
wgpu::TextureAspect getAspect() const { return mAspect; }
|
||||
size_t getBlockWidth() const { return mBlockWidth; }
|
||||
size_t getBlockHeight() const { return mBlockHeight; }
|
||||
|
||||
[[nodiscard]] const wgpu::Texture& getTexture() const { return mTexture; }
|
||||
[[nodiscard]] const wgpu::TextureView& getTexView() const { return mTexView; }
|
||||
[[nodiscard]] const wgpu::TextureView& getTextureView() const { return mTexureView; }
|
||||
[[nodiscard]] wgpu::TextureFormat getFormat() const { return mFormat; }
|
||||
|
||||
static wgpu::TextureFormat fToWGPUTextureFormat(
|
||||
filament::backend::TextureFormat const& fFormat);
|
||||
@@ -197,8 +199,10 @@ private:
|
||||
wgpu::TextureFormat mFormat = wgpu::TextureFormat::Undefined;
|
||||
wgpu::TextureAspect mAspect = wgpu::TextureAspect::Undefined;
|
||||
uint32_t mArrayLayerCount = 1;
|
||||
wgpu::TextureView mTexView = nullptr;
|
||||
wgpu::TextureView mTexureView = nullptr;
|
||||
wgpu::TextureUsage fToWGPUTextureUsage(filament::backend::TextureUsage const& fUsage);
|
||||
size_t mBlockWidth;
|
||||
size_t mBlockHeight;
|
||||
};
|
||||
|
||||
struct WGPURenderPrimitive : public HwRenderPrimitive {
|
||||
@@ -209,55 +213,60 @@ struct WGPURenderPrimitive : public HwRenderPrimitive {
|
||||
|
||||
class WGPURenderTarget : public HwRenderTarget {
|
||||
public:
|
||||
class Attachment {
|
||||
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;
|
||||
};
|
||||
using Attachment = TargetBufferInfo; // Using TargetBufferInfo directly for attachments
|
||||
|
||||
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()
|
||||
: HwRenderTarget(0, 0),
|
||||
defaultRenderTarget(true) {}
|
||||
defaultRenderTarget(true),
|
||||
samples(1) {}
|
||||
|
||||
void setUpRenderPassAttachments(wgpu::RenderPassDescriptor& descriptor,
|
||||
wgpu::TextureView const& textureView, RenderPassParams const& params);
|
||||
// Updated signature: takes resolved views for custom RTs, and default views for default RT
|
||||
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; }
|
||||
uint8_t getSamples() const { return samples; }
|
||||
|
||||
Attachment getDrawColorAttachment(size_t index);
|
||||
Attachment getReadColorAttachment(size_t index);
|
||||
// Accessors for the driver to get stored attachment info
|
||||
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::StoreOp getStoreOperation(const RenderPassParams& params, TargetBufferFlags buffer);
|
||||
|
||||
private:
|
||||
bool defaultRenderTarget = false;
|
||||
uint8_t samples = 1;
|
||||
|
||||
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
|
||||
math::uint2 attachmentSize = {};
|
||||
std::vector<wgpu::RenderPassColorAttachment> colorAttachments{};
|
||||
};
|
||||
MRT mColorAttachments{};
|
||||
// TODO WebGPU only supports a DepthStencil attachment, should this be just mDepthStencilAttachment?
|
||||
Attachment mDepthAttachment{};
|
||||
Attachment mStencilAttachment{};
|
||||
|
||||
// Cached descriptors for the render pass
|
||||
std::vector<wgpu::RenderPassColorAttachment> mColorAttachmentDescriptors;
|
||||
wgpu::RenderPassDepthStencilAttachment mDepthStencilAttachmentDescriptor{};
|
||||
bool mHasDepthStencilAttachment = false;
|
||||
};
|
||||
}// namespace filament::backend
|
||||
#endif// TNT_FILAMENT_BACKEND_WEBGPUHANDLES_H
|
||||
|
||||
@@ -154,9 +154,9 @@ constexpr wgpu::BlendFactor toWebGPU(BlendFunction blendFunction) {
|
||||
wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
|
||||
WGPUProgram const& program, WGPUVertexBufferInfo const& vertexBufferInfo,
|
||||
wgpu::PipelineLayout const& layout, RasterState const& rasterState,
|
||||
StencilState const& stencilState, PolygonOffset const& polygonOffset,
|
||||
PrimitiveType primitiveType, wgpu::TextureFormat colorFormat,
|
||||
wgpu::TextureFormat depthFormat) {
|
||||
StencilState const& stencilState, PolygonOffset const& polygonOffset, PrimitiveType primitiveType,
|
||||
std::vector<wgpu::TextureFormat> const& colorFormats,
|
||||
wgpu::TextureFormat depthFormat, uint8_t samplesCount) {
|
||||
assert_invariant(program.vertexShaderModule);
|
||||
const wgpu::DepthStencilState depthStencilState {
|
||||
.format = depthFormat,
|
||||
@@ -191,8 +191,8 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
|
||||
.entryPoint = "main",
|
||||
.constantCount = program.constants.size(),
|
||||
.constants = program.constants.data(),
|
||||
.bufferCount = vertexBufferInfo.getVertexBufferLayoutSize(),
|
||||
.buffers = vertexBufferInfo.getVertexBufferLayout()
|
||||
.bufferCount = vertexBufferInfo.getVertexBufferLayoutCount(),
|
||||
.buffers = vertexBufferInfo.getVertexBufferLayouts()
|
||||
},
|
||||
.primitive = {
|
||||
.topology = toWebGPU(primitiveType),
|
||||
@@ -207,9 +207,9 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
|
||||
.unclippedDepth = !rasterState.depthClamp &&
|
||||
device.HasFeature(wgpu::FeatureName::DepthClipControl)
|
||||
},
|
||||
.depthStencil = &depthStencilState,
|
||||
.depthStencil = depthFormat != wgpu::TextureFormat::Undefined ? &depthStencilState: nullptr,
|
||||
.multisample = {
|
||||
.count = 1, // TODO need to get this from the render target
|
||||
.count = samplesCount,
|
||||
.mask = 0xFFFFFFFF,
|
||||
.alphaToCoverageEnabled = rasterState.alphaToCoverage
|
||||
},
|
||||
@@ -234,12 +234,15 @@ wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const& device,
|
||||
fragmentState.entryPoint = "main";
|
||||
fragmentState.constantCount = program.constants.size(),
|
||||
fragmentState.constants = program.constants.data(),
|
||||
fragmentState.targetCount = 1; // TODO need to get this from the render target
|
||||
fragmentState.targetCount = colorFormats.size();
|
||||
fragmentState.targets = colorTargets.data();
|
||||
assert_invariant(fragmentState.targetCount <= MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT);
|
||||
// A fragment shader implies at least one color target if it outputs color.
|
||||
// This should be guaranteed by the caller ensuring colorFormats is not empty.
|
||||
assert_invariant(fragmentState.targetCount > 0);
|
||||
for (size_t targetIndex = 0; targetIndex < fragmentState.targetCount; targetIndex++) {
|
||||
auto& colorTarget = colorTargets[targetIndex];
|
||||
colorTarget.format = colorFormat;
|
||||
colorTarget.format = colorFormats[targetIndex];
|
||||
colorTarget.blend = rasterState.hasBlending() ? &blendState : nullptr;
|
||||
colorTarget.writeMask =
|
||||
rasterState.colorWrite ? wgpu::ColorWriteMask::All : wgpu::ColorWriteMask::None;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#define TNT_FILAMENT_BACKEND_WEBGPUPIPELINECREATION_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace wgpu {
|
||||
class Device;
|
||||
@@ -38,8 +39,9 @@ class WGPUProgram;
|
||||
|
||||
[[nodiscard]] wgpu::RenderPipeline createWebGPURenderPipeline(wgpu::Device const&,
|
||||
WGPUProgram const&, WGPUVertexBufferInfo const&, wgpu::PipelineLayout const&,
|
||||
RasterState const&, StencilState const&, PolygonOffset const&, PrimitiveType,
|
||||
wgpu::TextureFormat colorFormat, wgpu::TextureFormat depthFormat);
|
||||
RasterState const&, StencilState const&, PolygonOffset const&, PrimitiveType primitiveType,
|
||||
std::vector<wgpu::TextureFormat> const& colorFormats,
|
||||
wgpu::TextureFormat depthFormat, uint8_t samplesCount);
|
||||
|
||||
}// namespace filament::backend
|
||||
|
||||
|
||||
@@ -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/WebGPUConstants.h"
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
#include "webgpu/WebGPUStrings.h"
|
||||
#endif
|
||||
|
||||
#include "backend/DriverEnums.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <dawn/webgpu_cpp_print.h>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
|
||||
namespace {
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
utils::io::ostream& operator<<(utils::io::ostream& out, const wgpu::TextureFormat format) noexcept {
|
||||
return filament::backend::streamInsertWebGPUPrintable(out, format);
|
||||
}
|
||||
|
||||
utils::io::ostream& operator<<(utils::io::ostream& out,
|
||||
const wgpu::TextureUsage textureUsage) noexcept {
|
||||
return filament::backend::streamInsertWebGPUPrintable(out, textureUsage);
|
||||
}
|
||||
|
||||
utils::io::ostream& operator<<(utils::io::ostream& out,
|
||||
const wgpu::PresentMode presentMode) noexcept {
|
||||
return filament::backend::streamInsertWebGPUPrintable(out, presentMode);
|
||||
}
|
||||
|
||||
utils::io::ostream& operator<<(utils::io::ostream& out,
|
||||
const wgpu::CompositeAlphaMode alphaMode) noexcept {
|
||||
return filament::backend::streamInsertWebGPUPrintable(out, alphaMode);
|
||||
}
|
||||
|
||||
void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabilities) {
|
||||
std::stringstream usagesStream{};
|
||||
usagesStream << capabilities.usages;
|
||||
FWGPU_LOGI << "WebGPU surface capabilities:" << utils::io::endl;
|
||||
FWGPU_LOGI << " surface usages: " << usagesStream.str().data() << utils::io::endl;
|
||||
FWGPU_LOGI << " surface usages: " << capabilities.usages << utils::io::endl;
|
||||
FWGPU_LOGI << " surface formats (" << capabilities.formatCount << "):" << utils::io::endl;
|
||||
if (capabilities.formatCount > 0 && capabilities.formats != nullptr) {
|
||||
std::for_each(capabilities.formats, capabilities.formats + capabilities.formatCount,
|
||||
[](wgpu::TextureFormat const format) {
|
||||
std::stringstream formatStream{};
|
||||
formatStream << format;
|
||||
FWGPU_LOGI << " " << formatStream.str().data() << utils::io::endl;
|
||||
FWGPU_LOGI << " " << format << utils::io::endl;
|
||||
});
|
||||
}
|
||||
FWGPU_LOGI << " surface present modes (" << capabilities.presentModeCount
|
||||
@@ -53,9 +69,7 @@ void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabiliti
|
||||
std::for_each(capabilities.presentModes,
|
||||
capabilities.presentModes + capabilities.presentModeCount,
|
||||
[](wgpu::PresentMode const presentMode) {
|
||||
std::stringstream presentModeStream{};
|
||||
presentModeStream << presentMode;
|
||||
FWGPU_LOGI << " " << presentModeStream.str().data() << utils::io::endl;
|
||||
FWGPU_LOGI << " " << presentMode << utils::io::endl;
|
||||
});
|
||||
}
|
||||
FWGPU_LOGI << " surface alpha modes (" << capabilities.alphaModeCount
|
||||
@@ -64,46 +78,30 @@ void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabiliti
|
||||
std::for_each(capabilities.alphaModes,
|
||||
capabilities.alphaModes + capabilities.alphaModeCount,
|
||||
[](wgpu::CompositeAlphaMode const alphaMode) {
|
||||
std::stringstream alphaModeStream{};
|
||||
alphaModeStream << alphaMode;
|
||||
FWGPU_LOGI << " " << alphaModeStream.str().data() << utils::io::endl;
|
||||
FWGPU_LOGI << " " << alphaMode << utils::io::endl;
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printSurfaceConfiguration(wgpu::SurfaceConfiguration const& config,
|
||||
wgpu::TextureFormat depthFormat) {
|
||||
std::stringstream formatStream{};
|
||||
formatStream << config.format;
|
||||
std::stringstream usageStream{};
|
||||
usageStream << config.usage;
|
||||
std::stringstream alphaModeStream{};
|
||||
alphaModeStream << config.alphaMode;
|
||||
std::stringstream presentModeStream{};
|
||||
presentModeStream << config.presentMode;
|
||||
std::stringstream depthFormatStream;
|
||||
depthFormatStream << depthFormat;
|
||||
FWGPU_LOGI << "WebGPU surface configuration:" << utils::io::endl;
|
||||
FWGPU_LOGI << " surface format: " << formatStream.str() << utils::io::endl;
|
||||
FWGPU_LOGI << " surface usage: " << usageStream.str() << utils::io::endl;
|
||||
FWGPU_LOGI << " surface format: " << config.format << utils::io::endl;
|
||||
FWGPU_LOGI << " surface usage: " << config.usage << utils::io::endl;
|
||||
FWGPU_LOGI << " surface view formats (" << config.viewFormatCount << "):" << utils::io::endl;
|
||||
if (config.viewFormatCount > 0 && config.viewFormats != nullptr) {
|
||||
std::for_each(config.viewFormats, config.viewFormats + config.viewFormatCount,
|
||||
[](wgpu::TextureFormat const viewFormat) {
|
||||
std::stringstream viewFormatStream{};
|
||||
viewFormatStream << viewFormat;
|
||||
FWGPU_LOGI << " " << viewFormatStream.str().data() << utils::io::endl;
|
||||
FWGPU_LOGI << " " << viewFormat << utils::io::endl;
|
||||
});
|
||||
}
|
||||
FWGPU_LOGI << " surface alpha mode: " << alphaModeStream.str() << utils::io::endl;
|
||||
FWGPU_LOGI << " surface alpha mode: " << config.alphaMode << utils::io::endl;
|
||||
FWGPU_LOGI << " surface width: " << config.width << utils::io::endl;
|
||||
FWGPU_LOGI << " surface height: " << config.height << utils::io::endl;
|
||||
FWGPU_LOGI << " surface present mode: " << presentModeStream.str() << utils::io::endl;
|
||||
FWGPU_LOGI << "WebGPU selected depth format: " << depthFormatStream.str() << utils::io::endl;
|
||||
FWGPU_LOGI << " surface present mode: " << config.presentMode << utils::io::endl;
|
||||
FWGPU_LOGI << "WebGPU selected depth format: " << depthFormat << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
#endif// FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
|
||||
[[nodiscard]] constexpr wgpu::TextureFormat selectColorFormat(size_t availableFormatsCount,
|
||||
wgpu::TextureFormat const* availableFormats, bool useSRGBColorSpace) {
|
||||
|
||||
@@ -18,16 +18,30 @@
|
||||
|
||||
#include "webgpu/WebGPUConstants.h"
|
||||
#include "webgpu/WebGPUDriver.h"
|
||||
#include "webgpu/WebGPUStrings.h"
|
||||
|
||||
#include <backend/DriverEnums.h>
|
||||
#include <backend/Platform.h>
|
||||
#include <utils/Hash.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <dawn/webgpu_cpp_print.h>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <sstream>// for one-time-ish setup string concatenation, namely error messaging
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* WebGPU Backend implementation common across platforms or operating systems (at least for now).
|
||||
@@ -39,6 +53,213 @@ namespace filament::backend {
|
||||
|
||||
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
|
||||
[[nodiscard]] wgpu::Instance createInstance() {
|
||||
wgpu::DawnTogglesDescriptor dawnTogglesDescriptor{};
|
||||
@@ -63,119 +284,409 @@ namespace {
|
||||
}
|
||||
};
|
||||
wgpu::Instance instance = wgpu::CreateInstance(&instanceDescriptor);
|
||||
FILAMENT_CHECK_POSTCONDITION(instance != nullptr) << "Unable to create webgpu instance.";
|
||||
FILAMENT_CHECK_POSTCONDITION(instance != nullptr) << "Unable to create WebGPU instance.";
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printInstanceDetails(instance);
|
||||
#endif
|
||||
return instance;
|
||||
}
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printLimit(std::string_view name, const std::variant<uint32_t, uint64_t> value) {
|
||||
FWGPU_LOGI << " " << name.data() << ": ";
|
||||
bool undefined = true;
|
||||
if (std::holds_alternative<uint32_t>(value)) {
|
||||
if (std::get<uint32_t>(value) != WGPU_LIMIT_U32_UNDEFINED) {
|
||||
undefined = false;
|
||||
FWGPU_LOGI << std::get<uint32_t>(value);
|
||||
}
|
||||
} else if (std::holds_alternative<uint64_t>(value)) {
|
||||
if (std::get<uint64_t>(value) != WGPU_LIMIT_U64_UNDEFINED) {
|
||||
undefined = false;
|
||||
FWGPU_LOGI << std::get<uint64_t>(value);
|
||||
}
|
||||
}
|
||||
if (undefined) {
|
||||
FWGPU_LOGI << "UNDEFINED";
|
||||
}
|
||||
FWGPU_LOGI << utils::io::endl;
|
||||
}
|
||||
#endif// FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printLimits(wgpu::Limits const& limits) {
|
||||
printLimit("maxTextureDimension1D", limits.maxTextureDimension1D);
|
||||
printLimit("maxTextureDimension2D", limits.maxTextureDimension2D);
|
||||
printLimit("maxTextureDimension3D", limits.maxTextureDimension3D);
|
||||
printLimit("maxTextureArrayLayers", limits.maxTextureArrayLayers);
|
||||
printLimit("maxBindGroups", limits.maxBindGroups);
|
||||
printLimit("maxBindGroupsPlusVertexBuffers", limits.maxBindGroupsPlusVertexBuffers);
|
||||
printLimit("maxBindingsPerBindGroup", limits.maxBindingsPerBindGroup);
|
||||
printLimit("maxDynamicUniformBuffersPerPipelineLayout",
|
||||
limits.maxDynamicUniformBuffersPerPipelineLayout);
|
||||
printLimit("maxDynamicStorageBuffersPerPipelineLayout",
|
||||
limits.maxDynamicStorageBuffersPerPipelineLayout);
|
||||
printLimit("maxSampledTexturesPerShaderStage", limits.maxSampledTexturesPerShaderStage);
|
||||
printLimit("maxSamplersPerShaderStage", limits.maxSamplersPerShaderStage);
|
||||
printLimit("maxStorageBuffersPerShaderStage", limits.maxStorageBuffersPerShaderStage);
|
||||
printLimit("maxStorageTexturesPerShaderStage", limits.maxStorageTexturesPerShaderStage);
|
||||
printLimit("maxUniformBuffersPerShaderStage", limits.maxUniformBuffersPerShaderStage);
|
||||
printLimit("maxUniformBufferBindingSize", limits.maxUniformBufferBindingSize);
|
||||
printLimit("maxStorageBufferBindingSize", limits.maxStorageBufferBindingSize);
|
||||
printLimit("minUniformBufferOffsetAlignment", limits.minUniformBufferOffsetAlignment);
|
||||
printLimit("minStorageBufferOffsetAlignment", limits.minStorageBufferOffsetAlignment);
|
||||
printLimit("maxVertexBuffers", limits.maxVertexBuffers);
|
||||
printLimit("maxBufferSize", limits.maxBufferSize);
|
||||
printLimit("maxVertexAttributes", limits.maxVertexAttributes);
|
||||
printLimit("maxVertexBufferArrayStride", limits.maxVertexBufferArrayStride);
|
||||
printLimit("maxInterStageShaderVariables", limits.maxInterStageShaderVariables);
|
||||
printLimit("maxColorAttachments", limits.maxColorAttachments);
|
||||
printLimit("maxColorAttachmentBytesPerSample", limits.maxColorAttachmentBytesPerSample);
|
||||
printLimit("maxComputeWorkgroupStorageSize", limits.maxComputeWorkgroupStorageSize);
|
||||
printLimit("maxComputeInvocationsPerWorkgroup", limits.maxComputeInvocationsPerWorkgroup);
|
||||
printLimit("maxComputeWorkgroupSizeX", limits.maxComputeWorkgroupSizeX);
|
||||
printLimit("maxComputeWorkgroupSizeY", limits.maxComputeWorkgroupSizeY);
|
||||
printLimit("maxComputeWorkgroupSizeZ", limits.maxComputeWorkgroupSizeZ);
|
||||
printLimit("maxComputeWorkgroupsPerDimension", limits.maxComputeWorkgroupsPerDimension);
|
||||
printLimit("maxStorageBuffersInVertexStage", limits.maxStorageBuffersInVertexStage);
|
||||
printLimit("maxStorageTexturesInVertexStage", limits.maxStorageTexturesInVertexStage);
|
||||
printLimit("maxStorageBuffersInFragmentStage", limits.maxStorageBuffersInFragmentStage);
|
||||
printLimit("maxStorageTexturesInFragmentStage", limits.maxStorageTexturesInFragmentStage);
|
||||
}
|
||||
#endif
|
||||
|
||||
struct AdapterDetails final {
|
||||
AdapterDetails()
|
||||
: AdapterDetails(nullptr) {}
|
||||
explicit AdapterDetails(wgpu::Adapter adapter)
|
||||
: adapter(std::move(adapter)) {
|
||||
info.nextInChain = &powerPreference;
|
||||
}
|
||||
AdapterDetails(wgpu::AdapterInfo&& info,
|
||||
wgpu::DawnAdapterPropertiesPowerPreference& powerPreference, wgpu::Adapter&& adapter)
|
||||
: info(std::move(info)),
|
||||
powerPreference(powerPreference),
|
||||
adapter(std::move(adapter)) {}
|
||||
AdapterDetails& operator=(AdapterDetails&& other) noexcept {
|
||||
adapter = std::exchange(other.adapter, nullptr);
|
||||
info = std::exchange(other.info, {});
|
||||
powerPreference = std::exchange(other.powerPreference, {});
|
||||
return *this;
|
||||
}
|
||||
|
||||
wgpu::AdapterInfo info{};
|
||||
wgpu::DawnAdapterPropertiesPowerPreference powerPreference{};
|
||||
wgpu::Adapter adapter = nullptr;
|
||||
|
||||
bool operator==(AdapterDetails const& other) const {
|
||||
return info.vendorID == other.info.vendorID &&
|
||||
info.deviceID == other.info.deviceID &&
|
||||
info.backendType == other.info.backendType &&
|
||||
info.adapterType == other.info.adapterType &&
|
||||
powerPreference.powerPreference == other.powerPreference.powerPreference;
|
||||
}
|
||||
};
|
||||
|
||||
template<class STREAM_TYPE>
|
||||
STREAM_TYPE& operator<<(STREAM_TYPE& out, AdapterDetails const& details) noexcept {
|
||||
out << details.info
|
||||
<< " power preference " << powerPreferenceToString(details.powerPreference);
|
||||
return out;
|
||||
}
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printAdapterDetails(AdapterDetails const& details) {
|
||||
FWGPU_LOGI << "Selected WebGPU adapter info: " << details << utils::io::endl;
|
||||
wgpu::SupportedFeatures supportedFeatures{};
|
||||
details.adapter.GetFeatures(&supportedFeatures);
|
||||
FWGPU_LOGI << "WebGPU adapter supported features (" << supportedFeatures.featureCount
|
||||
<< "):" << utils::io::endl;
|
||||
if (supportedFeatures.featureCount > 0 && supportedFeatures.features != nullptr) {
|
||||
std::for_each(supportedFeatures.features,
|
||||
supportedFeatures.features + supportedFeatures.featureCount,
|
||||
[](wgpu::FeatureName const featureName) {
|
||||
FWGPU_LOGI << " " << featureName << utils::io::endl;
|
||||
});
|
||||
}
|
||||
wgpu::Limits supportedLimits{};
|
||||
if (!details.adapter.GetLimits(&supportedLimits)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU adapter supported limits" << utils::io::endl;
|
||||
} else {
|
||||
FWGPU_LOGI << "WebGPU adapter supported limits:" << utils::io::endl;
|
||||
printLimits(supportedLimits);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
struct AdapterDetailsHash final {
|
||||
size_t operator()(AdapterDetails const& details) const {
|
||||
using utils::hash::combine;
|
||||
size_t hash = 0;
|
||||
combine(hash, details.info.vendorID);
|
||||
combine(hash, details.info.deviceID);
|
||||
combine(hash, static_cast<uint32_t>(details.info.backendType));
|
||||
combine(hash, static_cast<uint32_t>(details.info.adapterType));
|
||||
combine(hash, static_cast<uint32_t>(details.powerPreference.powerPreference));
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
[[nodiscard]] bool adapterMeetsMinimumRequirements(AdapterDetails const& details) {
|
||||
// check if the adapter has all required features...
|
||||
if (!std::all_of(REQUIRED_FEATURES.begin(), REQUIRED_FEATURES.end(),
|
||||
[&details](auto const& featureName) {
|
||||
return details.adapter.HasFeature(featureName);
|
||||
})) {
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
FWGPU_LOGI << "WebGPU adapter " << details
|
||||
<< " does not have the minimum required features." << utils::io::endl;
|
||||
FWGPU_LOGI << " Missing required feature(s): ";
|
||||
for (wgpu::FeatureName const& requiredFeature: REQUIRED_FEATURES) {
|
||||
if (!details.adapter.HasFeature(requiredFeature)) {
|
||||
FWGPU_LOGI << requiredFeature << " ";
|
||||
}
|
||||
}
|
||||
FWGPU_LOGI << utils::io::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
wgpu::Limits supportedLimits {};
|
||||
FILAMENT_CHECK_POSTCONDITION(details.adapter.GetLimits(&supportedLimits))
|
||||
<< "Failed to get limits for WebGPU adapter: " << details;
|
||||
if (!satisfiesLimits(supportedLimits)) {
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
FWGPU_LOGI << " (for WebGPU adapter " << details << ")" << utils::io::endl;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// trys to get a set of compatible adapters (whether the meet minimum requirements or not).
|
||||
// It panics if none are found.
|
||||
[[nodiscard]] std::unordered_set<AdapterDetails, AdapterDetailsHash> requestCompatibleAdapters(
|
||||
wgpu::Instance const& instance, std::vector<wgpu::RequestAdapterOptions> const& requests) {
|
||||
// make the series of requests asynchronously, collecting compatible adapter results...
|
||||
std::unordered_set<AdapterDetails, AdapterDetailsHash> compatibleAdapters;
|
||||
compatibleAdapters.reserve(requests.size());
|
||||
std::mutex adaptersMutex;
|
||||
std::vector<wgpu::Future> futures(requests.size());
|
||||
for (size_t i = 0; i < requests.size(); i++) {
|
||||
wgpu::RequestAdapterOptions const& options = requests[i];
|
||||
futures[i] = instance.RequestAdapter(&options, wgpu::CallbackMode::WaitAnyOnly,
|
||||
[&options, &compatibleAdapters,
|
||||
&adaptersMutex](wgpu::RequestAdapterStatus const status,
|
||||
wgpu::Adapter const& readyAdapter, wgpu::StringView const message) {
|
||||
FILAMENT_CHECK_POSTCONDITION(
|
||||
status != wgpu::RequestAdapterStatus::CallbackCancelled)
|
||||
<< "Failed to request a WebGPU adapter due to the request callback "
|
||||
"being cancelled? Options: "
|
||||
<< options << " " << message.data;
|
||||
FILAMENT_CHECK_POSTCONDITION(status != wgpu::RequestAdapterStatus::Error)
|
||||
<< "Failed to request a WebGPU adapter due to an error. Options: "
|
||||
<< options << " Error: " << message.data;
|
||||
if (status == wgpu::RequestAdapterStatus::Success) {
|
||||
AdapterDetails details = AdapterDetails(readyAdapter);
|
||||
FILAMENT_CHECK_POSTCONDITION(readyAdapter.GetInfo(&details.info))
|
||||
<< "Failed to get info for adapter (options: " << options << ")";
|
||||
const std::lock_guard<std::mutex> lock(adaptersMutex);
|
||||
compatibleAdapters.emplace(std::move(details.info), details.powerPreference,
|
||||
std::move(details.adapter));
|
||||
return;
|
||||
}
|
||||
assert_invariant(status == wgpu::RequestAdapterStatus::Unavailable);
|
||||
});
|
||||
}
|
||||
// wait for all the results to return...
|
||||
for (size_t i = 0; i < futures.size(); i++) {
|
||||
wgpu::RequestAdapterOptions const& options = requests[i];
|
||||
wgpu::Future& future = futures[i];
|
||||
wgpu::WaitStatus status = instance.WaitAny(future, REQUEST_ADAPTER_TIMEOUT_NANOSECONDS);
|
||||
FILAMENT_CHECK_POSTCONDITION(status != wgpu::WaitStatus::TimedOut)
|
||||
<< "Timed out requesting a WebGPU adapter with options " << options;
|
||||
FILAMENT_CHECK_POSTCONDITION(status != wgpu::WaitStatus::Error)
|
||||
<< "Failed to request a WebGPU adapter with options " << options
|
||||
<< " due to an error (as request was made synchronous)";
|
||||
assert_invariant(status == wgpu::WaitStatus::Success);
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(!compatibleAdapters.empty()) << "No WebGPU adapters found!";
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
FWGPU_LOGI << compatibleAdapters.size() << " WebGPU adapter(s) found:" << utils::io::endl;
|
||||
for (auto& details: compatibleAdapters) {
|
||||
FWGPU_LOGI << " WebGPU adapter: " << details << utils::io::endl;
|
||||
}
|
||||
#endif
|
||||
return compatibleAdapters;
|
||||
}
|
||||
|
||||
// selects one preferred adapter or panics if none can be found
|
||||
wgpu::Adapter selectPreferredAdapter(
|
||||
std::unordered_set<AdapterDetails, AdapterDetailsHash> const& compatibleAdapters) {
|
||||
// for each unique adapter...
|
||||
AdapterDetails const* selectedAdapter = nullptr;
|
||||
size_t selectedOptionalFeaturesCount = 0;
|
||||
|
||||
// choose the most desirable adapter that meets the minimum requirements...
|
||||
for (AdapterDetails const& details: compatibleAdapters) {
|
||||
if (!adapterMeetsMinimumRequirements(details)) {
|
||||
continue;
|
||||
}
|
||||
size_t supportedOptionalFeaturesCount = std::count_if(OPTIONAL_FEATURES.begin(),
|
||||
OPTIONAL_FEATURES.end(), [&details](auto const& featureName) {
|
||||
return details.adapter.HasFeature(featureName);
|
||||
});
|
||||
// select this one if it is the first (perhaps it is the only one anyway)...
|
||||
if (selectedAdapter == nullptr) {
|
||||
selectedAdapter = &details;
|
||||
selectedOptionalFeaturesCount = supportedOptionalFeaturesCount;
|
||||
continue;
|
||||
}
|
||||
// NOTE: we can make this selection logic more dynamic, configurable,
|
||||
// and robust in the future as needed, such as the app preferring lower power
|
||||
// (prioritizing battery life) or weighting specific optional features in its decision, etc.
|
||||
// This is just a good start that works for now.
|
||||
//
|
||||
// first, prefer higher performance...
|
||||
if (details.powerPreference.powerPreference >
|
||||
selectedAdapter->powerPreference.powerPreference) {
|
||||
selectedAdapter = &details;
|
||||
selectedOptionalFeaturesCount = supportedOptionalFeaturesCount;
|
||||
continue;
|
||||
}
|
||||
// second, prefer more optional features supported...
|
||||
if (supportedOptionalFeaturesCount > selectedOptionalFeaturesCount) {
|
||||
selectedAdapter = &details;
|
||||
selectedOptionalFeaturesCount = supportedOptionalFeaturesCount;
|
||||
continue;
|
||||
}
|
||||
// otherwise keep the selected adapter
|
||||
assert_invariant(selectedAdapter != nullptr);
|
||||
}
|
||||
FILAMENT_CHECK_POSTCONDITION(selectedAdapter != nullptr)
|
||||
<< "Could not find a WebGPU adapter that meets the minimum requirements.";
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printAdapterDetails(*selectedAdapter);
|
||||
#endif
|
||||
return selectedAdapter->adapter;
|
||||
}
|
||||
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
void printDeviceDetails(wgpu::Device const& device) {
|
||||
wgpu::SupportedFeatures supportedFeatures{};
|
||||
device.GetFeatures(&supportedFeatures);
|
||||
FWGPU_LOGI << "WebGPU device supported features (" << supportedFeatures.featureCount
|
||||
<< "):" << utils::io::endl;
|
||||
if (supportedFeatures.featureCount > 0 && supportedFeatures.features != nullptr) {
|
||||
std::for_each(supportedFeatures.features,
|
||||
supportedFeatures.features + supportedFeatures.featureCount,
|
||||
[](wgpu::FeatureName const featureName) {
|
||||
FWGPU_LOGI << " " << featureName << utils::io::endl;
|
||||
});
|
||||
}
|
||||
wgpu::Limits supportedLimits{};
|
||||
if (!device.GetLimits(&supportedLimits)) {
|
||||
FWGPU_LOGW << "Failed to get WebGPU supported device limits" << utils::io::endl;
|
||||
} else {
|
||||
FWGPU_LOGI << "WebGPU device supported limits:" << utils::io::endl;
|
||||
printLimits(supportedLimits);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}// namespace
|
||||
|
||||
wgpu::Adapter WebGPUPlatform::requestAdapter(wgpu::Surface const& surface) {
|
||||
// TODO consider power preference etc. (can be custom preferences passed to the platform or
|
||||
// based on whether this is a Mobile or Desktop system,
|
||||
// etc...)
|
||||
wgpu::RequestAdapterOptions adaptorOptions{ .compatibleSurface = surface };
|
||||
// note this just gets the first adapter
|
||||
wgpu::Adapter adapter = nullptr;
|
||||
wgpu::WaitStatus status = mInstance.WaitAny(
|
||||
mInstance.RequestAdapter(&adaptorOptions, wgpu::CallbackMode::WaitAnyOnly,
|
||||
[&adapter](wgpu::RequestAdapterStatus const status,
|
||||
wgpu::Adapter const& readyAdapter, wgpu::StringView const message) {
|
||||
// TODO consider more robust error handling
|
||||
FILAMENT_CHECK_POSTCONDITION(status == wgpu::RequestAdapterStatus::Success)
|
||||
<< "Unable to request a WebGPU adapter. Status "
|
||||
<< static_cast<uint32_t>(status)
|
||||
<< " with message: " << message.data;
|
||||
adapter = readyAdapter;
|
||||
}),
|
||||
UINT16_MAX);// TODO define reasonable timeout (or do this asynchronously)
|
||||
FILAMENT_CHECK_POSTCONDITION(status == wgpu::WaitStatus::Success)
|
||||
<< "Non-successful wait status requesting a WebGPU adapter "
|
||||
<< static_cast<uint32_t>(status);
|
||||
FILAMENT_CHECK_POSTCONDITION(adapter != nullptr)
|
||||
<< "Failed to get a WebGPU adapter for the platform";
|
||||
// TODO consider validating adapter has required features and/or limits
|
||||
return adapter;
|
||||
assertLimitsAreExpressedInRequirementsStruct();
|
||||
std::vector<wgpu::RequestAdapterOptions> requests = getAdapterOptions();
|
||||
for (auto& request: requests) {
|
||||
request.compatibleSurface = surface;
|
||||
}
|
||||
const std::unordered_set<AdapterDetails, AdapterDetailsHash> compatibleAdapters =
|
||||
requestCompatibleAdapters(mInstance, requests);
|
||||
return selectPreferredAdapter(compatibleAdapters);
|
||||
}
|
||||
|
||||
wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
|
||||
// TODO consider passing limits
|
||||
constexpr std::array optionalFeatures = { wgpu::FeatureName::DepthClipControl,
|
||||
wgpu::FeatureName::Depth32FloatStencil8, wgpu::FeatureName::CoreFeaturesAndLimits };
|
||||
|
||||
constexpr std::array requiredFeatures = { wgpu::FeatureName::TransientAttachments };
|
||||
|
||||
wgpu::SupportedFeatures supportedFeatures;
|
||||
adapter.GetFeatures(&supportedFeatures);
|
||||
|
||||
std::vector<wgpu::FeatureName> enabledFeatures;
|
||||
enabledFeatures.reserve(requiredFeatures.size() + optionalFeatures.size());
|
||||
|
||||
std::set_intersection(supportedFeatures.features,
|
||||
supportedFeatures.features + supportedFeatures.featureCount, requiredFeatures.begin(),
|
||||
requiredFeatures.end(), std::back_inserter(enabledFeatures));
|
||||
|
||||
if (enabledFeatures.size() != requiredFeatures.size()) {
|
||||
std::vector<wgpu::FeatureName> missingFeatures;
|
||||
std::set_difference(requiredFeatures.begin(), requiredFeatures.end(),
|
||||
supportedFeatures.features,
|
||||
supportedFeatures.features + supportedFeatures.featureCount,
|
||||
std::back_inserter(missingFeatures));
|
||||
|
||||
std::stringstream missingFeaturesStream{};
|
||||
for (const auto& entry: missingFeatures) {
|
||||
missingFeaturesStream << std::to_string(static_cast<uint32_t>(entry)) << " ";
|
||||
enabledFeatures.reserve(REQUIRED_FEATURES.size() + OPTIONAL_FEATURES.size());
|
||||
for (auto const& requiredFeature : REQUIRED_FEATURES) {
|
||||
enabledFeatures.push_back(requiredFeature);
|
||||
}
|
||||
for (auto const& optionalFeature : OPTIONAL_FEATURES) {
|
||||
if (adapter.HasFeature(optionalFeature)) {
|
||||
enabledFeatures.push_back(optionalFeature);
|
||||
}
|
||||
PANIC_POSTCONDITION("Some required features are not available %s/n",
|
||||
missingFeaturesStream.str().c_str());
|
||||
}
|
||||
|
||||
std::set_intersection(supportedFeatures.features,
|
||||
supportedFeatures.features + supportedFeatures.featureCount, optionalFeatures.begin(),
|
||||
optionalFeatures.end(), std::back_inserter(enabledFeatures));
|
||||
|
||||
wgpu::DeviceDescriptor deviceDescriptor{};
|
||||
deviceDescriptor.label = "graphics_device";
|
||||
deviceDescriptor.defaultQueue.label = "default_queue";
|
||||
deviceDescriptor.requiredFeatureCount = enabledFeatures.size();
|
||||
deviceDescriptor.requiredFeatures = enabledFeatures.data();
|
||||
deviceDescriptor.requiredLimits = &REQUIRED_LIMITS;
|
||||
deviceDescriptor.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous,
|
||||
[](wgpu::Device const&, wgpu::DeviceLostReason const& reason,
|
||||
wgpu::StringView message) {
|
||||
if (reason == wgpu::DeviceLostReason::Destroyed) {
|
||||
#if FWGPU_ENABLED(FWGPU_DEBUG_VALIDATION)
|
||||
FWGPU_LOGD << "WebGPU device lost due to being destroyed (expected)"
|
||||
<< utils::io::endl;
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
// TODO try recreating the device instead of just panicking
|
||||
std::stringstream reasonStream{};
|
||||
reasonStream << reason;
|
||||
FILAMENT_CHECK_POSTCONDITION(reason != wgpu::DeviceLostReason::Destroyed)
|
||||
<< "WebGPU device lost: " << reasonStream.str() << " " << message.data;
|
||||
<< "WebGPU device lost: " << deviceLostReasonToString(reason) << " "
|
||||
<< message.data;
|
||||
});
|
||||
deviceDescriptor.SetUncapturedErrorCallback(
|
||||
[](wgpu::Device const&, wgpu::ErrorType errorType, wgpu::StringView message) {
|
||||
std::stringstream typeStream{};
|
||||
typeStream << errorType;
|
||||
FWGPU_LOGE << "WebGPU device error: " << typeStream.str() << " " << message.data
|
||||
<< utils::io::endl;
|
||||
FWGPU_LOGE << "WebGPU device error: " << errorTypeToString(errorType) << " "
|
||||
<< message.data << utils::io::endl;
|
||||
});
|
||||
wgpu::Device device = nullptr;
|
||||
wgpu::WaitStatus status = mInstance.WaitAny(
|
||||
adapter.RequestDevice(&deviceDescriptor, wgpu::CallbackMode::WaitAnyOnly,
|
||||
[&device](wgpu::RequestDeviceStatus const status,
|
||||
wgpu::Device const& readyDevice, wgpu::StringView const message) {
|
||||
FILAMENT_CHECK_POSTCONDITION(status == wgpu::RequestDeviceStatus::Success)
|
||||
<< "Unable to request a WebGPU device. Status: "
|
||||
<< static_cast<uint32_t>(status)
|
||||
<< " with message: " << message.data;
|
||||
FILAMENT_CHECK_POSTCONDITION(
|
||||
status != wgpu::RequestDeviceStatus::CallbackCancelled)
|
||||
<< "Failed to request a WebGPU device due to the callback being "
|
||||
"cancelled? "
|
||||
<< message;
|
||||
FILAMENT_CHECK_POSTCONDITION(status != wgpu::RequestDeviceStatus::Error)
|
||||
<< "Failed to request a WebGPU device due to en error: " << message;
|
||||
assert_invariant(status == wgpu::RequestDeviceStatus::Success);
|
||||
device = readyDevice;
|
||||
}),
|
||||
UINT64_MAX);// TODO define reasonable timeout (or do this asynchronously)
|
||||
FILAMENT_CHECK_POSTCONDITION(status == wgpu::WaitStatus::Success)
|
||||
<< "Non-successful wait status requesting a WebGPU device "
|
||||
<< static_cast<uint32_t>(status);
|
||||
REQUEST_DEVICE_TIMEOUT_NANOSECONDS);
|
||||
FILAMENT_CHECK_POSTCONDITION(status != wgpu::WaitStatus::TimedOut)
|
||||
<< "Failed to request a WebGPU device due to a timeout.";
|
||||
FILAMENT_CHECK_POSTCONDITION(status != wgpu::WaitStatus::Error)
|
||||
<< "Failed to request a WebGPU device due to an error.";
|
||||
assert_invariant(status == wgpu::WaitStatus::Success);
|
||||
FILAMENT_CHECK_POSTCONDITION(device != nullptr)
|
||||
<< "Failed to get a WebGPU device for the platform.";
|
||||
<< "Failed to get a WebGPU device for the platform. null device returned?";
|
||||
size_t missingFeatures = 0;
|
||||
std::stringstream featureNamesStream;
|
||||
for (wgpu::FeatureName const& enabledFeature : enabledFeatures) {
|
||||
if (!device.HasFeature(enabledFeature)) {
|
||||
missingFeatures += 1;
|
||||
featureNamesStream << enabledFeature << " ";
|
||||
}
|
||||
}
|
||||
if (missingFeatures > 0) {
|
||||
PANIC_POSTCONDITION("WebGPU device is missing %d requested feature(s) even though the "
|
||||
"adapter should support them: %s\n",
|
||||
missingFeatures, featureNamesStream.str().data());
|
||||
}
|
||||
wgpu::Limits supportedLimits {};
|
||||
FILAMENT_CHECK_POSTCONDITION(device.GetLimits(&supportedLimits))
|
||||
<< "Failed to get limits for the device?";
|
||||
FILAMENT_CHECK_POSTCONDITION(satisfiesLimits(supportedLimits))
|
||||
<< "WebGPU device failed to statify required limits.";
|
||||
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
|
||||
printDeviceDetails(device);
|
||||
#endif
|
||||
return device;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
#include <android/native_window.h>
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* Android OS specific implementation aspects of the WebGPU backend
|
||||
@@ -29,6 +31,30 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
std::vector<wgpu::RequestAdapterOptions> WebGPUPlatform::getAdapterOptions() {
|
||||
constexpr std::array powerPreferences = {
|
||||
wgpu::PowerPreference::HighPerformance,
|
||||
wgpu::PowerPreference::LowPower };
|
||||
constexpr std::array backendTypes = { wgpu::BackendType::Vulkan, wgpu::BackendType::OpenGLES };
|
||||
constexpr std::array forceFallbackAdapters = { false, true };
|
||||
constexpr size_t totalCombinations =
|
||||
powerPreferences.size() * backendTypes.size() * forceFallbackAdapters.size();
|
||||
std::vector<wgpu::RequestAdapterOptions> requests;
|
||||
requests.reserve(totalCombinations);
|
||||
for (auto powerPreference: powerPreferences) {
|
||||
for (auto backendType: backendTypes) {
|
||||
for (auto forceFallbackAdapter: forceFallbackAdapters) {
|
||||
requests.emplace_back(
|
||||
wgpu::RequestAdapterOptions{
|
||||
.powerPreference = powerPreference,
|
||||
.forceFallbackAdapter = forceFallbackAdapter,
|
||||
.backendType = backendType });
|
||||
}
|
||||
}
|
||||
}
|
||||
return requests;
|
||||
}
|
||||
|
||||
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||
ANativeWindow* window = static_cast<ANativeWindow*>(nativeWindow);
|
||||
return wgpu::Extent2D{
|
||||
|
||||
@@ -21,7 +21,9 @@
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
// Platform specific includes and defines
|
||||
#include <Cocoa/Cocoa.h>
|
||||
@@ -33,6 +35,30 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
std::vector<wgpu::RequestAdapterOptions> WebGPUPlatform::getAdapterOptions() {
|
||||
constexpr std::array powerPreferences = {
|
||||
wgpu::PowerPreference::HighPerformance,
|
||||
wgpu::PowerPreference::LowPower };
|
||||
constexpr std::array backendTypes = { wgpu::BackendType::Metal };
|
||||
constexpr std::array forceFallbackAdapters = { false, true };
|
||||
constexpr size_t totalCombinations =
|
||||
powerPreferences.size() * backendTypes.size() * forceFallbackAdapters.size();
|
||||
std::vector<wgpu::RequestAdapterOptions> requests;
|
||||
requests.reserve(totalCombinations);
|
||||
for (auto powerPreference: powerPreferences) {
|
||||
for (auto backendType: backendTypes) {
|
||||
for (auto forceFallbackAdapter: forceFallbackAdapters) {
|
||||
requests.emplace_back(
|
||||
wgpu::RequestAdapterOptions{
|
||||
.powerPreference = powerPreference,
|
||||
.forceFallbackAdapter = forceFallbackAdapter,
|
||||
.backendType = backendType });
|
||||
}
|
||||
}
|
||||
}
|
||||
return requests;
|
||||
}
|
||||
|
||||
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||
// Both IOS and MacOS expects CAMetalLayer.
|
||||
CAMetalLayer* metalLayer = (__bridge CAMetalLayer*) nativeWindow;
|
||||
|
||||
@@ -21,8 +21,10 @@
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#if defined(__linux__) || defined(__FreeBSD__)
|
||||
#define LINUX_OR_FREEBSD 1
|
||||
@@ -79,6 +81,33 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
std::vector<wgpu::RequestAdapterOptions> WebGPUPlatform::getAdapterOptions() {
|
||||
constexpr std::array powerPreferences = {
|
||||
wgpu::PowerPreference::HighPerformance,
|
||||
wgpu::PowerPreference::LowPower };
|
||||
constexpr std::array backendTypes = {
|
||||
wgpu::BackendType::Vulkan,
|
||||
wgpu::BackendType::OpenGL,
|
||||
wgpu::BackendType::OpenGLES };
|
||||
constexpr std::array forceFallbackAdapters = { false, true };
|
||||
constexpr size_t totalCombinations =
|
||||
powerPreferences.size() * backendTypes.size() * forceFallbackAdapters.size();
|
||||
std::vector<wgpu::RequestAdapterOptions> requests;
|
||||
requests.reserve(totalCombinations);
|
||||
for (auto powerPreference: powerPreferences) {
|
||||
for (auto backendType: backendTypes) {
|
||||
for (auto forceFallbackAdapter: forceFallbackAdapters) {
|
||||
requests.emplace_back(
|
||||
wgpu::RequestAdapterOptions{
|
||||
.powerPreference = powerPreference,
|
||||
.forceFallbackAdapter = forceFallbackAdapter,
|
||||
.backendType = backendType });
|
||||
}
|
||||
}
|
||||
}
|
||||
return requests;
|
||||
}
|
||||
|
||||
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||
auto surfaceExtent = wgpu::Extent2D{};
|
||||
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
|
||||
|
||||
@@ -20,7 +20,9 @@
|
||||
|
||||
#include <webgpu/webgpu_cpp.h>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
@@ -30,6 +32,35 @@
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
std::vector<wgpu::RequestAdapterOptions> WebGPUPlatform::getAdapterOptions() {
|
||||
constexpr std::array powerPreferences = {
|
||||
wgpu::PowerPreference::HighPerformance,
|
||||
wgpu::PowerPreference::LowPower };
|
||||
constexpr std::array backendTypes = {
|
||||
wgpu::BackendType::Vulkan,
|
||||
wgpu::BackendType::OpenGL,
|
||||
wgpu::BackendType::OpenGLES,
|
||||
wgpu::BackendType::D3D12,
|
||||
wgpu::BackendType::D3D11 };
|
||||
constexpr std::array forceFallbackAdapters = { false, true };
|
||||
constexpr size_t totalCombinations =
|
||||
powerPreferences.size() * backendTypes.size() * forceFallbackAdapters.size();
|
||||
std::vector<wgpu::RequestAdapterOptions> requests;
|
||||
requests.reserve(totalCombinations);
|
||||
for (auto powerPreference: powerPreferences) {
|
||||
for (auto backendType: backendTypes) {
|
||||
for (auto forceFallbackAdapter: forceFallbackAdapters) {
|
||||
requests.emplace_back(
|
||||
wgpu::RequestAdapterOptions{
|
||||
.powerPreference = powerPreference,
|
||||
.forceFallbackAdapter = forceFallbackAdapter,
|
||||
.backendType = backendType });
|
||||
}
|
||||
}
|
||||
}
|
||||
return requests;
|
||||
}
|
||||
|
||||
wgpu::Extent2D WebGPUPlatform::getSurfaceExtent(void* nativeWindow) const {
|
||||
HWND window = static_cast<HWND>(nativeWindow);
|
||||
RECT windowRect;
|
||||
|
||||
@@ -32,6 +32,7 @@ static constexpr size_t CONFIG_COMMAND_BUFFERS_SIZE = 3 * CONFIG_MIN_COMMAND
|
||||
|
||||
using namespace filament;
|
||||
using namespace filament::backend;
|
||||
using namespace filament::math;
|
||||
|
||||
#ifndef FILAMENT_IOS
|
||||
#include <imageio/ImageEncoder.h>
|
||||
@@ -57,6 +58,8 @@ BackendTest::BackendTest() : commandBufferQueue(CONFIG_MIN_COMMAND_BUFFERS_SIZE,
|
||||
CONFIG_COMMAND_BUFFERS_SIZE, /*mPaused=*/false) {
|
||||
initializeDriver();
|
||||
mImageExpectations.emplace(getDriverApi());
|
||||
NativeView nativeView = getNativeView();
|
||||
mScreenSize = {nativeView.width, nativeView.height};
|
||||
}
|
||||
|
||||
BackendTest::~BackendTest() {
|
||||
@@ -106,59 +109,43 @@ Handle<HwSwapChain> BackendTest::createSwapChain() {
|
||||
return getDriverApi().createSwapChain(view.ptr, 0);
|
||||
}
|
||||
|
||||
void BackendTest::fullViewport(RenderPassParams& params) {
|
||||
fullViewport(params.viewport);
|
||||
PipelineState BackendTest::getColorWritePipelineState() {
|
||||
PipelineState result;
|
||||
result.rasterState.colorWrite = true;
|
||||
result.rasterState.depthWrite = false;
|
||||
result.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||
return result;
|
||||
}
|
||||
|
||||
void BackendTest::fullViewport(Viewport& viewport) {
|
||||
const NativeView& view = getNativeView();
|
||||
viewport.left = 0;
|
||||
viewport.bottom = 0;
|
||||
viewport.width = view.width;
|
||||
viewport.height = view.height;
|
||||
filament::backend::Viewport BackendTest::getFullViewport() const {
|
||||
const NativeView& view = getNativeView();
|
||||
return Viewport {
|
||||
.left = 0,
|
||||
.bottom = 0,
|
||||
.width = static_cast<uint32_t>(view.width),
|
||||
.height = static_cast<uint32_t>(view.height)
|
||||
};
|
||||
}
|
||||
|
||||
void BackendTest::renderTriangle(
|
||||
PipelineLayout const& pipelineLayout,
|
||||
Handle<filament::backend::HwRenderTarget> renderTarget,
|
||||
Handle<filament::backend::HwSwapChain> swapChain,
|
||||
Handle<filament::backend::HwProgram> program) {
|
||||
filament::backend::RenderPassParams BackendTest::getClearColorRenderPass(float4 color) {
|
||||
RenderPassParams params = {};
|
||||
fullViewport(params);
|
||||
params.flags.clear = TargetBufferFlags::COLOR;
|
||||
params.clearColor = {0.f, 0.f, 1.f, 1.f};
|
||||
params.flags.discardStart = TargetBufferFlags::ALL;
|
||||
params.flags.discardEnd = TargetBufferFlags::NONE;
|
||||
params.viewport.height = 512;
|
||||
params.viewport.width = 512;
|
||||
renderTriangle(pipelineLayout, renderTarget, swapChain, program, params);
|
||||
params.clearColor = color;
|
||||
return params;
|
||||
}
|
||||
|
||||
void BackendTest::renderTriangle(
|
||||
PipelineLayout const& pipelineLayout,
|
||||
Handle<HwRenderTarget> renderTarget,
|
||||
Handle<HwSwapChain> swapChain,
|
||||
Handle<HwProgram> program,
|
||||
const RenderPassParams& params) {
|
||||
auto& api = getDriverApi();
|
||||
filament::backend::RenderPassParams BackendTest::getNoClearRenderPass() {
|
||||
return RenderPassParams{};
|
||||
}
|
||||
|
||||
TrianglePrimitive triangle(api);
|
||||
std::size_t BackendTest::screenWidth() const {
|
||||
return mScreenSize[0];
|
||||
}
|
||||
|
||||
api.makeCurrent(swapChain, swapChain);
|
||||
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
|
||||
PipelineState state;
|
||||
state.program = program;
|
||||
state.pipelineLayout = pipelineLayout;
|
||||
state.rasterState.colorWrite = true;
|
||||
state.rasterState.depthWrite = false;
|
||||
state.rasterState.depthFunc = RasterState::DepthFunc::A;
|
||||
state.rasterState.culling = CullingMode::NONE;
|
||||
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
|
||||
api.endRenderPass();
|
||||
std::size_t BackendTest::screenHeight() const {
|
||||
return mScreenSize[1];
|
||||
}
|
||||
|
||||
bool BackendTest::matchesEnvironment(Backend backend) {
|
||||
|
||||
@@ -52,28 +52,23 @@ protected:
|
||||
|
||||
filament::backend::Handle<filament::backend::HwSwapChain> createSwapChain();
|
||||
|
||||
// Helper methods to set the viewport to the full extent of the swap chain.
|
||||
static void fullViewport(filament::backend::RenderPassParams& params);
|
||||
static void fullViewport(filament::backend::Viewport& viewport);
|
||||
static filament::backend::PipelineState getColorWritePipelineState();
|
||||
|
||||
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);
|
||||
// Gets the full back buffer's viewport
|
||||
filament::backend::Viewport getFullViewport() const;
|
||||
// If color is unset this defaults to using opaque cyan
|
||||
static filament::backend::RenderPassParams getClearColorRenderPass(
|
||||
filament::math::float4 color = filament::math::float4(0, 1, 1, 1));
|
||||
static filament::backend::RenderPassParams getNoClearRenderPass();
|
||||
|
||||
filament::backend::DriverApi& getDriverApi() { return *commandStream; }
|
||||
filament::backend::Driver& getDriver() { return *driver; }
|
||||
|
||||
ImageExpectations& getExpectations() { return *mImageExpectations; }
|
||||
|
||||
std::size_t screenWidth() const;
|
||||
std::size_t screenHeight() const;
|
||||
|
||||
static bool matchesEnvironment(Backend backend);
|
||||
static bool matchesEnvironment(OperatingSystem operatingSystem);
|
||||
private:
|
||||
@@ -93,6 +88,8 @@ private:
|
||||
// This isn't truly optional, it just needs to delay construction until after the driver has
|
||||
// been initialized
|
||||
std::optional<ImageExpectations> mImageExpectations;
|
||||
|
||||
std::array<size_t, 2> mScreenSize;
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -56,26 +56,35 @@ Shader::Shader(DriverApi& api, Cleanup& cleanup, ShaderConfig config) : mCleanup
|
||||
mProgram = cleanup.add(api.createProgram(std::move(prog)));
|
||||
|
||||
if (!kLayouts.empty()) {
|
||||
mDescriptorSetLayout =
|
||||
cleanup.add(api.createDescriptorSetLayout(DescriptorSetLayout{ .bindings = kLayouts }));
|
||||
mDescriptorSetLayout = cleanup.add(
|
||||
api.createDescriptorSetLayout(DescriptorSetLayout{ .bindings = kLayouts }));
|
||||
}
|
||||
}
|
||||
|
||||
filament::backend::DescriptorSetHandle Shader::createDescriptorSet(DriverApi& api) const {
|
||||
DescriptorSetHandle Shader::createDescriptorSet(DriverApi& api) const {
|
||||
return mCleanup.add(api.createDescriptorSet(mDescriptorSetLayout));
|
||||
}
|
||||
|
||||
filament::backend::ProgramHandle Shader::getProgram() const {
|
||||
ProgramHandle Shader::getProgram() const {
|
||||
assert(mProgram);
|
||||
EXPECT_THAT(mProgram, ::testing::IsTrue())
|
||||
<< "Shader program accessed despite being null.";
|
||||
return mProgram;
|
||||
}
|
||||
|
||||
filament::backend::DescriptorSetLayoutHandle Shader::getDescriptorSetLayout() const {
|
||||
DescriptorSetLayoutHandle Shader::getDescriptorSetLayout() const {
|
||||
EXPECT_THAT(mDescriptorSetLayout, ::testing::IsTrue())
|
||||
<< "Shader descriptor set layout accessed despite being null.";
|
||||
return mDescriptorSetLayout;
|
||||
}
|
||||
|
||||
void Shader::addProgramToPipelineState(PipelineState& state) const {
|
||||
state.program = getProgram();
|
||||
// In case another shader was set first, clear the set layout and then set this shader's values.
|
||||
state.pipelineLayout.setLayout = PipelineLayout::SetLayout();
|
||||
if (mDescriptorSetLayout) {
|
||||
state.pipelineLayout.setLayout[0] = { getDescriptorSetLayout() };
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -90,6 +90,8 @@ public:
|
||||
filament::backend::ProgramHandle getProgram() const;
|
||||
filament::backend::DescriptorSetLayoutHandle getDescriptorSetLayout() const;
|
||||
|
||||
void addProgramToPipelineState(filament::backend::PipelineState& state) const;
|
||||
|
||||
filament::backend::DescriptorSetHandle createDescriptorSet(
|
||||
filament::backend::DriverApi& api) const;
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(ShaderStage stage, std::s
|
||||
} else if (backend == Backend::VULKAN) {
|
||||
shader.insert(pos, "#define TARGET_VULKAN_ENVIRONMENT\n");
|
||||
} else if (backend == Backend::WEBGPU) {
|
||||
shader.insert(pos, "#define TARGET_VULKAN_ENVIRONMENT\n");
|
||||
shader.insert(pos, "#define TARGET_WEBGPU_ENVIRONMENT\n");
|
||||
}
|
||||
|
||||
const char* shaderCString = shader.c_str();
|
||||
@@ -186,4 +186,16 @@ Program ShaderGenerator::getProgram(filament::backend::DriverApi&) noexcept {
|
||||
return program;
|
||||
}
|
||||
|
||||
Program ShaderGenerator::getProgramWithPushConstants(filament::backend::DriverApi&,
|
||||
std::array<PushConstants, filament::backend::Program::SHADER_TYPE_COUNT> constants) {
|
||||
Program program;
|
||||
program.shaderLanguage(mShaderLanguage);
|
||||
program.shader(ShaderStage::VERTEX, mVertexBlob.data(), mVertexBlob.size());
|
||||
program.shader(ShaderStage::FRAGMENT, mFragmentBlob.data(), mFragmentBlob.size());
|
||||
for (auto const stage : {ShaderStage::VERTEX, ShaderStage::FRAGMENT }) {
|
||||
program.pushConstants(stage, constants[uint8_t(stage)]);
|
||||
}
|
||||
return program;
|
||||
}
|
||||
|
||||
} // namespace test
|
||||
|
||||
@@ -48,6 +48,10 @@ public:
|
||||
|
||||
filament::backend::Program getProgram(filament::backend::DriverApi&) noexcept;
|
||||
|
||||
using PushConstants = utils::FixedCapacityVector<filament::backend::Program::PushConstant>;
|
||||
filament::backend::Program getProgramWithPushConstants(filament::backend::DriverApi&,
|
||||
std::array<PushConstants, filament::backend::Program::SHADER_TYPE_COUNT> constants);
|
||||
|
||||
private:
|
||||
using ShaderStage = filament::backend::ShaderStage;
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ std::vector<UniformConfig> GetUniformConfig(ShaderUniformType type) {
|
||||
"backend_test", "test_tex", 0,
|
||||
SamplerType::SAMPLER_2D, SamplerFormat::FLOAT, Precision::HIGH, false };
|
||||
return {{
|
||||
"test_tex", DescriptorType::SAMPLER, samplerInfo
|
||||
"test_tex", DescriptorType::SAMPLER_2D_FLOAT, samplerInfo
|
||||
}};
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -55,6 +55,8 @@ public:
|
||||
void updateIndices(const index_type* indices) noexcept;
|
||||
void updateIndices(const index_type* indices, int count, int offset) noexcept;
|
||||
|
||||
VertexInfoHandle getVertexBufferInfo() const { return mVertexBufferInfo; }
|
||||
|
||||
private:
|
||||
|
||||
size_t mVertexCount = 3;
|
||||
|
||||
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |