Compare commits

..
Author SHA1 Message Date
Eliza Velasquez 3308ec7e34 wip: editor stuff 2025-03-13 17:59:50 -07: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
Sungun Park 0bcb45613e Release Filament 1.58.0 2025-03-10 22:11:59 +00:00
Powei Feng 0a0d4ed23a Update HandleAllocator in test (#8508) 2025-03-10 12:49:58 -07:00
b9a39bc066 Add a way to pass a transform matrix along with Stream::setAcquiredImage (#8496)
BUGS=[399959254]
---------

Co-authored-by: Syed Idris Shah <idrisshah@google.com>
Co-authored-by: Haneul Kim <haneulk730@gmail.com>
Co-authored-by: Powei Feng <powei@google.com>
Co-authored-by: Doris Wu <doriswu@google.com>
Co-authored-by: Mathias Agopian <mathias@google.com>
2025-03-10 09:58:58 -07:00
Powei Feng e825f43935 matdbg: indicate shader model (#8504)
Shader model (desktop or mobile) wasn't really accounted for
in the UI. This means that we will get shaders that look like
duplicates (same variant). In this work, we pass the current
shader model from engine into the frontend and filter out
variants of a different shader model.

Moreover, for matinfo, we use a specific dbg shader model (matinfo)
to indicate it is in that mode. We add UI in matinfo to show the
shadermodel.

So UI updates as well.
2025-03-07 23:54:22 +00:00
alizahlalani ee68872a47 Add ahb mapping util functions to map ahb usage and format to TextureUsage and TextureFormat (#8491) 2025-03-07 22:28:56 +00:00
Konstantin SakhinandRomain Guy 2f4e48b6b9 Texture: prevent SIGILL in sampler validator. (#8468)
Co-authored-by: Romain Guy <romainguy@curious-creature.com>
2025-03-07 12:11:04 -08:00
Powei Feng 5091b3112d matdbg: fix variant issue for surface materials (#8503)
- The set of active variants were not set correctly for surface
   materials since the frag/vert pairs could have different
   variant.
 - Fix the default selection logic in the UI
 - Random clean-ups

FIXES=328699979
2025-03-07 17:46:04 +00:00
Ajmal Kunnummal 7199961248 Update the associated uniforms every frame for materials with attached Streams (#8493)
BUGS=[399959254]
2025-03-07 01:05:44 +00:00
bridgewaterrobbie d0a00803c1 Restore workaround for CONFIG_MAX_INSTANCES and CONFIG_FROXEL_BUFFER_HEIGHT not being a spec constant, because WGSL does not support that being an array length (#8479) 2025-03-07 00:28:36 +00:00
Sungun Park 0e3ef4bd63 Disable picking for multiview rendering (#8502)
Picking is not designed to work with multiview rendering. Disable it
when multiview is enabled.
2025-03-07 00:05:36 +00:00
Ben Doherty c24535a6e4 Update permissions for GitHub release job 2025-03-06 15:00:48 -08:00
bridgewaterrobbie 74b09c708a Enable TINT_BUILD_SPV_READER on host platform webgpu builds 2025-03-06 17:12:03 -05:00
bridgewaterrobbie 80e0dbaa8a When preprocess fails, return false from process 2025-03-06 17:12:03 -05:00
bridgewaterrobbie 31e966f63f Add initial Tint support for shader transpile
Early version that will allow errors to be ignored currently, outputting what we can into filamat results.
2025-03-06 17:12:03 -05:00
Ajmal Kunnummal 02223dcaab Add a way to specify an additional uniform name along with a sampler param for the associated transform matrix (#8490)
BUGS=[399959254]
2025-03-06 18:42:24 +00:00
Ajmal Kunnummal 47112f136e Add a way to query the transform matrix of a surface texture from the driver (#8489)
* Add a way to query the transform matrix of a surface texture from the driver

BUGS=[399959254]
2025-03-06 10:23:06 -08:00
HanYunChenLuo 30bb4ad6eb fix SourceFormatter.h missing header file (#8498)
missing <string>
2025-03-06 09:22:29 -08:00
Doris Wu d9e30460dc Enable fgviewer for android remote debugging (#8483)
* Enable fgviewer for the users

* Fix incorrect position

* Update

* Support remote debug server

* Enable fgviewer on android

* Link and include fgviewer_resources

* Adjust js to show untitled view
2025-03-06 05:36:41 +00:00
Doris Wu 075743ef59 Aggregate the subresource usage when collapsed (#8492) 2025-03-06 02:08:55 +00:00
71 changed files with 1368 additions and 404 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
+10 -2
View File
@@ -1,7 +1,15 @@
;;; Directory Local Variables -*- no-byte-compile: t -*-
;;; For more information see (info "(emacs) Directory Variables")
((c++-mode . ((c-file-style . "filament")
((c++-mode . ((eval . (progn
(make-local-variable 'eglot-ignored-server-capabilities)
(add-to-list 'eglot-ignored-server-capabilities
:documentOnTypeFormattingProvider)))
(c-file-style . "filament")
(apheleia-inhibit . t)))
(c-mode . ((c-file-style . "filament")
(c-mode . ((eval . (progn
(make-local-variable 'eglot-ignored-server-capabilities)
(add-to-list 'eglot-ignored-server-capabilities
:documentOnTypeFormattingProvider)))
(c-file-style . "filament")
(apheleia-inhibit . t))))
+1 -1
View File
@@ -5,7 +5,7 @@ end_of_line = lf
insert_final_newline = true
charset = utf-8
[*.{c,cpp,h,inc,kt,java,js,md}]
[*.{c,cpp,h,inc,kt,java,js,md,fs,vs}]
indent_style = space
indent_size = 4
max_line_length = 100
+11
View File
@@ -104,3 +104,14 @@ jobs:
with:
name: presubmit-renderdiff-result
path: ./out/renderdiff_tests
validate-wgsl-pipeline:
name: validate-wgsl-pipeline
runs-on: macos-14-xlarge
steps:
- uses: actions/checkout@v4.1.6
- name: Run build script
run: ./build.sh -W debug test_filamat
- name: Run test
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
+5
View File
@@ -9,6 +9,11 @@ name: Release
env:
RELEASE_TAG: ${{ github.event.inputs.release_tag }}
# The default GITHUB_TOKEN does not have write permissions, which is needed to upload release
# assets.
permissions:
contents: write
on:
workflow_dispatch:
inputs:
-1
View File
@@ -7,4 +7,3 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- materials: add an optional `transformName` field to sampler material parameters. [⚠️ **New Material Version**]
+7
View File
@@ -7,6 +7,13 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.58.1
## v1.58.0
- materials: add an optional `transformName` field to sampler material parameters. [⚠️ **New Material Version**]
## v1.57.3
- android: breaking changes to API KTX1Loader::createIndirectLight and KTX1Loader::createSkybox
+4 -6
View File
@@ -71,10 +71,9 @@ buildscript {
.gradleProperty("com.google.android.filament.include-webgpu")
.isPresent()
// TODO: Uncomment below when fgviewer is ready
// def fgviewer = providers
// .gradleProperty("com.google.android.filament.fgviewer")
// .isPresent()
def fgviewer = providers
.gradleProperty("com.google.android.filament.fgviewer")
.isPresent()
def matdbg = providers
.gradleProperty("com.google.android.filament.matdbg")
@@ -131,8 +130,7 @@ buildscript {
"-DFILAMENT_DIST_DIR=${filamentPath}".toString(),
"-DFILAMENT_SUPPORTS_VULKAN=${excludeVulkan ? 'OFF' : 'ON'}".toString(),
"-DFILAMENT_SUPPORTS_WEBGPU=${includeWebGPU ? 'ON' : 'OFF'}".toString(),
// TODO: Uncomment below when fgviewer is ready
// "-DFILAMENT_ENABLE_FGVIEWER=${fgviewer ? 'ON' : 'OFF'}".toString(),
"-DFILAMENT_ENABLE_FGVIEWER=${fgviewer ? 'ON' : 'OFF'}".toString(),
"-DFILAMENT_ENABLE_MATDBG=${matdbg ? 'ON' : 'OFF'}".toString(),
"-DFILAMENT_DISABLE_MATOPT=${matnopt ? 'ON' : 'OFF'}".toString()
]
+8
View File
@@ -11,6 +11,13 @@ if (FILAMENT_SUPPORTS_VULKAN)
message("Library filamat ignores Vulkan settings")
endif()
if (FILAMENT_SUPPORTS_WEBGPU)
add_library(webgpu_dawn STATIC IMPORTED)
set_target_properties(webgpu_dawn PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libwebgpu_dawn.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>:webgpu_dawn> # for tint
)
+7 -10
View File
@@ -2,8 +2,7 @@ cmake_minimum_required(VERSION 3.19)
project(filament-android)
option(FILAMENT_SUPPORTS_VULKAN "Enables Vulkan on Android" OFF)
# TODO: Uncomment below when fgviewer is ready
# option(FILAMENT_ENABLE_FGVIEWER "Enables Frame Graph Viewer" OFF)
option(FILAMENT_ENABLE_FGVIEWER "Enables Frame Graph Viewer" OFF)
option(FILAMENT_ENABLE_MATDBG "Enables Material debugger" OFF)
option(FILAMENT_DISABLE_MATOPT "Disables material optimizations" OFF)
option(FILAMENT_SUPPORTS_WEBGPU "Enables WebGPU on Android" OFF)
@@ -60,12 +59,11 @@ add_library(smol-v STATIC IMPORTED)
set_target_properties(smol-v PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libsmol-v.a)
# TODO: Uncomment below when fgviewer is ready
# 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)
@@ -133,8 +131,7 @@ target_link_libraries(filament-jni
# libgeometry is PUBLIC because gltfio uses it.
PUBLIC geometry
# TODO: Uncomment below when fgviewer is ready
# $<$<STREQUAL:${FILAMENT_ENABLE_FGVIEWER},ON>:fgviewer>
$<$<STREQUAL:${FILAMENT_ENABLE_FGVIEWER},ON>:fgviewer>
$<$<STREQUAL:${FILAMENT_ENABLE_MATDBG},ON>:matdbg>
$<$<STREQUAL:${FILAMENT_ENABLE_MATDBG},ON>:filamat>
$<$<STREQUAL:${FILAMENT_SUPPORTS_VULKAN},ON>:bluevk>
+1
View File
@@ -30,4 +30,5 @@ com.google.android.filament.abis=all
#com.google.android.filament.include-webgpu
#com.google.android.filament.matdbg
#com.google.android.filament.fgviewer
#com.google.android.filament.matnopt
+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
+9 -1
View File
@@ -233,7 +233,10 @@ 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()
@@ -246,6 +249,10 @@ if (FILAMENT_SUPPORTS_WEBGPU)
)
endif()
if (ANDROID)
list(APPEND SRCS src/BackendUtilsAndroid.cpp)
endif()
# ==================================================================================================
# Definitions
# ==================================================================================================
@@ -454,6 +461,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
@@ -182,6 +182,7 @@ enum class ShaderLanguage {
SPIRV = 2,
MSL = 3,
METAL_LIBRARY = 4,
WGSL = 5,
};
static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) {
@@ -196,6 +197,8 @@ static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguag
return "MSL";
case ShaderLanguage::METAL_LIBRARY:
return "Metal precompiled library";
case ShaderLanguage::WGSL:
return "WGSL";
}
}
@@ -807,6 +810,33 @@ static constexpr bool isStencilFormat(TextureFormat format) noexcept {
}
}
inline constexpr bool isColorFormat(TextureFormat format) noexcept {
switch (format) {
// Standard color formats
case TextureFormat::R8:
case TextureFormat::RG8:
case TextureFormat::RGBA8:
case TextureFormat::R16F:
case TextureFormat::RG16F:
case TextureFormat::RGBA16F:
case TextureFormat::R32F:
case TextureFormat::RG32F:
case TextureFormat::RGBA32F:
case TextureFormat::RGB10_A2:
case TextureFormat::R11F_G11F_B10F:
case TextureFormat::SRGB8:
case TextureFormat::SRGB8_A8:
case TextureFormat::RGB8:
case TextureFormat::RGB565:
case TextureFormat::RGB5_A1:
case TextureFormat::RGBA4:
return true;
default:
break;
}
return false;
}
static constexpr bool isUnsignedIntFormat(TextureFormat format) {
switch (format) {
case TextureFormat::R8UI:
@@ -347,18 +347,20 @@ 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,
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
const ExternalImageMetadata& metadata);
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);
// 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,36 @@
/*
* 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_PRIVATE_BACKENDUTILSANDROID_H
#define TNT_FILAMENT_BACKEND_PRIVATE_BACKENDUTILSANDROID_H
#include <backend/DriverEnums.h>
namespace filament::backend {
/**
* Maps AHardwareBuffer format to TextureFormat.
*/
TextureFormat mapToFilamentFormat(unsigned int format, bool isSrgbTransfer) noexcept;
/**
* Maps AHardwareBuffer usage to TextureUsage.
*/
TextureUsage mapToFilamentUsage(unsigned int usage, TextureFormat format) noexcept;
} // namespace filament
#endif // TNT_FILAMENT_BACKEND_PRIVATE_BACKENDUTILSANDROID_H
@@ -0,0 +1,98 @@
/*
* 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 "private/backend/BackendUtilsAndroid.h"
#include <android/hardware_buffer.h>
namespace filament::backend {
TextureFormat mapToFilamentFormat(unsigned int format, bool isSrgbTransfer) noexcept {
if (isSrgbTransfer) {
switch (format) {
case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
return TextureFormat::SRGB8;
default:
return TextureFormat::SRGB8_A8;
}
}
switch (format) {
case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
case AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM:
return TextureFormat::RGBA8;
case AHARDWAREBUFFER_FORMAT_R8G8B8_UNORM:
case AHARDWAREBUFFER_FORMAT_Y8Cb8Cr8_420:
return TextureFormat::RGB8;
case AHARDWAREBUFFER_FORMAT_R5G6B5_UNORM:
return TextureFormat::RGB565;
case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
return TextureFormat::RGBA16F;
case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
return TextureFormat::RGB10_A2;
case AHARDWAREBUFFER_FORMAT_D16_UNORM:
return TextureFormat::DEPTH16;
case AHARDWAREBUFFER_FORMAT_D24_UNORM:
return TextureFormat::DEPTH24;
case AHARDWAREBUFFER_FORMAT_D24_UNORM_S8_UINT:
return TextureFormat::DEPTH24_STENCIL8;
case AHARDWAREBUFFER_FORMAT_D32_FLOAT:
return TextureFormat::DEPTH32F;
case AHARDWAREBUFFER_FORMAT_D32_FLOAT_S8_UINT:
return TextureFormat::DEPTH32F_STENCIL8;
case AHARDWAREBUFFER_FORMAT_S8_UINT:
return TextureFormat::STENCIL8;
case AHARDWAREBUFFER_FORMAT_R8_UNORM:
return TextureFormat::R8;
default:
return TextureFormat::UNUSED;
}
}
TextureUsage mapToFilamentUsage(unsigned int usage, TextureFormat format) noexcept {
TextureUsage usageFlags = TextureUsage::NONE;
if (usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
usageFlags |= TextureUsage::SAMPLEABLE;
}
if (usage & AHARDWAREBUFFER_USAGE_GPU_FRAMEBUFFER) {
if (isDepthFormat(format)) {
usageFlags |= TextureUsage::DEPTH_ATTACHMENT;
}
if (isStencilFormat(format)) {
usageFlags |= TextureUsage::STENCIL_ATTACHMENT;
}
if (isColorFormat(format)) {
usageFlags |= TextureUsage::COLOR_ATTACHMENT;
}
}
if (usage & AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER) {
usageFlags |= TextureUsage::UPLOADABLE;
}
if (usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
usageFlags |= TextureUsage::PROTECTED;
}
if (usageFlags == TextureUsage::NONE) {
usageFlags = TextureUsage::COLOR_ATTACHMENT | TextureUsage::SAMPLEABLE;
}
return usageFlags;
}
} // namespace backend::filament
@@ -138,6 +138,7 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
case ShaderLanguage::ESSL1:
case ShaderLanguage::ESSL3:
case ShaderLanguage::SPIRV:
case ShaderLanguage::WGSL:
break;
}
@@ -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!";
+13 -14
View File
@@ -560,14 +560,6 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th,
Platform::ExternalImageHandleRef externalImage) {
FVK_SYSTRACE_SCOPE();
// FIXME: implement createTextureExternalImage2R
}
void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* externalImage) {
FVK_SYSTRACE_SCOPE();
const auto& metadata = mPlatform->getExternalImageMetadata(externalImage);
if (metadata.isProtected) {
usage |= backend::TextureUsage::PROTECTED;
@@ -577,15 +569,23 @@ void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::Sa
assert_invariant(height == metadata.height);
assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
const auto& data = mPlatform->createExternalImage(externalImage, metadata);
const auto& data = mPlatform->createExternalImageData(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);
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();
}
void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* externalImage) {
FVK_SYSTRACE_SCOPE();
// not supported in this backend
}
void VulkanDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, uint32_t plane) {
@@ -1266,10 +1266,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,
@@ -978,13 +978,13 @@ 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) {
return createExternalImageDataImpl(externalImage, mImpl->mDevice, metadata);
}
#undef SWAPCHAIN_RET_FUNC
@@ -15,6 +15,10 @@
*/
#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>
@@ -24,17 +28,44 @@
#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 +113,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 +131,13 @@ 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::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevice device,
VulkanPlatform::ExternalImageMetadata const& metadata) {
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;
@@ -140,7 +170,7 @@ VulkanPlatform::ImageData allocateExternalImage(void* externalBuffer,
if (metadata.isProtected == false)
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
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);
@@ -160,27 +190,57 @@ VulkanPlatform::ImageData allocateExternalImage(void* externalBuffer,
.pNext = &memoryDedicatedAllocateInfo,
.allocationSize = metadata.allocationSize,
.memoryTypeIndex = metadata.memoryTypeBits};
result = vkAllocateMemory(device, &allocInfo, allocator, &data.second);
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);
metadata.isProtected = isProtectedFromUsage(bufferDesc.usage);
std::tie(metadata.format, metadata.usage) =
getVKFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
}
// In the unprotected case add R/W capabilities
if (metadata.isProtected == false) {
@@ -197,20 +257,25 @@ 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,
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata) {
ImageData data = allocateExternalImage(externalImage, device, allocator, metadata);
auto const* fvkExternalImage =
static_cast<fvkandroid::ExternalImageVulkanAndroid const*>(externalImage.get());
ImageData data = allocateExternalImage(fvkExternalImage->aHardwareBuffer, device, metadata);
VkResult result = vkBindImageMemory(device, data.first, data.second, 0);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
@@ -64,12 +64,12 @@ 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,
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata) {
return {};
}
@@ -85,12 +85,12 @@ 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,
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata) {
return {};
}
+6 -4
View File
@@ -45,7 +45,7 @@ ShaderModel WebGPUDriver::getShaderModel() const noexcept {
}
ShaderLanguage WebGPUDriver::getShaderLanguage() const noexcept {
return ShaderLanguage::ESSL3;
return ShaderLanguage::WGSL;
}
// explicit instantiation of the Dispatcher
@@ -131,9 +131,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) {
}
+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
+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:
+2 -1
View File
@@ -751,7 +751,8 @@ int FEngine::loop() {
#endif
if (portString != nullptr) {
const int port = atoi(portString);
debug.server = new matdbg::DebugServer(mBackend, mDriver->getShaderLanguage(), port);
debug.server = new matdbg::DebugServer(mBackend, mDriver->getShaderLanguage(),
matdbg::DbgShaderModel((uint8_t) mDriver->getShaderModel()), port);
// Sometimes the server can fail to spin up (e.g. if the above port is already in use).
// When this occurs, carry onward, developers can look at civetweb.txt for details.
+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;
+8 -1
View File
@@ -154,7 +154,14 @@ public:
#if FILAMENT_ENABLE_MATDBG
assert_invariant((size_t)variant.key < VARIANT_COUNT);
std::unique_lock<utils::Mutex> lock(mActiveProgramsLock);
mActivePrograms.set(variant.key);
if (getMaterialDomain() == MaterialDomain::SURFACE) {
auto vert = Variant::filterVariantVertex(variant);
auto frag = Variant::filterVariantFragment(variant);
mActivePrograms.set(vert.key);
mActivePrograms.set(frag.key);
} else {
mActivePrograms.set(variant.key);
}
lock.unlock();
if (isSharedVariant(variant)) {
+3
View File
@@ -638,6 +638,9 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
if (isRenderingMultiview) {
hasPostProcess = false;
msaaOptions.enabled = false;
// Picking is not supported for multiview rendering. Clear any pending picking queries.
view.clearPickingQueries();
}
const uint8_t msaaSampleCount = msaaOptions.enabled ? msaaOptions.sampleCount : 1u;
+20 -18
View File
@@ -164,6 +164,26 @@ Texture* Texture::Builder::build(Engine& engine) {
(isProtectedTexturesSupported && useProtectedMemory) || !useProtectedMemory)
<< "Texture is PROTECTED but protected textures are not supported";
const auto validateSamplerType = [&engine = downcast(engine)](SamplerType const sampler) -> bool {
switch (sampler) {
case SamplerType::SAMPLER_2D:
case SamplerType::SAMPLER_CUBEMAP:
case SamplerType::SAMPLER_EXTERNAL:
return true;
case SamplerType::SAMPLER_3D:
case SamplerType::SAMPLER_2D_ARRAY:
return engine.hasFeatureLevel(FeatureLevel::FEATURE_LEVEL_1);
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
return engine.hasFeatureLevel(FeatureLevel::FEATURE_LEVEL_2);
}
return false;
};
// Validate sampler before any further interaction with it.
FILAMENT_CHECK_PRECONDITION(validateSamplerType(mImpl->mTarget))
<< "SamplerType " << uint8_t(mImpl->mTarget) << " not support at feature level "
<< uint8_t(engine.getActiveFeatureLevel());
// SAMPLER_EXTERNAL implies imported.
if (mImpl->mTarget == SamplerType::SAMPLER_EXTERNAL) {
mImpl->mExternal = true;
@@ -216,24 +236,6 @@ Texture* Texture::Builder::build(Engine& engine) {
FILAMENT_CHECK_PRECONDITION(!swizzled) << "WebGL does not support texture swizzling.";
#endif
auto validateSamplerType = [&engine = downcast(engine)](SamplerType const sampler) -> bool {
switch (sampler) {
case SamplerType::SAMPLER_2D:
case SamplerType::SAMPLER_CUBEMAP:
case SamplerType::SAMPLER_EXTERNAL:
return true;
case SamplerType::SAMPLER_3D:
case SamplerType::SAMPLER_2D_ARRAY:
return engine.hasFeatureLevel(FeatureLevel::FEATURE_LEVEL_1);
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
return engine.hasFeatureLevel(FeatureLevel::FEATURE_LEVEL_2);
}
};
FILAMENT_CHECK_PRECONDITION(validateSamplerType(mImpl->mTarget))
<< "SamplerType " << uint8_t(mImpl->mTarget) << " not support at feature level "
<< uint8_t(engine.getActiveFeatureLevel());
FILAMENT_CHECK_PRECONDITION((swizzled && sampleable) || !swizzled)
<< "Swizzled texture must be SAMPLEABLE";
+10 -6
View File
@@ -137,12 +137,7 @@ FView::~FView() noexcept = default;
void FView::terminate(FEngine& engine) {
// Here we would cleanly free resources we've allocated, or we own (currently none).
while (mActivePickingQueriesList) {
FPickingQuery* const pQuery = mActivePickingQueriesList;
mActivePickingQueriesList = pQuery->next;
pQuery->callback(pQuery->result, pQuery);
FPickingQuery::put(pQuery);
}
clearPickingQueries();
DriverApi& driver = engine.getDriverApi();
driver.destroyBufferObject(mLightUbh);
@@ -1178,6 +1173,15 @@ void FView::executePickingQueries(DriverApi& driver,
}
}
void FView::clearPickingQueries() noexcept {
while (mActivePickingQueriesList) {
FPickingQuery* const pQuery = mActivePickingQueriesList;
mActivePickingQueriesList = pQuery->next;
pQuery->callback(pQuery->result, pQuery);
FPickingQuery::put(pQuery);
}
}
void FView::setTemporalAntiAliasingOptions(TemporalAntiAliasingOptions options) noexcept {
options.feedback = clamp(options.feedback, 0.0f, 1.0f);
options.filterWidth = std::max(0.2f, options.filterWidth); // below 0.2 causes issues
+2
View File
@@ -463,6 +463,8 @@ public:
void executePickingQueries(backend::DriverApi& driver,
backend::RenderTargetHandle handle, math::float2 scale) noexcept;
void clearPickingQueries() noexcept;
void setMaterialGlobal(uint32_t index, math::float4 const& value);
math::float4 getMaterialGlobal(uint32_t index) const;
+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;
}
+35 -3
View File
@@ -26,16 +26,47 @@ set(SRCS
src/JsonWriter.cpp
)
# ==================================================================================================
# Resources
# ==================================================================================================
set(RESOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR})
set(RESOURCE_BINS
${CMAKE_CURRENT_SOURCE_DIR}/web/api.js
${CMAKE_CURRENT_SOURCE_DIR}/web/app.js
${CMAKE_CURRENT_SOURCE_DIR}/web/index.html
)
get_resgen_vars(${RESOURCE_DIR} fgviewer_resources)
add_custom_command(
OUTPUT ${RESGEN_OUTPUTS}
COMMAND resgen -t ${RESGEN_FLAGS} ${RESOURCE_BINS}
DEPENDS resgen ${RESOURCE_BINS}
)
if (DEFINED RESGEN_SOURCE_FLAGS)
set_source_files_properties(${RESGEN_SOURCE} PROPERTIES COMPILE_FLAGS ${RESGEN_SOURCE_FLAGS})
endif()
set(DUMMY_SRC "${RESOURCE_DIR}/dummy.c")
add_custom_command(OUTPUT ${DUMMY_SRC} COMMAND echo "//" > ${DUMMY_SRC})
add_library(fgviewer_resources ${DUMMY_SRC} ${RESGEN_SOURCE})
set_target_properties(fgviewer_resources PROPERTIES FOLDER Libs)
# ==================================================================================================
# Include and target definitions
# ==================================================================================================
include_directories(${PUBLIC_HDR_DIR})
include_directories(${PUBLIC_HDR_DIR} ${RESOURCE_DIR})
add_library(${TARGET} STATIC ${PUBLIC_HDRS} ${SRCS})
target_link_libraries(${TARGET} PUBLIC
civetweb
fgviewer_resources
utils
)
@@ -57,11 +88,12 @@ endif()
# Installation
# ==================================================================================================
# matdbg has dependencies on non-installed libraries. Here we bundle them all together into a single
# fgviewer has dependencies on non-installed libraries. Here we bundle them all together into a single
# library that gets copied into the installation folder so users are only required to link against
# matdbg.
# fgviewer.
set(FGVIEWER_DEPS
fgviewer
fgviewer_resources
civetweb
)
+43
View File
@@ -30,9 +30,26 @@
namespace filament::fgviewer {
// If set to 0, this serves HTML from a resgen resource. Use 1 only during local development, which
// serves files directly from the source code tree.
#define SERVE_FROM_SOURCE_TREE 0
#if SERVE_FROM_SOURCE_TREE
namespace {
std::string const BASE_URL = "libs/fgviewer/web";
} // anonymous
#else
#include "fgviewer_resources.h"
#include <unordered_map>
namespace {
struct Asset {
std::string_view mime;
std::string_view data;
};
std::unordered_map<std::string_view, Asset> ASSET_MAP;
} // anonymous
#endif // SERVE_FROM_SOURCE_TREE
using namespace utils;
@@ -56,10 +73,21 @@ public:
uri = "/index.html";
}
#if SERVE_FROM_SOURCE_TREE
if (uri == "/index.html" || uri == "/app.js" || uri == "/api.js") {
mg_send_file(conn, (BASE_URL + uri).c_str());
return true;
}
#else
auto const& asset_itr = ASSET_MAP.find(uri);
if (asset_itr != ASSET_MAP.end()) {
auto const& mime = asset_itr->second.mime;
auto const& data = asset_itr->second.data;
mg_printf(conn, kSuccessHeader.data(), mime.data());
mg_write(conn, data.data(), data.size());
return true;
}
#endif
slog.e << "[fgviewer] DebugServer: bad request at line " << __LINE__ << ": " << uri << io::endl;
return false;
}
@@ -68,6 +96,21 @@ private:
};
DebugServer::DebugServer(int port) {
#if !SERVE_FROM_SOURCE_TREE
ASSET_MAP["/index.html"] = {
.mime = "text/html",
.data = {(char const*) FGVIEWER_RESOURCES_INDEX_DATA},
};
ASSET_MAP["/app.js"] = {
.mime = "text/javascript",
.data = {(char const*) FGVIEWER_RESOURCES_APP_DATA},
};
ASSET_MAP["/api.js"] = {
.mime = "text/javascript",
.data = {(char const*) FGVIEWER_RESOURCES_API_DATA},
};
#endif
// By default the server spawns 50 threads so we override this to 10. According to the civetweb
// documentation, "it is recommended to use num_threads of at least 5, since browsers often
/// establish multiple connections to load a single web page, including all linked documents
+45 -29
View File
@@ -16,7 +16,7 @@
import {LitElement, html, css, unsafeCSS, nothing} from "https://unpkg.com/lit@2.8.0?module";
const kUntitledPlaceholder = "untitled";
const kUntitledPlaceholder = "Untitled View";
// CSS constants
const FOREGROUND_COLOR = '#fafafa';
@@ -293,11 +293,6 @@ class FrameGraphTable extends LitElement {
margin-right: 5px;
user-select: none;
}
.hidden {
display: none;
}
.scrollable-table td,
.scrollable-table th {
padding: 12px;
@@ -351,6 +346,7 @@ class FrameGraphTable extends LitElement {
super();
this.frameGraphData = null;
this.selectedResourceId = -1;
this.expandedResourceSet = new Set();
}
updated(props) {
@@ -369,11 +365,13 @@ class FrameGraphTable extends LitElement {
}
_toggleCollapse(resourceIndex) {
const subresourceRows = this.shadowRoot.querySelectorAll(`[id^="subresource-${resourceIndex}-"]`);
const icon = this.shadowRoot.querySelector(`#resource-${resourceIndex} .toggle-icon`);
const isHidden = subresourceRows[0]?.classList.contains('hidden');
subresourceRows.forEach(row => row.classList.toggle('hidden'));
icon.textContent = isHidden ? '▼' : '▶';
if (this.expandedResourceSet.has(resourceIndex)) {
this.expandedResourceSet.delete(resourceIndex);
}
else {
this.expandedResourceSet.add(resourceIndex);
}
this.requestUpdate();
}
_handleResourceClick(ev) {
@@ -384,13 +382,16 @@ class FrameGraphTable extends LitElement {
}));
}
_renderResourceUsage(allPasses, resourceId, defaultColor) {
_renderResourceUsage(allPasses, resourceIds, defaultColor) {
return allPasses.map((passData, index) => {
const isRead = passData?.reads.includes(resourceId);
const isWrite = passData?.writes.includes(resourceId);
const isRead = resourceIds.some(resourceId => passData?.reads.includes(resourceId));
const isWrite = resourceIds.some(resourceId => passData?.writes.includes(resourceId));
let type = null;
const hasUsed = (passData) => {
return passData?.reads.includes(resourceId) || passData?.writes.includes(resourceId);
return resourceIds.some(resourceId =>
passData?.reads.includes(resourceId) || passData?.writes.includes(resourceId)
);
};
const hasBeenUsedBefore = allPasses.slice(0, index).some(hasUsed);
const willBeUsedLater = allPasses.slice(index + 1).some(hasUsed);
@@ -399,6 +400,7 @@ class FrameGraphTable extends LitElement {
else if (isRead) type = RESOURCE_USAGE_TYPE_READ;
else if (isWrite) type = RESOURCE_USAGE_TYPE_WRITE;
else if (hasBeenUsedBefore && willBeUsedLater) type = RESOURCE_USAGE_TYPE_NO_ACCESS;
return html`
<td style="background-color: ${unsafeCSS(this._getCellColor(type, defaultColor))};">
${type ?? nothing}
@@ -423,24 +425,38 @@ class FrameGraphTable extends LitElement {
}
_renderResourceRow(resource, resourceIndex, resources, allPasses) {
const hasSubresources = resources.some(subresource => this._isSubresourceOfParent(subresource, resource));
const subresourceIds = resources
.filter(subresource => this._isSubresourceOfParent(subresource, resource))
.map(subresource => subresource.id);
const hasSubresources = subresourceIds.length > 0;
const isExpanded = this.expandedResourceSet.has(resourceIndex);
// Show the aggregated resource usage when the subresources are collapsed.
const resourceIds = isExpanded ? [resource.id]:[resource.id, ...subresourceIds];
const onClickResource = () => this._handleResourceClick(resource.id);
const selectedStyle = resource.id === this.selectedResourceId ? "selected" : "";
return html`
<tr id="resource-${resourceIndex}">
<th class="sticky-col resource ${selectedStyle}" @click="${onClickResource}">
${hasSubresources ? html`
<span class="toggle-icon"
@click="${() => this._toggleCollapse(resourceIndex)}">▶</span>` : nothing}
${resource.name}
</th>
${this._renderResourceUsage(allPasses, resource.id, DEFAULT_COLOR)}
</tr>
${this._renderSubresourceRows(resources, resource, resourceIndex, allPasses)}
`;
<tr id="resource-${resourceIndex}">
<th class="sticky-col resource ${selectedStyle}" @click="${onClickResource}">
${hasSubresources
? html`
<span class="toggle-icon"
@click="${(e) => { e.stopPropagation(); this._toggleCollapse(resourceIndex); }}">
${isExpanded ? '▼' : '▶'}
</span>`
: nothing}
${resource.name}
${hasSubresources && isExpanded ? nothing : html`(${subresourceIds.length})`}
</th>
${this._renderResourceUsage(allPasses, resourceIds, DEFAULT_COLOR)}
</tr>
${isExpanded ? this._renderSubresourceRows(resources, resource, resourceIndex, allPasses) : nothing}
`;
}
_renderSubresourceRows(resources, parentResource, resourceIndex, allPasses) {
return resources
.filter(subresource => this._isSubresourceOfParent(subresource, parentResource))
@@ -452,13 +468,13 @@ class FrameGraphTable extends LitElement {
const selectedStyle = subresource.id === this.selectedResourceId ? "selected" : "";
return html`
<tr id="subresource-${resourceIndex}-${subIndex}" class="collapsible hidden">
<tr id="subresource-${resourceIndex}-${subIndex}" class="collapsible">
<td class="sticky-col resource ${selectedStyle}"
@click="${onClickResource}"
style="background-color: ${SUBRESOURCE_COLOR}">
${subresource.name}
</td>
${this._renderResourceUsage(allPasses, subresource.id, SUBRESOURCE_COLOR)}
${this._renderResourceUsage(allPasses, [subresource.id], SUBRESOURCE_COLOR)}
</tr>
`;
}
@@ -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:
+4
View File
@@ -85,6 +85,10 @@ target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
set_target_properties(${TARGET} PROPERTIES FOLDER Libs)
target_link_libraries(${TARGET} backend_headers shaders filabridge utils smol-v)
if (FILAMENT_SUPPORTS_WEBGPU)
target_link_libraries(${TARGET} libtint)
endif ()
# We are being naughty and accessing private headers here
# For spirv-tools, we're just following glslang's example
target_include_directories(${TARGET} PRIVATE ${spirv-tools_SOURCE_DIR}/include)
@@ -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
};
/*
+65 -3
View File
@@ -44,6 +44,10 @@
#include <unordered_map>
#include <vector>
#ifdef FILAMENT_SUPPORTS_WEBGPU
#include <tint/tint.h>
#endif
using namespace glslang;
using namespace spirv_cross;
using namespace spvtools;
@@ -514,8 +518,44 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl,
}
}
bool GLSLPostProcessor::spirvToWgsl(const SpirvBlob *spirv, std::string *outWsl) {
#if FILAMENT_SUPPORTS_WEBGPU
//Currently no options we want to use
const tint::spirv::reader::Options readerOpts{};
tint::wgsl::writer::Options writerOpts{};
tint::Program tintRead = tint::spirv::reader::Read(*spirv, readerOpts);
if (tintRead.Diagnostics().ContainsErrors()) {
//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;
}
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);
/// An instance of SuccessType that can be used to check a tint Result.
tint::SuccessType tintSuccess;
if (wgslOut != tintSuccess) {
slog.e << "Tint writer error: " << wgslOut.Failure().reason.Str() << io::endl;
return false;
}
*outWsl = wgslOut->wgsl;
return true;
#else
slog.i << "Trying to emit WGSL without including WebGPU dependencies, please set CMake arg FILAMENT_SUPPORTS_WEBGPU and FILAMENT_SUPPORTS_WEBGPU" << io::endl;
return false;
#endif
}
bool GLSLPostProcessor::process(const std::string& inputShader, Config const& config,
std::string* outputGlsl, SpirvBlob* outputSpirv, std::string* outputMsl) {
std::string* outputGlsl, SpirvBlob* outputSpirv, std::string* outputMsl, std::string* outputWgsl) {
using TargetLanguage = MaterialBuilder::TargetLanguage;
if (config.targetLanguage == TargetLanguage::GLSL) {
@@ -530,6 +570,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
.glslOutput = outputGlsl,
.spirvOutput = outputSpirv,
.mslOutput = outputMsl,
.wgslOutput = outputWgsl,
};
switch (config.shaderType) {
@@ -610,13 +651,20 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
config.shaderType, config.shaderModel, config.hasFramebufferFetch, descriptors,
mGenerateDebugInfo ? &internalConfig.minifier : nullptr);
}
if (internalConfig.wgslOutput) {
if (!spirvToWgsl(internalConfig.spirvOutput, internalConfig.wgslOutput)) {
return false;
}
}
} else {
slog.e << "GLSL post-processor invoked with optimization level NONE"
<< io::endl;
}
break;
case MaterialBuilder::Optimization::PREPROCESSOR:
preprocessOptimization(tShader, config, internalConfig);
if (!preprocessOptimization(tShader, config, internalConfig)) {
return false;
}
break;
case MaterialBuilder::Optimization::SIZE:
case MaterialBuilder::Optimization::PERFORMANCE:
@@ -646,7 +694,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co
return true;
}
void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
bool GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const {
using TargetApi = MaterialBuilder::TargetApi;
assert_invariant(bool(internalConfig.spirvOutput) == (config.targetApi != TargetApi::OPENGL));
@@ -662,6 +710,7 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
if (!ok) {
slog.e << tShader.getInfoLog() << io::endl;
return false;
}
if (internalConfig.spirvOutput) {
@@ -683,6 +732,7 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
bool const linkOk = program.link(msg);
if (!ok || !linkOk) {
slog.e << spirvShader.getInfoLog() << io::endl;
return false;
} else {
SpvOptions options;
options.generateDebugInfo = mGenerateDebugInfo;
@@ -702,10 +752,17 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader,
config.shaderModel, config.hasFramebufferFetch, descriptors,
mGenerateDebugInfo ? &internalConfig.minifier : nullptr);
}
if (internalConfig.wgslOutput) {
if (!spirvToWgsl(internalConfig.spirvOutput, internalConfig.wgslOutput)) {
return false;
}
}
if (internalConfig.glslOutput) {
*internalConfig.glslOutput = glsl;
}
return true;
}
bool GLSLPostProcessor::fullOptimization(const TShader& tShader,
@@ -746,6 +803,11 @@ bool GLSLPostProcessor::fullOptimization(const TShader& tShader,
config.hasFramebufferFetch, descriptors,
mGenerateDebugInfo ? &internalConfig.minifier : nullptr);
}
if (internalConfig.wgslOutput) {
if (!spirvToWgsl(&spirv, internalConfig.wgslOutput)) {
return false;
}
}
// Transpile back to GLSL
if (internalConfig.glslOutput) {
+6 -2
View File
@@ -81,7 +81,8 @@ public:
bool process(const std::string& inputShader, Config const& config,
std::string* outputGlsl,
SpirvBlob* outputSpirv,
std::string* outputMsl);
std::string* outputMsl,
std::string* outputWgsl);
// public so backend_test can also use it
static void spirvToMsl(const SpirvBlob* spirv, std::string* outMsl,
@@ -89,11 +90,14 @@ public:
bool useFramebufferFetch, const DescriptorSets& descriptorSets,
const ShaderMinifier* minifier);
static bool spirvToWgsl(const SpirvBlob* spirv, std::string* outWsl);
private:
struct InternalConfig {
std::string* glslOutput = nullptr;
SpirvBlob* spirvOutput = nullptr;
std::string* mslOutput = nullptr;
std::string* wgslOutput = nullptr;
EShLanguage shLang = EShLangFragment;
// use 100 for ES environment, 110 for desktop
int langVersion = 0;
@@ -103,7 +107,7 @@ private:
bool fullOptimization(const glslang::TShader& tShader,
GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const;
void preprocessOptimization(glslang::TShader& tShader,
bool preprocessOptimization(glslang::TShader& tShader,
GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const;
/**
+34 -2
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,8 +917,9 @@ 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;
// Set when a job fails
@@ -923,21 +934,26 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
// TODO: avoid allocations when not required
std::vector<uint32_t> spirv;
std::string msl;
std::string wgsl;
std::vector<uint32_t>* pSpirv = targetApiNeedsSpirv ? &spirv : nullptr;
std::string* pMsl = targetApiNeedsMsl ? &msl : nullptr;
std::string* pWgsl = targetApiNeedsWgsl ? &wgsl : nullptr;
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
@@ -1006,7 +1022,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
config.glsl.subpassInputToColorLocation.emplace_back(0, 0);
}
bool const ok = postProcessor.process(shader, config, pGlsl, pSpirv, pMsl);
bool const ok = postProcessor.process(shader, config, pGlsl, pSpirv, pMsl, pWgsl);
if (!ok) {
showErrorMessage(mMaterialName.c_str_safe(), v.variant, targetApi, v.stage,
featureLevel, shader);
@@ -1035,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;
@@ -1096,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) {
@@ -1111,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>(
@@ -1141,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;
+24 -5
View File
@@ -175,6 +175,10 @@ 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";
break;
case TargetApi::ALL:
// invalid should never happen
break;
@@ -190,6 +194,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) {
@@ -297,12 +302,22 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
generateSpecializationConstant(out, "BACKEND_FEATURE_LEVEL",
+ReservedSpecializationConstants::BACKEND_FEATURE_LEVEL, 1);
generateSpecializationConstant(out, "CONFIG_MAX_INSTANCES",
+ReservedSpecializationConstants::CONFIG_MAX_INSTANCES, (int)CONFIG_MAX_INSTANCES);
if (mTargetApi == TargetApi::WEBGPU) {
// Note: This is a revived hack for a hack.
//
// WGSL doesn't support specialization constants as an array length
// CONFIG_MAX_INSTANCES is only needed for WebGL, so we can replace it with a constant.
// More information at https://github.com/gpuweb/gpuweb/issues/572#issuecomment-649760005
out << "const int CONFIG_MAX_INSTANCES = " << (int)CONFIG_MAX_INSTANCES << ";\n";
out << "const int CONFIG_FROXEL_BUFFER_HEIGHT = 2048;\n";
} else {
generateSpecializationConstant(out, "CONFIG_MAX_INSTANCES",
+ReservedSpecializationConstants::CONFIG_MAX_INSTANCES, (int)CONFIG_MAX_INSTANCES);
// the default of 1024 (16KiB) is needed for 32% of Android devices
generateSpecializationConstant(out, "CONFIG_FROXEL_BUFFER_HEIGHT",
+ReservedSpecializationConstants::CONFIG_FROXEL_BUFFER_HEIGHT, 1024);
// the default of 1024 (16KiB) is needed for 32% of Android devices
generateSpecializationConstant(out, "CONFIG_FROXEL_BUFFER_HEIGHT",
+ReservedSpecializationConstants::CONFIG_FROXEL_BUFFER_HEIGHT, 1024);
}
// directional shadowmap visualization
generateSpecializationConstant(out, "CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP",
@@ -715,6 +730,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;
@@ -803,6 +820,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;
+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;
}
+15 -3
View File
@@ -41,6 +41,13 @@ struct MaterialRecord {
VariantList activeVariants;
};
// Matches DriverEnums' ShaderModel
enum class DbgShaderModel {
MOBILE = 1, //!< Mobile level functionality
DESKTOP = 2, //!< Desktop level functionality
MATINFO = 10, //!< To indicate debug server is running from matinfo
};
/**
* Server-side material debugger.
*
@@ -52,7 +59,8 @@ public:
static std::string_view const kSuccessHeader;
static std::string_view const kErrorHeader;
DebugServer(backend::Backend backend, backend::ShaderLanguage shaderLanguage, int port);
DebugServer(backend::Backend backend, backend::ShaderLanguage shaderLanguage,
DbgShaderModel perferredShaderModel, int port);
~DebugServer();
/**
@@ -85,10 +93,13 @@ public:
bool isReady() const { return mServer; }
private:
// called from ApiHandler
MaterialRecord const* getRecord(const MaterialKey& key) const;
// called from ApiHandler
void updateActiveVariants();
// called from ApiHandler
/**
* Replaces the entire content of a particular shader variant. The given shader index uses the
* same ordering that the variants have within the package.
@@ -96,8 +107,9 @@ private:
bool handleEditCommand(const MaterialKey& mat, backend::Backend api, int shaderIndex,
const char* newShaderContent, size_t newShaderLength);
const backend::Backend mBackend;
const backend::ShaderLanguage mShaderLanguage;
backend::Backend const mBackend;
backend::ShaderLanguage const mShaderLanguage;
DbgShaderModel const mPreferredShaderModel;
CivetServer* mServer;
+4 -1
View File
@@ -17,6 +17,8 @@
#ifndef MATDBG_JSONWRITER_H
#define MATDBG_JSONWRITER_H
#include <matdbg/DebugServer.h>
#include <filaflat/ChunkContainer.h>
#include <backend/DriverEnums.h>
@@ -47,7 +49,8 @@ public:
// shader index is an active variant. Each bit in the activeVariants bitmask
// represents one of the possible variant combinations.
bool writeActiveInfo(const filaflat::ChunkContainer& package,
backend::ShaderLanguage shaderLanguage, VariantList activeVariants);
backend::ShaderLanguage shaderLanguage, DbgShaderModel shaderModel,
VariantList activeVariants);
private:
utils::CString mJsonString;
+20 -19
View File
@@ -332,11 +332,12 @@ bool ApiHandler::handleGet(CivetServer* server, struct mg_connection* conn) {
return error(__LINE__, uri);
}
JsonWriter writer;
if (!writer.writeActiveInfo(package, mServer->mShaderLanguage, record.activeVariants)) {
if (!writer.writeActiveInfo(package, mServer->mShaderLanguage,
mServer->mPreferredShaderModel, record.activeVariants)) {
return error(__LINE__, uri);
}
bool const last = (++index) == mServer->mMaterialRecords.size();
mg_printf(conn, "\"%8.8x\": %s %s", pair.first, writer.getJsonString(),
mg_printf(conn, "\"%8.8x\": %s%s", pair.first, writer.getJsonString(),
last ? "" : ",");
}
mg_printf(conn, "}");
@@ -356,6 +357,18 @@ bool ApiHandler::handleGet(CivetServer* server, struct mg_connection* conn) {
return true;
}
auto writeMaterialRecord = [&](JsonWriter* writer, MaterialRecord const* record) {
ChunkContainer package(record->package, record->packageSize);
if (!package.parse()) {
return error(__LINE__, uri);
}
if (!writer->writeMaterialInfo(package)) {
return error(__LINE__, uri);
}
return true;
};
if (uri == "/api/materials") {
std::unique_lock const lock(mServer->mMaterialRecordsMutex);
mg_printf(conn, kSuccessHeader.data(), "application/json");
@@ -363,17 +376,11 @@ bool ApiHandler::handleGet(CivetServer* server, struct mg_connection* conn) {
int index = 0;
for (auto const& record: mServer->mMaterialRecords) {
bool const last = (++index) == mServer->mMaterialRecords.size();
ChunkContainer package(record.second.package, record.second.packageSize);
if (!package.parse()) {
return error(__LINE__, uri);
}
auto const& mat = record.second;
JsonWriter writer;
if (!writer.writeMaterialInfo(package)) {
return error(__LINE__, uri);
if (!writeMaterialRecord(&writer, &mat)) {
return false;
}
mg_printf(conn, "{ \"matid\": \"%8.8x\", %s } %s", record.first, writer.getJsonString(),
last ? "" : ",");
}
@@ -386,15 +393,9 @@ bool ApiHandler::handleGet(CivetServer* server, struct mg_connection* conn) {
if (!result) {
return error(__LINE__, uri);
}
ChunkContainer package(result->package, result->packageSize);
if (!package.parse()) {
return error(__LINE__, uri);
}
JsonWriter writer;
if (!writer.writeMaterialInfo(package)) {
return error(__LINE__, uri);
if (!writeMaterialRecord(&writer, result)) {
return false;
}
mg_printf(conn, kSuccessHeader.data(), "application/json");
mg_printf(conn, "{ %s }", writer.getJsonString());
+5 -2
View File
@@ -122,8 +122,11 @@ private:
DebugServer* mServer;
};
DebugServer::DebugServer(Backend backend, ShaderLanguage shaderLanguage, int port)
: mBackend(backend), mShaderLanguage(shaderLanguage) {
DebugServer::DebugServer(Backend backend, ShaderLanguage shaderLanguage,
DbgShaderModel perferredShaderModel, int port)
: mBackend(backend),
mShaderLanguage(shaderLanguage),
mPreferredShaderModel(perferredShaderModel) {
#if !SERVE_FROM_SOURCE_TREE
ASSET_MAP["/index.html"] = {
+18 -1
View File
@@ -232,7 +232,7 @@ size_t JsonWriter::getJsonSize() const {
}
bool JsonWriter::writeActiveInfo(const filaflat::ChunkContainer& package,
ShaderLanguage shaderLanguage, VariantList activeVariants) {
ShaderLanguage shaderLanguage, DbgShaderModel shaderModel, VariantList activeVariants) {
vector<ShaderInfo> shaders;
ostringstream json;
json << "[\"";
@@ -254,13 +254,30 @@ 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;
}
shaders.resize(getShaderCount(package, chunkType));
getShaderInfo(package, shaders.data(), chunkType);
json << "\", \"";
switch (shaderModel) {
case DbgShaderModel::DESKTOP:
json << toString(ShaderModel::DESKTOP);
break;
case DbgShaderModel::MOBILE:
json << toString(ShaderModel::MOBILE);
break;
case DbgShaderModel::MATINFO:
json << "matinfo";
break;
}
json << "\"";
for (size_t variant = 0; variant < activeVariants.size(); variant++) {
if (activeVariants[variant]) {
json << ", " << variant;
+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;
+13 -1
View File
@@ -16,6 +16,8 @@
#include <matdbg/ShaderInfo.h>
#include "CommonWriter.h"
#include <filaflat/ChunkContainer.h>
#include <filaflat/DictionaryReader.h>
#include <filaflat/MaterialChunk.h>
@@ -48,12 +50,16 @@ size_t getShaderCount(const ChunkContainer& container, ChunkType type) {
return shaderCount;
}
bool getShaderInfo(const ChunkContainer& container, ShaderInfo* info, ChunkType chunkType) {
if (!container.hasChunk(chunkType)) {
return true;
}
MaterialDomain domain;
if (!read(container, ChunkType::MaterialDomain, reinterpret_cast<uint8_t*>(&domain))) {
return false;
}
auto [start, end] = container.getChunkRange(chunkType);
Unflattener unflattener(start, end);
@@ -84,6 +90,12 @@ bool getShaderInfo(const ChunkContainer& container, ShaderInfo* info, ChunkType
return false;
}
auto stage = ShaderStage(pipelineStageValue);
if (domain == MaterialDomain::SURFACE) {
variant = stage == ShaderStage::VERTEX ?
Variant::filterVariantVertex(variant) :
Variant::filterVariantFragment(variant);
}
*info++ = {
.shaderModel = ShaderModel(shaderModelValue),
.variant = variant,
+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;
+1
View File
@@ -18,6 +18,7 @@
#define MATDBG_SOURCE_FORMATTER_H
#include <mutex>
#include <string>
namespace filament::matdbg {
+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);
+6 -7
View File
@@ -68,14 +68,13 @@ async function fetchMatIds() {
}
async function queryActiveShaders() {
const activeMaterials = await _fetchJson("api/active");
const activeVariants = await _fetchJson("api/active");
const actives = {};
for (matid in activeMaterials) {
const backend = activeMaterials[matid][0];
const variants = activeMaterials[matid].slice(1);
actives[matid] = {
backend, variants
};
for (matid in activeVariants) {
const backend = activeVariants[matid][0];
const shaderModel = activeVariants[matid][1];
const variants = activeVariants[matid].slice(2);
actives[matid] = { backend, shaderModel, variants };
}
return actives;
}
+69 -50
View File
@@ -59,8 +59,8 @@ const FOREGROUND_COLOR = '#fafafa';
const INACTIVE_COLOR = '#9a9a9a';
const DARKER_INACTIVE_COLOR = '#6f6f6f';
const LIGHTER_INACTIVE_COLOR = '#d9d9d9';
const UNSELECTED_COLOR = '#dfdfdf';
const BACKGROUND_COLOR = '#5362e5';
const UNSELECTED_COLOR = '#d0d0d0';
const BACKGROUND_COLOR = '#6885c5';
const HOVER_BACKGROUND_COLOR = '#b3c2ff';
const CODE_VIEWER_BOTTOM_ROW_HEIGHT = 60;
const REGULAR_FONT_SIZE = 12;
@@ -343,7 +343,7 @@ class MenuSection extends LitElement {
color: ${unsafeCSS(UNSELECTED_COLOR)};
}
.section-title {
font-size: 16px;
font-size: 20px;
color: ${unsafeCSS(UNSELECTED_COLOR)};
cursor: pointer;
}
@@ -365,6 +365,9 @@ class MenuSection extends LitElement {
align-items: center;
justify-content: space-between;
}
.expanded-title {
color: ${unsafeCSS(FOREGROUND_COLOR)};
}
`;
}
@@ -380,9 +383,10 @@ class MenuSection extends LitElement {
render() {
const expandedIcon = this.showing ? '-' : '+';
const slot = (() => html`<slot></slot>`)();
const epandedTitleClass = this.showing ? 'expanded-title' : '';
return html`
<div class="container">
<div class="section-title expander" @click="${this._showClick}">
<div class="section-title expander ${epandedTitleClass}" @click="${this._showClick}">
<span>${this.title}</span> <span>${expandedIcon}</span>
</div>
<hr />
@@ -460,6 +464,7 @@ class AdvancedOptions extends LitElement {
static get properties() {
return {
currentBackend: {type: String, attribute: 'current-backend'},
currentShaderModel: {type: String, attribute: 'current-shader-model'},
hideInactiveVariants: {type: Boolean, attribute: 'hide-inactive-variants'},
availableBackends: {type: Array, state: true},
};
@@ -478,6 +483,7 @@ class AdvancedOptions extends LitElement {
flex-direction: column;
justify-content: center;
align-items: flex-start;
margin-bottom: 6px;
}
.borderless {
border: 1px solid rgba(0,0,0,0);
@@ -570,7 +576,7 @@ class AdvancedOptions extends LitElement {
return html`
<div class="option">
<div class="option-heading">Current Backend</div>
<form action="" id="backend-option-form">
<form style="display:flex;flex-wrap:wrap" action="" id="backend-option-form">
${div}
</form>
</div>
@@ -578,6 +584,9 @@ class AdvancedOptions extends LitElement {
}
_hideInactiveVariantsOption() {
if (this.currentShaderModel == 'matinfo') {
return null;
}
const onChange = (ev) => {
this.dispatchEvent(
new CustomEvent(
@@ -585,7 +594,7 @@ class AdvancedOptions extends LitElement {
{detail: null, bubbles: true, composed: true}));
}
return html`
<div class="option borderless">
<div class="option">
<!--<div class="option-heading">Current Backend</div>-->
<form action="" id="hide-inactive-variants-form">
<input type="checkbox" name="InactiveVariants"
@@ -630,8 +639,8 @@ class MaterialSidePanel extends LitElement {
color: white;
width: 100%;
text-align: center;
margin: 0 0 10px 0;
font-size: 20px;
margin: 0 0 20px 0;
font-size: 25px;
}
.materials {
display: flex;
@@ -675,6 +684,7 @@ class MaterialSidePanel extends LitElement {
currentBackend: {type: String, attribute: 'current-backend'},
currentLanguage: {type: String, attribute: 'current-language'},
hideInactiveVariants: {type: Boolean, attribute: 'hide-inactive-variants'},
currentShaderModel: {type: String, attribute: 'current-shader-model'},
database: {type: Object, state: true},
materials: {type: Array, state: true},
@@ -702,29 +712,19 @@ class MaterialSidePanel extends LitElement {
updated(props) {
if (props.has('database')) {
const items = [];
// Names need not be unique, so we display a numeric suffix for non-unique names.
// To achieve stable ordering of anonymous materials, we first sort by matid.
const labels = new Set();
const matids = Object.keys(this.database).sort();
const duplicatedLabels = {};
for (const matid of matids) {
const name = this.database[matid].name || kUntitledPlaceholder;
if (labels.has(name)) {
duplicatedLabels[name] = 0;
} else {
labels.add(name);
}
}
const names = matids.map(matid => (this.database[matid].name || kUntitledPlaceholder));
const labelCount = {};
names.forEach(name => labelCount[name] = (labelCount[name] || 0) + 1);
const duplicates = names.filter(name => labelCount[name] > 1);
this.materials = matids.map((matid) => {
const material = this.database[matid];
let name = material.name || kUntitledPlaceholder;
if (name in duplicatedLabels) {
const index = duplicatedLabels[name];
duplicatedLabels[name] = index + 1;
name = `${name} (${index})`;
if (duplicates.includes(name)) {
name = `${name} (${labelCount[name]--})`;
}
return {
matid: matid,
@@ -784,7 +784,7 @@ class MaterialSidePanel extends LitElement {
let divClass =
'material_variant_language language' +
(isLanguageSelected ? ' selected' : '') +
(!isActive ? ' inactive' : '');
(!isActive && this.currentShaderModel != 'matinfo' ? ' inactive' : '');
const onClickLanguage = this._handleLanguageClick.bind(this, lang);
lang = (isLanguageSelected ? '● ' : '') + lang;
return html`
@@ -796,10 +796,11 @@ class MaterialSidePanel extends LitElement {
return html`<div class="languages">${languagesDiv}</div>`;
}
_buildShaderDiv(showAllShaders) {
_buildShaderDiv() {
if (!this.variants) {
return null;
}
const isMatinfo = this.currentShaderModel == 'matinfo';
let variants =
this.variants
.sort((a, b) => {
@@ -808,7 +809,10 @@ class MaterialSidePanel extends LitElement {
if (b.active && !a.active) return 1;
return 0;
})
.filter((variant) => (!this.hideInactiveVariants || variant.shader.active))
.filter(variant => (!this.hideInactiveVariants || variant.shader.active))
.filter(variant => (
isMatinfo || variant.shader.shaderModel == this.currentShaderModel
))
.map((variant) => {
let divClass = 'material_variant_language';
const shaderIndex = +variant.shader.index;
@@ -817,7 +821,7 @@ class MaterialSidePanel extends LitElement {
if (isVariantSelected) {
divClass += ' selected';
}
if (!isActive) {
if (!isActive && !isMatinfo) {
divClass += ' inactive';
}
const onClickVariant = this._handleVariantClick.bind(this, shaderIndex);
@@ -826,11 +830,18 @@ class MaterialSidePanel extends LitElement {
if (vstring.length > 0) {
vstring = `[${vstring}]`;
}
const shaderModelSymbol = {
'desktop': '🄳 ',
'mobile': '🄼 ',
};
let languagesDiv = isVariantSelected ? this._buildLanguagesDiv(isActive) : null;
const stage = (isVariantSelected ? '● ' : '') + variant.shader.pipelineStage;
const stage =
(isMatinfo ? shaderModelSymbol[variant.shader.shaderModel] : '') +
(variant.shader.pipelineStage.indexOf('vertex') >=0 ? '𝕍ert' : '𝔽rag');
return html`
<div class="${divClass}" @click="${onClickVariant}">
<div>${stage} ${vstring} </div>
<div>${stage}&nbsp ${vstring} </div>
</div>
${languagesDiv ?? nothing}
`
@@ -839,6 +850,7 @@ class MaterialSidePanel extends LitElement {
}
render() {
const isMatinfo = this.currentShaderModel == 'matinfo';
const sections = (title, domain) => {
const mats = this.materials
.filter((m) => m.domain == domain)
@@ -854,14 +866,12 @@ class MaterialSidePanel extends LitElement {
const isMaterialSelected = mat.matid === this.currentMaterial;
if (isMaterialSelected) {
divClass += ' selected';
// If we are looking at an inactive material, show all shaders regardless.
const showAllShaders = !material.active;
shaderDiv = this._buildShaderDiv(showAllShaders);
shaderDiv = this._buildShaderDiv();
}
if (!material.active) {
if (!material.active && !isMatinfo) {
divClass += " inactive";
}
const matName = (isMaterialSelected ? '● ' : '') + mat.name;
const matName = mat.name;
return html`
<div class="${divClass}" @click="${onClick}" data-id="${mat.matid}">
${matName}
@@ -878,6 +888,7 @@ class MaterialSidePanel extends LitElement {
(() => html`
<advanced-options id="advanced-options"
current-backend=${this.currentBackend}
current-shader-model=${this.currentShaderModel}
?hide-inactive-variants=${this.hideInactiveVariants}
></advanced-options>
`)();
@@ -885,7 +896,7 @@ class MaterialSidePanel extends LitElement {
return html`
<style>${this.dynamicStyle()}</style>
<div class="container">
<div class="title">matdbg</div>
<div class="title">${isMatinfo ? 'matinfo' : 'matdbg'}</div>
${sections("Surface", "surface") ?? nothing}
${sections("Post-processing", "postpro") ?? nothing}
<material-info id="material-info"></material-info>
@@ -938,8 +949,7 @@ class MatdbgViewer extends LitElement {
}
);
let materials = await fetchMaterials();
this.database = materials;
this.database = await fetchMaterials();
// This is the user preferences stored in localStorage
let hideInactiveVariantsVal = localStorage.getItem('option-hide-inactive-variants');
@@ -979,6 +989,7 @@ class MatdbgViewer extends LitElement {
this.currentMaterial = null;
this.currentLanguage = null;
this.currentBackend = null;
this.currentShaderModel = null;
this.hideInactiveVariants = false;
this.init();
@@ -1049,6 +1060,7 @@ class MatdbgViewer extends LitElement {
// Each material has a list of variants compiled for it, this index tracks a position in the list.
currentShaderIndex: {type: Number, state: true},
currentBackend: {type: String, state: true},
currentShaderModel: {type: String, state: true},
codeViewerExpectedWidth: {type: Number, state: true},
codeViewerExpectedHeight: {type: Number, state: true},
@@ -1070,8 +1082,8 @@ class MatdbgViewer extends LitElement {
const activeVariants = this.activeShaders[this.currentMaterial].variants;
const materialShaders = material[this.currentBackend];
for (let shader in materialShaders) {
let ind = activeVariants.indexOf(+shader);
if (ind >= 0) {
const shaderVariant = materialShaders[shader].variant;
if (activeVariants.indexOf(shaderVariant) >= 0) {
this.currentShaderIndex = +shader;
break;
}
@@ -1095,7 +1107,6 @@ class MatdbgViewer extends LitElement {
shader.modified = false;
this.database = this.database;
}
// Size of the editor will be adjusted due to the code being loaded, we try to
// fit the editor again by calling the resize signal.
setTimeout(this._onResize.bind(this), 700);
@@ -1119,22 +1130,29 @@ class MatdbgViewer extends LitElement {
}
}
if (_validDict(this.activeShaders)) {
let backends = {};
let setBackend = null;
let setShaderModel = null;
for (let matid in this.activeShaders) {
const backend = this.activeShaders[matid].backend;
if (backend in backends) {
backends[backend] = backends[backend] + 1;
} else {
backends[backend] = 1;
if (!setBackend && backend) {
setBackend = backend;
}
const shaderModel = this.activeShaders[matid].shaderModel;
if (!setShaderModel && shaderModel) {
setShaderModel = shaderModel;
}
}
let backendList = Object.keys(backends);
if (backendList.length > 0) {
this.currentBackend = backendList[0];
if (this.currentBackend != setBackend) {
this.currentBackend = setBackend;
}
} else if (!this.currentBackend) {
if (this.currentShaderModel != setShaderModel) {
this.currentShaderModel = setShaderModel;
}
} else if (!this.currentBackend || !this.currentShaderModel) {
// Make a guess on the backend if one wasn't from activeShaders.
this.currentBackend = guessBackend();
this.currentShaderModel = 'matinfo';
}
this._sidepanel.database = this.database;
@@ -1166,6 +1184,7 @@ class MatdbgViewer extends LitElement {
current-shader-index="${this.currentShaderIndex}"
current-material="${this.currentMaterial}"
current-backend="${this.currentBackend}"
current-shader-model="${this.currentShaderModel}"
?hide-inactive-variants="${this.hideInactiveVariants}" >
</material-sidepanel>
<code-viewer id="code-viewer"
+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 \
+3 -1
View File
@@ -12,5 +12,7 @@ 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(TINT_BUILD_SPV_READER ON)
endif ()
add_subdirectory(../ ${PROJECT_BINARY_DIR}/third_party/dawn)
@@ -262,6 +262,8 @@ bool CommandlineConfig::parse() {
mTargetApi |= TargetApi::VULKAN;
} else if (arg == "metal") {
mTargetApi |= TargetApi::METAL;
} else if (arg == "webgpu") {
mTargetApi |= TargetApi::WEBGPU;
} else if (arg == "all") {
mTargetApi |= TargetApi::ALL;
} else {
+2 -2
View File
@@ -423,7 +423,7 @@ static bool parseChunks(Config config, void* data, size_t size) {
if (config.serverPort) {
// Spin up a web server on a secondary thread.
DebugServer server(Backend::DEFAULT, filament::backend::ShaderLanguage::ESSL3,
config.serverPort);
filament::matdbg::DbgShaderModel::MATINFO, config.serverPort);
if (!server.isReady()) {
return false;
}
@@ -532,7 +532,7 @@ static bool parseChunks(Config config, void* data, size_t size) {
return true;
}
//TODO Include a printWgsl logic here
if (config.printMetal) {
const filament::backend::ShaderLanguage language = config.binary
? filament::backend::ShaderLanguage::METAL_LIBRARY