Compare commits

..
Author SHA1 Message Date
Powei Feng ad42ee66b1 continuing to test splitting combined samplers
Use this to test:

./build.sh -W debug matc tint spirv-opt spirv_test && \
    ./out/cmake-debug/tools/matc/matc -R -a vulkan -o out.cmat ./filament/src/materials/dof/dof.mat && \
    glslc dof_0x00.frag -o dof_0x00.spv && \
    ./out/cmake-debug/third_party/spirv-tools/tools/spirv-opt --split-combined-image-sampler dof_0x00.spv -o split.spv && \
    ./out/cmake-debug/samples/spirv_test split.spv offset.spv && \
    ./out/cmake-debug/third_party/dawn/tint --format wgsl offset.spv > out.wgsl
2025-03-19 23:07:32 -07:00
bridgewaterrobbie b2e77d6847 split combined fixes 2025-03-19 16:20:03 -04:00
bridgewaterrobbie 784b1650c3 quick commit of combined image sampler fix testing 2025-03-18 16:45:58 -04:00
Matthew Hoffman 78c6456fbe Update gtest to 1.16 (#8528) 2025-03-17 16:31:48 -05:00
bridgewaterrobbie 84cce8cfdf Run dead code elimination for WGSL even if all other optimizations are disabled 2025-03-17 15:16:19 -04:00
bridgewaterrobbie 49b7c7169f Avoid building point size demo and material if WebGPU is enabled. 2025-03-17 15:16:19 -04:00
Powei Feng dbfe2dbc5e Disable renderdiff for breakage (#8500)
Disable renderdiff for breakage
2025-03-17 11:47:19 -07:00
Serge Metral 0ec60de287 vk: External sampler platform code (#8500)
Adds enums for enabling external sampler / immutable samplers. No actual implementation yet.
2025-03-17 09:51:34 -07:00
rafadevai 7eb756ca96 vk: Fix Vulkan Validation errors when importing an AHB (#8509)
- Make sure we setup a valid sample count of 1.
https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-VkImageCreateInfo-samples-parameter
- Select a proper memory type based on the list
provided by the `metadata.memoryTypeBits`
https://www.khronos.org/registry/vulkan/specs/1.3-extensions/html/vkspec.html#VUID-VkMemoryAllocateInfo-memoryTypeIndex-02385
2025-03-16 01:56:42 +00:00
Mathias Agopian 3abddc4584 APIs to query the texture limit sizes.
Assert textures dimensions are in range when creating a Texture object.

FIXES=[398901038]
2025-03-15 16:32:29 -07:00
bridgewaterrobbie 35661a1974 Add appropriate matc flags for WebGPU when enabled
Currently WebGPU does not support skinning nor stereo, so they also must be disabled if targeting WebGPU.
2025-03-14 13:17:23 -04:00
bridgewaterrobbie 606a1259f6 Avoid generating subpass code if using WebGPU
Subpasses should not be used anyways due to the result of WebGPUDriver::isFrameBufferFetchSupported. However, we need to make sure the GLSL is not generated for WebGPU, or Tint is unable to convert.
2025-03-14 13:17:23 -04:00
Powei Feng 6479a5bc12 github: change wgsl presubmit to use linux (#8525) 2025-03-14 16:59:11 +00:00
Andy Hovingh c95f9ed7e0 WebGPU: backend components created and conditionally logged
Initial groundwork in creating WebGPU backend components, namely
the instance, adapter, device, and queue.
If configured to do so, the backend will print out details about
these components.
The samples/hellotriangle.cpp was slightly modified to include a
webgpu option which allows for exercising the above, but does not
yet draw anything to the screen/window.
NOTE: This has only been sanity tested with hello triangle
on Mac OS and the Android emulator at this time, NOT IOS,
Windows, or Linux yet.
2025-03-13 19:45:48 -05:00
Powei Feng e3b7861aa2 webgpu: combine tint static libs into single lib (#8520) 2025-03-13 17:57:25 +00:00
Powei Feng 84980d89af Add .clang-format to project root (#8487) 2025-03-12 22:19:01 +00:00
Powei Feng 60640be5e8 vk: fix generateMipmaps level (#8495)
The level counting was off by one.

FIXES=400710879
2025-03-12 21:47:52 +00:00
Powei Feng 5f730ac173 osmesa: allow static linking of osmesa (#8486)
- Add path for static linking
- Fix get_mesa script.
2025-03-12 21:00:22 +00:00
Powei Feng e12ed900d0 webgpu: fix filamat android build (#8514)
Also fix a change in backend API.
2025-03-12 19:05:28 +00:00
Doris Wu 78487f4836 [fgviewer] Collapse subresources by default in web view (#8517)
* Collapse subresources by default

* Fix typo
2025-03-12 14:58:54 +00:00
bridgewaterrobbie 25e9802fd5 Add validate wgsl pipleine test 2025-03-11 17:40:21 -04:00
bridgewaterrobbie dacd03ce42 Adjust running the test to fail on 0 tests. This is to ensure we don't pass the test due to the webgpu build failing or similar 2025-03-11 17:40:21 -04:00
bridgewaterrobbie c9cfc63df0 Add tests to convert sample shaders with Tint to WGSL 2025-03-11 17:40:21 -04:00
Juan David Caldas 9c69cb8484 Enable WebGPU option for matc and add WGSL options for shader generation 2025-03-11 17:40:21 -04:00
Matthew Hoffman 8ea4cb50d2 Add helpers for backend tests. (#8505)
Both for cleaning up graphics resources at the end of a test and for starting/stopping render loops.

BUGS=[402472882]
2025-03-11 14:25:32 -05:00
Mathias Agopian 4f8080ead2 Fix material instance check log when destroying material
When the feature flags assert_destroy_material_before_material_instance
is disabled, we emit a log instead of an assert. Unfortunately this log
would be emitted even when all material instances were destroyed.
2025-03-11 11:09:11 -07:00
rafadevai 34ebe45e2a vk: External Texture implementation for Vulkan (#8488)
Add support for the new createTextureExternalImage2R
API instead of createTextureExternalImageR.

Add the support to import AHardwareBuffer objects
into Vulkan in Android.
2025-03-11 06:01:28 +00:00
Sungun Park bb3e3df2f4 Fix release notes 2025-03-10 22:35:35 +00:00
460 changed files with 51398 additions and 46661 deletions
+68
View File
@@ -0,0 +1,68 @@
BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: DontAlign
AlignConsecutiveAssignments: None
AlignOperands: Align
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Always
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterReturnType: None
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBraces: Custom
BracedInitializerIndentWidth: 4
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: true
BreakBeforeBinaryOperators: None
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
ColumnLimit: 100
CompactNamespaces: false
ContinuationIndentWidth: 8
Cpp11BracedListStyle: false
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: true
MaxEmptyLinesToKeep: 2
NamespaceIndentation: None
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PointerAlignment: Left
ReflowComments: true
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 0
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
TabWidth: 4
UseTab: Never
PackConstructorInitializers: Never
+14 -2
View File
@@ -92,15 +92,27 @@ jobs:
test-renderdiff:
name: test-renderdiff
runs-on: ubuntu-22.04-16core
runs-on: ubuntu-22.04-4core
steps:
- uses: actions/checkout@v4.1.6
- uses: ./.github/actions/ubuntu-apt-add-src
- name: Run script
run: |
source ./build/linux/ci-common.sh && bash test/renderdiff/test.sh
echo "Disabled renderdiff due to Mesa -> Currently planned outage: 2025-03-16 -> 2025-03-22"
# source ./build/linux/ci-common.sh && bash test/renderdiff/test.sh
- uses: actions/upload-artifact@v4
with:
name: presubmit-renderdiff-result
path: ./out/renderdiff_tests
validate-wgsl-pipeline:
name: validate-wgsl-pipeline
runs-on: ubuntu-22.04-4core
steps:
- uses: actions/checkout@v4.1.6
- name: Run build script
run: source ./build/linux/ci-common.sh && ./build.sh -W debug test_filamat
- name: Run test
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
+8
View File
@@ -601,6 +601,14 @@ if (FILAMENT_SUPPORTS_METAL)
set(MATC_API_FLAGS ${MATC_API_FLAGS} -a metal)
endif()
# With WebGPU, push constants are not supported. Skinning uses them.
# WebGPU has a proposal to add push constants at https://github.com/gpuweb/gpuweb/blob/main/proposals/push-constants.md
# With WebGPU, Tint does not support ClipDistance which is used in Stereo. Mentioned in comment
# https://github.com/google/dawn/blob/855d17b08abdf02f9142bf5a8f14d0ea088810a4/src/tint/lang/spirv/reader/ast_parser/function.cc#L4434
if (FILAMENT_SUPPORTS_WEBGPU)
set(MATC_API_FLAGS ${MATC_API_FLAGS} -a webgpu --variant-filter=skinning,stereo)
endif()
# Disable ESSL 1.0 code generation.
if (NOT FILAMENT_ENABLE_FEATURE_LEVEL_0)
set(MATC_API_FLAGS ${MATC_API_FLAGS} -1)
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.58.1'
implementation 'com.google.android.filament:filament-android:1.58.0'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.58.1'
pod 'Filament', '~> 1.58.0'
```
## Documentation
+3
View File
@@ -9,6 +9,9 @@ Instead, if you are authoring a PR for the main branch, add your release note to
## v1.58.1
## v1.58.0
- materials: add an optional `transformName` field to sampler material parameters. [⚠️ **New Material Version**]
## v1.57.3
+8
View File
@@ -11,6 +11,13 @@ if (FILAMENT_SUPPORTS_VULKAN)
message("Library filamat ignores Vulkan settings")
endif()
if (FILAMENT_SUPPORTS_WEBGPU)
# See third_party/dawn/tnt/CMakeLists.txt and libs/filamat/CMakeLists.txt
add_library(tint STATIC IMPORTED)
set_target_properties(tint PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libtint_combined.a)
endif()
add_library(${FILAMAT_FLAVOR} STATIC IMPORTED)
set_target_properties(${FILAMAT_FLAVOR} PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/lib${FILAMAT_FLAVOR}.a)
@@ -50,4 +57,5 @@ target_link_libraries(filamat-jni
utils
log
smol-v
$<$<STREQUAL:${FILAMENT_SUPPORTS_WEBGPU},ON>:tint>
)
+5 -9
View File
@@ -29,10 +29,6 @@ add_library(filaflat STATIC IMPORTED)
set_target_properties(filaflat PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilaflat.a)
add_library(filamat STATIC IMPORTED)
set_target_properties(filamat PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfilamat.a)
add_library(geometry STATIC IMPORTED)
set_target_properties(geometry PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libgeometry.a)
@@ -59,11 +55,11 @@ add_library(smol-v STATIC IMPORTED)
set_target_properties(smol-v PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libsmol-v.a)
if (FILAMENT_ENABLE_FGVIEWER)
add_library(fgviewer STATIC IMPORTED)
set_target_properties(fgviewer PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfgviewer.a)
endif()
if (FILAMENT_ENABLE_FGVIEWER)
add_library(fgviewer STATIC IMPORTED)
set_target_properties(fgviewer PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libfgviewer.a)
endif()
if (FILAMENT_ENABLE_MATDBG)
add_library(matdbg STATIC IMPORTED)
@@ -68,6 +68,21 @@ Java_com_google_android_filament_Texture_nIsTextureSwizzleSupported(JNIEnv*, jcl
return (jboolean) Texture::isTextureSwizzleSupported(*engine);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nGetMaxTextureSize(JNIEnv *, jclass,
jlong nativeEngine, jint sampler) {
Engine *engine = (Engine *) nativeEngine;
return Texture::getMaxTextureSize(*engine, (Texture::Sampler)sampler);
}
extern "C" JNIEXPORT jint JNICALL
Java_com_google_android_filament_Texture_nGetMaxArrayTextureLayers(JNIEnv *, jclass,
jlong nativeEngine) {
Engine *engine = (Engine *) nativeEngine;
return Texture::getMaxArrayTextureLayers(*engine);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Texture_nValidatePixelFormatAndType(JNIEnv*, jclass,
jint internalFormat, jint pixelDataFormat, jint pixelDataType) {
@@ -691,6 +691,24 @@ public class Texture {
pixelDataType.ordinal());
}
/**
* @param engine {@link Engine}
* @param type Texture sampler type
* @return The maximum size in texels of a texture of type \p type. At least 2048 for
* 2D textures, 256 for 3D textures
*/
public static int getMaxTextureSize(@NonNull Engine engine, Sampler type) {
return nGetMaxTextureSize(engine.getNativeObject(), type.ordinal());
}
/**
* @param engine {@link Engine}
* @return The maximum number of layers supported by texture arrays. At least 256.
*/
public static int getMaxArrayTextureLayers(@NonNull Engine engine) {
return nGetMaxArrayTextureLayers(engine.getNativeObject());
}
/**
* Use <code>Builder</code> to construct a <code>Texture</code> object instance.
*/
@@ -1289,6 +1307,8 @@ public class Texture {
private static native boolean nIsTextureFormatSupported(long nativeEngine, int internalFormat);
private static native boolean nIsTextureFormatMipmappable(long nativeEngine, int internalFormat);
private static native boolean nIsTextureSwizzleSupported(long nativeEngine);
private static native int nGetMaxTextureSize(long nativeObject, int ordinal);
private static native int nGetMaxArrayTextureLayers(long nativeObject);
private static native boolean nValidatePixelFormatAndType(int internalFormat, int pixelDataFormat,
int pixelDataType);
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.58.1
VERSION_NAME=1.58.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+2
View File
@@ -541,6 +541,7 @@ function build_android {
./gradlew \
-Pcom.google.android.filament.dist-dir=../out/android-debug/filament \
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
${WEBGPU_ANDROID_GRADLE_OPTION} \
:filamat-android:assembleDebug
if [[ "${BUILD_ANDROID_SAMPLES}" == "true" ]]; then
@@ -591,6 +592,7 @@ function build_android {
./gradlew \
-Pcom.google.android.filament.dist-dir=../out/android-release/filament \
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
${WEBGPU_ANDROID_GRADLE_OPTION} \
:filamat-android:assembleRelease
if [[ "${BUILD_ANDROID_SAMPLES}" == "true" ]]; then
+3
View File
@@ -30,6 +30,9 @@ if [[ "$GITHUB_WORKFLOW" ]]; then
sudo apt-get install clang-$GITHUB_CLANG_VERSION libc++-$GITHUB_CLANG_VERSION-dev libc++abi-$GITHUB_CLANG_VERSION-dev
sudo apt-get install mesa-common-dev libxi-dev libxxf86vm-dev
# For dawn
sudo apt-get install libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev
sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-${GITHUB_CLANG_VERSION} 100
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${GITHUB_CLANG_VERSION} 100
fi
+31 -3
View File
@@ -233,17 +233,40 @@ if (FILAMENT_SUPPORTS_VULKAN)
elseif (APPLE OR IOS)
list(APPEND SRCS src/vulkan/platform/VulkanPlatformApple.mm)
elseif (ANDROID)
list(APPEND SRCS src/vulkan/platform/VulkanPlatformAndroid.cpp)
list(APPEND SRCS
include/backend/platforms/VulkanPlatformAndroid.h
src/vulkan/platform/VulkanPlatformAndroid.cpp
)
endif()
endif()
if (FILAMENT_SUPPORTS_WEBGPU)
list(APPEND SRCS
include/backend/platforms/WebGPUPlatform.h
src/webgpu/platform/WebGPUPlatform.cpp
src/webgpu/WebGPUConstants.h
src/webgpu/WebGPUDriver.cpp
src/webgpu/WebGPUDriver.h
src/webgpu/WebGPUPlatform.cpp
src/webgpu/WebGPUPlatform.h
)
if (WIN32)
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformWindows.cpp)
elseif (LINUX)
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformLinux.cpp)
elseif (APPLE OR IOS)
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformApple.mm)
elseif (ANDROID)
list(APPEND SRCS src/webgpu/platform/WebGPUPlatformAndroid.cpp)
endif()
if (TNT_DEV)
set(FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING_DEFAULT ON)
else()
set(FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING_DEFAULT OFF)
endif()
option(FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING
"Enable immediate_error_handling for the WebGPU backend Dawn implementation"
${FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING_DEFAULT})
endif()
if (ANDROID)
@@ -430,6 +453,10 @@ if (FILAMENT_SUPPORTS_METAL)
target_compile_definitions(${TARGET} PRIVATE $<$<BOOL:${FILAMENT_METAL_PROFILING}>:FILAMENT_METAL_PROFILING>)
endif()
if (FILAMENT_SUPPORTS_WEBGPU)
target_compile_definitions(${TARGET} PRIVATE $<$<BOOL:${FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING}>:FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING>)
endif()
target_link_libraries(${TARGET} PRIVATE
${OSMESA_LINKER_FLAGS}
$<$<AND:$<PLATFORM_ID:Linux>,$<CONFIG:Release>>:${LINUX_LINKER_OPTIMIZATION_FLAGS}>
@@ -458,6 +485,7 @@ if (APPLE OR LINUX)
test/ShaderGenerator.cpp
test/TrianglePrimitive.cpp
test/Arguments.cpp
test/Lifetimes.cpp
test/test_FeedbackLoops.cpp
test/test_Blit.cpp
test/test_MissingRequiredAttributes.cpp
+120 -7
View File
@@ -126,6 +126,10 @@ static_assert(MAX_VERTEX_BUFFER_COUNT <= MAX_VERTEX_ATTRIBUTE_COUNT,
static constexpr size_t CONFIG_UNIFORM_BINDING_COUNT = 9; // This is guaranteed by OpenGL ES.
static constexpr size_t CONFIG_SAMPLER_BINDING_COUNT = 4; // This is guaranteed by OpenGL ES.
static constexpr uint8_t EXTERNAL_SAMPLER_DATA_INDEX_UNUSED =
uint8_t(-1);// Case where the descriptor set binding isnt using any external sampler state
// and therefore doesn't have a valid entry.
/**
* Defines the backend's feature levels.
*/
@@ -182,6 +186,7 @@ enum class ShaderLanguage {
SPIRV = 2,
MSL = 3,
METAL_LIBRARY = 4,
WGSL = 5,
};
static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) {
@@ -196,6 +201,8 @@ static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguag
return "MSL";
case ShaderLanguage::METAL_LIBRARY:
return "Metal precompiled library";
case ShaderLanguage::WGSL:
return "WGSL";
}
}
@@ -249,21 +256,19 @@ struct DescriptorSetLayoutBinding {
DescriptorFlags flags = DescriptorFlags::NONE;
uint16_t count = 0;
uint8_t externalSamplerDataIndex = EXTERNAL_SAMPLER_DATA_INDEX_UNUSED;
friend inline bool operator==(
DescriptorSetLayoutBinding const& lhs,
DescriptorSetLayoutBinding const& rhs) noexcept {
return lhs.type == rhs.type &&
lhs.flags == rhs.flags &&
lhs.count == rhs.count &&
lhs.stageFlags == rhs.stageFlags;
lhs.stageFlags == rhs.stageFlags &&
lhs.externalSamplerDataIndex == rhs.externalSamplerDataIndex;
}
};
struct DescriptorSetLayout {
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
};
/**
* Bitmask for selecting render buffers
*/
@@ -965,8 +970,28 @@ enum class SamplerCompareFunc : uint8_t {
N //!< Never. The depth / stencil test always fails.
};
//! this API is copied from (and only applies to) the Vulkan spec.
//! These specify YUV to RGB conversions.
enum class SamplerYcbcrModelConversion : uint8_t {
RGB_IDENTITY = 0,
YCBCR_IDENTITY = 1,
YCBCR_709 = 2,
YCBCR_601 = 3,
YCBCR_2020 = 4,
};
enum class SamplerYcbcrRange : uint8_t {
ITU_FULL = 0,
ITU_NARROW = 1,
};
enum class ChromaLocation : uint8_t {
COSITED_EVEN = 0,
MIDPOINT = 1,
};
//! Sampler parameters
struct SamplerParams { // NOLINT
struct SamplerParams { // NOLINT
SamplerMagFilter filterMag : 1; //!< magnification filter (NEAREST)
SamplerMinFilter filterMin : 3; //!< minification filter (NEAREST)
SamplerWrapMode wrapS : 2; //!< s-coordinate wrap mode (CLAMP_TO_EDGE)
@@ -1021,6 +1046,7 @@ private:
return SamplerParams::LessThan{}(lhs, rhs);
}
};
static_assert(sizeof(SamplerParams) == 4);
// The limitation to 64-bits max comes from how we store a SamplerParams in our JNI code
@@ -1028,6 +1054,93 @@ static_assert(sizeof(SamplerParams) == 4);
static_assert(sizeof(SamplerParams) <= sizeof(uint64_t),
"SamplerParams must be no more than 64 bits");
//! Sampler parameters
struct SamplerYcbcrConversion {// NOLINT
SamplerYcbcrModelConversion ycbcrModel : 4;
TextureSwizzle r : 4;
TextureSwizzle g : 4;
TextureSwizzle b : 4;
TextureSwizzle a : 4;
SamplerYcbcrRange ycbcrRange : 1;
ChromaLocation xChromaOffset : 1;
ChromaLocation yChromaOffset : 1;
SamplerMagFilter chromaFilter : 1;
uint8_t padding;
struct Hasher {
size_t operator()(const SamplerYcbcrConversion p) const noexcept {
// we don't use std::hash<> here, so we don't have to include <functional>
return *reinterpret_cast<uint32_t const*>(reinterpret_cast<char const*>(&p));
}
};
struct EqualTo {
bool operator()(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) const noexcept {
assert_invariant(lhs.padding == 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;
}
};
struct LessThan {
bool operator()(SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs) const noexcept {
assert_invariant(lhs.padding == 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;
}
};
private:
friend inline bool operator == (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
noexcept {
return SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
}
friend inline bool operator != (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
noexcept {
return !SamplerYcbcrConversion::EqualTo{}(lhs, rhs);
}
friend inline bool operator < (SamplerYcbcrConversion lhs, SamplerYcbcrConversion rhs)
noexcept {
return SamplerYcbcrConversion::LessThan{}(lhs, rhs);
}
};
static_assert(sizeof(SamplerYcbcrConversion) == 4);
static_assert(sizeof(SamplerYcbcrConversion) <= sizeof(uint64_t),
"SamplerYcbcrConversion must be no more than 64 bits");
struct ExternalSamplerDatum {
ExternalSamplerDatum(SamplerYcbcrConversion ycbcr, SamplerParams spm, uint32_t extFmt)
: YcbcrConversion(ycbcr),
samplerParams(spm),
externalFormat(extFmt) {}
bool operator==(ExternalSamplerDatum const& rhs) const {
return (YcbcrConversion == rhs.YcbcrConversion && samplerParams == rhs.samplerParams &&
externalFormat == rhs.externalFormat);
}
struct EqualTo {
bool operator()(const ExternalSamplerDatum& lhs,
const ExternalSamplerDatum& rhs) const noexcept {
return (lhs.YcbcrConversion == rhs.YcbcrConversion &&
lhs.samplerParams == rhs.samplerParams &&
lhs.externalFormat == rhs.externalFormat);
}
};
SamplerYcbcrConversion YcbcrConversion;
SamplerParams samplerParams;
uint32_t externalFormat;
};
// No implicit padding allowed due to it being a hash key.
static_assert(sizeof(ExternalSamplerDatum) == 12);
struct DescriptorSetLayout {
utils::FixedCapacityVector<DescriptorSetLayoutBinding> bindings;
utils::FixedCapacityVector<ExternalSamplerDatum> externalSamplerData;
};
//! blending equation function
enum class BlendEquation : uint8_t {
ADD, //!< the fragment is added to the color buffer
@@ -18,6 +18,7 @@
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKANPLATFORM_H
#include <backend/Platform.h>
#include <backend/DriverEnums.h>
#include <bluevk/BlueVK.h>
@@ -317,6 +318,11 @@ public:
*/
uint32_t layers;
/**
* The numbers of samples per texel
*/
VkSampleCountFlagBits samples;
/**
* The format of the external image
*/
@@ -347,18 +353,37 @@ public:
*/
uint32_t memoryTypeBits;
};
virtual ExternalImageMetadata getExternalImageMetadata(void* externalImage);
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage);
using ImageData = std::pair<VkImage, VkDeviceMemory>;
virtual ImageData createExternalImage(void* externalImage,
const ExternalImageMetadata& metadata);
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage);
virtual VkSampler createExternalSampler(SamplerYcbcrConversion chroma,
SamplerParams sampler,
uint32_t internalFormat);
virtual VkImageView createExternalImageView(SamplerYcbcrConversion chroma,
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
VkImageViewType viewType, VkComponentMapping swizzle);
private:
static ExtensionSet getSwapchainInstanceExtensions();
static ExternalImageMetadata getExternalImageMetadataImpl(void* externalImage,
static ExternalImageMetadata getExternalImageMetadataImpl(ExternalImageHandleRef externalImage,
VkDevice device);
static ImageData createExternalImageImpl(void* externalImage, VkDevice device,
const VkAllocationCallbacks* allocator, const ExternalImageMetadata& metadata);
static ImageData createExternalImageDataImpl(ExternalImageHandleRef externalImage,
VkDevice device, const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage);
static VkSampler createExternalSamplerImpl(VkDevice device,
SamplerYcbcrConversion chroma, SamplerParams sampler,
uint32_t internalFormat);
static VkImageView createExternalImageViewImpl(VkDevice device,
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
VkImageSubresourceRange range, VkImageViewType viewType,
VkComponentMapping swizzle);
// Platform dependent helper methods
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
@@ -0,0 +1,43 @@
/*
* 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_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
#include <backend/Platform.h>
#include <android/hardware_buffer.h>
namespace filament::backend::fvkandroid {
struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
AHardwareBuffer* aHardwareBuffer = nullptr;
bool sRGB = false;
unsigned int width; // Texture width
unsigned int height; // Texture height
TextureFormat format; // Texture format
TextureUsage usage; // Texture usage flags
protected:
~ExternalImageVulkanAndroid() override;
};
Platform::ExternalImageHandle createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept;
} // namespace filament::backend::fvkandroid
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
@@ -0,0 +1,59 @@
/*
* 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_PLATFORMS_WEBGPUPLATFORM_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H
#include <backend/Platform.h>
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
namespace filament::backend {
/**
* A Platform interface, handling the environment-specific concerns, e.g. OS, for creating a WebGPU
* driver (backend).
*/
class WebGPUPlatform final : public Platform {
public:
WebGPUPlatform();
~WebGPUPlatform() override = default;
[[nodiscard]] int getOSVersion() const noexcept final { return 0; }
[[nodiscard]] wgpu::Instance& getInstance() noexcept { return mInstance; }
// either returns a valid surface or panics
[[nodiscard]] wgpu::Surface createSurface(void* nativeWindow, uint64_t flags);
// either returns a valid adapter or panics
[[nodiscard]] wgpu::Adapter requestAdapter(wgpu::Surface const& surface);
// either returns a valid device or panics
[[nodiscard]] wgpu::Device requestDevice(wgpu::Adapter const& adapter);
protected:
[[nodiscard]] Driver* createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) noexcept override;
private:
// we may consider having the driver own this in the future
wgpu::Instance mInstance;
};
}// namespace filament::backend
#endif// TNT_FILAMENT_BACKEND_PLATFORMS_WEBGPUPLATFORM_H
@@ -362,6 +362,8 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isProtectedTexturesSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthClampSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxUniformBufferSize)
DECL_DRIVER_API_SYNCHRONOUS_N(size_t, getMaxTextureSize, backend::SamplerType, target)
DECL_DRIVER_API_SYNCHRONOUS_0(size_t, getMaxArrayTextureLayers)
DECL_DRIVER_API_SYNCHRONOUS_0(math::float2, getClipSpaceParams)
DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage2, backend::Platform::ExternalImageHandleRef, image)
DECL_DRIVER_API_SYNCHRONOUS_N(void, setupExternalImage, void*, image)
+1 -1
View File
@@ -66,7 +66,7 @@ filament::backend::Platform* createDefaultMetalPlatform();
#include "noop/PlatformNoop.h"
#if defined(FILAMENT_SUPPORTS_WEBGPU)
#include "webgpu/WebGPUPlatform.h"
#include "backend/platforms/WebGPUPlatform.h"
#endif
namespace filament::backend {
+10
View File
@@ -1177,6 +1177,16 @@ size_t MetalDriver::getMaxUniformBufferSize() {
return 256 * 1024 * 1024; // TODO: return the actual size instead of hardcoding the minspec
}
size_t MetalDriver::getMaxTextureSize(SamplerType) {
// TODO: return the actual size instead of hardcoding the minspec
return 2048;
}
size_t MetalDriver::getMaxArrayTextureLayers() {
// TODO: return the actual size instead of hardcoding the minspec
return 256;
}
void MetalDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& data,
uint32_t byteOffset) {
FILAMENT_CHECK_PRECONDITION(data.buffer)
@@ -138,6 +138,7 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
case ShaderLanguage::ESSL1:
case ShaderLanguage::ESSL3:
case ShaderLanguage::SPIRV:
case ShaderLanguage::WGSL:
break;
}
+8
View File
@@ -240,6 +240,14 @@ size_t NoopDriver::getMaxUniformBufferSize() {
return 16384u;
}
size_t NoopDriver::getMaxTextureSize(SamplerType target) {
return 2048u;
}
size_t NoopDriver::getMaxArrayTextureLayers() {
return 256u;
}
void NoopDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
uint32_t byteOffset) {
scheduleDestroy(std::move(p));
+16 -4
View File
@@ -94,18 +94,22 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
}
#endif
OpenGLContext::initExtensions(&ext, state.major, state.minor);
initExtensions(&ext, state.major, state.minor);
OpenGLContext::initProcs(&procs, ext, state.major, state.minor);
initProcs(&procs, ext, state.major, state.minor);
OpenGLContext::initBugs(&bugs, ext, state.major, state.minor,
initBugs(&bugs, ext, state.major, state.minor,
state.vendor, state.renderer, state.version, state.shader);
glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &gets.max_renderbuffer_size);
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &gets.max_texture_image_units);
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &gets.max_combined_texture_image_units);
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &gets.max_texture_size);
glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &gets.max_cubemap_texture_size);
glGetIntegerv(GL_MAX_3D_TEXTURE_SIZE, &gets.max_3d_texture_size);
glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &gets.max_array_texture_layers);
mFeatureLevel = OpenGLContext::resolveFeatureLevel(state.major, state.minor, ext, gets, bugs);
mFeatureLevel = resolveFeatureLevel(state.major, state.minor, ext, gets, bugs);
#ifdef BACKEND_OPENGL_VERSION_GLES
mShaderModel = ShaderModel::MOBILE;
@@ -177,6 +181,14 @@ OpenGLContext::OpenGLContext(OpenGLPlatform& platform,
<< gets.max_anisotropy << '\n'
<< "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS = "
<< gets.max_combined_texture_image_units << '\n'
<< "GL_MAX_TEXTURE_SIZE = "
<< gets.max_texture_size << '\n'
<< "GL_MAX_CUBE_MAP_TEXTURE_SIZE = "
<< gets.max_cubemap_texture_size << '\n'
<< "GL_MAX_3D_TEXTURE_SIZE = "
<< gets.max_3d_texture_size << '\n'
<< "GL_MAX_ARRAY_TEXTURE_LAYERS = "
<< gets.max_array_texture_layers << '\n'
<< "GL_MAX_DRAW_BUFFERS = "
<< gets.max_draw_buffers << '\n'
<< "GL_MAX_RENDERBUFFER_SIZE = "
@@ -203,6 +203,10 @@ public:
GLint max_renderbuffer_size;
GLint max_samples;
GLint max_texture_image_units;
GLint max_texture_size;
GLint max_cubemap_texture_size;
GLint max_3d_texture_size;
GLint max_array_texture_layers;
GLint max_transform_feedback_separate_attribs;
GLint max_uniform_block_size;
GLint max_uniform_buffer_bindings;
@@ -2460,6 +2460,26 @@ size_t OpenGLDriver::getMaxUniformBufferSize() {
return mContext.gets.max_uniform_block_size;
}
size_t OpenGLDriver::getMaxTextureSize(SamplerType target) {
switch (target) {
case SamplerType::SAMPLER_2D:
case SamplerType::SAMPLER_2D_ARRAY:
case SamplerType::SAMPLER_EXTERNAL:
return mContext.gets.max_texture_size;
case SamplerType::SAMPLER_CUBEMAP:
return mContext.gets.max_cubemap_texture_size;
case SamplerType::SAMPLER_3D:
return mContext.gets.max_3d_texture_size;
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
return mContext.gets.max_cubemap_texture_size;
}
return 0;
}
size_t OpenGLDriver::getMaxArrayTextureLayers() {
return mContext.gets.max_array_texture_layers;
}
// ------------------------------------------------------------------------------------------------
// Swap chains
// ------------------------------------------------------------------------------------------------
@@ -22,6 +22,10 @@
#include <dlfcn.h>
#include <memory>
// This is to ensure that linking during compilation will not fail even if
// OSMesaGetProcAddress is not linked.
__attribute__((weak)) OSMESAproc OSMesaGetProcAddress(char const*);
namespace filament::backend {
using namespace backend;
@@ -51,10 +55,10 @@ private:
using GetProcAddressFunc = OSMESAproc (*)(const char* funcName);
public:
CreateContextFunc OSMesaCreateContext;
DestroyContextFunc OSMesaDestroyContext;
MakeCurrentFunc OSMesaMakeCurrent;
GetProcAddressFunc OSMesaGetProcAddress;
CreateContextFunc fOSMesaCreateContext;
DestroyContextFunc fOSMesaDestroyContext;
MakeCurrentFunc fOSMesaMakeCurrent;
GetProcAddressFunc fOSMesaGetProcAddress;
OSMesaAPI() {
constexpr char const* libraryNames[] = {"libOSMesa.so", "libosmesa.so"};
@@ -65,18 +69,25 @@ public:
}
}
if (mLib) {
OSMesaGetProcAddress = (GetProcAddressFunc) dlsym(mLib, "OSMesaGetProcAddress");
// Loading from a libosmesa.os
fOSMesaGetProcAddress = (GetProcAddressFunc) dlsym(mLib, "OSMesaGetProcAddress");
} else {
OSMesaGetProcAddress = (GetProcAddressFunc) dlsym(RTLD_LOCAL, "OSMesaGetProcAddress");
// Filament is built into a .so
fOSMesaGetProcAddress = (GetProcAddressFunc) dlsym(RTLD_LOCAL, "OSMesaGetProcAddress");
}
FILAMENT_CHECK_PRECONDITION(OSMesaGetProcAddress)
<< "Unable to against libOSMesa to create a software GL context";
if (!fOSMesaGetProcAddress) {
// Statically linking osmesa
fOSMesaGetProcAddress = OSMesaGetProcAddress;
}
OSMesaCreateContext = (CreateContextFunc) OSMesaGetProcAddress("OSMesaCreateContext");
OSMesaDestroyContext =
(DestroyContextFunc) OSMesaGetProcAddress("OSMesaDestroyContext");
OSMesaMakeCurrent = (MakeCurrentFunc) OSMesaGetProcAddress("OSMesaMakeCurrent");
FILAMENT_CHECK_PRECONDITION(fOSMesaGetProcAddress)
<< "Unable to link against libOSMesa to create a software GL context";
fOSMesaCreateContext = (CreateContextFunc) fOSMesaGetProcAddress("OSMesaCreateContext");
fOSMesaDestroyContext =
(DestroyContextFunc) fOSMesaGetProcAddress("OSMesaDestroyContext");
fOSMesaMakeCurrent = (MakeCurrentFunc) fOSMesaGetProcAddress("OSMesaMakeCurrent");
}
~OSMesaAPI() {
@@ -97,7 +108,7 @@ Driver* PlatformOSMesa::createDriver(void* const sharedGLContext,
FILAMENT_CHECK_PRECONDITION(sharedGLContext == nullptr)
<< "shared GL context is not supported with PlatformOSMesa";
mContext = api->OSMesaCreateContext(GL_RGBA, NULL);
mContext = api->fOSMesaCreateContext(GL_RGBA, NULL);
// We need to do a no-op makecurrent here so that the context will be in a correct state before
// any GL calls.
@@ -113,7 +124,7 @@ Driver* PlatformOSMesa::createDriver(void* const sharedGLContext,
void PlatformOSMesa::terminate() noexcept {
OSMesaAPI* api = (OSMesaAPI*) mOsMesaApi;
api->OSMesaDestroyContext(mContext);
api->fOSMesaDestroyContext(mContext);
delete api;
mOsMesaApi = nullptr;
@@ -141,7 +152,7 @@ bool PlatformOSMesa::makeCurrent(ContextType type, SwapChain* drawSwapChain,
OSMesaAPI* api = (OSMesaAPI*) mOsMesaApi;
OSMesaSwapchain* impl = (OSMesaSwapchain*) drawSwapChain;
auto result = api->OSMesaMakeCurrent(mContext, (BackingType*) impl->buffer.get(),
auto result = api->fOSMesaMakeCurrent(mContext, (BackingType*) impl->buffer.get(),
BACKING_GL_TYPE, impl->width, impl->height);
FILAMENT_CHECK_POSTCONDITION(result == GL_TRUE) << "OSMesaMakeCurrent failed!";
+48 -22
View File
@@ -554,13 +554,45 @@ void VulkanDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwText
texture.inc();
}
void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th,
backend::SamplerType target, backend::TextureFormat format,
uint32_t width, uint32_t height, backend::TextureUsage usage,
void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
Platform::ExternalImageHandleRef externalImage) {
FVK_SYSTRACE_SCOPE();
const auto& metadata = mPlatform->getExternalImageMetadata(externalImage);
if (metadata.isProtected) {
usage |= backend::TextureUsage::PROTECTED;
}
// FIXME: implement createTextureExternalImage2R
VkImageUsageFlags vkUsage = metadata.usage;
if (any(usage & TextureUsage::BLIT_SRC)) {
vkUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
}
if (any(usage & (TextureUsage::BLIT_DST & TextureUsage::UPLOADABLE))) {
vkUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT;
}
assert_invariant(width == metadata.width);
assert_invariant(height == metadata.height);
assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
VkMemoryPropertyFlags const requiredMemoryFlags = any(usage & TextureUsage::UPLOADABLE)
? VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
: VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
uint32_t const memoryTypeIndex =
mContext.selectMemoryType(metadata.memoryTypeBits, requiredMemoryFlags);
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex != VK_MAX_MEMORY_TYPES)
<< "failed to find a valid memory type for external image memory.";
const auto& data =
mPlatform->createExternalImageData(externalImage, metadata, memoryTypeIndex, vkUsage);
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
mAllocator, &mResourceManager, &mCommands, data.first, data.second, metadata.format,
metadata.samples, metadata.width, metadata.height, metadata.layerCount, usage,
mStagePool);
texture.inc();
}
void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
@@ -568,22 +600,7 @@ void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::Sa
void* externalImage) {
FVK_SYSTRACE_SCOPE();
const auto& metadata = mPlatform->getExternalImageMetadata(externalImage);
if (metadata.isProtected) {
usage |= backend::TextureUsage::PROTECTED;
}
assert_invariant(width == metadata.width);
assert_invariant(height == metadata.height);
assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
const auto& data = mPlatform->createExternalImage(externalImage, metadata);
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
mAllocator, &mResourceManager, &mCommands, data.first, data.second, metadata.format,
1, metadata.width, metadata.height, /*depth=*/1, usage, mStagePool);
texture.inc();
// not supported in this backend
}
void VulkanDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
@@ -1130,6 +1147,16 @@ size_t VulkanDriver::getMaxUniformBufferSize() {
return 32768;
}
size_t VulkanDriver::getMaxTextureSize(SamplerType) {
// TODO: return the actual size instead of hardcoded value
return 2048;
}
size_t VulkanDriver::getMaxArrayTextureLayers() {
// TODO: return the actual size instead of hardcoded value
return 256;
}
void VulkanDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t index,
Handle<HwBufferObject> boh) {
auto vb = resource_ptr<VulkanVertexBuffer>::cast(&mResourceManager, vbh);
@@ -1266,10 +1293,9 @@ void VulkanDriver::generateMipmaps(Handle<HwTexture> th) {
mBlitter.blit(VK_FILTER_LINEAR, dst, dstOffsets, src, srcOffsets);
}
level++;
srcw = dstw;
srch = dsth;
} while ((srcw > 1 || srch > 1) && level < t->levels);
} while ((srcw > 1 || srch > 1) && ++level < t->levels - 1);
}
void VulkanDriver::compilePrograms(CompilerPriorityQueue priority,
@@ -23,79 +23,6 @@ using namespace bluevk;
namespace filament::backend {
constexpr inline VkSamplerAddressMode getWrapMode(SamplerWrapMode mode) noexcept {
switch (mode) {
case SamplerWrapMode::REPEAT:
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case SamplerWrapMode::CLAMP_TO_EDGE:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case SamplerWrapMode::MIRRORED_REPEAT:
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
}
}
constexpr inline VkFilter getFilter(SamplerMinFilter filter) noexcept {
switch (filter) {
case SamplerMinFilter::NEAREST:
return VK_FILTER_NEAREST;
case SamplerMinFilter::LINEAR:
return VK_FILTER_LINEAR;
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
return VK_FILTER_NEAREST;
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
return VK_FILTER_LINEAR;
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
return VK_FILTER_NEAREST;
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
return VK_FILTER_LINEAR;
}
}
constexpr inline VkFilter getFilter(SamplerMagFilter filter) noexcept {
switch (filter) {
case SamplerMagFilter::NEAREST:
return VK_FILTER_NEAREST;
case SamplerMagFilter::LINEAR:
return VK_FILTER_LINEAR;
}
}
constexpr inline VkSamplerMipmapMode getMipmapMode(SamplerMinFilter filter) noexcept {
switch (filter) {
case SamplerMinFilter::NEAREST:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerMinFilter::LINEAR:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
}
}
constexpr inline float getMaxLod(SamplerMinFilter filter) noexcept {
switch (filter) {
case SamplerMinFilter::NEAREST:
case SamplerMinFilter::LINEAR:
// The Vulkan spec recommends a max LOD of 0.25 to "disable" mipmapping.
// See "Mapping of OpenGL to Vulkan filter modes" in the VK Spec.
return 0.25f;
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
return VK_LOD_CLAMP_NONE;
}
}
constexpr inline VkBool32 getCompareEnable(SamplerCompareMode mode) noexcept {
return mode == SamplerCompareMode::NONE ? VK_FALSE : VK_TRUE;
}
VulkanSamplerCache::VulkanSamplerCache(VkDevice device)
: mDevice(device) {}
@@ -106,18 +33,18 @@ VkSampler VulkanSamplerCache::getSampler(SamplerParams params) noexcept {
}
VkSamplerCreateInfo samplerInfo {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.magFilter = getFilter(params.filterMag),
.minFilter = getFilter(params.filterMin),
.mipmapMode = getMipmapMode(params.filterMin),
.addressModeU = getWrapMode(params.wrapS),
.addressModeV = getWrapMode(params.wrapT),
.addressModeW = getWrapMode(params.wrapR),
.anisotropyEnable = params.anisotropyLog2 == 0 ? 0u : 1u,
.magFilter = fvkutils::getFilter(params.filterMag),
.minFilter = fvkutils::getFilter(params.filterMin),
.mipmapMode = fvkutils::getMipmapMode(params.filterMin),
.addressModeU = fvkutils::getWrapMode(params.wrapS),
.addressModeV = fvkutils::getWrapMode(params.wrapT),
.addressModeW = fvkutils::getWrapMode(params.wrapR),
.anisotropyEnable = params.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
.maxAnisotropy = (float)(1u << params.anisotropyLog2),
.compareEnable = getCompareEnable(params.compareMode),
.compareEnable = fvkutils::getCompareEnable(params.compareMode),
.compareOp = fvkutils::getCompareOp(params.compareFunc),
.minLod = 0.0f,
.maxLod = getMaxLod(params.filterMin),
.maxLod = fvkutils::getMaxLod(params.filterMin),
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE
};
@@ -978,15 +978,28 @@ VkQueue VulkanPlatform::getProtectedGraphicsQueue() const noexcept {
}
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadata(
void* externalImage) {
ExternalImageHandleRef externalImage) {
return getExternalImageMetadataImpl(externalImage, mImpl->mDevice);
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImage(void* externalImage,
const ExternalImageMetadata& metadata) {
return createExternalImageImpl(externalImage, mImpl->mDevice, nullptr, metadata);
VulkanPlatform::ImageData VulkanPlatform::createExternalImageData(
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata,
uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return createExternalImageDataImpl(externalImage, mImpl->mDevice, metadata, memoryTypeIndex,
usage);
}
VkSampler VulkanPlatform::createExternalSampler(SamplerYcbcrConversion chroma,
SamplerParams sampler, uint32_t internalFormat) {
return createExternalSamplerImpl(mImpl->mDevice, chroma, sampler, internalFormat);
}
VkImageView VulkanPlatform::createExternalImageView(SamplerYcbcrConversion chroma,
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
VkImageViewType viewType, VkComponentMapping swizzle) {
return createExternalImageViewImpl(mImpl->mDevice, chroma, internalFormat, image, range,
viewType, swizzle);
}
#undef SWAPCHAIN_RET_FUNC
}// namespace filament::backend
@@ -15,26 +15,59 @@
*/
#include <backend/platforms/VulkanPlatform.h>
#include <backend/DriverEnums.h>
#include <backend/platforms/VulkanPlatformAndroid.h>
#include <private/backend/BackendUtilsAndroid.h>
#include "vulkan/VulkanConstants.h"
#include <utils/Panic.h>
#include "vulkan/utils/Image.h"
#include "vulkan/utils/Conversion.h"
#include <bluevk/BlueVK.h>
#include <android/hardware_buffer.h>
#include <android/native_window.h>
#include <utility>
using namespace bluevk;
namespace filament::backend {
namespace {
void getVKFormatAndUsage(const AHardwareBuffer_Desc& desc, VkFormat& format,
VkImageUsageFlags& usage, bool& isProtected) {
VkFormat transformVkFormat(VkFormat format, bool sRGB) {
if (!sRGB) {
return format;
}
switch (format) {
case VK_FORMAT_R8G8B8A8_UNORM:
format = VK_FORMAT_R8G8B8A8_SRGB;
break;
case VK_FORMAT_R8G8B8_UNORM:
format = VK_FORMAT_R8G8B8_SRGB;
break;
default:
break;
}
return format;
}
bool isProtectedFromUsage(uint64_t usage) {
return (usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) ? true : false;
}
std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer_Desc& desc,
bool sRGB) {
VkFormat format = VK_FORMAT_UNDEFINED;
VkImageUsageFlags usage = 0;
// Refer to "11.2.17. External Memory Handle Types" in the spec, and
// Tables 13/14 for how the following derivation works.
bool isDepthFormat = false;
isProtected = false;
switch (desc.format) {
case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
format = VK_FORMAT_R8G8B8A8_UNORM;
@@ -82,6 +115,8 @@ void getVKFormatAndUsage(const AHardwareBuffer_Desc& desc, VkFormat& format,
format = VK_FORMAT_UNDEFINED;
}
format = transformVkFormat(format, sRGB);
// The following only concern usage flags derived from Table 14.
usage = 0;
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
@@ -98,16 +133,14 @@ void getVKFormatAndUsage(const AHardwareBuffer_Desc& desc, VkFormat& format,
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER) {
usage = VK_IMAGE_USAGE_STORAGE_BIT;
}
if (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
isProtected = true;
}
return { format, usage };
}
VulkanPlatform::ImageData allocateExternalImage(void* externalBuffer,
VkDevice device, const VkAllocationCallbacks* allocator,
VulkanPlatform::ExternalImageMetadata const& metadata) {
VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevice device,
VulkanPlatform::ExternalImageMetadata const& metadata, uint32_t memoryTypeIndex,
VkImageUsageFlags usage) {
VulkanPlatform::ImageData data;
AHardwareBuffer* buffer = static_cast<AHardwareBuffer*>(externalBuffer);
// if external format we need to specifiy it in the allocation
const bool useExternalFormat = metadata.format == VK_FORMAT_UNDEFINED;
@@ -115,8 +148,8 @@ VulkanPlatform::ImageData allocateExternalImage(void* externalBuffer,
const VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.pNext = nullptr,
.externalFormat = metadata
.externalFormat,// pass down the format (external means we don't have it VK defined)
// pass down the format (external means we don't have it VK defined)
.externalFormat = metadata.externalFormat,
};
const VkExternalMemoryImageCreateInfo externalCreateInfo = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
@@ -135,12 +168,10 @@ VulkanPlatform::ImageData allocateExternalImage(void* externalBuffer,
};
imageInfo.mipLevels = 1;
imageInfo.arrayLayers = metadata.layers;
imageInfo.usage = metadata.usage;
// In the unprotected case add R/W capabilities
if (metadata.isProtected == false)
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
imageInfo.samples = metadata.samples;
imageInfo.usage = usage;
VkResult result = vkCreateImage(device, &imageInfo, allocator, &data.first);
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &data.first);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkCreateImage failed with error=" << static_cast<int32_t>(result);
@@ -156,36 +187,66 @@ VulkanPlatform::ImageData allocateExternalImage(void* externalBuffer,
.image = data.first,
.buffer = VK_NULL_HANDLE,
};
VkMemoryAllocateInfo allocInfo = {.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
VkMemoryAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = &memoryDedicatedAllocateInfo,
.allocationSize = metadata.allocationSize,
.memoryTypeIndex = metadata.memoryTypeBits};
result = vkAllocateMemory(device, &allocInfo, allocator, &data.second);
.memoryTypeIndex = memoryTypeIndex,
};
result = vkAllocateMemory(device, &allocInfo, VKALLOC, &data.second);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkAllocateMemory failed with error=" << static_cast<int32_t>(result);
return data;
}
}// namespace
} // namespace
namespace fvkandroid {
ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() = default;
Platform::ExternalImageHandle createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept {
if (__builtin_available(android 26, *)) {
AHardwareBuffer_Desc hardwareBufferDescription = {};
AHardwareBuffer_describe(buffer, &hardwareBufferDescription);
auto* const p = new (std::nothrow) ExternalImageVulkanAndroid;
p->aHardwareBuffer = const_cast<AHardwareBuffer*>(buffer);
p->sRGB = sRGB;
p->height = hardwareBufferDescription.height;
p->width = hardwareBufferDescription.width;
TextureFormat textureFormat = mapToFilamentFormat(hardwareBufferDescription.format, sRGB);
p->format = textureFormat;
p->usage = mapToFilamentUsage(hardwareBufferDescription.usage, textureFormat);
return Platform::ExternalImageHandle{ p };
}
return Platform::ExternalImageHandle{};
}
} // namespace fvkandroid
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
ExternalImageHandleRef externalImage, VkDevice device) {
auto const* fvkExternalImage =
static_cast<fvkandroid::ExternalImageVulkanAndroid const*>(externalImage.get());
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(void* externalImage,
VkDevice device) {
ExternalImageMetadata metadata;
AHardwareBuffer* buffer = static_cast<AHardwareBuffer*>(externalImage);
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
if (__builtin_available(android 26, *)) {
AHardwareBuffer_Desc bufferDesc;
AHardwareBuffer_describe(buffer, &bufferDesc);
metadata.width = bufferDesc.width;
metadata.height = bufferDesc.height;
metadata.layers = bufferDesc.layers;
getVKFormatAndUsage(bufferDesc, metadata.format, metadata.usage, metadata.isProtected);
}
// In the unprotected case add R/W capabilities
if (metadata.isProtected == false) {
metadata.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
metadata.isProtected = isProtectedFromUsage(bufferDesc.usage);
std::tie(metadata.format, metadata.usage) =
getVKFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
}
metadata.samples = VK_SAMPLE_COUNT_1_BIT;
VkAndroidHardwareBufferFormatPropertiesANDROID formatInfo = {
.sType = VK_STRUCTURE_TYPE_ANDROID_HARDWARE_BUFFER_FORMAT_PROPERTIES_ANDROID,
@@ -197,26 +258,145 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataIm
};
VkResult result = vkGetAndroidHardwareBufferPropertiesANDROID(device, buffer, &properties);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkGetAndroidHardwareBufferProperties failed with error="
<< static_cast<int32_t>(result);
FILAMENT_CHECK_POSTCONDITION(metadata.format == formatInfo.format)
<< "mismatched image format for external image (AHB)";
<< "vkGetAndroidHardwareBufferProperties failed with error="
<< static_cast<int32_t>(result);
VkFormat bufferPropertiesFormat = transformVkFormat(formatInfo.format, fvkExternalImage->sRGB);
FILAMENT_CHECK_POSTCONDITION(metadata.format == bufferPropertiesFormat)
<< "mismatched image format( " << metadata.format << ") and queried format("
<< bufferPropertiesFormat << ") for external image (AHB)";
metadata.externalFormat = formatInfo.externalFormat;
metadata.allocationSize = properties.allocationSize;
metadata.memoryTypeBits = properties.memoryTypeBits;
return metadata;
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageImpl(void* externalImage,
VkDevice device, const VkAllocationCallbacks* allocator,
const ExternalImageMetadata& metadata) {
ImageData data = allocateExternalImage(externalImage, device, allocator, metadata);
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
auto const* fvkExternalImage =
static_cast<fvkandroid::ExternalImageVulkanAndroid const*>(externalImage.get());
ImageData data = allocateExternalImage(fvkExternalImage->aHardwareBuffer, device, metadata,
memoryTypeIndex, usage);
VkResult result = vkBindImageMemory(device, data.first, data.second, 0);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
return data;
}
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device, SamplerYcbcrConversion chroma,
uint32_t internalFormat, VkImage image, VkImageSubresourceRange range,
VkImageViewType viewType, VkComponentMapping swizzle){
VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.pNext = nullptr,
.externalFormat = internalFormat,
};
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
.pNext = &externalFormat,
.format = VK_FORMAT_UNDEFINED,
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
.components = fvkutils::getSwizzleMap(swizzleArray),
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
};
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
nullptr, &conversion);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create Ycbcr Conversion."
<< " error=" << static_cast<int32_t>(result);
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.pNext = nullptr,
.conversion = conversion,
};
VkImageViewCreateInfo viewInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = &samplerYcbcrConversionInfo,
.flags = 0,
.image = image,
.viewType = viewType,
.format = VK_FORMAT_UNDEFINED,
.components = swizzle,
.subresourceRange = range,
};
VkImageView imageView;
result = vkCreateImageView(device, &viewInfo, VKALLOC, &imageView);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create VkImageView."
<< " error=" << static_cast<int32_t>(result);
return imageView;
}
VkSampler VulkanPlatform::createExternalSamplerImpl(
VkDevice device, SamplerYcbcrConversion chroma, SamplerParams params,
uint32_t internalFormat) {
VkExternalFormatANDROID externalFormat = {
.sType = VK_STRUCTURE_TYPE_EXTERNAL_FORMAT_ANDROID,
.pNext = nullptr,
.externalFormat = internalFormat,
};
TextureSwizzle const swizzleArray[] = {chroma.r, chroma.g, chroma.b, chroma.a};
VkSamplerYcbcrConversionCreateInfo conversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_CREATE_INFO,
.pNext = &externalFormat,
.format = VK_FORMAT_UNDEFINED,
.ycbcrModel = fvkutils::getYcbcrModelConversion(chroma.ycbcrModel),
.ycbcrRange = fvkutils::getYcbcrRange(chroma.ycbcrRange),
.components = fvkutils::getSwizzleMap(swizzleArray),
.xChromaOffset = fvkutils::getChromaLocation(chroma.xChromaOffset),
.yChromaOffset = fvkutils::getChromaLocation(chroma.yChromaOffset),
.chromaFilter = fvkutils::getFilter(chroma.chromaFilter),
};
VkSamplerYcbcrConversion conversion = VK_NULL_HANDLE;
VkResult result = vkCreateSamplerYcbcrConversion(device, &conversionInfo,
nullptr, &conversion);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create Ycbcr Conversion."
<< " error=" << static_cast<int32_t>(result);
VkSamplerYcbcrConversionInfo samplerYcbcrConversionInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_YCBCR_CONVERSION_INFO,
.pNext = nullptr,
.conversion = conversion,
};
VkSamplerCreateInfo samplerInfo = {
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
.pNext = &samplerYcbcrConversionInfo,
.magFilter = fvkutils::getFilter(params.filterMag),
.minFilter = fvkutils::getFilter(params.filterMin),
.mipmapMode = fvkutils::getMipmapMode(params.filterMin),
.addressModeU = fvkutils::getWrapMode(params.wrapS),
.addressModeV = fvkutils::getWrapMode(params.wrapT),
.addressModeW = fvkutils::getWrapMode(params.wrapR),
.anisotropyEnable = params.anisotropyLog2 == 0 ? VK_FALSE : VK_TRUE,
.maxAnisotropy = (float)(1u << params.anisotropyLog2),
.compareEnable = fvkutils::getCompareEnable(params.compareMode),
.compareOp = fvkutils::getCompareOp(params.compareFunc),
.minLod = 0.0f,
.maxLod = fvkutils::getMaxLod(params.filterMin),
.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK,
.unnormalizedCoordinates = VK_FALSE,
};
VkSampler sampler;
result = vkCreateSampler(device, &samplerInfo, VKALLOC, &sampler);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create sampler."
<< " error=" << static_cast<int32_t>(result);
return sampler;
}
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
return {
VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
@@ -64,16 +64,29 @@ VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
}
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
void* externalImage, VkDevice device) {
ExternalImageHandleRef externalImage, VkDevice device) {
return {};
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageImpl(void* externalImage,
VkDevice device, const VkAllocationCallbacks* allocator,
const ExternalImageMetadata& metadata) {
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return {};
}
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device,
SamplerYcbcrConversion chroma,
SamplerParams sampler,
uint32_t internalFormat) {
return VK_NULL_HANDLE;
}
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
return VK_NULL_HANDLE;
}
VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWindow,
VkInstance instance, uint64_t flags) noexcept {
VkSurfaceKHR surface;
@@ -85,16 +85,29 @@ using namespace bluevk;
namespace filament::backend {
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
void* externalImage, VkDevice device) {
ExternalImageHandleRef externalImage, VkDevice device) {
return {};
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageImpl(void* externalImage,
VkDevice device, const VkAllocationCallbacks* allocator,
const ExternalImageMetadata& metadata) {
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata, uint32_t memoryTypeIndex, VkImageUsageFlags usage) {
return {};
}
VkSampler VulkanPlatform::createExternalSamplerImpl(VkDevice device,
SamplerYcbcrConversion chroma,
SamplerParams sampler,
uint32_t internalFormat) {
return VK_NULL_HANDLE;
}
VkImageView VulkanPlatform::createExternalImageViewImpl(VkDevice device,
SamplerYcbcrConversion chroma, uint32_t internalFormat, VkImage image,
VkImageSubresourceRange range, VkImageViewType viewType, VkComponentMapping swizzle) {
return VK_NULL_HANDLE;
}
VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
VulkanPlatform::ExtensionSet const ret = {
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
@@ -588,6 +588,79 @@ VkComponentMapping getSwizzleMap(TextureSwizzle const swizzle[4]) {
return map;
}
VkFilter getFilter(SamplerMinFilter filter) {
switch (filter) {
case SamplerMinFilter::NEAREST:
return VK_FILTER_NEAREST;
case SamplerMinFilter::LINEAR:
return VK_FILTER_LINEAR;
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
return VK_FILTER_NEAREST;
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
return VK_FILTER_LINEAR;
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
return VK_FILTER_NEAREST;
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
return VK_FILTER_LINEAR;
}
}
VkFilter getFilter(SamplerMagFilter filter) {
switch (filter) {
case SamplerMagFilter::NEAREST:
return VK_FILTER_NEAREST;
case SamplerMagFilter::LINEAR:
return VK_FILTER_LINEAR;
}
}
VkSamplerMipmapMode getMipmapMode(SamplerMinFilter filter) {
switch (filter) {
case SamplerMinFilter::NEAREST:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerMinFilter::LINEAR:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
return VK_SAMPLER_MIPMAP_MODE_NEAREST;
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
return VK_SAMPLER_MIPMAP_MODE_LINEAR;
}
}
VkSamplerAddressMode getWrapMode(SamplerWrapMode mode) {
switch (mode) {
case SamplerWrapMode::REPEAT:
return VK_SAMPLER_ADDRESS_MODE_REPEAT;
case SamplerWrapMode::CLAMP_TO_EDGE:
return VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;
case SamplerWrapMode::MIRRORED_REPEAT:
return VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT;
}
}
VkBool32 getCompareEnable(SamplerCompareMode mode) {
return mode == SamplerCompareMode::NONE ? VK_FALSE : VK_TRUE;
}
float getMaxLod(SamplerMinFilter filter) {
switch (filter) {
case SamplerMinFilter::NEAREST:
case SamplerMinFilter::LINEAR:
// The Vulkan spec recommends a max LOD of 0.25 to "disable" mipmapping.
// See "Mapping of OpenGL to Vulkan filter modes" in the VK Spec.
return 0.25f;
case SamplerMinFilter::NEAREST_MIPMAP_NEAREST:
case SamplerMinFilter::LINEAR_MIPMAP_NEAREST:
case SamplerMinFilter::NEAREST_MIPMAP_LINEAR:
case SamplerMinFilter::LINEAR_MIPMAP_LINEAR:
return VK_LOD_CLAMP_NONE;
}
}
VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags) {
VkShaderStageFlags flags = 0x0;
if (any(stageFlags & ShaderStageFlags::VERTEX)) flags |= VK_SHADER_STAGE_VERTEX_BIT;
@@ -56,6 +56,19 @@ uint32_t getComponentCount(VkFormat format);
VkComponentMapping getSwizzleMap(TextureSwizzle const swizzle[4]);
VkShaderStageFlags getShaderStageFlags(ShaderStageFlags stageFlags);
// Needed by the Platform for external sampler creation
VkFilter getFilter(SamplerMinFilter filter);
VkFilter getFilter(SamplerMagFilter filter);
VkSamplerMipmapMode getMipmapMode(SamplerMinFilter filter);
VkSamplerAddressMode getWrapMode(SamplerWrapMode mode);
VkBool32 getCompareEnable(SamplerCompareMode mode);
float getMaxLod(SamplerMinFilter filter);
// Ycbcr related functions
VkSamplerYcbcrModelConversion getYcbcrModelConversion(SamplerYcbcrModelConversion model);
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range);
VkChromaLocation getChromaLocation(ChromaLocation loc);
inline VkImageViewType getViewType(SamplerType target) {
switch (target) {
case SamplerType::SAMPLER_CUBEMAP:
@@ -193,6 +193,49 @@ uint8_t reduceSampleCount(uint8_t sampleCount, VkSampleCountFlags mask) {
return mostSignificantBit((sampleCount - 1) & mask);
}
VkSamplerYcbcrModelConversion getYcbcrModelConversion(
SamplerYcbcrModelConversion model) {
switch (model) {
case SamplerYcbcrModelConversion::RGB_IDENTITY:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_RGB_IDENTITY;
case SamplerYcbcrModelConversion::YCBCR_IDENTITY:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_IDENTITY;
case SamplerYcbcrModelConversion::YCBCR_709:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_709;
case SamplerYcbcrModelConversion::YCBCR_601:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_601;
case SamplerYcbcrModelConversion::YCBCR_2020:
return VK_SAMPLER_YCBCR_MODEL_CONVERSION_YCBCR_2020;
default:
assert_invariant(false &&
"Unknown data type, conversion is not supported.");
}
}
VkSamplerYcbcrRange getYcbcrRange(SamplerYcbcrRange range) {
switch (range) {
case SamplerYcbcrRange::ITU_FULL:
return VK_SAMPLER_YCBCR_RANGE_ITU_FULL;
case SamplerYcbcrRange::ITU_NARROW:
return VK_SAMPLER_YCBCR_RANGE_ITU_NARROW;
default:
assert_invariant(false &&
"Unknown data type, conversion is not supported.");
}
}
VkChromaLocation getChromaLocation(ChromaLocation loc) {
switch (loc) {
case ChromaLocation::COSITED_EVEN:
return VK_CHROMA_LOCATION_COSITED_EVEN;
case ChromaLocation::MIDPOINT:
return VK_CHROMA_LOCATION_MIDPOINT;
default:
assert_invariant(false &&
"Unknown data type, conversion is not supported.");
}
}
} // namespace filament::backend::fvkutils
bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange& b) {
@@ -0,0 +1,66 @@
/*
* 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_WEBGPUCONSTANTS_H
#define TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
#include <utils/Log.h>
#include <cstdint>
// FWGPU is short for Filament WebGPU
// turn on runtime validation, namely for debugging, that would normally not run (for release)
#define FWGPU_DEBUG_VALIDATION 0x00000001
// print/log system component details to the console, e.g. about the
// instance, surface, adapter, device, etc.
#define FWGPU_PRINT_SYSTEM 0x00000002
// Set this to enable logging "only" to one output stream. This is useful in the case where we want
// to debug with print statements and want ordered logging (e.g slog.i and slog.e will not appear in
// order of calls).
#define FWGPU_DEBUG_FORCE_LOG_TO_I 0x00000004
// Useful default combinations
#define FWGPU_DEBUG_EVERYTHING 0xFFFFFFFF
#if defined(FILAMENT_BACKEND_DEBUG_FLAG)
#define FWGPU_DEBUG_FORWARDED_FLAG (FILAMENT_BACKEND_DEBUG_FLAG & FWGPU_DEBUG_EVERYTHING)
#else
#define FWGPU_DEBUG_FORWARDED_FLAG 0
#endif
#ifndef NDEBUG
#define FWGPU_DEBUG_FLAGS FWGPU_DEBUG_FORWARDED_FLAG
#else
#define FWGPU_DEBUG_FLAGS 0
#endif
#define FWGPU_ENABLED(flags) (((FWGPU_DEBUG_FLAGS) & (flags)) == (flags))
#if FWGPU_ENABLED(FWGPU_DEBUG_FORCE_LOG_TO_I)
#define FWGPU_LOGI (utils::slog.i)
#define FWGPU_LOGD FWGPU_LOGI
#define FWGPU_LOGE FWGPU_LOGI
#define FWGPU_LOGW FWGPU_LOGI
#else
#define FWGPU_LOGE (utils::slog.e)
#define FWGPU_LOGW (utils::slog.w)
#define FWGPU_LOGD (utils::slog.d)
#define FWGPU_LOGI (utils::slog.i)
#endif
#endif// TNT_FILAMENT_BACKEND_WEBGPUCONSTANTS_H
+436 -11
View File
@@ -14,21 +14,254 @@
* limitations under the License.
*/
#include "webgpu/WebGPUDriver.h"
#include "webgpu/WebGPUConstants.h"
#include <backend/platforms/WebGPUPlatform.h>
#include "CommandStreamDispatcher.h"
#include "DriverBase.h"
#include "private/backend/Dispatcher.h"
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include "webgpu/WebGPUDriver.h"
#include "CommandStreamDispatcher.h"
#include <math/mat3.h>
#include <utils/CString.h>
#include <utils/ostream.h>
#include <stdint.h>
#include <dawn/webgpu_cpp_print.h>
#include <webgpu/webgpu_cpp.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <sstream>
#include <string_view>
#include <utility>
#include <variant>
namespace filament::backend {
Driver* WebGPUDriver::create() {
return new WebGPUDriver();
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("maxInterStageShaderComponents", limits.maxInterStageShaderComponents);
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 << " compatibility mode: " << bool(adapterInfo.compatibilityMode)
<< 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::SupportedLimits 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.limits);
}
}
#endif
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
void printSurfaceCapabilitiesDetails(wgpu::SurfaceCapabilities const& capabilities) {
std::stringstream usages_stream{};
usages_stream << capabilities.usages;
FWGPU_LOGI << "WebGPU surface capabilities:" << utils::io::endl;
FWGPU_LOGI << " surface usages: " << usages_stream.str().data() << 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 format_stream{};
format_stream << format;
FWGPU_LOGI << " " << format_stream.str().data() << utils::io::endl;
});
}
FWGPU_LOGI << " surface present modes (" << capabilities.presentModeCount
<< "):" << utils::io::endl;
if (capabilities.presentModeCount > 0 && capabilities.presentModes != nullptr) {
std::for_each(capabilities.presentModes,
capabilities.presentModes + capabilities.presentModeCount,
[](wgpu::PresentMode const presentMode) {
std::stringstream present_mode_stream{};
present_mode_stream << presentMode;
FWGPU_LOGI << " " << present_mode_stream.str().data() << utils::io::endl;
});
}
FWGPU_LOGI << " surface alpha modes (" << capabilities.alphaModeCount
<< "):" << utils::io::endl;
if (capabilities.alphaModeCount > 0 && capabilities.alphaModes != nullptr) {
std::for_each(capabilities.alphaModes,
capabilities.alphaModes + capabilities.alphaModeCount,
[](wgpu::CompositeAlphaMode const alphaMode) {
std::stringstream alpha_mode_stream{};
alpha_mode_stream << alphaMode;
FWGPU_LOGI << " " << alpha_mode_stream.str().data() << utils::io::endl;
});
}
}
#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::SupportedLimits 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.limits);
}
}
#endif
}// namespace
Driver* WebGPUDriver::create(WebGPUPlatform& platform) noexcept {
return new WebGPUDriver(platform);
}
WebGPUDriver::WebGPUDriver() noexcept = default;
WebGPUDriver::WebGPUDriver(WebGPUPlatform& platform) noexcept
: mPlatform(platform) {
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printInstanceDetails(mPlatform.getInstance());
#endif
}
WebGPUDriver::~WebGPUDriver() noexcept = default;
@@ -45,7 +278,7 @@ ShaderModel WebGPUDriver::getShaderModel() const noexcept {
}
ShaderLanguage WebGPUDriver::getShaderLanguage() const noexcept {
return ShaderLanguage::ESSL3;
return ShaderLanguage::WGSL;
}
// explicit instantiation of the Dispatcher
@@ -123,6 +356,188 @@ void WebGPUDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> tqh)
void WebGPUDriver::destroyDescriptorSet(Handle<HwDescriptorSet> tqh) {
}
Handle<HwSwapChain> WebGPUDriver::createSwapChainS() noexcept {
return Handle<HwSwapChain>((Handle<HwSwapChain>::HandleId) mNextFakeHandle++);
}
Handle<HwSwapChain> WebGPUDriver::createSwapChainHeadlessS() noexcept {
return Handle<HwSwapChain>((Handle<HwSwapChain>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::createTextureS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::importTextureS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwProgram> WebGPUDriver::createProgramS() noexcept {
return Handle<HwProgram>((Handle<HwProgram>::HandleId) mNextFakeHandle++);
}
Handle<HwFence> WebGPUDriver::createFenceS() noexcept {
return Handle<HwFence>((Handle<HwFence>::HandleId) mNextFakeHandle++);
}
Handle<HwTimerQuery> WebGPUDriver::createTimerQueryS() noexcept {
return Handle<HwTimerQuery>((Handle<HwTimerQuery>::HandleId) mNextFakeHandle++);
}
Handle<HwIndexBuffer> WebGPUDriver::createIndexBufferS() noexcept {
return Handle<HwIndexBuffer>((Handle<HwIndexBuffer>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::createTextureViewS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwBufferObject> WebGPUDriver::createBufferObjectS() noexcept {
return Handle<HwBufferObject>((Handle<HwBufferObject>::HandleId) mNextFakeHandle++);
}
Handle<HwRenderTarget> WebGPUDriver::createRenderTargetS() noexcept {
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
}
Handle<HwVertexBuffer> WebGPUDriver::createVertexBufferS() noexcept {
return Handle<HwVertexBuffer>((Handle<HwVertexBuffer>::HandleId) mNextFakeHandle++);
}
Handle<HwDescriptorSet> WebGPUDriver::createDescriptorSetS() noexcept {
return Handle<HwDescriptorSet>((Handle<HwDescriptorSet>::HandleId) mNextFakeHandle++);
}
Handle<HwRenderPrimitive> WebGPUDriver::createRenderPrimitiveS() noexcept {
return Handle<HwRenderPrimitive>((Handle<HwRenderPrimitive>::HandleId) mNextFakeHandle++);
}
Handle<HwVertexBufferInfo> WebGPUDriver::createVertexBufferInfoS() noexcept {
return Handle<HwVertexBufferInfo>((Handle<HwVertexBufferInfo>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::createTextureViewSwizzleS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwRenderTarget> WebGPUDriver::createDefaultRenderTargetS() noexcept {
return Handle<HwRenderTarget>((Handle<HwRenderTarget>::HandleId) mNextFakeHandle++);
}
Handle<HwDescriptorSetLayout> WebGPUDriver::createDescriptorSetLayoutS() noexcept {
return Handle<HwDescriptorSetLayout>(
(Handle<HwDescriptorSetLayout>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::createTextureExternalImageS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::createTextureExternalImage2S() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
Handle<HwTexture> WebGPUDriver::createTextureExternalImagePlaneS() noexcept {
return Handle<HwTexture>((Handle<HwTexture>::HandleId) mNextFakeHandle++);
}
void WebGPUDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
mSurface= mPlatform.createSurface(nativeWindow, flags);
mAdapter = mPlatform.requestAdapter(mSurface);
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printAdapterDetails(mAdapter);
wgpu::SurfaceCapabilities surfaceCapabilities{};
if (!mSurface.GetCapabilities(mAdapter, &surfaceCapabilities)) {
FWGPU_LOGW << "Failed to get WebGPU surface capabilities" << utils::io::endl;
} else {
printSurfaceCapabilitiesDetails(surfaceCapabilities);
}
#endif
mDevice = mPlatform.requestDevice(mAdapter);
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
printDeviceDetails(mDevice);
#endif
mQueue = mDevice.GetQueue();
// TODO configure the surface (maybe before or after creating the swapchain?
// how do we get the surface extent?)
// TODO actually create the swapchain
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."
<< 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
<< FWGPU_PRINT_SYSTEM << utils::io::dec
<< " bit flag on during build time the application would print system details "
"about the selected graphics device, surface, etc. To see this try "
"rebuilding Filament with that flag, e.g. ./build.sh -x "
<< FWGPU_PRINT_SYSTEM << " ..." << utils::io::endl;
#endif
}
void WebGPUDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
uint32_t height, uint64_t flags) {}
void WebGPUDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes) {}
void WebGPUDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint32_t vertexCount,
Handle<HwVertexBufferInfo> vbih) {}
void WebGPUDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
uint32_t indexCount, BufferUsage usage) {}
void WebGPUDriver::createBufferObjectR(Handle<HwBufferObject> boh, uint32_t byteCount,
BufferObjectBinding bindingType, BufferUsage usage) {}
void WebGPUDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage usage) {}
void WebGPUDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
uint8_t baseLevel, uint8_t levelCount) {}
void WebGPUDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTexture> srch,
backend::TextureSwizzle r, backend::TextureSwizzle g, backend::TextureSwizzle b,
backend::TextureSwizzle a) {}
void WebGPUDriver::createTextureExternalImage2R(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
Platform::ExternalImageHandleRef externalImage) {}
void WebGPUDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* externalImage) {}
void WebGPUDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, uint32_t plane) {}
void WebGPUDriver::importTextureR(Handle<HwTexture> th, intptr_t id, SamplerType target,
uint8_t levels, TextureFormat format, uint8_t samples, uint32_t w, uint32_t h,
uint32_t depth, TextureUsage usage) {}
void WebGPUDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
Handle<HwIndexBuffer> ibh, PrimitiveType pt) {}
void WebGPUDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {}
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) {}
void WebGPUDriver::createFenceR(Handle<HwFence> fh, int) {}
void WebGPUDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {}
void WebGPUDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh,
backend::DescriptorSetLayout&& info) {}
void WebGPUDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
Handle<HwDescriptorSetLayout> dslh) {}
Handle<HwStream> WebGPUDriver::createStreamNative(void* nativeStream) {
return {};
}
@@ -131,9 +546,11 @@ Handle<HwStream> WebGPUDriver::createStreamAcquired() {
return {};
}
void WebGPUDriver::setAcquiredImage(Handle<HwStream> sh, void* image,
CallbackHandler* handler, StreamCallback cb, void* userData) {
}
void WebGPUDriver::setAcquiredImage(Handle<HwStream> sh, void* image, const math::mat3f& transform,
CallbackHandler* handler, StreamCallback cb, void* userData) {}
void WebGPUDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh,
BufferObjectStreamDescriptor&& streams) {}
void WebGPUDriver::setStreamDimensions(Handle<HwStream> sh, uint32_t width, uint32_t height) {
}
@@ -234,10 +651,18 @@ uint8_t WebGPUDriver::getMaxDrawBuffers() {
return MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT;
}
size_t WebGPUDriver::getMaxUniformBufferSize() {
size_t WebGPUDriver::getMaxUniformBufferSize(SamplerType) {
return 16384u;
}
size_t WebGPUDriver::getMaxTextureSize() {
return 2048u;
}
size_t WebGPUDriver::getMaxArrayTextureLayers() {
return 256u;
}
void WebGPUDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
uint32_t byteOffset) {
scheduleDestroy(std::move(p));
+34 -17
View File
@@ -17,26 +17,44 @@
#ifndef TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
#define TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
#include "private/backend/Driver.h"
#include <backend/platforms/WebGPUPlatform.h>
#include "DriverBase.h"
#include "private/backend/Dispatcher.h"
#include "private/backend/Driver.h"
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
namespace filament::backend {
/**
* WebGPU backend (driver) implementation
*/
class WebGPUDriver final : public DriverBase {
WebGPUDriver() noexcept;
~WebGPUDriver() noexcept override;
Dispatcher getDispatcher() const noexcept final;
public:
static Driver* create();
~WebGPUDriver() noexcept override;
[[nodiscard]] Dispatcher getDispatcher() const noexcept final;
[[nodiscard]] static Driver* create(WebGPUPlatform& platform) noexcept;
private:
ShaderModel getShaderModel() const noexcept final;
ShaderLanguage getShaderLanguage() const noexcept final;
explicit WebGPUDriver(WebGPUPlatform& platform) noexcept;
[[nodiscard]] ShaderModel getShaderModel() const noexcept final;
[[nodiscard]] ShaderLanguage getShaderLanguage() const noexcept final;
uint64_t nextFakeHandle = 1;
// the platform (e.g. OS) specific aspects of the WebGPU backend are strictly only
// handled in the WebGPUPlatform
WebGPUPlatform& mPlatform;
wgpu::Surface mSurface = nullptr;
wgpu::Adapter mAdapter = nullptr;
wgpu::Device mDevice = nullptr;
wgpu::Queue mQueue = nullptr;
uint64_t mNextFakeHandle = 1;
/*
* Driver interface
@@ -45,20 +63,19 @@ private:
template<typename T>
friend class ConcreteDispatcher;
#define DECL_DRIVER_API(methodName, paramsDecl, params) \
#define DECL_DRIVER_API(methodName, paramsDecl, params) \
UTILS_ALWAYS_INLINE inline void methodName(paramsDecl);
#define DECL_DRIVER_API_SYNCHRONOUS(RetType, methodName, paramsDecl, params) \
#define DECL_DRIVER_API_SYNCHRONOUS(RetType, methodName, paramsDecl, params) \
RetType methodName(paramsDecl) override;
#define DECL_DRIVER_API_RETURN(RetType, methodName, paramsDecl, params) \
RetType methodName##S() noexcept override { \
return RetType((RetType::HandleId)nextFakeHandle++); } \
UTILS_ALWAYS_INLINE inline void methodName##R(RetType, paramsDecl) { }
#define DECL_DRIVER_API_RETURN(RetType, methodName, paramsDecl, params) \
RetType methodName##S() noexcept override; \
UTILS_ALWAYS_INLINE inline void methodName##R(RetType, paramsDecl);
#include "private/backend/DriverAPI.inc"
};
} // namespace filament
}// namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
#endif// TNT_FILAMENT_BACKEND_WEBGPUDRIVER_H
@@ -1,36 +0,0 @@
/*
* 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.
*/
#include "webgpu/WebGPUPlatform.h"
#include "webgpu/WebGPUDriver.h"
#include "utils/Log.h"
namespace filament::backend {
Driver* WebGPUPlatform::createDriver(void* const sharedContext , const Platform::DriverConfig& driverConfig) noexcept {
wgpu::InstanceDescriptor instance_descriptor {};
wgpu::Instance instance = wgpu::CreateInstance(&instance_descriptor);
if (instance) {
utils::slog.i << " WebGPU instance created\n";
} else {
utils::slog.e << " WebGPU instance failed to create\n";
return nullptr;
}
return WebGPUDriver::create();
}
} // namespace filament
@@ -1,42 +0,0 @@
/*
* 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_WEBGPUPLATFORM_H
#define TNT_FILAMENT_BACKEND_WEBGPUPLATFORM_H
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include "webgpu/webgpu_cpp.h"
namespace filament::backend {
class WebGPUPlatform
final : public Platform {
public:
int getOSVersion() const noexcept final { return 0; }
~WebGPUPlatform() noexcept override = default;
protected:
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
};
} // namespace filament
#endif // TNT_FILAMENT_BACKEND_WEBGPUPLATFORM_H
@@ -0,0 +1,159 @@
/*
* 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.
*/
#include <backend/platforms/WebGPUPlatform.h>
#include "webgpu/WebGPUConstants.h"
#include "webgpu/WebGPUDriver.h"
#include <backend/Platform.h>
#include <utils/Panic.h>
#include <utils/ostream.h>
#include <dawn/webgpu_cpp_print.h>
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
#include <sstream>
/**
* WebGPU Backend implementation common across platforms or operating systems (at least for now).
* Some of these functions may likely be refactored to platform/OS-specific implementations
* over time as needed. The caller of the WebGPUPlatform doesn't need to care which is the case.
*/
namespace filament::backend {
namespace {
//either returns a valid instance or panics
[[nodiscard]] wgpu::Instance createInstance() {
wgpu::DawnTogglesDescriptor dawnTogglesDescriptor{};
#if defined(FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING)
#if FWGPU_ENABLED(FWGPU_PRINT_SYSTEM)
FWGPU_LOGI << "setting on toggle enable_immediate_error_handling" << utils::io::endl;
#endif
/**
* Have the un-captured error callback invoked immediately when an error occurs, rather than
* waiting for the next Tick. This enables using the stack trace in which the un-captured error
* occurred when breaking into the un-captured error callback.
* https://crbug.com/dawn/1789
*/
static const char* toggleName = "enable_immediate_error_handling";
dawnTogglesDescriptor.enabledToggleCount = 1;
dawnTogglesDescriptor.enabledToggles = &toggleName;
#endif
wgpu::InstanceDescriptor instanceDescriptor{
.nextInChain = &dawnTogglesDescriptor,
.capabilities = {
.timedWaitAnyEnable = true// TODO consider using pure async instead
}
};
wgpu::Instance instance = wgpu::CreateInstance(&instanceDescriptor);
FILAMENT_CHECK_POSTCONDITION(instance != nullptr) << "Unable to create webgpu instance.";
return instance;
}
}// 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;
}
wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
// TODO consider passing required features and/or limits
wgpu::DeviceDescriptor deviceDescriptor{};
deviceDescriptor.label = "graphics_device";
deviceDescriptor.defaultQueue.label = "default_queue";
deviceDescriptor.SetDeviceLostCallback(wgpu::CallbackMode::AllowSpontaneous,
[](wgpu::Device const&, wgpu::DeviceLostReason const& reason,
wgpu::StringView message) {
if (reason == wgpu::DeviceLostReason::Destroyed) {
FWGPU_LOGD << "WebGPU device lost due to being destroyed (expected)"
<< utils::io::endl;
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;
});
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;
});
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;
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);
FILAMENT_CHECK_POSTCONDITION(device != nullptr)
<< "Failed to get a WebGPU device for the platform.";
return device;
}
Driver* WebGPUPlatform::createDriver(void* const sharedContext,
const Platform::DriverConfig& /*driverConfig*/) noexcept {
if (sharedContext) {
FWGPU_LOGW << "sharedContext is ignored/unused in the WebGPU backend. A non-null "
"sharedContext was provided, but it will be ignored."
<< utils::io::endl;
}
return WebGPUDriver::create(*this);
}
WebGPUPlatform::WebGPUPlatform()
: mInstance(createInstance()) {}
}// namespace filament::backend
@@ -0,0 +1,43 @@
/*
* 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.
*/
#include <backend/platforms/WebGPUPlatform.h>
#include <utils/Panic.h>
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
/**
* Android OS specific implementation aspects of the WebGPU backend
*/
namespace filament::backend {
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
wgpu::SurfaceSourceAndroidNativeWindow surfaceSourceAndroidWindow{};
surfaceSourceAndroidWindow.window = nativeWindow;
wgpu::SurfaceDescriptor surfaceDescriptor{
.nextInChain = &surfaceSourceAndroidWindow,
.label = "android_surface"
};
wgpu::Surface surface = mInstance.CreateSurface(&surfaceDescriptor);
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Android-backed surface.";
return surface;
}
}// namespace filament::backend
@@ -0,0 +1,81 @@
/*
* 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.
*/
#include <backend/platforms/WebGPUPlatform.h>
#include <utils/Panic.h>
#include <utils/ostream.h>
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
// Platform specific includes and defines
#if defined(__APPLE__)
#include <Cocoa/Cocoa.h>
#import <QuartzCore/CAMetalLayer.h>
#elif defined(FILAMENT_IOS)
// Metal is not available when building for the iOS simulator on Desktop.
#define METAL_AVAILABLE __has_include(<QuartzCore/CAMetalLayer.h>)
#if METAL_AVAILABLE
#import <Metal/Metal.h>
#import <QuartzCore/CAMetalLayer.h>
#endif
// is this needed?
#define METALVIEW_TAG 255
#else
#error Not a supported Apple + WebGPU platform
#endif
/**
* Apple (Mac OS and IOS) specific implementation aspects of the WebGPU backend
*/
namespace filament::backend {
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
wgpu::Surface surface = nullptr;
#if defined(__APPLE__)
auto nsView = (__bridge NSView*) nativeWindow;
FILAMENT_CHECK_POSTCONDITION(nsView) << "Unable to obtain Metal-backed NSView.";
[nsView setWantsLayer:YES];
id metalLayer = [CAMetalLayer layer];
[nsView setLayer:metalLayer];
wgpu::SurfaceSourceMetalLayer surfaceSourceMetalLayer{};
surfaceSourceMetalLayer.layer = (__bridge void*) metalLayer;
wgpu::SurfaceDescriptor surfaceDescriptor = {
.nextInChain = &surfaceSourceMetalLayer,
.label = "metal_surface",
};
surface = mInstance.CreateSurface(&surfaceDescriptor);
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Metal-backed surface.";
#elif defined(FILAMENT_IOS)
CAMetalLayer* metalLayer = (CAMetalLayer*) nativeWindow;
wgpu::SurfaceSourceMetalLayer surfaceSourceMetalLayer{};
surfaceSourceMetalLayer.layer = (__bridge void*) metalLayer;
wgpu::SurfaceDescriptor surfaceDescriptor = {
.nextInChain = &surfaceSourceMetalLayer,
.label = "metal_surface",
};
surface = mInstance.CreateSurface(&surfaceDescriptor);
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Metal-backed surface.";
#else
#error Not a supported Apple + WebGPU platform
#endif
return surface;
}
}// namespace filament::backend
@@ -0,0 +1,159 @@
/*
* 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.
*/
#include <backend/platforms/WebGPUPlatform.h>
#include <utils/Panic.h>
#include <webgpu/webgpu_cpp.h>
#include <cstddef>
#include <cstdint>
#if defined(__linux__) || defined(__FreeBSD__)
#define LINUX_OR_FREEBSD 1
#endif
// Platform specific includes and defines
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
#include <dlfcn.h>
namespace {
typedef struct _wl {
struct wl_display* display;
struct wl_surface* surface;
uint32_t width;
uint32_t height;
} wl;
}// namespace
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11)
// TODO: we should allow for headless on Linux explicitly. Right now this is the headless path
// (with no FILAMENT_SUPPORTS_XCB or FILAMENT_SUPPORTS_XLIB).
#include <dlfcn.h>
#if defined(FILAMENT_SUPPORTS_XCB)
#include <xcb/xcb.h>
namespace {
typedef xcb_connection_t* (*XCB_CONNECT)(const char* displayname, int* screenp);
}// namespace
#endif
#if defined(FILAMENT_SUPPORTS_XLIB)
#include <X11/Xlib.h>
namespace {
typedef Display* (*X11_OPEN_DISPLAY)(const char*);
}// namespace
#endif
static constexpr const char* LIBRARY_X11 = "libX11.so.6";
namespace {
struct XEnv {
#if defined(FILAMENT_SUPPORTS_XCB)
XCB_CONNECT xcbConnect;
xcb_connection_t* connection;
#endif
#if defined(FILAMENT_SUPPORTS_XLIB)
X11_OPEN_DISPLAY openDisplay;
Display* display;
#endif
void* library = nullptr;
} g_x11;
}// namespace
#else
#error Not a supported Linux or FeeBSD + WebGPU platform
#endif
/**
* Linux OS specific implementation aspects of the WebGPU Backend
*/
namespace filament::backend {
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t flags) {
wgpu::Surface surface = nullptr;
#if defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND)
wl* ptrval = reinterpret_cast<wl*>(nativeWindow);
wgpu::SurfaceSourceWaylandSurface surfaceSourceWayland{};
surfaceSourceWayland.display = ptrval->display;
surfaceSourceWayland.surface = ptrval->surface;
wgpu::SurfaceDescriptor surfaceDescriptor{
.nextInChain = &surfaceSourceWayland,
.label = "linux_wayland_surface"
};
surface = mInstance.CreateSurface(&surfaceDescriptor);
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
<< "Unable to create Linux Wayland-backed surface.";
#elif defined(LINUX_OR_FREEBSD) && defined(FILAMENT_SUPPORTS_X11)
if (g_x11.library == nullptr) {
g_x11.library = dlopen(LIBRARY_X11, RTLD_LOCAL | RTLD_NOW);
FILAMENT_CHECK_PRECONDITION(g_x11.library) << "Unable to open X11 library.";
#if defined(FILAMENT_SUPPORTS_XCB)
g_x11.xcbConnect = (XCB_CONNECT) dlsym(g_x11.library, "xcb_connect");
int screen = 0;
g_x11.connection = g_x11.xcbConnect(nullptr, &screen);
#endif
#if defined(FILAMENT_SUPPORTS_XLIB)
g_x11.openDisplay = (X11_OPEN_DISPLAY) dlsym(g_x11.library, "XOpenDisplay");
g_x11.display = g_x11.openDisplay(NULL);
FILAMENT_CHECK_PRECONDITION(g_x11.display) << "Unable to open X11 display.";
#endif
}
#if defined(FILAMENT_SUPPORTS_XCB) || defined(FILAMENT_SUPPORTS_XLIB)
bool useXcb = false;
#endif
#if defined(FILAMENT_SUPPORTS_XCB)
#if defined(FILAMENT_SUPPORTS_XLIB)
useXcb = (flags & SWAP_CHAIN_CONFIG_ENABLE_XCB) != 0;
#else
useXcb = true;
#endif
if (useXcb) {
wgpu::SurfaceSourceXCBWindow surfaceSourceXcb{};
surfaceSourceXcb.connection = g_x11.connection;
surfaceSourceXcb.window = reinterpret_cast<uint32_t>(nativeWindow);
wgpu::SurfaceDescriptor surfaceDescriptor{
.nextInChain = &surfaceSourceXcb,
.label = "linux_xcb_surface"
};
surface = mInstance.CreateSurface(&surfaceDescriptor);
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
<< "Unable to create Linux (or FreeBSD) XCB-backed surface.";
}
#endif
#if defined(FILAMENT_SUPPORTS_XLIB)
if (!useXcb) {
wgpu::SurfaceSourceXlibWindow surfaceSourceXlib{};
surfaceSourceXlib.display = g_x11.display;
surfaceSourceXlib.window = reinterpret_cast<uint64_t>(nativeWindow);
wgpu::SurfaceDescriptor surfaceDescriptor{
.nextInChain = &surfaceSourceXlib,
.label = "linux_xlib_surface"
};
surface = mInstance.CreateSurface(&surfaceDescriptor);
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
<< "Unable to create Linux (or FreeBSD) XLib-backed surface.";
}
#endif
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
<< "Cannot create WebGPU X11 surface for Linux (or FreeBSD) OS "
"(not built with support for XCB or XLIB?)";
#elif defined(__linux__)
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
<< "Cannot create WebGPU surface for Linux (or FreeBSD) OS "
"(not built with support for Wayland or X11?)";
#else
FILAMENT_CHECK_POSTCONDITION(surface != nullptr)
<< "Not a supported (Linux) OS + WebGPU platform";
#endif
return surface;
}
}// namespace filament::backend
@@ -0,0 +1,53 @@
/*
* 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.
*/
#include <backend/platforms/WebGPUPlatform.h>
#include <utils/Panic.h>
#include <webgpu/webgpu_cpp.h>
#include <cstdint>
/**
* Windows OS specific implementation aspects of the WebGPU backend
*/
namespace filament::backend {
wgpu::Surface WebGPUPlatform::createSurface(void* nativeWindow, uint64_t /*flags*/) {
// TODO verify this is necessary for Dawn implementation as well:
// On (at least) NVIDIA drivers, the Vulkan implementation (specifically the call to
// vkGetPhysicalDeviceSurfaceCapabilitiesKHR()) does not correctly handle the fact that
// each native window has its own DPI_AWARENESS_CONTEXT, and erroneously uses the context
// of the calling thread. As a workaround, we set the current thread's DPI_AWARENESS_CONTEXT
// to that of the native window we've been given. This isn't a perfect solution, because an
// application could create swap chains on multiple native windows with varying DPI-awareness,
// but even then, at least one of the windows would be guaranteed to work correctly.
SetThreadDpiAwarenessContext(GetWindowDpiAwarenessContext((HWND) nativeWindow));
wgpu::SurfaceSourceWindowsHWND surfaceSourceWin{};
surfaceSourceWin.hinstance = GetModuleHandle(nullptr);
surfaceSourceWin.hwnd = nativeWindow;
wgpu::SurfaceDescriptor surfaceDescriptor{
.nextInChain = &surfaceSourceWin,
.label = "windows_surface"
};
wgpu::Surface surface = mInstance.CreateSurface(&surfaceDescriptor);
FILAMENT_CHECK_POSTCONDITION(surface != nullptr) << "Unable to create Windows-backed surface.";
return surface;
}
}// namespace filament::backend
+92
View File
@@ -0,0 +1,92 @@
/*
* 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.
*/
#include "Lifetimes.h"
#include "backend/Platform.h"
#include "private/backend/DriverApi.h"
RenderFrame::RenderFrame(filament::backend::DriverApi& api): mApi(api) {
mApi.beginFrame(0, 0, 0);
}
RenderFrame::~RenderFrame() {
mApi.endFrame(0);
}
Cleanup::Cleanup(filament::backend::DriverApi& driverApi) : mDriverApi(driverApi) {}
Cleanup::~Cleanup() {
for (const auto swapChain: mSwapChains) {
mDriverApi.destroySwapChain(swapChain);
}
for (const auto descriptorSet : mDescriptorSets) {
mDriverApi.destroyDescriptorSet(descriptorSet);
}
for (const auto descriptorSetLayout : mDescriptorSetLayouts) {
mDriverApi.destroyDescriptorSetLayout(descriptorSetLayout);
}
for (const auto bufferObject : mBufferObjects) {
mDriverApi.destroyBufferObject(bufferObject);
}
for (const auto program : mPrograms) {
mDriverApi.destroyProgram(program);
}
for (const auto texture : mTextures) {
mDriverApi.destroyTexture(texture);
}
for (const auto renderTarget : mRenderTargets) {
mDriverApi.destroyRenderTarget(renderTarget);
}
for (const auto& callback : mCallbacks) {
callback();
}
}
template <>
void Cleanup::addInternal(filament::backend::SwapChainHandle handle) {
mSwapChains.push_back(handle);
}
template <>
void Cleanup::addInternal(filament::backend::DescriptorSetHandle handle) {
mDescriptorSets.push_back(handle);
}
template <>
void Cleanup::addInternal(
filament::backend::DescriptorSetLayoutHandle handle) {
mDescriptorSetLayouts.push_back(handle);
}
template <>
void Cleanup::addInternal(filament::backend::BufferObjectHandle handle) {
mBufferObjects.push_back(handle);
}
template <>
void Cleanup::addInternal(filament::backend::ProgramHandle handle) {
mPrograms.push_back(handle);
}
template <>
void Cleanup::addInternal(filament::backend::TextureHandle handle) {
mTextures.push_back(handle);
}
template <>
void Cleanup::addInternal(filament::backend::RenderTargetHandle handle) {
mRenderTargets.push_back(handle);
}
void Cleanup::addPostCall(std::function<void()> callback){
mCallbacks.emplace_back(std::move(callback));
}
+78
View File
@@ -0,0 +1,78 @@
/*
* 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_LIFETIMES_H
#define TNT_LIFETIMES_H
#include <vector>
#include <functional>
#include <optional>
#include "backend/Handle.h"
#include "backend/DriverApiForward.h"
#include "BackendTest.h"
class RenderFrame {
public:
explicit RenderFrame(filament::backend::DriverApi& api);
~RenderFrame();
RenderFrame(RenderFrame const& rhs) noexcept = delete;
RenderFrame& operator=(RenderFrame const& rhs) noexcept = delete;
RenderFrame(RenderFrame const&& rhs) noexcept = delete;
RenderFrame& operator=(RenderFrame const&& rhs) noexcept = delete;
private:
filament::backend::DriverApi& mApi;
};
class Cleanup {
public:
/**
* @param driverApi Must be valid for the entire lifetime of the Cleanup object.
*/
explicit Cleanup(filament::backend::DriverApi& driverApi);
~Cleanup();
template <typename HandleType>
filament::backend::Handle<HandleType> add(filament::backend::Handle<HandleType> handle);
void addPostCall(std::function<void()> callback);
private:
template <typename HandleType>
void addInternal(filament::backend::Handle<HandleType> handle);
filament::backend::DriverApi & mDriverApi;
std::vector<filament::backend::SwapChainHandle> mSwapChains;
std::vector<filament::backend::DescriptorSetHandle> mDescriptorSets;
std::vector<filament::backend::DescriptorSetLayoutHandle> mDescriptorSetLayouts;
std::vector<filament::backend::BufferObjectHandle> mBufferObjects;
std::vector<filament::backend::ProgramHandle> mPrograms;
std::vector<filament::backend::TextureHandle> mTextures;
std::vector<filament::backend::RenderTargetHandle> mRenderTargets;
std::vector<std::function<void()>> mCallbacks;
};
template <typename HandleType>
filament::backend::Handle<HandleType> Cleanup::add(filament::backend::Handle<HandleType> handle) {
addInternal(handle);
return handle;
}
#endif //TNT_LIFETIMES_H
+2 -1
View File
@@ -30,7 +30,8 @@ enum class Backend : uint8_t {
OPENGL = 1,
VULKAN = 2,
METAL = 3,
NOOP = 4,
WEBGPU = 4,
NOOP = 5,
};
struct NativeView {
+9 -1
View File
@@ -90,6 +90,9 @@ ShaderGenerator::ShaderGenerator(std::string vertex, std::string fragment, Backe
case Backend::METAL:
mShaderLanguage = filament::backend::ShaderLanguage::MSL;
break;
case Backend::WEBGPU:
mShaderLanguage = filament::backend::ShaderLanguage::WGSL;
break;
case Backend::NOOP:
mShaderLanguage = filament::backend::ShaderLanguage::ESSL3;
break;
@@ -115,6 +118,8 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(ShaderStage stage, std::s
shader.insert(pos, "#define TARGET_METAL_ENVIRONMENT\n");
} 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");
}
const char* shaderCString = shader.c_str();
@@ -150,7 +155,8 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(ShaderStage stage, std::s
assert_invariant(backend == Backend::OPENGL ||
backend == Backend::METAL ||
backend == Backend::VULKAN);
backend == Backend::VULKAN ||
backend == Backend::WEBGPU);
if (backend == Backend::OPENGL) {
if (isMobile) {
@@ -166,6 +172,8 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(ShaderStage stage, std::s
return { result.c_str(), result.c_str() + result.length() + 1 };
} else if (backend == Backend::VULKAN) {
return { (uint8_t*)spirv.data(), (uint8_t*)(spirv.data() + spirv.size()) };
} else if (backend == Backend::WEBGPU){
filamat::GLSLPostProcessor::spirvToWgsl(&spirv, &result);
}
return {};
+110 -141
View File
@@ -16,6 +16,7 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -195,6 +196,8 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
TEST_F(BackendTest, ColorMagnify) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 256;
constexpr int kSrcTexHeight = 256;
@@ -205,30 +208,30 @@ TEST_F(BackendTest, ColorMagnify) {
constexpr int kNumLevels = 3;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
Handle<HwTexture> const srcTexture = api.createTexture(
Handle<HwTexture> const srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
const bool flipY = sBackend == Backend::OPENGL;
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
// Create a destination texture.
Handle<HwTexture> const dstTexture = api.createTexture(
Handle<HwTexture> const dstTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
// Create a RenderTarget for each texture's miplevel.
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
Handle<HwRenderTarget> dstRenderTargets[kNumLevels];
for (uint8_t level = 0; level < kNumLevels; level++) {
srcRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {});
dstRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {});
srcRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {}));
dstRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {}));
}
// Do a "magnify" blit from level 1 of the source RT to the level 0 of the destination RT.
@@ -238,16 +241,18 @@ TEST_F(BackendTest, ColorMagnify) {
{0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel}, SamplerMagFilter::LINEAR);
// Push through an empty frame to allow the texture to upload and the blit to execute.
api.beginFrame(0, 0, 0);
api.commit(swapChain);
api.endFrame(0);
{
RenderFrame frame(api);
api.commit(swapChain);
}
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMagnify.png" };
api.beginFrame(0, 0, 0);
dumpScreenshot(api, dstRenderTargets[0], &params);
api.commit(swapChain);
api.endFrame(0);
{
RenderFrame frame(api);
dumpScreenshot(api, dstRenderTargets[0], &params);
api.commit(swapChain);
}
// Wait for the ReadPixels result to come back.
api.finish();
@@ -258,18 +263,12 @@ TEST_F(BackendTest, ColorMagnify) {
const uint32_t expected = 0x410bdd31;
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
EXPECT_TRUE(params.pixelHashResult == expected);
// Cleanup.
api.destroyTexture(srcTexture);
api.destroyTexture(dstTexture);
api.destroySwapChain(swapChain);
for (auto rt : srcRenderTargets) api.destroyRenderTarget(rt);
for (auto rt : dstRenderTargets) api.destroyRenderTarget(rt);
executeCommands();
}
TEST_F(BackendTest, ColorMinify) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 1024;
constexpr int kSrcTexHeight = 1024;
@@ -280,30 +279,30 @@ TEST_F(BackendTest, ColorMinify) {
constexpr int kNumLevels = 3;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
Handle<HwTexture> const srcTexture = api.createTexture(
Handle<HwTexture> const srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
const bool flipY = sBackend == Backend::OPENGL;
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
// Create a destination texture.
Handle<HwTexture> const dstTexture = api.createTexture(
Handle<HwTexture> const dstTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
// Create a RenderTarget for each texture's miplevel.
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
Handle<HwRenderTarget> dstRenderTargets[kNumLevels];
for (uint8_t level = 0; level < kNumLevels; level++) {
srcRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {});
dstRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {});
srcRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {}));
dstRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {}));
}
// Do a "minify" blit from level 1 of the source RT to the level 0 of the destination RT.
@@ -314,6 +313,7 @@ TEST_F(BackendTest, ColorMinify) {
srcRenderTargets[srcLevel], {0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel},
SamplerMagFilter::LINEAR);
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMinify.png" };
dumpScreenshot(api, dstRenderTargets[0], &params);
@@ -325,18 +325,11 @@ TEST_F(BackendTest, ColorMinify) {
const uint32_t expected = 0xf3d9c53f;
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
EXPECT_TRUE(params.pixelHashResult == expected);
// Cleanup.
api.destroyTexture(srcTexture);
api.destroyTexture(dstTexture);
api.destroySwapChain(swapChain);
for (auto rt : srcRenderTargets) api.destroyRenderTarget(rt);
for (auto rt : dstRenderTargets) api.destroyRenderTarget(rt);
executeCommands();
}
TEST_F(BackendTest, ColorResolve) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
constexpr int kSrcTexWidth = 256;
constexpr int kSrcTexHeight = 256;
@@ -356,40 +349,40 @@ TEST_F(BackendTest, ColorResolve) {
triangleVs, triangleFs, sBackend, sIsMobilePlatform, std::move(descriptors));
Program prog = shaderGen.getProgram(api);
prog.descriptorBindings(1, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }});
program = api.createProgram(std::move(prog));
program = cleanup.add(api.createProgram(std::move(prog)));
}
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
{{
DescriptorType::UNIFORM_BUFFER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}});
}}}));
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
// Create a VertexBuffer, IndexBuffer, and RenderPrimitive.
TrianglePrimitive const triangle(api);
// Create 4-sample texture.
Handle<HwTexture> const srcColorTexture = api.createTexture(
Handle<HwTexture> const srcColorTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, 1, kColorTexFormat, kSampleCount, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::COLOR_ATTACHMENT);
TextureUsage::COLOR_ATTACHMENT));
// Create 1-sample texture.
Handle<HwTexture> const dstColorTexture = api.createTexture(
Handle<HwTexture> const dstColorTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, 1, kColorTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
// Create a 4-sample render target with the 4-sample texture.
Handle<HwRenderTarget> const srcRenderTarget = api.createRenderTarget(
Handle<HwRenderTarget> const srcRenderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR, kSrcTexWidth, kSrcTexHeight, kSampleCount, 0,
{{ srcColorTexture }}, {}, {});
{{ srcColorTexture }}, {}, {}));
// Create a 1-sample render target with the 1-sample texture.
Handle<HwRenderTarget> const dstRenderTarget = api.createRenderTarget(
Handle<HwRenderTarget> const dstRenderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR, kDstTexWidth, kDstTexHeight, 1, 0,
{{ dstColorTexture }}, {}, {});
{{ dstColorTexture }}, {}, {}));
// Prep for rendering.
RenderPassParams params = {};
@@ -408,8 +401,8 @@ TEST_F(BackendTest, ColorResolve) {
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
auto ubuffer = api.createBufferObject(sizeof(MaterialParams),
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams),
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
// Draw red triangle into srcRenderTarget.
uploadUniforms(api, ubuffer, {
.color = float4(1, 0, 0, 1),
@@ -420,11 +413,12 @@ TEST_F(BackendTest, ColorResolve) {
api.bindDescriptorSet(descriptorSet, 1, {});
// FIXME: on Metal this triangle is not drawn. Can't understand why.
api.beginFrame(0, 0, 0);
{
RenderFrame frame(api);
api.beginRenderPass(srcRenderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
api.endFrame(0);
}
// Resolve the MSAA render target into the single-sample render target.
api.blitDEPRECATED(TargetBufferFlags::COLOR,
@@ -443,23 +437,15 @@ TEST_F(BackendTest, ColorResolve) {
const uint32_t expected = 0xebfac2ef;
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", sparams.pixelHashResult, expected);
EXPECT_TRUE(sparams.pixelHashResult == expected);
// Cleanup.
api.destroyDescriptorSet(descriptorSet);
api.destroyDescriptorSetLayout(descriptorSetLayout);
api.destroyBufferObject(ubuffer);
api.destroyProgram(program);
api.destroyTexture(srcColorTexture);
api.destroyTexture(dstColorTexture);
api.destroyRenderTarget(srcRenderTarget);
api.destroyRenderTarget(dstRenderTarget);
flushAndWait();
}
TEST_F(BackendTest, Blit2DTextureArray) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
api.startCapture(0);
cleanup.addPostCall([&]() { api.stopCapture(0); });
constexpr int kSrcTexWidth = 256;
constexpr int kSrcTexHeight = 256;
@@ -474,27 +460,27 @@ TEST_F(BackendTest, Blit2DTextureArray) {
constexpr int kDstTexLayer = 0;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
Handle<HwTexture> srcTexture = api.createTexture(
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D_ARRAY, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, kSrcTexDepth,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
const bool flipY = sBackend == Backend::OPENGL;
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, kSrcTexLayer, float3(0.5, 0, 0), flipY);
// Create a destination texture.
Handle<HwTexture> dstTexture = api.createTexture(
Handle<HwTexture> dstTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
// Create two RenderTargets.
const int level = 0;
Handle<HwRenderTarget> srcRenderTarget = api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, kSrcTexLayer }, {}, {});
Handle<HwRenderTarget> dstRenderTarget = api.createRenderTarget( TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, kDstTexLayer }, {}, {});
Handle<HwRenderTarget> srcRenderTarget = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, kSrcTexLayer }, {}, {}));
Handle<HwRenderTarget> dstRenderTarget = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, kDstTexLayer }, {}, {}));
// Do a blit from kSrcTexLayer of the source RT to kDstTexLayer of the destination RT.
const int srcLevel = 0;
@@ -503,16 +489,18 @@ TEST_F(BackendTest, Blit2DTextureArray) {
{0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel}, SamplerMagFilter::LINEAR);
// Push through an empty frame to allow the texture to upload and the blit to execute.
api.beginFrame(0, 0, 0);
api.commit(swapChain);
api.endFrame(0);
{
RenderFrame frame(api);
api.commit(swapChain);
}
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "Blit2DTextureArray.png" };
api.beginFrame(0, 0, 0);
dumpScreenshot(api, dstRenderTarget, &params);
api.commit(swapChain);
api.endFrame(0);
{
RenderFrame frame(api);
dumpScreenshot(api, dstRenderTarget, &params);
api.commit(swapChain);
}
// Wait for the ReadPixels result to come back.
api.finish();
@@ -523,20 +511,12 @@ TEST_F(BackendTest, Blit2DTextureArray) {
const uint32_t expected = 0x8de7d55b;
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
EXPECT_TRUE(params.pixelHashResult == expected);
// Cleanup.
api.destroyTexture(srcTexture);
api.destroyTexture(dstTexture);
api.destroySwapChain(swapChain);
api.destroyRenderTarget(srcRenderTarget);
api.destroyRenderTarget(dstRenderTarget);
executeCommands();
api.stopCapture(0);
}
TEST_F(BackendTest, BlitRegion) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 1024;
constexpr int kSrcTexHeight = 1024;
@@ -549,21 +529,21 @@ TEST_F(BackendTest, BlitRegion) {
constexpr int kDstLevel = 0;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
Handle<HwTexture> srcTexture = api.createTexture(
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
const bool flipY = sBackend == Backend::OPENGL;
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
// Create a destination texture.
Handle<HwTexture> dstTexture = api.createTexture(
Handle<HwTexture> dstTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
// Blit one-quarter of src level 1 to dst level 0.
Viewport srcRect = {
@@ -583,34 +563,34 @@ TEST_F(BackendTest, BlitRegion) {
// We purposely set the render target width and height to smaller than the texture, to check
// that this case is handled correctly.
Handle<HwRenderTarget> srcRenderTarget =
api.createRenderTarget(TargetBufferFlags::COLOR, srcRect.width,
srcRect.height, 1, 0, {srcTexture, kSrcLevel, 0}, {}, {});
cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR, srcRect.width,
srcRect.height, 1, 0, {srcTexture, kSrcLevel, 0}, {}, {}));
Handle<HwRenderTarget> dstRenderTarget =
api.createRenderTarget(TargetBufferFlags::COLOR, kDstTexWidth >> kDstLevel,
kDstTexHeight >> kDstLevel, 1, 0, {dstTexture, kDstLevel, 0}, {}, {});
cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR, kDstTexWidth >> kDstLevel,
kDstTexHeight >> kDstLevel, 1, 0, {dstTexture, kDstLevel, 0}, {}, {}));
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget, dstRect, srcRenderTarget, srcRect,
SamplerMagFilter::LINEAR);
// Push through an empty frame to allow the texture to upload and the blit to execute.
api.beginFrame(0, 0, 0);
api.commit(swapChain);
api.endFrame(0);
{
RenderFrame frame(api);
api.commit(swapChain);
}
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegion.png" };
api.beginFrame(0, 0, 0);
dumpScreenshot(api, dstRenderTarget, &params);
api.commit(swapChain);
api.endFrame(0);
{
RenderFrame frame(api);
dumpScreenshot(api, dstRenderTarget, &params);
api.commit(swapChain);
}
// Wait for the ReadPixels result to come back.
api.finish();
executeCommands();
getDriver().purge();
// Check if the image matches perfectly to our golden run.
//
// TODO: for some reason, this test has very, very slight (as in one pixel) differences between
// OpenGL and Metal. So disable golden checking for now.
// Use the compare tool from ImageMagick to see visual differences:
@@ -619,18 +599,12 @@ TEST_F(BackendTest, BlitRegion) {
// const uint32_t expected = 0x74fa34ed;
// printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
// EXPECT_TRUE(params.pixelHashResult == expected);
// Cleanup.
api.destroyTexture(srcTexture);
api.destroyTexture(dstTexture);
api.destroySwapChain(swapChain);
api.destroyRenderTarget(srcRenderTarget);
api.destroyRenderTarget(dstRenderTarget);
executeCommands();
}
TEST_F(BackendTest, BlitRegionToSwapChain) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 1024;
constexpr int kSrcTexHeight = 1024;
@@ -640,14 +614,15 @@ TEST_F(BackendTest, BlitRegionToSwapChain) {
constexpr int kNumLevels = 3;
// Create a SwapChain and make it current.
auto swapChain = createSwapChain();
Handle<HwRenderTarget> dstRenderTarget = api.createDefaultRenderTarget();
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
Handle<HwRenderTarget> dstRenderTarget = cleanup.add(api.createDefaultRenderTarget());
// Create a source texture.
Handle<HwTexture> srcTexture = api.createTexture(
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
const bool flipY = sBackend == Backend::OPENGL;
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
@@ -655,8 +630,8 @@ TEST_F(BackendTest, BlitRegionToSwapChain) {
// Create a RenderTarget for each texture's miplevel.
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
for (uint8_t level = 0; level < kNumLevels; level++) {
srcRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {});
srcRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {}));
}
// Blit one-quarter of src level 1 to dst level 0.
@@ -674,29 +649,23 @@ TEST_F(BackendTest, BlitRegionToSwapChain) {
.height = kDstTexHeight - 10,
};
api.beginFrame(0, 0, 0);
{
RenderFrame frame(api);
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
dstRect, srcRenderTargets[srcLevel],
srcRect, SamplerMagFilter::LINEAR);
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
dstRect, srcRenderTargets[srcLevel],
srcRect, SamplerMagFilter::LINEAR);
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain.png" };
dumpScreenshot(api, dstRenderTarget, &params);
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain.png" };
dumpScreenshot(api, dstRenderTarget, &params);
api.commit(swapChain);
api.endFrame(0);
api.commit(swapChain);
}
// Wait for the ReadPixels result to come back.
api.finish();
executeCommands();
getDriver().purge();
// Cleanup.
api.destroyTexture(srcTexture);
api.destroySwapChain(swapChain);
for (auto rt : srcRenderTargets) api.destroyRenderTarget(rt);
executeCommands();
}
} // namespace test
+7
View File
@@ -106,6 +106,13 @@ public:
/** @return Whether a combination of texture format, pixel format and type is valid. */
static bool validatePixelFormatAndType(InternalFormat internalFormat, Format format, Type type) noexcept;
/** @return the maximum size in texels of a texture of type \p type. At least 2048 for
* 2D textures, 256 for 3D textures. */
static size_t getMaxTextureSize(Engine& engine, Sampler type) noexcept;
/** @return the maximum number of layers supported by texture arrays. At least 256. */
static size_t getMaxArrayTextureLayers(Engine& engine) noexcept;
/**
* Options for environment prefiltering into reflection map
*
+2
View File
@@ -62,6 +62,8 @@ constexpr std::pair<ChunkType, ChunkType> shaderLanguageToTags(ShaderLanguage co
return { MaterialEssl1, DictionaryText };
case ShaderLanguage::MSL:
return { MaterialMetal, DictionaryText };
case ShaderLanguage::WGSL:
return { MaterialWgsl, DictionaryText };
case ShaderLanguage::SPIRV:
return { MaterialSpirv, DictionarySpirv };
case ShaderLanguage::METAL_LIBRARY:
+8
View File
@@ -108,4 +108,12 @@ bool Texture::validatePixelFormatAndType(InternalFormat internalFormat, Format f
return FTexture::validatePixelFormatAndType(internalFormat, format, type);
}
size_t Texture::getMaxTextureSize(Engine& engine, Sampler type) noexcept {
return FTexture::getMaxTextureSize(downcast(engine), type);
}
size_t Texture::getMaxArrayTextureLayers(Engine& engine) noexcept {
return FTexture::getMaxArrayTextureLayers(downcast(engine));
}
} // namespace filament
+4 -3
View File
@@ -388,12 +388,13 @@ void FMaterial::terminate(FEngine& engine) {
<< "destroying material \"" << this->getName().c_str_safe() << "\" but "
<< pos->second.size() << " instances still alive.";
} else {
utils::slog.e << "destroying material \"" << this->getName().c_str_safe() << "\" but "
<< pos->second.size() << " instances still alive.";
if (UTILS_UNLIKELY(!pos->second.empty())) {
slog.e << "destroying material \"" << this->getName().c_str_safe() << "\" but "
<< pos->second.size() << " instances still alive." << io::endl;
}
}
}
#if FILAMENT_ENABLE_MATDBG
// Unregister the material with matdbg.
matdbg::DebugServer* server = engine.debug.server;
+22
View File
@@ -164,6 +164,20 @@ Texture* Texture::Builder::build(Engine& engine) {
(isProtectedTexturesSupported && useProtectedMemory) || !useProtectedMemory)
<< "Texture is PROTECTED but protected textures are not supported";
size_t const maxTextureDimension = getMaxTextureSize(engine, mImpl->mTarget);
size_t const maxTextureDepth = (mImpl->mTarget == Sampler::SAMPLER_2D_ARRAY ||
mImpl->mTarget == Sampler::SAMPLER_CUBEMAP_ARRAY)
? getMaxArrayTextureLayers(engine)
: maxTextureDimension;
FILAMENT_CHECK_PRECONDITION(
mImpl->mWidth <= maxTextureDimension &&
mImpl->mHeight <= maxTextureDimension &&
mImpl->mDepth <= maxTextureDepth) << "Texture dimensions out of range: "
<< "width= " << mImpl->mWidth << " (>" << maxTextureDimension << ")"
<<", height= " << mImpl->mHeight << " (>" << maxTextureDimension << ")"
<< ", depth= " << mImpl->mDepth << " (>" << maxTextureDepth << ")";
const auto validateSamplerType = [&engine = downcast(engine)](SamplerType const sampler) -> bool {
switch (sampler) {
case SamplerType::SAMPLER_2D:
@@ -672,6 +686,14 @@ bool FTexture::isTextureSwizzleSupported(FEngine& engine) noexcept {
return engine.getDriverApi().isTextureSwizzleSupported();
}
size_t FTexture::getMaxTextureSize(FEngine& engine, Sampler type) noexcept {
return engine.getDriverApi().getMaxTextureSize(type);
}
size_t FTexture::getMaxArrayTextureLayers(FEngine& engine) noexcept {
return engine.getDriverApi().getMaxArrayTextureLayers();
}
size_t FTexture::computeTextureDataSize(Format const format, Type const type,
size_t const stride, size_t const height, size_t const alignment) noexcept {
return PixelBufferDescriptor::computeDataSize(format, type, stride, height, alignment);
+4
View File
@@ -124,6 +124,10 @@ public:
static bool validatePixelFormatAndType(backend::TextureFormat internalFormat,
backend::PixelDataFormat format, backend::PixelDataType type) noexcept;
static size_t getMaxTextureSize(FEngine& engine, Sampler type) noexcept;
static size_t getMaxArrayTextureLayers(FEngine& engine) noexcept;
bool textureHandleCanMutate() const noexcept;
void updateLodRange(uint8_t level) noexcept;
@@ -78,6 +78,10 @@ fragment {
void dummy(){}
vec4 resolveFragment() {
#ifdef TARGET_WEBGPU_ENVIRONMENT
//WebGPU Can not handle subpasses.
return vec4(0,0,0,1);
#else
#if POST_PROCESS_OPAQUE
return vec4(subpassLoad(materialParams_colorBuffer).rgb, 1.0);
#else
@@ -85,6 +89,7 @@ vec4 resolveFragment() {
color.rgb *= 1.0 / (color.a + FLT_EPS);
return color;
#endif
#endif
}
void postProcess(inout PostProcessInputs postProcess) {
@@ -39,11 +39,16 @@ fragment {
void dummy(){}
vec4 resolveFragment() {
#ifdef TARGET_WEBGPU_ENVIRONMENT
//WebGPU Can not handle subpasses.
return vec4(0,0,0,1);
#else
#if POST_PROCESS_OPAQUE
return vec4(subpassLoad(materialParams_colorBuffer).rgb, 1.0);
#else
return subpassLoad(materialParams_colorBuffer);
#endif
#endif
}
void postProcess(inout PostProcessInputs postProcess) {
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.58.1"
spec.version = "1.58.0"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.58.1/filament-v1.58.1-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.58.0/filament-v1.58.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
+4
View File
@@ -52,6 +52,10 @@ include_directories(${PUBLIC_HDR_DIR})
# we're building a library
add_library(${TARGET} STATIC ${PUBLIC_HDRS} ${SRCS})
if(FILAMENT_SUPPORTS_OSMESA)
target_compile_options(${TARGET} PRIVATE -I${FILAMENT_OSMESA_PATH}/include/GL)
endif()
# specify where the public headers of this library are
target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
set_target_properties(${TARGET} PROPERTIES FOLDER Libs)
+11 -2
View File
@@ -14,16 +14,21 @@
* limitations under the License.
*/
#include <GL/gl.h>
#include <dlfcn.h>
#include <string.h>
#include <osmesa.h>
namespace bluegl {
namespace {
using ProcAddressFunc = void*(*)(char const* funcName);
}
// This is to ensure that linking during compilation will not fail even if
// OSMesaGetProcAddress is not linked.
__attribute__((weak)) OSMESAproc OSMesaGetProcAddress(char const*);
struct Driver {
ProcAddressFunc OSMesaGetProcAddress;
void* library;
@@ -31,7 +36,7 @@ struct Driver {
bool initBinder() {
constexpr char const* libraryNames[] = {"libOSMesa.so", "libosmesa.so"};
for (char const* name : libraryNames) {
for (char const* name: libraryNames) {
g_driver.library = dlopen(name, RTLD_GLOBAL | RTLD_NOW);
if (g_driver.library) {
break;
@@ -45,6 +50,10 @@ bool initBinder() {
(ProcAddressFunc) dlsym(g_driver.library, "OSMesaGetProcAddress");
}
if (!g_driver.OSMesaGetProcAddress) {
g_driver.OSMesaGetProcAddress = (ProcAddressFunc) OSMesaGetProcAddress;
}
return g_driver.OSMesaGetProcAddress;
}
+9 -9
View File
@@ -346,7 +346,7 @@ class FrameGraphTable extends LitElement {
super();
this.frameGraphData = null;
this.selectedResourceId = -1;
this.collapsedResources = new Set();
this.expandedResourceSet = new Set();
}
updated(props) {
@@ -365,11 +365,11 @@ class FrameGraphTable extends LitElement {
}
_toggleCollapse(resourceIndex) {
if (this.collapsedResources.has(resourceIndex)) {
this.collapsedResources.delete(resourceIndex);
if (this.expandedResourceSet.has(resourceIndex)) {
this.expandedResourceSet.delete(resourceIndex);
}
else {
this.collapsedResources.add(resourceIndex);
this.expandedResourceSet.add(resourceIndex);
}
this.requestUpdate();
}
@@ -430,9 +430,9 @@ class FrameGraphTable extends LitElement {
.map(subresource => subresource.id);
const hasSubresources = subresourceIds.length > 0;
const isCollapsed = this.collapsedResources.has(resourceIndex);
const isExpanded = this.expandedResourceSet.has(resourceIndex);
// Show the aggregated resource usage when the subresources are collapsed.
const resourceIds = isCollapsed ? [resource.id, ...subresourceIds]:[resource.id];
const resourceIds = isExpanded ? [resource.id]:[resource.id, ...subresourceIds];
const onClickResource = () => this._handleResourceClick(resource.id);
const selectedStyle = resource.id === this.selectedResourceId ? "selected" : "";
@@ -444,15 +444,15 @@ class FrameGraphTable extends LitElement {
? html`
<span class="toggle-icon"
@click="${(e) => { e.stopPropagation(); this._toggleCollapse(resourceIndex); }}">
${isCollapsed ? '▶' : '▼'}
${isExpanded ? '▼' : '▶'}
</span>`
: nothing}
${resource.name}
${hasSubresources && isCollapsed ? html`(${subresourceIds.length})` : nothing}
${hasSubresources && isExpanded ? nothing : html`(${subresourceIds.length})`}
</th>
${this._renderResourceUsage(allPasses, resourceIds, DEFAULT_COLOR)}
</tr>
${!isCollapsed ? this._renderSubresourceRows(resources, resource, resourceIndex, allPasses) : nothing}
${isExpanded ? this._renderSubresourceRows(resources, resource, resourceIndex, allPasses) : nothing}
`;
}
@@ -45,6 +45,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
MaterialEssl1 = charTo64bitNum("MAT_ESS1"),
MaterialSpirv = charTo64bitNum("MAT_SPIR"),
MaterialMetal = charTo64bitNum("MAT_METL"),
MaterialWgsl = charTo64bitNum("MAT_WGSL"),
MaterialMetalLibrary = charTo64bitNum("MAT_MLIB"),
MaterialShaderModels = charTo64bitNum("MAT_SMDL"),
MaterialBindingUniformInfo = charTo64bitNum("MAT_UFRM"),
@@ -28,7 +28,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 58;
static constexpr size_t MATERIAL_VERSION = 57;
/**
* Supported shading models
+1
View File
@@ -183,6 +183,7 @@ bool MaterialChunk::getShader(ShaderContent& shaderContent, BlobDictionary const
switch (mMaterialTag) {
case filamat::ChunkType::MaterialGlsl:
case filamat::ChunkType::MaterialEssl1:
case filamat::ChunkType::MaterialWgsl:
case filamat::ChunkType::MaterialMetal:
return getTextShader(mUnflattener, dictionary, shaderContent, shaderModel, variant, stage);
case filamat::ChunkType::MaterialSpirv:
+6
View File
@@ -144,6 +144,12 @@ set(FILAMAT_LIB_NAME ${CMAKE_STATIC_LIBRARY_PREFIX}filamat${CMAKE_STATIC_LIBRARY
install(FILES "${FILAMAT_COMBINED_OUTPUT}" DESTINATION lib/${DIST_DIR} RENAME ${FILAMAT_LIB_NAME})
install(DIRECTORY ${PUBLIC_HDR_DIR}/filamat DESTINATION include)
# Need to install libtint for filamat on Android.
# See libs/filamat/CMakeLists.txt and android/filamat-android/CMakeLists.txt.
if (ANDROID)
target_link_libraries(${TARGET} libtint_combined)
endif()
# ==================================================================================================
# Tests
# ==================================================================================================
@@ -81,7 +81,11 @@ public:
VULKAN = 0x02u,
METAL = 0x04u,
WEBGPU = 0x08u,
#ifdef FILAMENT_SUPPORTS_WEBGPU
ALL = OPENGL | VULKAN | METAL | WEBGPU
#else
ALL = OPENGL | VULKAN | METAL
#endif
};
/*
+21 -10
View File
@@ -518,8 +518,13 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl,
}
}
bool GLSLPostProcessor::spirvToWgsl(const SpirvBlob *spirv, std::string *outWsl) {
bool GLSLPostProcessor::spirvToWgsl(SpirvBlob *spirv, std::string *outWsl) {
#if FILAMENT_SUPPORTS_WEBGPU
// We need to remove dead code for our variant-filter workaround for WebGPU to work
// This is especially relevant for removing the push constants that morphing uses when not disabled
spv::spirvbin_t remapper(0);
remapper.remap(*spirv, spv::spirvbin_base_t::DCE_ALL);
//Currently no options we want to use
const tint::spirv::reader::Options readerOpts{};
tint::wgsl::writer::Options writerOpts{};
@@ -527,11 +532,15 @@ bool GLSLPostProcessor::spirvToWgsl(const SpirvBlob *spirv, std::string *outWsl)
tint::Program tintRead = tint::spirv::reader::Read(*spirv, readerOpts);
if (tintRead.Diagnostics().ContainsErrors()) {
slog.w << "This tint reader error is currently ignored during WebGPU bringup:" << tintRead.Diagnostics().Str() << io::endl;
//TODO: We should actually return false here, but for initial debugging
// let it slide until combined image sampler is resolved
// return false;
//TODO remove this if block once combined image sampler conversion works.
if (tintRead.Diagnostics().Str().rfind("error: WGSL does not support combined image-samplers") != std::string::npos) {
slog.w << "This tint reader error is currently ignored during WebGPU bringup: " << tintRead.Diagnostics().Str() << io::endl;
return false;
}
else {
slog.e << "Tint Reader Error: " << tintRead.Diagnostics().Str() << io::endl;
return false;
}
}
tint::Result<tint::wgsl::writer::Output> wgslOut = tint::wgsl::writer::Generate(tintRead,writerOpts);
@@ -539,10 +548,8 @@ bool GLSLPostProcessor::spirvToWgsl(const SpirvBlob *spirv, std::string *outWsl)
tint::SuccessType tintSuccess;
if (wgslOut != tintSuccess) {
slog.w << "This tint writer error is currently ignored during WebGPU bringup:" << wgslOut.Failure().reason.Str() << io::endl;
//TODO: We should actually return false here, but for initial debugging
// let it slide until combined image sampler is resolved
// return false;
slog.e << "Tint writer error: " << wgslOut.Failure().reason.Str() << io::endl;
return false;
}
*outWsl = wgslOut->wgsl;
return true;
@@ -913,6 +920,10 @@ std::shared_ptr<spvtools::Optimizer> GLSLPostProcessor::createOptimizer(
optimizer->RegisterPass(CreateRedundancyEliminationPass());
optimizer->RegisterPass(CreateAggressiveDCEPass());
}
if (config.targetApi == MaterialBuilder::TargetApi::WEBGPU) {
optimizer->RegisterPass(CreateSplitCombinedImageSamplerPass());
optimizer->RegisterPass(CreateResolveBindingConflictsPass());
}
return optimizer;
}
+1 -1
View File
@@ -90,7 +90,7 @@ public:
bool useFramebufferFetch, const DescriptorSets& descriptorSets,
const ShaderMinifier* minifier);
static bool spirvToWgsl(const SpirvBlob* spirv, std::string* outWsl);
static bool spirvToWgsl(SpirvBlob* spirv, std::string* outWsl);
private:
struct InternalConfig {
+30 -1
View File
@@ -179,6 +179,14 @@ void MaterialBuilderBase::prepare(bool vulkanSemantics,
effectiveFeatureLevel,
});
}
if (any(mTargetApi & TargetApi::WEBGPU)) {
mCodeGenPermutations.push_back({
shaderModel,
TargetApi::WEBGPU,
TargetLanguage::SPIRV,
effectiveFeatureLevel,
});
}
}
}
@@ -836,6 +844,7 @@ static void showErrorMessage(const char* materialName, filament::Variant variant
break;
case TargetApi::WEBGPU:
targetApiString = "WebGPU.\n";
break;
case TargetApi::ALL:
assert(0); // Unreachable.
break;
@@ -879,6 +888,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
std::vector<TextEntry> essl1Entries;
std::vector<BinaryEntry> spirvEntries;
std::vector<TextEntry> metalEntries;
std::vector<TextEntry> wgslEntries;
LineDictionary textDictionary;
BlobDictionary spirvDictionary;
// End: must be protected by lock
@@ -907,7 +917,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
// Metal Shading Language is cross-compiled from Vulkan.
const bool targetApiNeedsSpirv =
(targetApi == TargetApi::VULKAN || targetApi == TargetApi::METAL);
(targetApi == TargetApi::VULKAN || targetApi == TargetApi::METAL || targetApi == TargetApi::WEBGPU);
const bool targetApiNeedsMsl = targetApi == TargetApi::METAL;
const bool targetApiNeedsWgsl = targetApi == TargetApi::WEBGPU;
const bool targetApiNeedsGlsl = targetApi == TargetApi::OPENGL;
@@ -933,14 +943,17 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
TextEntry glslEntry{};
BinaryEntry spirvEntry{};
TextEntry metalEntry{};
TextEntry wgslEntry{};
glslEntry.shaderModel = params.shaderModel;
spirvEntry.shaderModel = params.shaderModel;
metalEntry.shaderModel = params.shaderModel;
wgslEntry.shaderModel = params.shaderModel;
glslEntry.variant = v.variant;
spirvEntry.variant = v.variant;
metalEntry.variant = v.variant;
wgslEntry.variant = v.variant;
// Generate raw shader code.
// The quotes in Google-style line directives cause problems with certain drivers. These
@@ -1038,6 +1051,12 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
switch (targetApi) {
// TODO: Handle webgpu here
case TargetApi::WEBGPU:
assert(!spirv.empty());
assert(wgsl.length() > 0);
wgslEntry.stage = v.stage;
wgslEntry.shader = wgsl;
wgslEntries.push_back(wgslEntry);
break;
case TargetApi::ALL:
// should never happen
break;
@@ -1099,6 +1118,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
std::sort(essl1Entries.begin(), essl1Entries.end(), compare);
std::sort(spirvEntries.begin(), spirvEntries.end(), compare);
std::sort(metalEntries.begin(), metalEntries.end(), compare);
std::sort(wgslEntries.begin(), wgslEntries.end(), compare);
// Generate the dictionaries.
for (const auto& s : glslEntries) {
@@ -1114,6 +1134,9 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
for (const auto& s : metalEntries) {
textDictionary.addText(s.shader);
}
for (const auto& s : wgslEntries) {
textDictionary.addText(s.shader);
}
// Emit dictionary chunk (TextDictionaryReader and DictionaryTextChunk)
const auto& dictionaryChunk = container.push<filamat::DictionaryTextChunk>(
@@ -1144,6 +1167,12 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
dictionaryChunk.getDictionary(), ChunkType::MaterialMetal);
}
// Emit WGSL chunk (MaterialTextChunk).
if (!wgslEntries.empty()) {
container.push<MaterialTextChunk>(std::move(wgslEntries),
dictionaryChunk.getDictionary(), ChunkType::MaterialWgsl);
}
return true;
}
+6 -1
View File
@@ -525,6 +525,11 @@ EShMessages GLSLTools::glslangFlagsFromTargetApi(
// choke on gl_VertexIndex.
msg |= (Type)EShMessages::EShMsgVulkanRules;
}
if (targetApi == TargetApi::WEBGPU) {
// FIXME: We have to use EShMsgVulkanRules for WEBGPU, otherwise compilation will
// choke on gl_VertexIndex.
msg |= (Type)EShMessages::EShMsgVulkanRules;
}
return (EShMessages)msg;
}
}
@@ -540,13 +545,13 @@ void GLSLTools::prepareShaderParser(MaterialBuilder::TargetApi targetApi,
shader.setEnvInput(EShSourceGlsl, stage, EShClientOpenGL, version);
shader.setEnvClient(EShClientOpenGL, EShTargetOpenGL_450);
break;
case MaterialBuilderBase::TargetApi::WEBGPU:
case MaterialBuilderBase::TargetApi::VULKAN:
case MaterialBuilderBase::TargetApi::METAL:
shader.setEnvInput(EShSourceGlsl, stage, EShClientVulkan, version);
shader.setEnvClient(EShClientVulkan, EShTargetVulkan_1_1);
break;
// TODO: Handle webgpu here
case MaterialBuilderBase::TargetApi::WEBGPU:
case MaterialBuilderBase::TargetApi::ALL:
// can't happen
break;
@@ -175,6 +175,11 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
break;
// TODO: Handle webgpu here
case TargetApi::WEBGPU:
//For now, no differences so inherit the same changes.
// TODO Define a separte environment, OR relevant checks
out << "#define TARGET_VULKAN_ENVIRONMENT\n";
out << "#define TARGET_WEBGPU_ENVIRONMENT\n";
break;
case TargetApi::ALL:
// invalid should never happen
break;
@@ -190,6 +195,7 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
}
if (mTargetApi == TargetApi::VULKAN ||
mTargetApi == TargetApi::WEBGPU ||
mTargetApi == TargetApi::METAL ||
(mTargetApi == TargetApi::OPENGL && mShaderModel == ShaderModel::DESKTOP) ||
mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_2) {
@@ -725,6 +731,8 @@ io::sstream& CodeGenerator::generateBufferInterfaceBlock(io::sstream& out, Shade
break;
// TODO: Handle webgpu here
case TargetApi::WEBGPU:
out << "set = " << +set << ", binding = " << +binding << ", ";
break;
case TargetApi::ALL:
// nonsensical, shouldn't happen.
break;
@@ -813,6 +821,8 @@ io::sstream& CodeGenerator::generateCommonSamplers(utils::io::sstream& out,
break;
// TODO: Handle webgpu here
case TargetApi::WEBGPU:
out << "layout(binding = " << +info.binding << ", set = " << +set << ") ";
break;
case TargetApi::ALL:
// should not happen
break;
+4 -2
View File
@@ -796,8 +796,10 @@ std::string ShaderGenerator::createPostProcessFragmentProgram(ShaderModel sm,
cg.generateCommonSamplers(fs, DescriptorSetBindingPoints::PER_MATERIAL, material.sib);
// subpass
CodeGenerator::generatePostProcessSubpass(fs, material.subpass);
// Subpasses are not yet in WebGPU, https://github.com/gpuweb/gpuweb/issues/435
CodeGenerator::generatePostProcessSubpass(fs, targetApi == MaterialBuilderBase::TargetApi::WEBGPU
? SubpassInfo{}
: material.subpass);
CodeGenerator::generatePostProcessCommon(fs, ShaderStage::FRAGMENT);
CodeGenerator::generatePostProcessGetters(fs, ShaderStage::FRAGMENT);
+72 -1
View File
@@ -891,7 +891,78 @@ TEST_F(MaterialCompiler, FeatureLevel0Ess3CallFails) {
EXPECT_FALSE(result.isValid());
}
#if FILAMENT_SUPPORTS_WEBGPU
TEST_F(MaterialCompiler, WgslConversionBakedColor) {
std::string bakedColorCodeFrag(R"(
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor = getColor();
}
)");
filamat::MaterialBuilder builder;
builder.targetApi(filamat::MaterialBuilderBase::TargetApi::WEBGPU);
builder.material(bakedColorCodeFrag.c_str());
builder.shading(filamat::MaterialBuilder::Shading::UNLIT);
builder.name("BakedColor");
builder.culling(CullingMode::NONE);
builder.featureLevel(FeatureLevel::FEATURE_LEVEL_0);
builder.require(filament::VertexAttribute::COLOR);
builder.variantFilter(static_cast<filament::UserVariantFilterMask>(
filament::UserVariantFilterBit::SKINNING | filament::UserVariantFilterBit::STE));
filamat::Package result = builder.build(*jobSystem);
EXPECT_TRUE(result.isValid());
}
TEST_F(MaterialCompiler, WgslConversionSandboxLitTransparent) {
std::string litTransparentCodeFrag(R"(
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor.rgb = materialParams.baseColor * materialParams.alpha;
material.baseColor.a = materialParams.alpha;
material.roughness = materialParams.roughness;
material.metallic = materialParams.metallic;
material.reflectance = materialParams.reflectance;
material.sheenColor = materialParams.sheenColor;
material.sheenRoughness = materialParams.sheenRoughness;
material.clearCoat = materialParams.clearCoat;
material.clearCoatRoughness = materialParams.clearCoatRoughness;
material.anisotropy = materialParams.anisotropy;
material.emissive = materialParams.emissive;
}
)");
filamat::MaterialBuilder builder;
builder.targetApi(filamat::MaterialBuilderBase::TargetApi::WEBGPU);
builder.material(litTransparentCodeFrag.c_str());
builder.shading(filamat::MaterialBuilder::Shading::LIT);
builder.name("LitTransparent");
builder.blending(filament::BlendingMode::TRANSPARENT);
builder.specularAntiAliasing(true);
builder.parameter("alpha", 1, UniformType::FLOAT);
builder.parameter("baseColor", 1, UniformType::FLOAT3);
builder.parameter("roughness", 1, UniformType::FLOAT);
builder.parameter("metallic", 1, UniformType::FLOAT);
builder.parameter("reflectance", 1, UniformType::FLOAT);
builder.parameter("sheenColor", 1, UniformType::FLOAT3);
builder.parameter("sheenRoughness", 1, UniformType::FLOAT);
builder.parameter("clearCoat", 1, UniformType::FLOAT);
builder.parameter("clearCoatRoughness", 1, UniformType::FLOAT);
builder.parameter("anisotropy", 1, UniformType::FLOAT);
builder.parameter("emissive", 1, UniformType::FLOAT4);
builder.variantFilter(static_cast<filament::UserVariantFilterMask>(
filament::UserVariantFilterBit::SKINNING | filament::UserVariantFilterBit::STE));
filamat::Package result = builder.build(*jobSystem);
EXPECT_TRUE(result.isValid());
}
#endif
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
const int rv = RUN_ALL_TESTS();
if (testing::UnitTest::GetInstance()->test_to_run_count() == 0) {
//If you run a test filter that contains 0 tests that was likely not intentional. Fail in that scenario.
return 1;
}
return rv;
}
+4
View File
@@ -254,6 +254,10 @@ bool JsonWriter::writeActiveInfo(const filaflat::ChunkContainer& package,
json << "metal";
chunkType = ChunkType::MaterialMetal;
break;
case ShaderLanguage::WGSL:
json << "webgpu";
chunkType = ChunkType::MaterialWgsl;
break;
default:
return false;
}
+4
View File
@@ -57,6 +57,10 @@ ShaderExtractor::ShaderExtractor(backend::ShaderLanguage target, const void* dat
mMaterialTag = ChunkType::MaterialMetalLibrary;
mDictionaryTag = ChunkType::DictionaryMetalLibrary;
break;
case backend::ShaderLanguage::WGSL:
mMaterialTag = ChunkType::MaterialWgsl;
mDictionaryTag = ChunkType::DictionaryText;
break;
case backend::ShaderLanguage::SPIRV:
mMaterialTag = ChunkType::MaterialSpirv;
mDictionaryTag = ChunkType::DictionarySpirv;
+4
View File
@@ -135,6 +135,10 @@ ShaderReplacer::ShaderReplacer(Backend backend, ShaderLanguage language,
mMaterialTag = ChunkType::MaterialMetal;
mDictionaryTag = ChunkType::DictionaryText;
break;
case backend::ShaderLanguage::WGSL:
mMaterialTag = ChunkType::MaterialWgsl;
mDictionaryTag = ChunkType::DictionaryText;
break;
case backend::ShaderLanguage::SPIRV:
mMaterialTag = ChunkType::MaterialSpirv;
mDictionaryTag = ChunkType::DictionarySpirv;
+7 -1
View File
@@ -429,7 +429,10 @@ static bool printShaderInfo(ostream& text, const ChunkContainer& container, Chun
break;
case ChunkType::MaterialMetalLibrary:
text << "Metal precompiled shader libraries:" << endl;
break;
break;
case ChunkType::MaterialWgsl:
text << "WGSL precompiled shader libraries:" << endl;
break;
default:
assert(false && "Invalid shader ChunkType");
break;
@@ -467,6 +470,9 @@ bool TextWriter::writeMaterialInfo(const filaflat::ChunkContainer& container) {
if (!printShaderInfo(text, container, ChunkType::MaterialMetalLibrary)) {
return false;
}
if (!printShaderInfo(text, container, ChunkType::MaterialWgsl)) {
return false;
}
printChunks(text, container);
+28 -2
View File
@@ -20,7 +20,6 @@ set(MATERIAL_SRCS
materials/aiDefaultMat.mat
materials/bakedColor.mat
materials/bakedTexture.mat
materials/pointSprites.mat
materials/aoPreview.mat
materials/arrayTexture.mat
materials/groundShadow.mat
@@ -42,6 +41,12 @@ set(MATERIAL_SRCS
materials/texturedLit.mat
)
# Tint does not support setting gl_PointSize, disable the relevant sample if using WebGPU
# https://github.com/gpuweb/gpuweb/issues/1190
if (NOT FILAMENT_SUPPORTS_WEBGPU)
set(MATERIAL_SRCS ${MATERIAL_SRCS} materials/pointSprites.mat)
endif ()
if (CMAKE_CROSSCOMPILING)
include(${IMPORT_EXECUTABLES})
endif()
@@ -240,6 +245,20 @@ function(add_demo NAME)
endif()
endfunction()
function(add_demo_plain NAME)
include_directories(${GENERATION_ROOT})
add_executable(${NAME} ${NAME}.cpp)
target_compile_options(${NAME} PRIVATE ${COMPILER_FLAGS})
set_target_properties(${NAME} PROPERTIES FOLDER Samples)
# This is needed after XCode 15.3
if (APPLE)
set_target_properties(${NAME} PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)
set_target_properties(${NAME} PROPERTIES INSTALL_RPATH /usr/local/lib)
endif()
endfunction()
if (NOT ANDROID)
add_demo(animation)
add_demo(depthtesting)
@@ -258,7 +277,11 @@ if (NOT ANDROID)
add_demo(lightbulb)
add_demo(material_sandbox)
add_demo(multiple_windows)
add_demo(point_sprites)
# Tint does not support setting gl_PointSize, disable the relevant sample if using WebGPU
# https://github.com/gpuweb/gpuweb/issues/1190
if (NOT FILAMENT_SUPPORTS_WEBGPU)
add_demo(point_sprites)
endif ()
add_demo(rendertarget)
add_demo(sample_cloth)
add_demo(sample_full_pbr)
@@ -271,6 +294,9 @@ if (NOT ANDROID)
add_demo(vbotest)
add_demo(viewtest)
add_demo_plain(spirv_test)
target_link_libraries(spirv_test PRIVATE SPIRV-Headers utils)
# Sample app specific
target_link_libraries(frame_generator PRIVATE imageio)
target_link_libraries(gltf_viewer PRIVATE gltf-demo-resources uberarchive gltfio viewer)
+9 -2
View File
@@ -77,7 +77,11 @@ static void printUsage(char* name) {
" --help, -h\n"
" Prints this message\n\n"
" --api, -a\n"
" Specify the backend API: opengl, vulkan, or metal\n"
" Specify the backend API: opengl, vulkan, metal, or webgpu\n"
" (note: webgpu is a no-op for now, printing backend\n"
" component info if FILAMENT_BACKEND_DEBUG_FLAG, \n"
" set at build time, includes the \n"
" FWGPU_PRINT_SYSTEM bit flag 0x2)\n"
);
const std::string from("HELLOTRIANGLE");
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
@@ -109,8 +113,11 @@ static int handleCommandLineArguments(int argc, char* argv[], App* app) {
app->config.backend = Engine::Backend::VULKAN;
} else if (arg == "metal") {
app->config.backend = Engine::Backend::METAL;
} else if (arg == "webgpu") {
app->config.backend = Engine::Backend::WEBGPU;
} else {
std::cerr << "Unrecognized backend. Must be 'opengl'|'vulkan'|'metal'.\n";
std::cerr << "Unrecognized backend. Must be "
"'opengl'|'vulkan'|'metal'|'webgpu'.\n";
exit(1);
}
break;
+145
View File
@@ -0,0 +1,145 @@
#include "spirv/unified1/spirv.hpp"
#include <utils/Log.h>
#include <functional>
#include <fstream>
#include <vector>
#include <unordered_map>
size_t getFileSize(const std::string& filename) {
std::ifstream file(filename, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
return -1;
}
size_t size = file.tellg();
file.close();
return size;
}
std::vector<uint32_t> transform(std::vector<uint32_t> const& spirv) {
constexpr size_t HEADER_SIZE = 5;
size_t const dataSize = spirv.size();
std::vector<uint32_t> output;
output.resize(dataSize);
uint32_t const* data = (uint32_t*) spirv.data();
uint32_t* outputData = output.data();
memcpy(&outputData[0], &data[0], HEADER_SIZE * 4);
struct Descriptor {
uint32_t binding = 0xFFFFFFFF;
uint32_t set = 0xFFFFFFFF;
bool fixed = false;
};
std::unordered_map<uint32_t, Descriptor> descriptors;
auto getDesc = [&](uint32_t var) -> Descriptor& {
using itertype = std::unordered_map<uint32_t, Descriptor>::iterator;
itertype itr;
if (itr = descriptors.find(var); itr == descriptors.end()) {
itr = descriptors.insert({var, {}}).first;
}
return itr->second;
};
auto pass = [&](uint32_t targetOp, std::function<void(uint32_t, uint32_t)> f) {
for (uint32_t cursor = HEADER_SIZE, cursorEnd = dataSize; cursor < cursorEnd;) {
uint32_t const firstWord = data[cursor];
uint32_t const wordCount = firstWord >> 16;
uint32_t const op = firstWord & 0x0000FFFF;
if (targetOp == op) {
f(wordCount-1, cursor+1);
}
cursor += wordCount;
}
};
uint32_t samplerTypeId = 0;
uint32_t imageTypeId = 0;
// First we find the ID used to identify the sampler type and the image type.
pass(spv::Op::OpTypeSampler, [&](uint32_t count, uint32_t pos) {
samplerTypeId = data[pos];
});
pass(spv::Op::OpTypeImage, [&](uint32_t count, uint32_t pos) {
imageTypeId = data[pos];
});
// Next we identify all the descriptors and record their set id and binding.
pass(spv::Op::OpDecorate, [&](uint32_t count, uint32_t pos) {
uint32_t const type = data[pos + 1];
if (type == spv::Decoration::DecorationBinding) {
uint32_t const targetVar = data[pos];
uint32_t const binding = data[pos + 2];
auto& desc = getDesc(targetVar);
desc.binding = binding;
// Note these decorations do not need to be written to the output.
} else if (type == spv::Decoration::DecorationDescriptorSet) {
uint32_t const targetVar = data[pos];
uint32_t const set = data[pos + 2];
auto& desc = getDesc(targetVar);
desc.set = set;
}
});
// If any descriptor is loaded to be a sampler, we offset it by a fixed amount. Note that it
// could be "loaded" multiple times, so we use a boolean to skip if it's already offsetted.
constexpr uint32_t SAMPLER_BINDING_OFFSET = 100;
pass(spv::Op::OpLoad, [&](uint32_t count, uint32_t pos) {
uint32_t const retType = data[pos];
uint32_t const targetVar = data[pos + 2];
if (retType == samplerTypeId) {
auto& desc = getDesc(targetVar);
if (!desc.fixed) {
desc.binding += SAMPLER_BINDING_OFFSET;
desc.fixed = true;
}
}
});
// Write out the offsetted bindings and copy the rest.
for (uint32_t cursor = HEADER_SIZE, cursorEnd = dataSize; cursor < cursorEnd;) {
uint32_t const firstWord = data[cursor];
uint32_t const wordCount = firstWord >> 16;
uint32_t const op = firstWord & 0x0000FFFF;
uint32_t* out = &outputData[cursor];
std::memcpy(out, &data[cursor], wordCount * 4);
if (op == spv::Op::OpDecorate && data[cursor + 2] == spv::Decoration::DecorationBinding) {
uint32_t const targetVar = data[cursor + 1];
auto const& modified = getDesc(targetVar);
out[3] = modified.binding;
}
cursor += wordCount;
}
// Note that you should be able to do this in-place (without making another vector).
return output;
}
int main(int argc, char* argv[]) {
if (argc < 3) {
utils::slog.e <<"need input and output spirv filenames" << utils::io::endl;
return 1;
}
std::string infname {argv[1]};
size_t const fsize = getFileSize(infname);
size_t const wordCount = fsize / 4;
std::ifstream inputFile(infname, std::ios::binary);
std::vector<uint32_t> words;
words.resize(wordCount);
inputFile.read((char*) words.data(), fsize);
inputFile.close();
std::string outfname {argv[2]};
std::vector<uint32_t> output = transform(words);
std::ofstream outputFile(outfname, std::ios::binary);
outputFile.write((char const*) output.data(), output.size() * 4);
outputFile.close();
return 0;
}
+5 -2
View File
@@ -35,10 +35,13 @@ if [[ "$GITHUB_WORKFLOW" ]]; then
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${GITHUB_CLANG_VERSION} 100
else
set +e
apt-get -y build-dep mesa
sudo apt -y remove llvm-18 llvm-18-*
sudo apt-get -y build-dep mesa
sudo apt -y remove llvm-18 llvm-18-* llvm-19 llvm-19-*
set -e
CURRENT_CLANG_VERSION=$(clang --version | head -n 1 | awk '{ print $4 }' | awk 'BEGIN { FS="\\." } { print $1 }')
GITHUB_CLANG_VERSION=${GITHUB_CLANG_VERSION:-${CURRENT_CLANG_VERSION}}
sudo apt-get -y install clang-${GITHUB_CLANG_VERSION} \
libc++-${GITHUB_CLANG_VERSION}-dev \
libc++abi-${GITHUB_CLANG_VERSION}-dev \
+54 -2
View File
@@ -12,7 +12,59 @@ set(DAWN_ABSEIL_DIR ${EXTERNAL}/abseil)
set(DAWN_SPIRV_TOOLS_DIR ${EXTERNAL}/spirv-tools/)
set(DAWN_SPIRV_HEADERS_DIR ${EXTERNAL}/spirv-headers)
set(DAWN_GLSLANG_DIR ${EXTERNAL}/glslang/)
if(IS_HOST_PLATFORM)
set(DAWN_DXC_ENABLE_ASSERTS_IN_NDEBUG OFF)
#set(TINT_BUILD_CMD_TOOLS OFF)
if (FILAMENT_BUILD_FILAMAT)
set(TINT_BUILD_SPV_READER ON)
endif ()
add_subdirectory(../ ${PROJECT_BINARY_DIR}/third_party/dawn)
add_subdirectory(../ ${PROJECT_BINARY_DIR}/third_party/dawn)
# Need to install libtint for filamat on Android.
# See third_party/dawn/tnt/CMakeLists.txt and android/filamat-android/CMakeLists.txt.
if (ANDROID AND FILAMENT_BUILD_FILAMAT)
set(COMBINED_TARGET libtint_combined)
set(COMBINED_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${COMBINED_TARGET}.a")
# TODO: maybe use this target in place of libtint in filamat proper. (it's smaller)
set(LIBTINT_DEPS
tint_lang_core
tint_lang_core_constant
tint_lang_core_intrinsic
tint_lang_core_type
tint_lang_spirv_reader
tint_lang_spirv_reader_ast_lower
tint_lang_spirv_reader_ast_parser
tint_lang_spirv_reader_common
tint_lang_spirv_writer
tint_lang_wgsl
tint_lang_wgsl_ast
tint_lang_wgsl_ast_transform
tint_lang_wgsl_features
tint_lang_wgsl_intrinsic
tint_lang_wgsl_program
tint_lang_wgsl_resolver
tint_lang_wgsl_sem
tint_lang_wgsl_writer
tint_lang_wgsl_writer_ast_printer
tint_results
tint_utils
tint_utils_diagnostic
tint_utils_ice
tint_utils_result
tint_utils_rtti
tint_utils_strconv
tint_utils_symbol
tint_utils_text
)
# Use the following no-op definitions to introduce a library and its dependency.
add_library(${COMBINED_TARGET} STATIC dummy.cpp)
target_link_libraries(${COMBINED_TARGET} PRIVATE
${LIBTINT_DEPS}
)
combine_static_libs(${COMBINED_TARGET} "${COMBINED_OUTPUT}" "${LIBTINT_DEPS}")
install(FILES "${COMBINED_OUTPUT}" DESTINATION lib/${DIST_DIR})
endif()
+80 -9
View File
@@ -30,15 +30,44 @@
#
# Bazel Build for Google C++ Testing Framework(Google Test)
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
exports_files(["LICENSE"])
config_setting(
name = "qnx",
constraint_values = ["@platforms//os:qnx"],
)
config_setting(
name = "windows",
constraint_values = ["@bazel_tools//platforms:windows"],
constraint_values = ["@platforms//os:windows"],
)
config_setting(
name = "freebsd",
constraint_values = ["@platforms//os:freebsd"],
)
config_setting(
name = "openbsd",
constraint_values = ["@platforms//os:openbsd"],
)
# NOTE: Fuchsia is not an officially supported platform.
config_setting(
name = "fuchsia",
constraint_values = ["@platforms//os:fuchsia"],
)
config_setting(
name = "msvc_compiler",
flag_values = {
"@bazel_tools//tools/cpp:compiler": "msvc-cl",
},
visibility = [":__subpackages__"],
)
config_setting(
@@ -54,6 +83,10 @@ cc_library(
)
# Google Test including Google Mock
# For an actual test, use `gtest` and also `gtest_main` if you depend on gtest's
# main(). For a library, use `gtest_for_library` instead if the library can be
# testonly.
cc_library(
name = "gtest",
srcs = glob(
@@ -76,6 +109,7 @@ cc_library(
"googlemock/include/gmock/*.h",
]),
copts = select({
":qnx": [],
":windows": [],
"//conditions:default": ["-pthread"],
}),
@@ -94,22 +128,59 @@ cc_library(
"googletest/include",
],
linkopts = select({
":qnx": ["-lregex"],
":windows": [],
":freebsd": [
"-lm",
"-pthread",
],
":openbsd": [
"-lm",
"-pthread",
],
"//conditions:default": ["-pthread"],
}),
deps = select({
":has_absl": [
"@com_google_absl//absl/debugging:failure_signal_handler",
"@com_google_absl//absl/debugging:stacktrace",
"@com_google_absl//absl/debugging:symbolize",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:optional",
"@com_google_absl//absl/types:variant",
"@abseil-cpp//absl/container:flat_hash_set",
"@abseil-cpp//absl/debugging:failure_signal_handler",
"@abseil-cpp//absl/debugging:stacktrace",
"@abseil-cpp//absl/debugging:symbolize",
"@abseil-cpp//absl/flags:flag",
"@abseil-cpp//absl/flags:parse",
"@abseil-cpp//absl/flags:reflection",
"@abseil-cpp//absl/flags:usage",
"@abseil-cpp//absl/strings",
"@abseil-cpp//absl/types:any",
"@abseil-cpp//absl/types:optional",
"@abseil-cpp//absl/types:variant",
"@re2//:re2",
],
"//conditions:default": [],
}) + select({
# `gtest-death-test.cc` has `EXPECT_DEATH` that spawns a process,
# expects it to crash and inspects its logs with the given matcher,
# so that's why these libraries are needed.
# Otherwise, builds targeting Fuchsia would fail to compile.
":fuchsia": [
"@fuchsia_sdk//pkg/fdio",
"@fuchsia_sdk//pkg/syslog",
"@fuchsia_sdk//pkg/zx",
],
"//conditions:default": [],
}),
)
# `gtest`, but testonly. See guidance on `gtest` for when to use this.
alias(
name = "gtest_for_library",
actual = ":gtest",
testonly = True,
)
# Implements main() for tests using gtest. Prefer to depend on `gtest` as well
# to ensure compliance with the layering_check Bazel feature where only the
# direct hdrs values are available.
cc_library(
name = "gtest_main",
srcs = ["googlemock/src/gmock_main.cc"],
+15 -15
View File
@@ -1,23 +1,13 @@
# Note: CMake support is community-based. The maintainers do not use CMake
# internally.
cmake_minimum_required(VERSION 2.8.8)
if (POLICY CMP0048)
cmake_policy(SET CMP0048 NEW)
endif (POLICY CMP0048)
cmake_minimum_required(VERSION 3.16)
project(googletest-distribution)
set(GOOGLETEST_VERSION 1.9.0)
set(GOOGLETEST_VERSION 1.16.0)
if (CMAKE_VERSION VERSION_LESS "3.1")
add_definitions(-std=c++11)
else()
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT CYGWIN)
set(CMAKE_CXX_EXTENSIONS OFF)
endif()
if(NOT CYGWIN AND NOT MSYS AND NOT ${CMAKE_SYSTEM_NAME} STREQUAL QNX)
set(CMAKE_CXX_EXTENSIONS OFF)
endif()
enable_testing()
@@ -25,9 +15,19 @@ enable_testing()
include(CMakeDependentOption)
include(GNUInstallDirs)
#Note that googlemock target already builds googletest
# Note that googlemock target already builds googletest.
option(BUILD_GMOCK "Builds the googlemock subproject" ON)
option(INSTALL_GTEST "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" ON)
option(GTEST_HAS_ABSL "Use Abseil and RE2. Requires Abseil and RE2 to be separately added to the build." OFF)
if(GTEST_HAS_ABSL)
if(NOT TARGET absl::base)
find_package(absl REQUIRED)
endif()
if(NOT TARGET re2::re2)
find_package(re2 REQUIRED)
endif()
endif()
if(BUILD_GMOCK)
add_subdirectory( googlemock )
+31 -32
View File
@@ -21,14 +21,14 @@ accept your pull requests.
## Are you a Googler?
If you are a Googler, please make an attempt to submit an internal change rather
than a GitHub Pull Request. If you are not able to submit an internal change a
If you are a Googler, please make an attempt to submit an internal contribution
rather than a GitHub Pull Request. If you are not able to submit internally, a
PR is acceptable as an alternative.
## Contributing A Patch
1. Submit an issue describing your proposed change to the
[issue tracker](https://github.com/google/googletest).
[issue tracker](https://github.com/google/googletest/issues).
2. Please don't mix more than one logical change per submittal, because it
makes the history hard to follow. If you want to make a change that doesn't
have a corresponding issue in the issue tracker, please create one.
@@ -36,7 +36,8 @@ PR is acceptable as an alternative.
This ensures that work isn't being duplicated and communicating your plan
early also generally leads to better patches.
4. If your proposed change is accepted, and you haven't already done so, sign a
Contributor License Agreement (see details above).
Contributor License Agreement
([see details above](#contributor-license-agreements)).
5. Fork the desired repo, develop and test your code changes.
6. Ensure that your code adheres to the existing style in the sample to which
you are contributing.
@@ -46,11 +47,11 @@ PR is acceptable as an alternative.
## The Google Test and Google Mock Communities
The Google Test community exists primarily through the
[discussion group](http://groups.google.com/group/googletestframework) and the
[discussion group](https://groups.google.com/group/googletestframework) and the
GitHub repository. Likewise, the Google Mock community exists primarily through
their own [discussion group](http://groups.google.com/group/googlemock). You are
definitely encouraged to contribute to the discussion and you can also help us
to keep the effectiveness of the group high by following and promoting the
their own [discussion group](https://groups.google.com/group/googlemock). You
are definitely encouraged to contribute to the discussion and you can also help
us to keep the effectiveness of the group high by following and promoting the
guidelines listed here.
### Please Be Friendly
@@ -79,17 +80,17 @@ fairly rigid coding style, as defined by the
[google-styleguide](https://github.com/google/styleguide) project. All patches
will be expected to conform to the style outlined
[here](https://google.github.io/styleguide/cppguide.html). Use
[.clang-format](https://github.com/google/googletest/blob/master/.clang-format)
to check your formatting
[.clang-format](https://github.com/google/googletest/blob/main/.clang-format) to
check your formatting.
## Requirements for Contributors
If you plan to contribute a patch, you need to build Google Test, Google Mock,
and their own tests from a git checkout, which has further requirements:
* [Python](https://www.python.org/) v2.3 or newer (for running some of the
* [Python](https://www.python.org/) v3.6 or newer (for running some of the
tests and re-generating certain source files from templates)
* [CMake](https://cmake.org/) v2.6.4 or newer
* [CMake](https://cmake.org/) v2.8.12 or newer
## Developing Google Test and Google Mock
@@ -101,42 +102,40 @@ To make sure your changes work as intended and don't break existing
functionality, you'll want to compile and run Google Test and GoogleMock's own
tests. For that you can use CMake:
mkdir mybuild
cd mybuild
cmake -Dgtest_build_tests=ON -Dgmock_build_tests=ON ${GTEST_REPO_DIR}
```
mkdir mybuild
cd mybuild
cmake -Dgtest_build_tests=ON -Dgmock_build_tests=ON ${GTEST_REPO_DIR}
```
To choose between building only Google Test or Google Mock, you may modify your
cmake command to be one of each
cmake -Dgtest_build_tests=ON ${GTEST_DIR} # sets up Google Test tests
cmake -Dgmock_build_tests=ON ${GMOCK_DIR} # sets up Google Mock tests
```
cmake -Dgtest_build_tests=ON ${GTEST_DIR} # sets up Google Test tests
cmake -Dgmock_build_tests=ON ${GMOCK_DIR} # sets up Google Mock tests
```
Make sure you have Python installed, as some of Google Test's tests are written
in Python. If the cmake command complains about not being able to find Python
(`Could NOT find PythonInterp (missing: PYTHON_EXECUTABLE)`), try telling it
explicitly where your Python executable can be found:
cmake -DPYTHON_EXECUTABLE=path/to/python ...
```
cmake -DPYTHON_EXECUTABLE=path/to/python ...
```
Next, you can build Google Test and / or Google Mock and all desired tests. On
\*nix, this is usually done by
make
```
make
```
To run the tests, do
make test
```
make test
```
All tests should pass.
### Regenerating Source Files
Some of Google Test's source files are generated from templates (not in the C++
sense) using a script. For example, the file
include/gtest/internal/gtest-type-util.h.pump is used to generate
gtest-type-util.h in the same directory.
You don't need to worry about regenerating the source files unless you need to
modify them. You would then modify the corresponding `.pump` files and run the
'[pump.py](googletest/scripts/pump.py)' generator script. See the
[Pump Manual](googletest/docs/pump_manual.md).
@@ -5,33 +5,62 @@
Ajay Joshi <jaj@google.com>
Balázs Dán <balazs.dan@gmail.com>
Benoit Sigoure <tsuna@google.com>
Bharat Mediratta <bharat@menalto.com>
Bogdan Piloca <boo@google.com>
Chandler Carruth <chandlerc@google.com>
Chris Prince <cprince@google.com>
Chris Taylor <taylorc@google.com>
Dan Egnor <egnor@google.com>
Dave MacLachlan <dmaclach@gmail.com>
David Anderson <danderson@google.com>
Dean Sturtevant
Eric Roman <eroman@chromium.org>
Gene Volovich <gv@cite.com>
Hady Zalek <hady.zalek@gmail.com>
Hal Burch <gmock@hburch.com>
Jeffrey Yasskin <jyasskin@google.com>
Jim Keller <jimkeller@google.com>
Joe Walnes <joe@truemesh.com>
Jon Wray <jwray@google.com>
Jói Sigurðsson <joi@google.com>
Keir Mierle <mierle@gmail.com>
Keith Ray <keith.ray@gmail.com>
Kenton Varda <kenton@google.com>
Kostya Serebryany <kcc@google.com>
Krystian Kuzniarek <krystian.kuzniarek@gmail.com>
Lev Makhlis
Manuel Klimek <klimek@google.com>
Mario Tanev <radix@google.com>
Mark Paskin
Markus Heule <markus.heule@gmail.com>
Martijn Vels <mvels@google.com>
Matthew Simmons <simmonmt@acm.org>
Mika Raento <mikie@iki.fi>
Mike Bland <mbland@google.com>
Miklós Fazekas <mfazekas@szemafor.com>
Neal Norwitz <nnorwitz@gmail.com>
Nermin Ozkiranartli <nermin@google.com>
Owen Carlsen <ocarlsen@google.com>
Paneendra Ba <paneendra@google.com>
Pasi Valminen <pasi.valminen@gmail.com>
Patrick Hanna <phanna@google.com>
Patrick Riley <pfr@google.com>
Paul Menage <menage@google.com>
Peter Kaminski <piotrk@google.com>
Piotr Kaminski <piotrk@google.com>
Preston Jackson <preston.a.jackson@gmail.com>
Rainer Klaffenboeck <rainer.klaffenboeck@dynatrace.com>
Russ Cox <rsc@google.com>
Russ Rufer <russ@pentad.com>
Sean Mcafee <eefacm@gmail.com>
Sigurður Ásgeirsson <siggi@google.com>
Soyeon Kim <sxshx818@naver.com>
Sverre Sundsdal <sundsdal@gmail.com>
Szymon Sobik <sobik.szymon@gmail.com>
Takeshi Yoshino <tyoshino@google.com>
Tracy Bialik <tracy@pentad.com>
Vadim Berman <vadimb@google.com>
Vlad Losev <vladl@google.com>
Wolfgang Klier <wklier@google.com>
Zhanyong Wan <wan@google.com>
+44 -17
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env bash
# Copyright 2017 Google Inc.
# Copyright 2024 Google Inc.
# All Rights Reserved.
#
#
@@ -29,21 +28,49 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set -eu
# https://bazel.build/external/overview#bzlmod
if [ "${TRAVIS_OS_NAME}" != linux ]; then
echo "Not a Linux build; skipping installation"
exit 0
fi
module(
name = "googletest",
version = "head",
compatibility_level = 1,
)
# Only direct dependencies need to be listed below.
# Please keep the versions in sync with the versions in the WORKSPACE file.
if [ "${TRAVIS_SUDO}" = "true" ]; then
echo "deb [arch=amd64] http://storage.googleapis.com/bazel-apt stable jdk1.8" | \
sudo tee /etc/apt/sources.list.d/bazel.list
curl https://bazel.build/bazel-release.pub.gpg | sudo apt-key add -
sudo apt-get update && sudo apt-get install -y bazel gcc-4.9 g++-4.9 clang-3.9
elif [ "${CXX}" = "clang++" ]; then
# Use ccache, assuming $HOME/bin is in the path, which is true in the Travis build environment.
ln -sf /usr/bin/ccache $HOME/bin/${CXX};
ln -sf /usr/bin/ccache $HOME/bin/${CC};
fi
bazel_dep(
name = "abseil-cpp",
version = "20250127.0",
)
bazel_dep(
name = "platforms",
version = "0.0.10",
)
bazel_dep(
name = "re2",
version = "2024-07-02",
)
bazel_dep(
name = "rules_python",
version = "1.1.0",
dev_dependency = True,
)
# https://rules-python.readthedocs.io/en/stable/toolchains.html#library-modules-with-dev-only-python-usage
python = use_extension(
"@rules_python//python/extensions:python.bzl",
"python",
dev_dependency = True,
)
python.toolchain(
ignore_root_user_error = True,
is_default = True,
python_version = "3.12",
)
# See fake_fuchsia_sdk.bzl for instructions on how to override this with a real SDK, if needed.
fuchsia_sdk = use_extension("//:fake_fuchsia_sdk.bzl", "fuchsia_sdk")
fuchsia_sdk.create_fake()
use_repo(fuchsia_sdk, "fuchsia_sdk")
+92 -86
View File
@@ -1,93 +1,110 @@
# Google Test
# GoogleTest
#### Announcement:
### Announcements
Please note that Sept 13 - Sept 20 most of the maintainers will be attending
CppCon 2019. Our response time for Pull Requests and Issues will increase.
#### Live at Head
#### OSS Builds Status:
GoogleTest now follows the
[Abseil Live at Head philosophy](https://abseil.io/about/philosophy#upgrade-support).
We recommend
[updating to the latest commit in the `main` branch as often as possible](https://github.com/abseil/abseil-cpp/blob/master/FAQ.md#what-is-live-at-head-and-how-do-i-do-it).
We do publish occasional semantic versions, tagged with
`v${major}.${minor}.${patch}` (e.g. `v1.16.0`).
[![Build Status](https://api.travis-ci.org/google/googletest.svg?branch=master)](https://travis-ci.org/google/googletest)
[![Build status](https://ci.appveyor.com/api/projects/status/4o38plt0xbo1ubc8/branch/master?svg=true)](https://ci.appveyor.com/project/GoogleTestAppVeyor/googletest/branch/master)
#### Documentation Updates
### Future Plans
Our documentation is now live on GitHub Pages at
https://google.github.io/googletest/. We recommend browsing the documentation on
GitHub Pages rather than directly in the repository.
#### 1.8.x Release:
#### Release 1.16.0
[the 1.8.x](https://github.com/google/googletest/releases/tag/release-1.8.1) is
the last release that works with pre-C++11 compilers. The 1.8.x will not accept
any requests for any new features and any bugfix requests will only be accepted
if proven "critical"
[Release 1.16.0](https://github.com/google/googletest/releases/tag/v1.16.0) is
now available.
#### Post 1.8.x:
The 1.16.x branch requires at least C++14.
On-going work to improve/cleanup/pay technical debt. When this work is completed
there will be a 1.9.x tagged release
The 1.16.x branch will be the last to support C++14. Future development will
[require at least C++17](https://opensource.google/documentation/policies/cplusplus-support#c_language_standard).
#### Post 1.9.x
#### Continuous Integration
Post 1.9.x googletest will follow
[Abseil Live at Head philosophy](https://abseil.io/about/philosophy)
We use Google's internal systems for continuous integration.
## Welcome to **Google Test**, Google's C++ test framework!
#### Coming Soon
* We are planning to take a dependency on
[Abseil](https://github.com/abseil/abseil-cpp).
## Welcome to **GoogleTest**, Google's C++ test framework!
This repository is a merger of the formerly separate GoogleTest and GoogleMock
projects. These were so closely related that it makes sense to maintain and
release them together.
Please subscribe to the mailing list at googletestframework@googlegroups.com for
questions, discussions, and development.
### Getting Started
### Getting started:
See the [GoogleTest User's Guide](https://google.github.io/googletest/) for
documentation. We recommend starting with the
[GoogleTest Primer](https://google.github.io/googletest/primer.html).
The information for **Google Test** is available in the
[Google Test Primer](googletest/docs/primer.md) documentation.
**Google Mock** is an extension to Google Test for writing and using C++ mock
classes. See the separate [Google Mock documentation](googlemock/README.md).
More detailed documentation for googletest is in its interior
[googletest/README.md](googletest/README.md) file.
More information about building GoogleTest can be found at
[googletest/README.md](googletest/README.md).
## Features
* An [xUnit](https://en.wikipedia.org/wiki/XUnit) test framework.
* Test discovery.
* A rich set of assertions.
* User-defined assertions.
* Death tests.
* Fatal and non-fatal failures.
* Value-parameterized tests.
* Type-parameterized tests.
* Various options for running the tests.
* XML test report generation.
* xUnit test framework: \
Googletest is based on the [xUnit](https://en.wikipedia.org/wiki/XUnit)
testing framework, a popular architecture for unit testing
* Test discovery: \
Googletest automatically discovers and runs your tests, eliminating the need
to manually register your tests
* Rich set of assertions: \
Googletest provides a variety of assertions, such as equality, inequality,
exceptions, and more, making it easy to test your code
* User-defined assertions: \
You can define your own assertions with Googletest, making it simple to
write tests that are specific to your code
* Death tests: \
Googletest supports death tests, which verify that your code exits in a
certain way, making it useful for testing error-handling code
* Fatal and non-fatal failures: \
You can specify whether a test failure should be treated as fatal or
non-fatal with Googletest, allowing tests to continue running even if a
failure occurs
* Value-parameterized tests: \
Googletest supports value-parameterized tests, which run multiple times with
different input values, making it useful for testing functions that take
different inputs
* Type-parameterized tests: \
Googletest also supports type-parameterized tests, which run with different
data types, making it useful for testing functions that work with different
data types
* Various options for running tests: \
Googletest provides many options for running tests including running
individual tests, running tests in a specific order and running tests in
parallel
## Platforms
## Supported Platforms
Google test has been used on a variety of platforms:
GoogleTest follows Google's
[Foundational C++ Support Policy](https://opensource.google/documentation/policies/cplusplus-support).
See
[this table](https://github.com/google/oss-policies-info/blob/main/foundational-cxx-support-matrix.md)
for a list of currently supported versions of compilers, platforms, and build
tools.
* Linux
* Mac OS X
* Windows
* Cygwin
* MinGW
* Windows Mobile
* Symbian
* PlatformIO
## Who Is Using GoogleTest?
## Who Is Using Google Test?
In addition to many internal projects at Google, Google Test is also used by the
In addition to many internal projects at Google, GoogleTest is also used by the
following notable projects:
* The [Chromium projects](http://www.chromium.org/) (behind the Chrome browser
and Chrome OS).
* The [LLVM](http://llvm.org/) compiler.
* The [Chromium projects](https://www.chromium.org/) (behind the Chrome
browser and Chrome OS).
* The [LLVM](https://llvm.org/) compiler.
* [Protocol Buffers](https://github.com/google/protobuf), Google's data
interchange format.
* The [OpenCV](http://opencv.org/) computer vision library.
* [tiny-dnn](https://github.com/tiny-dnn/tiny-dnn): header only,
dependency-free deep learning framework in C++11.
* The [OpenCV](https://opencv.org/) computer vision library.
## Related Open Source Projects
@@ -95,13 +112,13 @@ following notable projects:
automated test-runner and Graphical User Interface with powerful features for
Windows and Linux platforms.
[Google Test UI](https://github.com/ospector/gtest-gbar) is test runner that
[GoogleTest UI](https://github.com/ospector/gtest-gbar) is a test runner that
runs your test binary, allows you to track its progress via a progress bar, and
displays a list of test failures. Clicking on one shows failure text. Google
Test UI is written in C#.
displays a list of test failures. Clicking on one shows failure text. GoogleTest
UI is written in C#.
[GTest TAP Listener](https://github.com/kinow/gtest-tap-listener) is an event
listener for Google Test that implements the
listener for GoogleTest that implements the
[TAP protocol](https://en.wikipedia.org/wiki/Test_Anything_Protocol) for test
result output. If your test runner understands TAP, you may find it useful.
@@ -109,31 +126,20 @@ result output. If your test runner understands TAP, you may find it useful.
runs tests from your binary in parallel to provide significant speed-up.
[GoogleTest Adapter](https://marketplace.visualstudio.com/items?itemName=DavidSchuldenfrei.gtest-adapter)
is a VS Code extension allowing to view Google Tests in a tree view, and
run/debug your tests.
is a VS Code extension allowing to view GoogleTest in a tree view and run/debug
your tests.
## Requirements
[C++ TestMate](https://github.com/matepek/vscode-catch2-test-adapter) is a VS
Code extension allowing to view GoogleTest in a tree view and run/debug your
tests.
Google Test is designed to have fairly minimal requirements to build and use
with your projects, but there are some. If you notice any problems on your
platform, please notify
[googletestframework@googlegroups.com](https://groups.google.com/forum/#!forum/googletestframework).
Patches for fixing them are welcome!
[Cornichon](https://pypi.org/project/cornichon/) is a small Gherkin DSL parser
that generates stub code for GoogleTest.
### Build Requirements
## Contributing Changes
These are the base requirements to build and use Google Test from a source
package:
* [Bazel](https://bazel.build/) or [CMake](https://cmake.org/). NOTE: Bazel is
the build system that googletest is using internally and tests against.
CMake is community-supported.
* a C++11-standard-compliant compiler
## Contributing change
Please read the [`CONTRIBUTING.md`](CONTRIBUTING.md) for details on how to
contribute to this project.
Please read
[`CONTRIBUTING.md`](https://github.com/google/googletest/blob/main/CONTRIBUTING.md)
for details on how to contribute to this project.
Happy testing!
+54 -16
View File
@@ -1,23 +1,61 @@
workspace(name = "com_google_googletest")
# Copyright 2024 Google Inc.
# All Rights Reserved.
#
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
workspace(name = "googletest")
load("//:googletest_deps.bzl", "googletest_deps")
googletest_deps()
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
# Abseil
http_archive(
name = "com_google_absl",
urls = ["https://github.com/abseil/abseil-cpp/archive/master.zip"],
strip_prefix = "abseil-cpp-master",
)
http_archive(
name = "rules_cc",
strip_prefix = "rules_cc-master",
urls = ["https://github.com/bazelbuild/rules_cc/archive/master.zip"],
)
http_archive(
name = "rules_python",
strip_prefix = "rules_python-master",
urls = ["https://github.com/bazelbuild/rules_python/archive/master.zip"],
sha256 = "9c6e26911a79fbf510a8f06d8eedb40f412023cf7fa6d1461def27116bff022c",
strip_prefix = "rules_python-1.1.0",
url = "https://github.com/bazelbuild/rules_python/releases/download/1.1.0/rules_python-1.1.0.tar.gz",
)
# https://github.com/bazelbuild/rules_python/releases/tag/1.1.0
load("@rules_python//python:repositories.bzl", "py_repositories")
py_repositories()
http_archive(
name = "bazel_skylib",
sha256 = "cd55a062e763b9349921f0f5db8c3933288dc8ba4f76dd9416aac68acee3cb94",
urls = ["https://github.com/bazelbuild/bazel-skylib/releases/download/1.5.0/bazel-skylib-1.5.0.tar.gz"],
)
http_archive(
name = "platforms",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/platforms/releases/download/0.0.10/platforms-0.0.10.tar.gz",
"https://github.com/bazelbuild/platforms/releases/download/0.0.10/platforms-0.0.10.tar.gz",
],
sha256 = "218efe8ee736d26a3572663b374a253c012b716d8af0c07e842e82f238a0a7ee",
)
+6 -11
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env bash
# Copyright 2017 Google Inc.
# Copyright 2024 Google Inc.
# All Rights Reserved.
#
#
@@ -29,12 +28,8 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set -eu
if [ "${TRAVIS_OS_NAME}" != "osx" ]; then
echo "Not a macOS build; skipping installation"
exit 0
fi
brew update
brew install ccache gcc@4.9
# https://bazel.build/external/migration#workspace.bzlmod
#
# This file is intentionally empty. When bzlmod is enabled and this
# file exists, the content of WORKSPACE is ignored. This prevents
# bzlmod builds from unintentionally depending on the WORKSPACE file.
-154
View File
@@ -1,154 +0,0 @@
version: '{build}'
os: Visual Studio 2015
environment:
matrix:
- compiler: msvc-15-seh
generator: "Visual Studio 15 2017"
build_system: cmake
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
- compiler: msvc-15-seh
generator: "Visual Studio 15 2017 Win64"
build_system: cmake
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
enabled_on_pr: yes
- compiler: msvc-15-seh
build_system: bazel
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
enabled_on_pr: yes
- compiler: msvc-14-seh
build_system: cmake
generator: "Visual Studio 14 2015"
enabled_on_pr: yes
- compiler: msvc-14-seh
build_system: cmake
generator: "Visual Studio 14 2015 Win64"
- compiler: gcc-6.3.0-posix
build_system: cmake
generator: "MinGW Makefiles"
cxx_path: 'C:\mingw-w64\i686-6.3.0-posix-dwarf-rt_v5-rev1\mingw32\bin'
enabled_on_pr: yes
configuration:
- Debug
build:
verbosity: minimal
install:
- ps: |
Write-Output "Compiler: $env:compiler"
Write-Output "Generator: $env:generator"
Write-Output "Env:Configuation: $env:configuration"
Write-Output "Env: $env"
if (-not (Test-Path env:APPVEYOR_PULL_REQUEST_NUMBER)) {
Write-Output "This is *NOT* a pull request build"
} else {
Write-Output "This is a pull request build"
if (-not (Test-Path env:enabled_on_pr) -or $env:enabled_on_pr -ne "yes") {
Write-Output "PR builds are *NOT* explicitly enabled"
}
}
# install Bazel
if ($env:build_system -eq "bazel") {
appveyor DownloadFile https://github.com/bazelbuild/bazel/releases/download/0.28.1/bazel-0.28.1-windows-x86_64.exe -FileName bazel.exe
}
if ($env:build_system -eq "cmake") {
# git bash conflicts with MinGW makefiles
if ($env:generator -eq "MinGW Makefiles") {
$env:path = $env:path.replace("C:\Program Files\Git\usr\bin;", "")
if ($env:cxx_path -ne "") {
$env:path += ";$env:cxx_path"
}
}
}
before_build:
- ps: |
$env:root=$env:APPVEYOR_BUILD_FOLDER
Write-Output "env:root: $env:root"
build_script:
- ps: |
# Only enable some builds for pull requests, the AppVeyor queue is too long.
if ((Test-Path env:APPVEYOR_PULL_REQUEST_NUMBER) -And (-not (Test-Path env:enabled_on_pr) -or $env:enabled_on_pr -ne "yes")) {
return
} else {
# special case - build with Bazel
if ($env:build_system -eq "bazel") {
& $env:root\bazel.exe build -c opt //:gtest_samples
if ($LastExitCode -eq 0) { # bazel writes to StdErr and PowerShell interprets it as an error
$host.SetShouldExit(0)
} else { # a real error
throw "Exec: $ErrorMessage"
}
return
}
}
# by default build with CMake
md _build -Force | Out-Null
cd _build
$conf = if ($env:generator -eq "MinGW Makefiles") {"-DCMAKE_BUILD_TYPE=$env:configuration"} else {"-DCMAKE_CONFIGURATION_TYPES=Debug;Release"}
# Disable test for MinGW (gtest tests fail, gmock tests can not build)
$gtest_build_tests = if ($env:generator -eq "MinGW Makefiles") {"-Dgtest_build_tests=OFF"} else {"-Dgtest_build_tests=ON"}
$gmock_build_tests = if ($env:generator -eq "MinGW Makefiles") {"-Dgmock_build_tests=OFF"} else {"-Dgmock_build_tests=ON"}
& cmake -G "$env:generator" $conf -Dgtest_build_samples=ON $gtest_build_tests $gmock_build_tests ..
if ($LastExitCode -ne 0) {
throw "Exec: $ErrorMessage"
}
$cmake_parallel = if ($env:generator -eq "MinGW Makefiles") {"-j2"} else {"/m"}
& cmake --build . --config $env:configuration -- $cmake_parallel
if ($LastExitCode -ne 0) {
throw "Exec: $ErrorMessage"
}
skip_commits:
files:
- '**/*.md'
test_script:
- ps: |
# Only enable some builds for pull requests, the AppVeyor queue is too long.
if ((Test-Path env:APPVEYOR_PULL_REQUEST_NUMBER) -And (-not (Test-Path env:enabled_on_pr) -or $env:enabled_on_pr -ne "yes")) {
return
}
if ($env:build_system -eq "bazel") {
# special case - testing with Bazel
& $env:root\bazel.exe test //:gtest_samples
if ($LastExitCode -eq 0) { # bazel writes to StdErr and PowerShell interprets it as an error
$host.SetShouldExit(0)
} else { # a real error
throw "Exec: $ErrorMessage"
}
}
if ($env:build_system -eq "cmake") {
# built with CMake - test with CTest
if ($env:generator -eq "MinGW Makefiles") {
return # No test available for MinGW
}
& ctest -C $env:configuration --timeout 600 --output-on-failure
if ($LastExitCode -ne 0) {
throw "Exec: $ErrorMessage"
}
}
artifacts:
- path: '_build/CMakeFiles/*.log'
name: logs
- path: '_build/Testing/**/*.xml'
name: test_results
- path: 'bazel-testlogs/**/test.log'
name: test_logs
- path: 'bazel-testlogs/**/test.xml'
name: test_results
-37
View File
@@ -1,37 +0,0 @@
#!/usr/bin/env bash
# Copyright 2017 Google Inc.
# All Rights Reserved.
#
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set -e
bazel version
bazel build --curses=no //...:all
bazel test --curses=no //...:all
bazel test --curses=no //...:all --define absl=1
-2
View File
@@ -1,2 +0,0 @@
# run PlatformIO builds
platformio run
-41
View File
@@ -1,41 +0,0 @@
#!/usr/bin/env bash
# Copyright 2017 Google Inc.
# All Rights Reserved.
#
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# This file should be sourced, and not executed as a standalone script.
#
# TODO() - we can check if this is being sourced using $BASH_VERSION and $BASH_SOURCE[0] != ${0}.
if [ "${TRAVIS_OS_NAME}" = "linux" ]; then
if [ "$CXX" = "g++" ]; then export CXX="g++-4.9" CC="gcc-4.9"; fi
if [ "$CXX" = "clang++" ]; then export CXX="clang++-3.9" CC="clang-3.9"; fi
fi
-48
View File
@@ -1,48 +0,0 @@
#!/usr/bin/env bash
# Copyright 2017 Google Inc.
# All Rights Reserved.
#
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# This file is typically sourced by another script.
# if possible, ask for the precise number of processors,
# otherwise take 2 processors as reasonable default; see
# https://docs.travis-ci.com/user/speeding-up-the-build/#Makefile-optimization
if [ -x /usr/bin/getconf ]; then
NPROCESSORS=$(/usr/bin/getconf _NPROCESSORS_ONLN)
else
NPROCESSORS=2
fi
# as of 2017-09-04 Travis CI reports 32 processors, but GCC build
# crashes if parallelized too much (maybe memory consumption problem),
# so limit to 4 processors for the time being.
if [ $NPROCESSORS -gt 4 ] ; then
echo "$0:Note: Limiting processors to use by make from $NPROCESSORS to 4."
NPROCESSORS=4
fi
-5
View File
@@ -1,5 +0,0 @@
# install PlatformIO
sudo pip install -U platformio
# update PlatformIO
platformio update
+157
View File
@@ -0,0 +1,157 @@
#!/bin/bash
#
# Copyright 2020, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above
# copyright notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with the
# distribution.
# * Neither the name of Google Inc. nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
set -euox pipefail
readonly LINUX_LATEST_CONTAINER="gcr.io/google.com/absl-177019/linux_hybrid-latest:20241218"
readonly LINUX_GCC_FLOOR_CONTAINER="gcr.io/google.com/absl-177019/linux_gcc-floor:20250205"
if [[ -z ${GTEST_ROOT:-} ]]; then
GTEST_ROOT="$(realpath $(dirname ${0})/..)"
fi
if [[ -z ${STD:-} ]]; then
STD="c++17 c++20"
fi
# Test CMake + GCC
for cmake_off_on in OFF ON; do
time docker run \
--volume="${GTEST_ROOT}:/src:ro" \
--tmpfs="/build:exec" \
--workdir="/build" \
--rm \
--env="CC=/usr/local/bin/gcc" \
--env=CXXFLAGS="-Werror -Wdeprecated" \
${LINUX_LATEST_CONTAINER} \
/bin/bash -c "
cmake /src \
-DCMAKE_CXX_STANDARD=17 \
-Dgtest_build_samples=ON \
-Dgtest_build_tests=ON \
-Dgmock_build_tests=ON \
-Dcxx_no_exception=${cmake_off_on} \
-Dcxx_no_rtti=${cmake_off_on} && \
make -j$(nproc) && \
ctest -j$(nproc) --output-on-failure"
done
# Test CMake + Clang
for cmake_off_on in OFF ON; do
time docker run \
--volume="${GTEST_ROOT}:/src:ro" \
--tmpfs="/build:exec" \
--workdir="/build" \
--rm \
--env="CC=/opt/llvm/clang/bin/clang" \
--env=CXXFLAGS="-Werror -Wdeprecated --gcc-toolchain=/usr/local" \
${LINUX_LATEST_CONTAINER} \
/bin/bash -c "
cmake /src \
-DCMAKE_CXX_STANDARD=17 \
-Dgtest_build_samples=ON \
-Dgtest_build_tests=ON \
-Dgmock_build_tests=ON \
-Dcxx_no_exception=${cmake_off_on} \
-Dcxx_no_rtti=${cmake_off_on} && \
make -j$(nproc) && \
ctest -j$(nproc) --output-on-failure"
done
# Do one test with an older version of GCC
time docker run \
--volume="${GTEST_ROOT}:/src:ro" \
--workdir="/src" \
--rm \
--env="CC=/usr/local/bin/gcc" \
--env="BAZEL_CXXOPTS=-std=c++17" \
${LINUX_GCC_FLOOR_CONTAINER} \
/usr/local/bin/bazel test ... \
--copt="-Wall" \
--copt="-Werror" \
--copt="-Wuninitialized" \
--copt="-Wundef" \
--copt="-Wno-error=pragmas" \
--enable_bzlmod=false \
--features=external_include_paths \
--keep_going \
--show_timestamps \
--test_output=errors
# Test GCC
for std in ${STD}; do
for absl in 0 1; do
time docker run \
--volume="${GTEST_ROOT}:/src:ro" \
--workdir="/src" \
--rm \
--env="CC=/usr/local/bin/gcc" \
--env="BAZEL_CXXOPTS=-std=${std}" \
${LINUX_LATEST_CONTAINER} \
/usr/local/bin/bazel test ... \
--copt="-Wall" \
--copt="-Werror" \
--copt="-Wuninitialized" \
--copt="-Wundef" \
--define="absl=${absl}" \
--enable_bzlmod=true \
--features=external_include_paths \
--keep_going \
--show_timestamps \
--test_output=errors
done
done
# Test Clang
for std in ${STD}; do
for absl in 0 1; do
time docker run \
--volume="${GTEST_ROOT}:/src:ro" \
--workdir="/src" \
--rm \
--env="CC=/opt/llvm/clang/bin/clang" \
--env="BAZEL_CXXOPTS=-std=${std}" \
${LINUX_LATEST_CONTAINER} \
/usr/local/bin/bazel test ... \
--copt="--gcc-toolchain=/usr/local" \
--copt="-Wall" \
--copt="-Werror" \
--copt="-Wuninitialized" \
--copt="-Wundef" \
--define="absl=${absl}" \
--enable_bzlmod=true \
--features=external_include_paths \
--keep_going \
--linkopt="--gcc-toolchain=/usr/local" \
--show_timestamps \
--test_output=errors
done
done
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#!/bin/bash
#
# Copyright 2009, Google Inc.
# Copyright 2020, Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
@@ -29,50 +29,52 @@
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""upload_gmock.py v0.1.0 -- uploads a Google Mock patch for review.
set -euox pipefail
This simple wrapper passes all command line flags and
--cc=googlemock@googlegroups.com to upload.py.
# Use Xcode 16.0
sudo xcode-select -s /Applications/Xcode_16.0.app/Contents/Developer
USAGE: upload_gmock.py [options for upload.py]
"""
if [[ -z ${GTEST_ROOT:-} ]]; then
GTEST_ROOT="$(realpath $(dirname ${0})/..)"
fi
__author__ = 'wan@google.com (Zhanyong Wan)'
# Test the CMake build
for cmake_off_on in OFF ON; do
BUILD_DIR=$(mktemp -d build_dir.XXXXXXXX)
cd ${BUILD_DIR}
time cmake ${GTEST_ROOT} \
-DCMAKE_CXX_STANDARD=17 \
-Dgtest_build_samples=ON \
-Dgtest_build_tests=ON \
-Dgmock_build_tests=ON \
-Dcxx_no_exception=${cmake_off_on} \
-Dcxx_no_rtti=${cmake_off_on}
time make -j$(nproc)
time ctest -j$(nproc) --output-on-failure
done
import os
import sys
# Test the Bazel build
CC_FLAG = '--cc='
GMOCK_GROUP = 'googlemock@googlegroups.com'
# If we are running on Kokoro, check for a versioned Bazel binary.
KOKORO_GFILE_BAZEL_BIN="bazel-8.0.0-darwin-x86_64"
if [[ ${KOKORO_GFILE_DIR:-} ]] && [[ -f ${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN} ]]; then
BAZEL_BIN="${KOKORO_GFILE_DIR}/${KOKORO_GFILE_BAZEL_BIN}"
chmod +x ${BAZEL_BIN}
else
BAZEL_BIN="bazel"
fi
def main():
# Finds the path to upload.py, assuming it is in the same directory
# as this file.
my_dir = os.path.dirname(os.path.abspath(__file__))
upload_py_path = os.path.join(my_dir, 'upload.py')
# Adds Google Mock discussion group to the cc line if it's not there
# already.
upload_py_argv = [upload_py_path]
found_cc_flag = False
for arg in sys.argv[1:]:
if arg.startswith(CC_FLAG):
found_cc_flag = True
cc_line = arg[len(CC_FLAG):]
cc_list = [addr for addr in cc_line.split(',') if addr]
if GMOCK_GROUP not in cc_list:
cc_list.append(GMOCK_GROUP)
upload_py_argv.append(CC_FLAG + ','.join(cc_list))
else:
upload_py_argv.append(arg)
if not found_cc_flag:
upload_py_argv.append(CC_FLAG + GMOCK_GROUP)
# Invokes upload.py with the modified command line flags.
os.execv(upload_py_path, upload_py_argv)
if __name__ == '__main__':
main()
cd ${GTEST_ROOT}
for absl in 0 1; do
${BAZEL_BIN} test ... \
--copt="-Wall" \
--copt="-Werror" \
--copt="-Wundef" \
--cxxopt="-std=c++17" \
--define="absl=${absl}" \
--enable_bzlmod=true \
--features=external_include_paths \
--keep_going \
--show_timestamps \
--test_output=errors
done

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