Merge branch 'rc/1.58.1' into release

This commit is contained in:
Benjamin Doherty
2025-03-19 16:17:22 -07:00
38 changed files with 604 additions and 178 deletions
+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**]
+2 -2
View File
@@ -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
+4
View File
@@ -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
+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()
]
+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>
+2 -1
View File
@@ -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
+4
View File
@@ -246,6 +246,10 @@ if (FILAMENT_SUPPORTS_WEBGPU)
)
endif()
if (ANDROID)
list(APPEND SRCS src/BackendUtilsAndroid.cpp)
endif()
# ==================================================================================================
# Definitions
# ==================================================================================================
@@ -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:
@@ -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
+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.
+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;
+2 -2
View File
@@ -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 = {
+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.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`
<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 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`
<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); }}">
${isCollapsed ? '▶' : '▼'}
</span>`
: nothing}
${resource.name}
${hasSubresources && isCollapsed ? html`(${subresourceIds.length})` : nothing}
</th>
${this._renderResourceUsage(allPasses, resourceIds, DEFAULT_COLOR)}
</tr>
${!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`
<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>
`;
}
+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)
+64 -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,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<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.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) {
+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;
/**
+4 -1
View File
@@ -909,6 +909,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
const bool targetApiNeedsSpirv =
(targetApi == TargetApi::VULKAN || targetApi == TargetApi::METAL);
const bool targetApiNeedsMsl = targetApi == TargetApi::METAL;
const bool targetApiNeedsWgsl = targetApi == TargetApi::WEBGPU;
const bool targetApiNeedsGlsl = targetApi == TargetApi::OPENGL;
// Set when a job fails
@@ -923,9 +924,11 @@ 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{};
@@ -1006,7 +1009,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);
+15 -5
View File
@@ -297,12 +297,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",
+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"] = {
+14 -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 << "[\"";
@@ -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;
+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,
+1
View File
@@ -18,6 +18,7 @@
#define MATDBG_SOURCE_FORMATTER_H
#include <mutex>
#include <string>
namespace filament::matdbg {
+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"
+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)
+1 -1
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;
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.58.0",
"version": "1.58.1",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",