diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bb9d0394e6..cf2048a9b1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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: diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 63090f0235..4a1a9c7fa7 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -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**] diff --git a/README.md b/README.md index 54795d7a83..ce0adf1558 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.58.0' + implementation 'com.google.android.filament:filament-android:1.58.1' } ``` @@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ```shell -pod 'Filament', '~> 1.58.0' +pod 'Filament', '~> 1.58.1' ``` ## Documentation diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index bed2c35ed9..74bf74a1d3 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,6 +7,10 @@ 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 + +- 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 diff --git a/android/build.gradle b/android/build.gradle index 5cbd9c33be..c2c3e5fed6 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -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() ] diff --git a/android/filament-android/CMakeLists.txt b/android/filament-android/CMakeLists.txt index 2b4c91add7..59bcb32335 100644 --- a/android/filament-android/CMakeLists.txt +++ b/android/filament-android/CMakeLists.txt @@ -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 - # $<$:fgviewer> + $<$:fgviewer> $<$:matdbg> $<$:filamat> $<$:bluevk> diff --git a/android/gradle.properties b/android/gradle.properties index a366c06a3a..3007050a8a 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.58.0 +VERSION_NAME=1.58.1 POM_DESCRIPTION=Real-time physically based rendering engine for Android. @@ -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 diff --git a/filament/backend/CMakeLists.txt b/filament/backend/CMakeLists.txt index e4cc81a472..b987e18ecb 100644 --- a/filament/backend/CMakeLists.txt +++ b/filament/backend/CMakeLists.txt @@ -246,6 +246,10 @@ if (FILAMENT_SUPPORTS_WEBGPU) ) endif() +if (ANDROID) + list(APPEND SRCS src/BackendUtilsAndroid.cpp) +endif() + # ================================================================================================== # Definitions # ================================================================================================== diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h index 551a7f19ad..f0561be709 100644 --- a/filament/backend/include/backend/DriverEnums.h +++ b/filament/backend/include/backend/DriverEnums.h @@ -807,6 +807,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: diff --git a/filament/backend/include/private/backend/BackendUtilsAndroid.h b/filament/backend/include/private/backend/BackendUtilsAndroid.h new file mode 100644 index 0000000000..21ad61f48a --- /dev/null +++ b/filament/backend/include/private/backend/BackendUtilsAndroid.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 + +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 diff --git a/filament/backend/src/BackendUtilsAndroid.cpp b/filament/backend/src/BackendUtilsAndroid.cpp new file mode 100644 index 0000000000..3eb967650b --- /dev/null +++ b/filament/backend/src/BackendUtilsAndroid.cpp @@ -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 + +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 diff --git a/filament/src/details/Engine.cpp b/filament/src/details/Engine.cpp index 01cc8ec9be..741e2b3b62 100644 --- a/filament/src/details/Engine.cpp +++ b/filament/src/details/Engine.cpp @@ -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. diff --git a/filament/src/details/Material.h b/filament/src/details/Material.h index d0e9d1492b..d6cca777be 100644 --- a/filament/src/details/Material.h +++ b/filament/src/details/Material.h @@ -154,7 +154,14 @@ public: #if FILAMENT_ENABLE_MATDBG assert_invariant((size_t)variant.key < VARIANT_COUNT); std::unique_lock 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)) { diff --git a/filament/src/details/Renderer.cpp b/filament/src/details/Renderer.cpp index e9963a17a3..13d3138bf9 100644 --- a/filament/src/details/Renderer.cpp +++ b/filament/src/details/Renderer.cpp @@ -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; diff --git a/filament/src/details/Texture.cpp b/filament/src/details/Texture.cpp index d018f3800f..3749344d35 100644 --- a/filament/src/details/Texture.cpp +++ b/filament/src/details/Texture.cpp @@ -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"; diff --git a/filament/src/details/View.cpp b/filament/src/details/View.cpp index c06e1667ef..48644370c9 100644 --- a/filament/src/details/View.cpp +++ b/filament/src/details/View.cpp @@ -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 diff --git a/filament/src/details/View.h b/filament/src/details/View.h index 054aa8e11c..13bdf9e503 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -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; diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index af2ff75e12..23a0c603c1 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.58.0" + spec.version = "1.58.1" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.58.0/filament-v1.58.0-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.58.1/filament-v1.58.1-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/libs/fgviewer/CMakeLists.txt b/libs/fgviewer/CMakeLists.txt index 559c975408..9f2ccc4c4b 100644 --- a/libs/fgviewer/CMakeLists.txt +++ b/libs/fgviewer/CMakeLists.txt @@ -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 ) diff --git a/libs/fgviewer/src/DebugServer.cpp b/libs/fgviewer/src/DebugServer.cpp index 268b410d22..a68413bdad 100644 --- a/libs/fgviewer/src/DebugServer.cpp +++ b/libs/fgviewer/src/DebugServer.cpp @@ -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 + +namespace { +struct Asset { + std::string_view mime; + std::string_view data; +}; +std::unordered_map 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 diff --git a/libs/fgviewer/web/app.js b/libs/fgviewer/web/app.js index 3ab55adc81..115cbffdf7 100644 --- a/libs/fgviewer/web/app.js +++ b/libs/fgviewer/web/app.js @@ -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.collapsedResources = 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.collapsedResources.has(resourceIndex)) { + this.collapsedResources.delete(resourceIndex); + } + else { + this.collapsedResources.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` ${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 isCollapsed = this.collapsedResources.has(resourceIndex); + // Show the aggregated resource usage when the subresources are collapsed. + const resourceIds = isCollapsed ? [resource.id, ...subresourceIds]:[resource.id]; + const onClickResource = () => this._handleResourceClick(resource.id); const selectedStyle = resource.id === this.selectedResourceId ? "selected" : ""; return html` - - - ${hasSubresources ? html` - ▶` : nothing} - ${resource.name} - - ${this._renderResourceUsage(allPasses, resource.id, DEFAULT_COLOR)} - - ${this._renderSubresourceRows(resources, resource, resourceIndex, allPasses)} - `; + + + ${hasSubresources + ? html` + + ${isCollapsed ? '▶' : '▼'} + ` + : nothing} + ${resource.name} + ${hasSubresources && isCollapsed ? html`(${subresourceIds.length})` : nothing} + + ${this._renderResourceUsage(allPasses, resourceIds, DEFAULT_COLOR)} + + ${!isCollapsed ? 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` - + ${subresource.name} - ${this._renderResourceUsage(allPasses, subresource.id, SUBRESOURCE_COLOR)} + ${this._renderResourceUsage(allPasses, [subresource.id], SUBRESOURCE_COLOR)} `; } diff --git a/libs/filamat/CMakeLists.txt b/libs/filamat/CMakeLists.txt index 43ab29e5cc..ef30612ae1 100644 --- a/libs/filamat/CMakeLists.txt +++ b/libs/filamat/CMakeLists.txt @@ -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) diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index 8d38ed7cc2..54c8f0645e 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -44,6 +44,10 @@ #include #include +#ifdef FILAMENT_SUPPORTS_WEBGPU +#include +#endif + using namespace glslang; using namespace spirv_cross; using namespace spvtools; @@ -514,8 +518,43 @@ 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()) { + slog.w << "This tint reader error is currently ignored during WebGPU bringup:" << tintRead.Diagnostics().Str() << io::endl; + + //TODO: We should actually return false here, but for initial debugging + // let it slide until combined image sampler is resolved + // return false; + } + + tint::Result 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.w << "This tint writer error is currently ignored during WebGPU bringup:" << wgslOut.Failure().reason.Str() << io::endl; + //TODO: We should actually return false here, but for initial debugging + // let it slide until combined image sampler is resolved + // return false; + } + *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 +569,7 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co .glslOutput = outputGlsl, .spirvOutput = outputSpirv, .mslOutput = outputMsl, + .wgslOutput = outputWgsl, }; switch (config.shaderType) { @@ -610,13 +650,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 +693,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 +709,7 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader, if (!ok) { slog.e << tShader.getInfoLog() << io::endl; + return false; } if (internalConfig.spirvOutput) { @@ -683,6 +731,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 +751,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 +802,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) { diff --git a/libs/filamat/src/GLSLPostProcessor.h b/libs/filamat/src/GLSLPostProcessor.h index 8616d0e4dc..e7a6363e82 100644 --- a/libs/filamat/src/GLSLPostProcessor.h +++ b/libs/filamat/src/GLSLPostProcessor.h @@ -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; /** diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index 15fba4b08b..300ebd7cfc 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -909,6 +909,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector spirv; std::string msl; + std::string wgsl; std::vector* pSpirv = targetApiNeedsSpirv ? &spirv : nullptr; std::string* pMsl = targetApiNeedsMsl ? &msl : nullptr; + std::string* pWgsl = targetApiNeedsWgsl ? &wgsl : nullptr; TextEntry glslEntry{}; BinaryEntry spirvEntry{}; @@ -1006,7 +1009,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector + #include #include @@ -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; diff --git a/libs/matdbg/src/ApiHandler.cpp b/libs/matdbg/src/ApiHandler.cpp index 80d00e2338..fb3c0673aa 100644 --- a/libs/matdbg/src/ApiHandler.cpp +++ b/libs/matdbg/src/ApiHandler.cpp @@ -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()); diff --git a/libs/matdbg/src/DebugServer.cpp b/libs/matdbg/src/DebugServer.cpp index d75952728b..9285bc38c5 100644 --- a/libs/matdbg/src/DebugServer.cpp +++ b/libs/matdbg/src/DebugServer.cpp @@ -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"] = { diff --git a/libs/matdbg/src/JsonWriter.cpp b/libs/matdbg/src/JsonWriter.cpp index 2a22eaeae2..7a5409927b 100644 --- a/libs/matdbg/src/JsonWriter.cpp +++ b/libs/matdbg/src/JsonWriter.cpp @@ -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 shaders; ostringstream json; json << "[\""; @@ -260,7 +260,20 @@ bool JsonWriter::writeActiveInfo(const filaflat::ChunkContainer& package, 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; diff --git a/libs/matdbg/src/ShaderInfo.cpp b/libs/matdbg/src/ShaderInfo.cpp index cba7bf648d..320e1b383a 100644 --- a/libs/matdbg/src/ShaderInfo.cpp +++ b/libs/matdbg/src/ShaderInfo.cpp @@ -16,6 +16,8 @@ #include +#include "CommonWriter.h" + #include #include #include @@ -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(&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, diff --git a/libs/matdbg/src/SourceFormatter.h b/libs/matdbg/src/SourceFormatter.h index 5793474d71..21fbe8f329 100644 --- a/libs/matdbg/src/SourceFormatter.h +++ b/libs/matdbg/src/SourceFormatter.h @@ -18,6 +18,7 @@ #define MATDBG_SOURCE_FORMATTER_H #include +#include namespace filament::matdbg { diff --git a/libs/matdbg/web/api.js b/libs/matdbg/web/api.js index f16fec3a90..79eedf78bc 100644 --- a/libs/matdbg/web/api.js +++ b/libs/matdbg/web/api.js @@ -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; } diff --git a/libs/matdbg/web/app.js b/libs/matdbg/web/app.js index 58858daa3c..56a85d7230 100644 --- a/libs/matdbg/web/app.js +++ b/libs/matdbg/web/app.js @@ -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``)(); + const epandedTitleClass = this.showing ? 'expanded-title' : ''; return html`
-
+
${this.title} ${expandedIcon}

@@ -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`
Current Backend
-
+ ${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` -
+
(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`
${languagesDiv}
`; } - _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`
-
${stage} ${vstring}
+
${stage}  ${vstring}
${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`
${matName} @@ -878,6 +888,7 @@ class MaterialSidePanel extends LitElement { (() => html` `)(); @@ -885,7 +896,7 @@ class MaterialSidePanel extends LitElement { return html`
-
matdbg
+
${isMatinfo ? 'matinfo' : 'matdbg'}
${sections("Surface", "surface") ?? nothing} ${sections("Post-processing", "postpro") ?? nothing} @@ -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}" >