Compare commits

..
Author SHA1 Message Date
Eliza Velasquez 3308ec7e34 wip: editor stuff 2025-03-13 17:59:50 -07:00
Powei Feng 84980d89af Add .clang-format to project root (#8487) 2025-03-12 22:19:01 +00:00
Powei Feng 60640be5e8 vk: fix generateMipmaps level (#8495)
The level counting was off by one.

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

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

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

Add the support to import AHardwareBuffer objects
into Vulkan in Android.
2025-03-11 06:01:28 +00:00
Sungun Park bb3e3df2f4 Fix release notes 2025-03-10 22:35:35 +00:00
49 changed files with 794 additions and 256 deletions
+68
View File
@@ -0,0 +1,68 @@
BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: DontAlign
AlignConsecutiveAssignments: None
AlignOperands: Align
AllowAllArgumentsOnNextLine: false
AllowAllConstructorInitializersOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Always
AllowShortLambdasOnASingleLine: All
AllowShortLoopsOnASingleLine: true
AlwaysBreakAfterReturnType: None
AlwaysBreakTemplateDeclarations: Yes
BreakBeforeBraces: Custom
BracedInitializerIndentWidth: 4
BraceWrapping:
AfterCaseLabel: false
AfterClass: false
AfterControlStatement: Never
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: false
SplitEmptyRecord: true
BreakBeforeBinaryOperators: None
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: BeforeColon
BreakInheritanceList: BeforeColon
ColumnLimit: 100
CompactNamespaces: false
ContinuationIndentWidth: 8
Cpp11BracedListStyle: false
IndentCaseLabels: true
IndentPPDirectives: None
IndentWidth: 4
KeepEmptyLinesAtTheStartOfBlocks: true
MaxEmptyLinesToKeep: 2
NamespaceIndentation: None
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PointerAlignment: Left
ReflowComments: true
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 0
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInContainerLiterals: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
TabWidth: 4
UseTab: Never
PackConstructorInitializers: Never
+10 -2
View File
@@ -1,7 +1,15 @@
;;; Directory Local Variables -*- no-byte-compile: t -*-
;;; For more information see (info "(emacs) Directory Variables")
((c++-mode . ((c-file-style . "filament")
((c++-mode . ((eval . (progn
(make-local-variable 'eglot-ignored-server-capabilities)
(add-to-list 'eglot-ignored-server-capabilities
:documentOnTypeFormattingProvider)))
(c-file-style . "filament")
(apheleia-inhibit . t)))
(c-mode . ((c-file-style . "filament")
(c-mode . ((eval . (progn
(make-local-variable 'eglot-ignored-server-capabilities)
(add-to-list 'eglot-ignored-server-capabilities
:documentOnTypeFormattingProvider)))
(c-file-style . "filament")
(apheleia-inhibit . t))))
+1 -1
View File
@@ -5,7 +5,7 @@ end_of_line = lf
insert_final_newline = true
charset = utf-8
[*.{c,cpp,h,inc,kt,java,js,md}]
[*.{c,cpp,h,inc,kt,java,js,md,fs,vs}]
indent_style = space
indent_size = 4
max_line_length = 100
+11
View File
@@ -104,3 +104,14 @@ jobs:
with:
name: presubmit-renderdiff-result
path: ./out/renderdiff_tests
validate-wgsl-pipeline:
name: validate-wgsl-pipeline
runs-on: macos-14-xlarge
steps:
- uses: actions/checkout@v4.1.6
- name: Run build script
run: ./build.sh -W debug test_filamat
- name: Run test
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.58.1'
implementation 'com.google.android.filament:filament-android:1.58.0'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.58.1'
pod 'Filament', '~> 1.58.0'
```
## Documentation
+3
View File
@@ -9,6 +9,9 @@ Instead, if you are authoring a PR for the main branch, add your release note to
## v1.58.1
## v1.58.0
- materials: add an optional `transformName` field to sampler material parameters. [⚠️ **New Material Version**]
## v1.57.3
+8
View File
@@ -11,6 +11,13 @@ if (FILAMENT_SUPPORTS_VULKAN)
message("Library filamat ignores Vulkan settings")
endif()
if (FILAMENT_SUPPORTS_WEBGPU)
add_library(webgpu_dawn STATIC IMPORTED)
set_target_properties(webgpu_dawn PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/libwebgpu_dawn.a)
endif()
add_library(${FILAMAT_FLAVOR} STATIC IMPORTED)
set_target_properties(${FILAMAT_FLAVOR} PROPERTIES IMPORTED_LOCATION
${FILAMENT_DIR}/lib/${ANDROID_ABI}/lib${FILAMAT_FLAVOR}.a)
@@ -50,4 +57,5 @@ target_link_libraries(filamat-jni
utils
log
smol-v
$<$<STREQUAL:${FILAMENT_SUPPORTS_WEBGPU},ON>:webgpu_dawn> # for tint
)
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.58.1
VERSION_NAME=1.58.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+2
View File
@@ -541,6 +541,7 @@ function build_android {
./gradlew \
-Pcom.google.android.filament.dist-dir=../out/android-debug/filament \
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
${WEBGPU_ANDROID_GRADLE_OPTION} \
:filamat-android:assembleDebug
if [[ "${BUILD_ANDROID_SAMPLES}" == "true" ]]; then
@@ -591,6 +592,7 @@ function build_android {
./gradlew \
-Pcom.google.android.filament.dist-dir=../out/android-release/filament \
-Pcom.google.android.filament.abis=${ABI_GRADLE_OPTION} \
${WEBGPU_ANDROID_GRADLE_OPTION} \
:filamat-android:assembleRelease
if [[ "${BUILD_ANDROID_SAMPLES}" == "true" ]]; then
+5 -1
View File
@@ -233,7 +233,10 @@ if (FILAMENT_SUPPORTS_VULKAN)
elseif (APPLE OR IOS)
list(APPEND SRCS src/vulkan/platform/VulkanPlatformApple.mm)
elseif (ANDROID)
list(APPEND SRCS src/vulkan/platform/VulkanPlatformAndroid.cpp)
list(APPEND SRCS
include/backend/platforms/VulkanPlatformAndroid.h
src/vulkan/platform/VulkanPlatformAndroid.cpp
)
endif()
endif()
@@ -458,6 +461,7 @@ if (APPLE OR LINUX)
test/ShaderGenerator.cpp
test/TrianglePrimitive.cpp
test/Arguments.cpp
test/Lifetimes.cpp
test/test_FeedbackLoops.cpp
test/test_Blit.cpp
test/test_MissingRequiredAttributes.cpp
@@ -182,6 +182,7 @@ enum class ShaderLanguage {
SPIRV = 2,
MSL = 3,
METAL_LIBRARY = 4,
WGSL = 5,
};
static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguage) {
@@ -196,6 +197,8 @@ static constexpr const char* shaderLanguageToString(ShaderLanguage shaderLanguag
return "MSL";
case ShaderLanguage::METAL_LIBRARY:
return "Metal precompiled library";
case ShaderLanguage::WGSL:
return "WGSL";
}
}
@@ -347,18 +347,20 @@ public:
*/
uint32_t memoryTypeBits;
};
virtual ExternalImageMetadata getExternalImageMetadata(void* externalImage);
virtual ExternalImageMetadata getExternalImageMetadata(ExternalImageHandleRef externalImage);
using ImageData = std::pair<VkImage, VkDeviceMemory>;
virtual ImageData createExternalImage(void* externalImage,
virtual ImageData createExternalImageData(ExternalImageHandleRef externalImage,
const ExternalImageMetadata& metadata);
private:
static ExtensionSet getSwapchainInstanceExtensions();
static ExternalImageMetadata getExternalImageMetadataImpl(void* externalImage,
static ExternalImageMetadata getExternalImageMetadataImpl(ExternalImageHandleRef externalImage,
VkDevice device);
static ImageData createExternalImageImpl(void* externalImage, VkDevice device,
const VkAllocationCallbacks* allocator, const ExternalImageMetadata& metadata);
static ImageData createExternalImageDataImpl(ExternalImageHandleRef externalImage,
VkDevice device, const ExternalImageMetadata& metadata);
// Platform dependent helper methods
using SurfaceBundle = std::tuple<VkSurfaceKHR, VkExtent2D>;
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
#define TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
#include <backend/Platform.h>
#include <android/hardware_buffer.h>
namespace filament::backend::fvkandroid {
struct ExternalImageVulkanAndroid : public Platform::ExternalImage {
AHardwareBuffer* aHardwareBuffer = nullptr;
bool sRGB = false;
unsigned int width; // Texture width
unsigned int height; // Texture height
TextureFormat format; // Texture format
TextureUsage usage; // Texture usage flags
protected:
~ExternalImageVulkanAndroid() override;
};
Platform::ExternalImageHandle createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept;
} // namespace filament::backend::fvkandroid
#endif // TNT_FILAMENT_BACKEND_PLATFORMS_VULKAN_PLATFORM_ANDROID_H
@@ -138,6 +138,7 @@ bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
case ShaderLanguage::ESSL1:
case ShaderLanguage::ESSL3:
case ShaderLanguage::SPIRV:
case ShaderLanguage::WGSL:
break;
}
@@ -22,6 +22,10 @@
#include <dlfcn.h>
#include <memory>
// This is to ensure that linking during compilation will not fail even if
// OSMesaGetProcAddress is not linked.
__attribute__((weak)) OSMESAproc OSMesaGetProcAddress(char const*);
namespace filament::backend {
using namespace backend;
@@ -51,10 +55,10 @@ private:
using GetProcAddressFunc = OSMESAproc (*)(const char* funcName);
public:
CreateContextFunc OSMesaCreateContext;
DestroyContextFunc OSMesaDestroyContext;
MakeCurrentFunc OSMesaMakeCurrent;
GetProcAddressFunc OSMesaGetProcAddress;
CreateContextFunc fOSMesaCreateContext;
DestroyContextFunc fOSMesaDestroyContext;
MakeCurrentFunc fOSMesaMakeCurrent;
GetProcAddressFunc fOSMesaGetProcAddress;
OSMesaAPI() {
constexpr char const* libraryNames[] = {"libOSMesa.so", "libosmesa.so"};
@@ -65,18 +69,25 @@ public:
}
}
if (mLib) {
OSMesaGetProcAddress = (GetProcAddressFunc) dlsym(mLib, "OSMesaGetProcAddress");
// Loading from a libosmesa.os
fOSMesaGetProcAddress = (GetProcAddressFunc) dlsym(mLib, "OSMesaGetProcAddress");
} else {
OSMesaGetProcAddress = (GetProcAddressFunc) dlsym(RTLD_LOCAL, "OSMesaGetProcAddress");
// Filament is built into a .so
fOSMesaGetProcAddress = (GetProcAddressFunc) dlsym(RTLD_LOCAL, "OSMesaGetProcAddress");
}
FILAMENT_CHECK_PRECONDITION(OSMesaGetProcAddress)
<< "Unable to against libOSMesa to create a software GL context";
if (!fOSMesaGetProcAddress) {
// Statically linking osmesa
fOSMesaGetProcAddress = OSMesaGetProcAddress;
}
OSMesaCreateContext = (CreateContextFunc) OSMesaGetProcAddress("OSMesaCreateContext");
OSMesaDestroyContext =
(DestroyContextFunc) OSMesaGetProcAddress("OSMesaDestroyContext");
OSMesaMakeCurrent = (MakeCurrentFunc) OSMesaGetProcAddress("OSMesaMakeCurrent");
FILAMENT_CHECK_PRECONDITION(fOSMesaGetProcAddress)
<< "Unable to link against libOSMesa to create a software GL context";
fOSMesaCreateContext = (CreateContextFunc) fOSMesaGetProcAddress("OSMesaCreateContext");
fOSMesaDestroyContext =
(DestroyContextFunc) fOSMesaGetProcAddress("OSMesaDestroyContext");
fOSMesaMakeCurrent = (MakeCurrentFunc) fOSMesaGetProcAddress("OSMesaMakeCurrent");
}
~OSMesaAPI() {
@@ -97,7 +108,7 @@ Driver* PlatformOSMesa::createDriver(void* const sharedGLContext,
FILAMENT_CHECK_PRECONDITION(sharedGLContext == nullptr)
<< "shared GL context is not supported with PlatformOSMesa";
mContext = api->OSMesaCreateContext(GL_RGBA, NULL);
mContext = api->fOSMesaCreateContext(GL_RGBA, NULL);
// We need to do a no-op makecurrent here so that the context will be in a correct state before
// any GL calls.
@@ -113,7 +124,7 @@ Driver* PlatformOSMesa::createDriver(void* const sharedGLContext,
void PlatformOSMesa::terminate() noexcept {
OSMesaAPI* api = (OSMesaAPI*) mOsMesaApi;
api->OSMesaDestroyContext(mContext);
api->fOSMesaDestroyContext(mContext);
delete api;
mOsMesaApi = nullptr;
@@ -141,7 +152,7 @@ bool PlatformOSMesa::makeCurrent(ContextType type, SwapChain* drawSwapChain,
OSMesaAPI* api = (OSMesaAPI*) mOsMesaApi;
OSMesaSwapchain* impl = (OSMesaSwapchain*) drawSwapChain;
auto result = api->OSMesaMakeCurrent(mContext, (BackingType*) impl->buffer.get(),
auto result = api->fOSMesaMakeCurrent(mContext, (BackingType*) impl->buffer.get(),
BACKING_GL_TYPE, impl->width, impl->height);
FILAMENT_CHECK_POSTCONDITION(result == GL_TRUE) << "OSMesaMakeCurrent failed!";
+13 -14
View File
@@ -560,14 +560,6 @@ void VulkanDriver::createTextureExternalImage2R(Handle<HwTexture> th,
Platform::ExternalImageHandleRef externalImage) {
FVK_SYSTRACE_SCOPE();
// FIXME: implement createTextureExternalImage2R
}
void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* externalImage) {
FVK_SYSTRACE_SCOPE();
const auto& metadata = mPlatform->getExternalImageMetadata(externalImage);
if (metadata.isProtected) {
usage |= backend::TextureUsage::PROTECTED;
@@ -577,15 +569,23 @@ void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::Sa
assert_invariant(height == metadata.height);
assert_invariant(fvkutils::getVkFormat(format) == metadata.format);
const auto& data = mPlatform->createExternalImage(externalImage, metadata);
const auto& data = mPlatform->createExternalImageData(externalImage, metadata);
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
mAllocator, &mResourceManager, &mCommands, data.first, data.second, metadata.format,
1, metadata.width, metadata.height, /*depth=*/1, usage, mStagePool);
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th,
mPlatform->getDevice(), mAllocator, &mResourceManager, &mCommands, data.first, data.second, metadata.format,
1, metadata.width, metadata.height, /*depth=*/1, usage, mStagePool);
texture.inc();
}
void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::SamplerType target,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* externalImage) {
FVK_SYSTRACE_SCOPE();
// not supported in this backend
}
void VulkanDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, uint32_t plane) {
@@ -1266,10 +1266,9 @@ void VulkanDriver::generateMipmaps(Handle<HwTexture> th) {
mBlitter.blit(VK_FILTER_LINEAR, dst, dstOffsets, src, srcOffsets);
}
level++;
srcw = dstw;
srch = dsth;
} while ((srcw > 1 || srch > 1) && level < t->levels);
} while ((srcw > 1 || srch > 1) && ++level < t->levels - 1);
}
void VulkanDriver::compilePrograms(CompilerPriorityQueue priority,
@@ -978,13 +978,13 @@ VkQueue VulkanPlatform::getProtectedGraphicsQueue() const noexcept {
}
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadata(
void* externalImage) {
ExternalImageHandleRef externalImage) {
return getExternalImageMetadataImpl(externalImage, mImpl->mDevice);
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImage(void* externalImage,
const ExternalImageMetadata& metadata) {
return createExternalImageImpl(externalImage, mImpl->mDevice, nullptr, metadata);
VulkanPlatform::ImageData VulkanPlatform::createExternalImageData(
ExternalImageHandleRef externalImage, const ExternalImageMetadata& metadata) {
return createExternalImageDataImpl(externalImage, mImpl->mDevice, metadata);
}
#undef SWAPCHAIN_RET_FUNC
@@ -15,6 +15,10 @@
*/
#include <backend/platforms/VulkanPlatform.h>
#include <backend/DriverEnums.h>
#include <backend/platforms/VulkanPlatformAndroid.h>
#include <private/backend/BackendUtilsAndroid.h>
#include "vulkan/VulkanConstants.h"
#include <utils/Panic.h>
@@ -24,17 +28,44 @@
#include <android/hardware_buffer.h>
#include <android/native_window.h>
#include <utility>
using namespace bluevk;
namespace filament::backend {
namespace {
void getVKFormatAndUsage(const AHardwareBuffer_Desc& desc, VkFormat& format,
VkImageUsageFlags& usage, bool& isProtected) {
VkFormat transformVkFormat(VkFormat format, bool sRGB) {
if (!sRGB) {
return format;
}
switch (format) {
case VK_FORMAT_R8G8B8A8_UNORM:
format = VK_FORMAT_R8G8B8A8_SRGB;
break;
case VK_FORMAT_R8G8B8_UNORM:
format = VK_FORMAT_R8G8B8_SRGB;
break;
default:
break;
}
return format;
}
bool isProtectedFromUsage(uint64_t usage) {
return (usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) ? true : false;
}
std::pair<VkFormat, VkImageUsageFlags> getVKFormatAndUsage(const AHardwareBuffer_Desc& desc,
bool sRGB) {
VkFormat format = VK_FORMAT_UNDEFINED;
VkImageUsageFlags usage = 0;
// Refer to "11.2.17. External Memory Handle Types" in the spec, and
// Tables 13/14 for how the following derivation works.
bool isDepthFormat = false;
isProtected = false;
switch (desc.format) {
case AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM:
format = VK_FORMAT_R8G8B8A8_UNORM;
@@ -82,6 +113,8 @@ void getVKFormatAndUsage(const AHardwareBuffer_Desc& desc, VkFormat& format,
format = VK_FORMAT_UNDEFINED;
}
format = transformVkFormat(format, sRGB);
// The following only concern usage flags derived from Table 14.
usage = 0;
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE) {
@@ -98,16 +131,13 @@ void getVKFormatAndUsage(const AHardwareBuffer_Desc& desc, VkFormat& format,
if (desc.usage & AHARDWAREBUFFER_USAGE_GPU_DATA_BUFFER) {
usage = VK_IMAGE_USAGE_STORAGE_BIT;
}
if (desc.usage & AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT) {
isProtected = true;
}
return { format, usage };
}
VulkanPlatform::ImageData allocateExternalImage(void* externalBuffer,
VkDevice device, const VkAllocationCallbacks* allocator,
VulkanPlatform::ImageData allocateExternalImage(AHardwareBuffer* buffer, VkDevice device,
VulkanPlatform::ExternalImageMetadata const& metadata) {
VulkanPlatform::ImageData data;
AHardwareBuffer* buffer = static_cast<AHardwareBuffer*>(externalBuffer);
// if external format we need to specifiy it in the allocation
const bool useExternalFormat = metadata.format == VK_FORMAT_UNDEFINED;
@@ -140,7 +170,7 @@ VulkanPlatform::ImageData allocateExternalImage(void* externalBuffer,
if (metadata.isProtected == false)
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
VkResult result = vkCreateImage(device, &imageInfo, allocator, &data.first);
VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &data.first);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkCreateImage failed with error=" << static_cast<int32_t>(result);
@@ -160,27 +190,57 @@ VulkanPlatform::ImageData allocateExternalImage(void* externalBuffer,
.pNext = &memoryDedicatedAllocateInfo,
.allocationSize = metadata.allocationSize,
.memoryTypeIndex = metadata.memoryTypeBits};
result = vkAllocateMemory(device, &allocInfo, allocator, &data.second);
result = vkAllocateMemory(device, &allocInfo, VKALLOC, &data.second);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkAllocateMemory failed with error=" << static_cast<int32_t>(result);
return data;
}
}// namespace
} // namespace
namespace fvkandroid {
ExternalImageVulkanAndroid::~ExternalImageVulkanAndroid() = default;
Platform::ExternalImageHandle createExternalImage(AHardwareBuffer const* buffer,
bool sRGB) noexcept {
if (__builtin_available(android 26, *)) {
AHardwareBuffer_Desc hardwareBufferDescription = {};
AHardwareBuffer_describe(buffer, &hardwareBufferDescription);
auto* const p = new (std::nothrow) ExternalImageVulkanAndroid;
p->aHardwareBuffer = const_cast<AHardwareBuffer*>(buffer);
p->sRGB = sRGB;
p->height = hardwareBufferDescription.height;
p->width = hardwareBufferDescription.width;
TextureFormat textureFormat = mapToFilamentFormat(hardwareBufferDescription.format, sRGB);
p->format = textureFormat;
p->usage = mapToFilamentUsage(hardwareBufferDescription.usage, textureFormat);
return Platform::ExternalImageHandle{ p };
}
return Platform::ExternalImageHandle{};
}
} // namespace fvkandroid
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
ExternalImageHandleRef externalImage, VkDevice device) {
auto const* fvkExternalImage =
static_cast<fvkandroid::ExternalImageVulkanAndroid const*>(externalImage.get());
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(void* externalImage,
VkDevice device) {
ExternalImageMetadata metadata;
AHardwareBuffer* buffer = static_cast<AHardwareBuffer*>(externalImage);
AHardwareBuffer* buffer = fvkExternalImage->aHardwareBuffer;
if (__builtin_available(android 26, *)) {
AHardwareBuffer_Desc bufferDesc;
AHardwareBuffer_describe(buffer, &bufferDesc);
metadata.width = bufferDesc.width;
metadata.height = bufferDesc.height;
metadata.layers = bufferDesc.layers;
getVKFormatAndUsage(bufferDesc, metadata.format, metadata.usage, metadata.isProtected);
metadata.isProtected = isProtectedFromUsage(bufferDesc.usage);
std::tie(metadata.format, metadata.usage) =
getVKFormatAndUsage(bufferDesc, fvkExternalImage->sRGB);
}
// In the unprotected case add R/W capabilities
if (metadata.isProtected == false) {
@@ -197,20 +257,25 @@ VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataIm
};
VkResult result = vkGetAndroidHardwareBufferPropertiesANDROID(device, buffer, &properties);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkGetAndroidHardwareBufferProperties failed with error="
<< static_cast<int32_t>(result);
FILAMENT_CHECK_POSTCONDITION(metadata.format == formatInfo.format)
<< "mismatched image format for external image (AHB)";
<< "vkGetAndroidHardwareBufferProperties failed with error="
<< static_cast<int32_t>(result);
VkFormat bufferPropertiesFormat = transformVkFormat(formatInfo.format, fvkExternalImage->sRGB);
FILAMENT_CHECK_POSTCONDITION(metadata.format == bufferPropertiesFormat)
<< "mismatched image format( " << metadata.format << ") and queried format("
<< bufferPropertiesFormat << ") for external image (AHB)";
metadata.externalFormat = formatInfo.externalFormat;
metadata.allocationSize = properties.allocationSize;
metadata.memoryTypeBits = properties.memoryTypeBits;
return metadata;
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageImpl(void* externalImage,
VkDevice device, const VkAllocationCallbacks* allocator,
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata) {
ImageData data = allocateExternalImage(externalImage, device, allocator, metadata);
auto const* fvkExternalImage =
static_cast<fvkandroid::ExternalImageVulkanAndroid const*>(externalImage.get());
ImageData data = allocateExternalImage(fvkExternalImage->aHardwareBuffer, device, metadata);
VkResult result = vkBindImageMemory(device, data.first, data.second, 0);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkBindImageMemory error=" << static_cast<int32_t>(result);
@@ -64,12 +64,12 @@ VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
}
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
void* externalImage, VkDevice device) {
ExternalImageHandleRef externalImage, VkDevice device) {
return {};
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageImpl(void* externalImage,
VkDevice device, const VkAllocationCallbacks* allocator,
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata) {
return {};
}
@@ -85,12 +85,12 @@ using namespace bluevk;
namespace filament::backend {
VulkanPlatform::ExternalImageMetadata VulkanPlatform::getExternalImageMetadataImpl(
void* externalImage, VkDevice device) {
ExternalImageHandleRef externalImage, VkDevice device) {
return {};
}
VulkanPlatform::ImageData VulkanPlatform::createExternalImageImpl(void* externalImage,
VkDevice device, const VkAllocationCallbacks* allocator,
VulkanPlatform::ImageData VulkanPlatform::createExternalImageDataImpl(
ExternalImageHandleRef externalImage, VkDevice device,
const ExternalImageMetadata& metadata) {
return {};
}
+6 -4
View File
@@ -45,7 +45,7 @@ ShaderModel WebGPUDriver::getShaderModel() const noexcept {
}
ShaderLanguage WebGPUDriver::getShaderLanguage() const noexcept {
return ShaderLanguage::ESSL3;
return ShaderLanguage::WGSL;
}
// explicit instantiation of the Dispatcher
@@ -131,9 +131,11 @@ Handle<HwStream> WebGPUDriver::createStreamAcquired() {
return {};
}
void WebGPUDriver::setAcquiredImage(Handle<HwStream> sh, void* image,
CallbackHandler* handler, StreamCallback cb, void* userData) {
}
void WebGPUDriver::setAcquiredImage(Handle<HwStream> sh, void* image, const math::mat3f& transform,
CallbackHandler* handler, StreamCallback cb, void* userData) {}
void WebGPUDriver::registerBufferObjectStreams(Handle<HwBufferObject> boh,
BufferObjectStreamDescriptor&& streams) {}
void WebGPUDriver::setStreamDimensions(Handle<HwStream> sh, uint32_t width, uint32_t height) {
}
+92
View File
@@ -0,0 +1,92 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Lifetimes.h"
#include "backend/Platform.h"
#include "private/backend/DriverApi.h"
RenderFrame::RenderFrame(filament::backend::DriverApi& api): mApi(api) {
mApi.beginFrame(0, 0, 0);
}
RenderFrame::~RenderFrame() {
mApi.endFrame(0);
}
Cleanup::Cleanup(filament::backend::DriverApi& driverApi) : mDriverApi(driverApi) {}
Cleanup::~Cleanup() {
for (const auto swapChain: mSwapChains) {
mDriverApi.destroySwapChain(swapChain);
}
for (const auto descriptorSet : mDescriptorSets) {
mDriverApi.destroyDescriptorSet(descriptorSet);
}
for (const auto descriptorSetLayout : mDescriptorSetLayouts) {
mDriverApi.destroyDescriptorSetLayout(descriptorSetLayout);
}
for (const auto bufferObject : mBufferObjects) {
mDriverApi.destroyBufferObject(bufferObject);
}
for (const auto program : mPrograms) {
mDriverApi.destroyProgram(program);
}
for (const auto texture : mTextures) {
mDriverApi.destroyTexture(texture);
}
for (const auto renderTarget : mRenderTargets) {
mDriverApi.destroyRenderTarget(renderTarget);
}
for (const auto& callback : mCallbacks) {
callback();
}
}
template <>
void Cleanup::addInternal(filament::backend::SwapChainHandle handle) {
mSwapChains.push_back(handle);
}
template <>
void Cleanup::addInternal(filament::backend::DescriptorSetHandle handle) {
mDescriptorSets.push_back(handle);
}
template <>
void Cleanup::addInternal(
filament::backend::DescriptorSetLayoutHandle handle) {
mDescriptorSetLayouts.push_back(handle);
}
template <>
void Cleanup::addInternal(filament::backend::BufferObjectHandle handle) {
mBufferObjects.push_back(handle);
}
template <>
void Cleanup::addInternal(filament::backend::ProgramHandle handle) {
mPrograms.push_back(handle);
}
template <>
void Cleanup::addInternal(filament::backend::TextureHandle handle) {
mTextures.push_back(handle);
}
template <>
void Cleanup::addInternal(filament::backend::RenderTargetHandle handle) {
mRenderTargets.push_back(handle);
}
void Cleanup::addPostCall(std::function<void()> callback){
mCallbacks.emplace_back(std::move(callback));
}
+78
View File
@@ -0,0 +1,78 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_LIFETIMES_H
#define TNT_LIFETIMES_H
#include <vector>
#include <functional>
#include <optional>
#include "backend/Handle.h"
#include "backend/DriverApiForward.h"
#include "BackendTest.h"
class RenderFrame {
public:
explicit RenderFrame(filament::backend::DriverApi& api);
~RenderFrame();
RenderFrame(RenderFrame const& rhs) noexcept = delete;
RenderFrame& operator=(RenderFrame const& rhs) noexcept = delete;
RenderFrame(RenderFrame const&& rhs) noexcept = delete;
RenderFrame& operator=(RenderFrame const&& rhs) noexcept = delete;
private:
filament::backend::DriverApi& mApi;
};
class Cleanup {
public:
/**
* @param driverApi Must be valid for the entire lifetime of the Cleanup object.
*/
explicit Cleanup(filament::backend::DriverApi& driverApi);
~Cleanup();
template <typename HandleType>
filament::backend::Handle<HandleType> add(filament::backend::Handle<HandleType> handle);
void addPostCall(std::function<void()> callback);
private:
template <typename HandleType>
void addInternal(filament::backend::Handle<HandleType> handle);
filament::backend::DriverApi & mDriverApi;
std::vector<filament::backend::SwapChainHandle> mSwapChains;
std::vector<filament::backend::DescriptorSetHandle> mDescriptorSets;
std::vector<filament::backend::DescriptorSetLayoutHandle> mDescriptorSetLayouts;
std::vector<filament::backend::BufferObjectHandle> mBufferObjects;
std::vector<filament::backend::ProgramHandle> mPrograms;
std::vector<filament::backend::TextureHandle> mTextures;
std::vector<filament::backend::RenderTargetHandle> mRenderTargets;
std::vector<std::function<void()>> mCallbacks;
};
template <typename HandleType>
filament::backend::Handle<HandleType> Cleanup::add(filament::backend::Handle<HandleType> handle) {
addInternal(handle);
return handle;
}
#endif //TNT_LIFETIMES_H
+2 -1
View File
@@ -30,7 +30,8 @@ enum class Backend : uint8_t {
OPENGL = 1,
VULKAN = 2,
METAL = 3,
NOOP = 4,
WEBGPU = 4,
NOOP = 5,
};
struct NativeView {
+9 -1
View File
@@ -90,6 +90,9 @@ ShaderGenerator::ShaderGenerator(std::string vertex, std::string fragment, Backe
case Backend::METAL:
mShaderLanguage = filament::backend::ShaderLanguage::MSL;
break;
case Backend::WEBGPU:
mShaderLanguage = filament::backend::ShaderLanguage::WGSL;
break;
case Backend::NOOP:
mShaderLanguage = filament::backend::ShaderLanguage::ESSL3;
break;
@@ -115,6 +118,8 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(ShaderStage stage, std::s
shader.insert(pos, "#define TARGET_METAL_ENVIRONMENT\n");
} else if (backend == Backend::VULKAN) {
shader.insert(pos, "#define TARGET_VULKAN_ENVIRONMENT\n");
} else if (backend == Backend::WEBGPU) {
shader.insert(pos, "#define TARGET_VULKAN_ENVIRONMENT\n");
}
const char* shaderCString = shader.c_str();
@@ -150,7 +155,8 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(ShaderStage stage, std::s
assert_invariant(backend == Backend::OPENGL ||
backend == Backend::METAL ||
backend == Backend::VULKAN);
backend == Backend::VULKAN ||
backend == Backend::WEBGPU);
if (backend == Backend::OPENGL) {
if (isMobile) {
@@ -166,6 +172,8 @@ ShaderGenerator::Blob ShaderGenerator::transpileShader(ShaderStage stage, std::s
return { result.c_str(), result.c_str() + result.length() + 1 };
} else if (backend == Backend::VULKAN) {
return { (uint8_t*)spirv.data(), (uint8_t*)(spirv.data() + spirv.size()) };
} else if (backend == Backend::WEBGPU){
filamat::GLSLPostProcessor::spirvToWgsl(&spirv, &result);
}
return {};
+110 -141
View File
@@ -16,6 +16,7 @@
#include "BackendTest.h"
#include "Lifetimes.h"
#include "ShaderGenerator.h"
#include "TrianglePrimitive.h"
@@ -195,6 +196,8 @@ static void createFaces(DriverApi& dapi, Handle<HwTexture> texture, int baseWidt
TEST_F(BackendTest, ColorMagnify) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 256;
constexpr int kSrcTexHeight = 256;
@@ -205,30 +208,30 @@ TEST_F(BackendTest, ColorMagnify) {
constexpr int kNumLevels = 3;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
Handle<HwTexture> const srcTexture = api.createTexture(
Handle<HwTexture> const srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
const bool flipY = sBackend == Backend::OPENGL;
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
// Create a destination texture.
Handle<HwTexture> const dstTexture = api.createTexture(
Handle<HwTexture> const dstTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
// Create a RenderTarget for each texture's miplevel.
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
Handle<HwRenderTarget> dstRenderTargets[kNumLevels];
for (uint8_t level = 0; level < kNumLevels; level++) {
srcRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {});
dstRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {});
srcRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {}));
dstRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {}));
}
// Do a "magnify" blit from level 1 of the source RT to the level 0 of the destination RT.
@@ -238,16 +241,18 @@ TEST_F(BackendTest, ColorMagnify) {
{0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel}, SamplerMagFilter::LINEAR);
// Push through an empty frame to allow the texture to upload and the blit to execute.
api.beginFrame(0, 0, 0);
api.commit(swapChain);
api.endFrame(0);
{
RenderFrame frame(api);
api.commit(swapChain);
}
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMagnify.png" };
api.beginFrame(0, 0, 0);
dumpScreenshot(api, dstRenderTargets[0], &params);
api.commit(swapChain);
api.endFrame(0);
{
RenderFrame frame(api);
dumpScreenshot(api, dstRenderTargets[0], &params);
api.commit(swapChain);
}
// Wait for the ReadPixels result to come back.
api.finish();
@@ -258,18 +263,12 @@ TEST_F(BackendTest, ColorMagnify) {
const uint32_t expected = 0x410bdd31;
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
EXPECT_TRUE(params.pixelHashResult == expected);
// Cleanup.
api.destroyTexture(srcTexture);
api.destroyTexture(dstTexture);
api.destroySwapChain(swapChain);
for (auto rt : srcRenderTargets) api.destroyRenderTarget(rt);
for (auto rt : dstRenderTargets) api.destroyRenderTarget(rt);
executeCommands();
}
TEST_F(BackendTest, ColorMinify) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 1024;
constexpr int kSrcTexHeight = 1024;
@@ -280,30 +279,30 @@ TEST_F(BackendTest, ColorMinify) {
constexpr int kNumLevels = 3;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
Handle<HwTexture> const srcTexture = api.createTexture(
Handle<HwTexture> const srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
const bool flipY = sBackend == Backend::OPENGL;
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
// Create a destination texture.
Handle<HwTexture> const dstTexture = api.createTexture(
Handle<HwTexture> const dstTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
// Create a RenderTarget for each texture's miplevel.
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
Handle<HwRenderTarget> dstRenderTargets[kNumLevels];
for (uint8_t level = 0; level < kNumLevels; level++) {
srcRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {});
dstRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {});
srcRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {}));
dstRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, 0 }, {}, {}));
}
// Do a "minify" blit from level 1 of the source RT to the level 0 of the destination RT.
@@ -314,6 +313,7 @@ TEST_F(BackendTest, ColorMinify) {
srcRenderTargets[srcLevel], {0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel},
SamplerMagFilter::LINEAR);
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "ColorMinify.png" };
dumpScreenshot(api, dstRenderTargets[0], &params);
@@ -325,18 +325,11 @@ TEST_F(BackendTest, ColorMinify) {
const uint32_t expected = 0xf3d9c53f;
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
EXPECT_TRUE(params.pixelHashResult == expected);
// Cleanup.
api.destroyTexture(srcTexture);
api.destroyTexture(dstTexture);
api.destroySwapChain(swapChain);
for (auto rt : srcRenderTargets) api.destroyRenderTarget(rt);
for (auto rt : dstRenderTargets) api.destroyRenderTarget(rt);
executeCommands();
}
TEST_F(BackendTest, ColorResolve) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
constexpr int kSrcTexWidth = 256;
constexpr int kSrcTexHeight = 256;
@@ -356,40 +349,40 @@ TEST_F(BackendTest, ColorResolve) {
triangleVs, triangleFs, sBackend, sIsMobilePlatform, std::move(descriptors));
Program prog = shaderGen.getProgram(api);
prog.descriptorBindings(1, {{ "Params", DescriptorType::UNIFORM_BUFFER, 0 }});
program = api.createProgram(std::move(prog));
program = cleanup.add(api.createProgram(std::move(prog)));
}
DescriptorSetLayoutHandle descriptorSetLayout = api.createDescriptorSetLayout({
DescriptorSetLayoutHandle descriptorSetLayout = cleanup.add(api.createDescriptorSetLayout({
{{
DescriptorType::UNIFORM_BUFFER,
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS, 0,
DescriptorFlags::NONE, 0
}}});
}}}));
DescriptorSetHandle descriptorSet = api.createDescriptorSet(descriptorSetLayout);
DescriptorSetHandle descriptorSet = cleanup.add(api.createDescriptorSet(descriptorSetLayout));
// Create a VertexBuffer, IndexBuffer, and RenderPrimitive.
TrianglePrimitive const triangle(api);
// Create 4-sample texture.
Handle<HwTexture> const srcColorTexture = api.createTexture(
Handle<HwTexture> const srcColorTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, 1, kColorTexFormat, kSampleCount, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::COLOR_ATTACHMENT);
TextureUsage::COLOR_ATTACHMENT));
// Create 1-sample texture.
Handle<HwTexture> const dstColorTexture = api.createTexture(
Handle<HwTexture> const dstColorTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, 1, kColorTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
// Create a 4-sample render target with the 4-sample texture.
Handle<HwRenderTarget> const srcRenderTarget = api.createRenderTarget(
Handle<HwRenderTarget> const srcRenderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR, kSrcTexWidth, kSrcTexHeight, kSampleCount, 0,
{{ srcColorTexture }}, {}, {});
{{ srcColorTexture }}, {}, {}));
// Create a 1-sample render target with the 1-sample texture.
Handle<HwRenderTarget> const dstRenderTarget = api.createRenderTarget(
Handle<HwRenderTarget> const dstRenderTarget = cleanup.add(api.createRenderTarget(
TargetBufferFlags::COLOR, kDstTexWidth, kDstTexHeight, 1, 0,
{{ dstColorTexture }}, {}, {});
{{ dstColorTexture }}, {}, {}));
// Prep for rendering.
RenderPassParams params = {};
@@ -408,8 +401,8 @@ TEST_F(BackendTest, ColorResolve) {
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
auto ubuffer = api.createBufferObject(sizeof(MaterialParams),
BufferObjectBinding::UNIFORM, BufferUsage::STATIC);
auto ubuffer = cleanup.add(api.createBufferObject(sizeof(MaterialParams),
BufferObjectBinding::UNIFORM, BufferUsage::STATIC));
// Draw red triangle into srcRenderTarget.
uploadUniforms(api, ubuffer, {
.color = float4(1, 0, 0, 1),
@@ -420,11 +413,12 @@ TEST_F(BackendTest, ColorResolve) {
api.bindDescriptorSet(descriptorSet, 1, {});
// FIXME: on Metal this triangle is not drawn. Can't understand why.
api.beginFrame(0, 0, 0);
{
RenderFrame frame(api);
api.beginRenderPass(srcRenderTarget, params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
api.endFrame(0);
}
// Resolve the MSAA render target into the single-sample render target.
api.blitDEPRECATED(TargetBufferFlags::COLOR,
@@ -443,23 +437,15 @@ TEST_F(BackendTest, ColorResolve) {
const uint32_t expected = 0xebfac2ef;
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", sparams.pixelHashResult, expected);
EXPECT_TRUE(sparams.pixelHashResult == expected);
// Cleanup.
api.destroyDescriptorSet(descriptorSet);
api.destroyDescriptorSetLayout(descriptorSetLayout);
api.destroyBufferObject(ubuffer);
api.destroyProgram(program);
api.destroyTexture(srcColorTexture);
api.destroyTexture(dstColorTexture);
api.destroyRenderTarget(srcRenderTarget);
api.destroyRenderTarget(dstRenderTarget);
flushAndWait();
}
TEST_F(BackendTest, Blit2DTextureArray) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
api.startCapture(0);
cleanup.addPostCall([&]() { api.stopCapture(0); });
constexpr int kSrcTexWidth = 256;
constexpr int kSrcTexHeight = 256;
@@ -474,27 +460,27 @@ TEST_F(BackendTest, Blit2DTextureArray) {
constexpr int kDstTexLayer = 0;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
Handle<HwTexture> srcTexture = api.createTexture(
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D_ARRAY, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, kSrcTexDepth,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
const bool flipY = sBackend == Backend::OPENGL;
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, kSrcTexLayer, float3(0.5, 0, 0), flipY);
// Create a destination texture.
Handle<HwTexture> dstTexture = api.createTexture(
Handle<HwTexture> dstTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
// Create two RenderTargets.
const int level = 0;
Handle<HwRenderTarget> srcRenderTarget = api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, kSrcTexLayer }, {}, {});
Handle<HwRenderTarget> dstRenderTarget = api.createRenderTarget( TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, kDstTexLayer }, {}, {});
Handle<HwRenderTarget> srcRenderTarget = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, kSrcTexLayer }, {}, {}));
Handle<HwRenderTarget> dstRenderTarget = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kDstTexWidth >> level, kDstTexHeight >> level, 1, 0, { dstTexture, level, kDstTexLayer }, {}, {}));
// Do a blit from kSrcTexLayer of the source RT to kDstTexLayer of the destination RT.
const int srcLevel = 0;
@@ -503,16 +489,18 @@ TEST_F(BackendTest, Blit2DTextureArray) {
{0, 0, kSrcTexWidth >> srcLevel, kSrcTexHeight >> srcLevel}, SamplerMagFilter::LINEAR);
// Push through an empty frame to allow the texture to upload and the blit to execute.
api.beginFrame(0, 0, 0);
api.commit(swapChain);
api.endFrame(0);
{
RenderFrame frame(api);
api.commit(swapChain);
}
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "Blit2DTextureArray.png" };
api.beginFrame(0, 0, 0);
dumpScreenshot(api, dstRenderTarget, &params);
api.commit(swapChain);
api.endFrame(0);
{
RenderFrame frame(api);
dumpScreenshot(api, dstRenderTarget, &params);
api.commit(swapChain);
}
// Wait for the ReadPixels result to come back.
api.finish();
@@ -523,20 +511,12 @@ TEST_F(BackendTest, Blit2DTextureArray) {
const uint32_t expected = 0x8de7d55b;
printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
EXPECT_TRUE(params.pixelHashResult == expected);
// Cleanup.
api.destroyTexture(srcTexture);
api.destroyTexture(dstTexture);
api.destroySwapChain(swapChain);
api.destroyRenderTarget(srcRenderTarget);
api.destroyRenderTarget(dstRenderTarget);
executeCommands();
api.stopCapture(0);
}
TEST_F(BackendTest, BlitRegion) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 1024;
constexpr int kSrcTexHeight = 1024;
@@ -549,21 +529,21 @@ TEST_F(BackendTest, BlitRegion) {
constexpr int kDstLevel = 0;
// Create a SwapChain and make it current. We don't really use it so the res doesn't matter.
auto swapChain = api.createSwapChainHeadless(256, 256, 0);
auto swapChain = cleanup.add(api.createSwapChainHeadless(256, 256, 0));
api.makeCurrent(swapChain, swapChain);
// Create a source texture.
Handle<HwTexture> srcTexture = api.createTexture(
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
const bool flipY = sBackend == Backend::OPENGL;
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
// Create a destination texture.
Handle<HwTexture> dstTexture = api.createTexture(
Handle<HwTexture> dstTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kDstTexFormat, 1, kDstTexWidth, kDstTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::COLOR_ATTACHMENT));
// Blit one-quarter of src level 1 to dst level 0.
Viewport srcRect = {
@@ -583,34 +563,34 @@ TEST_F(BackendTest, BlitRegion) {
// We purposely set the render target width and height to smaller than the texture, to check
// that this case is handled correctly.
Handle<HwRenderTarget> srcRenderTarget =
api.createRenderTarget(TargetBufferFlags::COLOR, srcRect.width,
srcRect.height, 1, 0, {srcTexture, kSrcLevel, 0}, {}, {});
cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR, srcRect.width,
srcRect.height, 1, 0, {srcTexture, kSrcLevel, 0}, {}, {}));
Handle<HwRenderTarget> dstRenderTarget =
api.createRenderTarget(TargetBufferFlags::COLOR, kDstTexWidth >> kDstLevel,
kDstTexHeight >> kDstLevel, 1, 0, {dstTexture, kDstLevel, 0}, {}, {});
cleanup.add(api.createRenderTarget(TargetBufferFlags::COLOR, kDstTexWidth >> kDstLevel,
kDstTexHeight >> kDstLevel, 1, 0, {dstTexture, kDstLevel, 0}, {}, {}));
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget, dstRect, srcRenderTarget, srcRect,
SamplerMagFilter::LINEAR);
// Push through an empty frame to allow the texture to upload and the blit to execute.
api.beginFrame(0, 0, 0);
api.commit(swapChain);
api.endFrame(0);
{
RenderFrame frame(api);
api.commit(swapChain);
}
// Grab a screenshot.
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegion.png" };
api.beginFrame(0, 0, 0);
dumpScreenshot(api, dstRenderTarget, &params);
api.commit(swapChain);
api.endFrame(0);
{
RenderFrame frame(api);
dumpScreenshot(api, dstRenderTarget, &params);
api.commit(swapChain);
}
// Wait for the ReadPixels result to come back.
api.finish();
executeCommands();
getDriver().purge();
// Check if the image matches perfectly to our golden run.
//
// TODO: for some reason, this test has very, very slight (as in one pixel) differences between
// OpenGL and Metal. So disable golden checking for now.
// Use the compare tool from ImageMagick to see visual differences:
@@ -619,18 +599,12 @@ TEST_F(BackendTest, BlitRegion) {
// const uint32_t expected = 0x74fa34ed;
// printf("Computed hash is 0x%8.8x, Expected 0x%8.8x\n", params.pixelHashResult, expected);
// EXPECT_TRUE(params.pixelHashResult == expected);
// Cleanup.
api.destroyTexture(srcTexture);
api.destroyTexture(dstTexture);
api.destroySwapChain(swapChain);
api.destroyRenderTarget(srcRenderTarget);
api.destroyRenderTarget(dstRenderTarget);
executeCommands();
}
TEST_F(BackendTest, BlitRegionToSwapChain) {
auto& api = getDriverApi();
Cleanup cleanup(getDriverApi());
cleanup.addPostCall([&]() { executeCommands(); });
constexpr int kSrcTexWidth = 1024;
constexpr int kSrcTexHeight = 1024;
@@ -640,14 +614,15 @@ TEST_F(BackendTest, BlitRegionToSwapChain) {
constexpr int kNumLevels = 3;
// Create a SwapChain and make it current.
auto swapChain = createSwapChain();
Handle<HwRenderTarget> dstRenderTarget = api.createDefaultRenderTarget();
auto swapChain = cleanup.add(createSwapChain());
api.makeCurrent(swapChain, swapChain);
Handle<HwRenderTarget> dstRenderTarget = cleanup.add(api.createDefaultRenderTarget());
// Create a source texture.
Handle<HwTexture> srcTexture = api.createTexture(
Handle<HwTexture> srcTexture = cleanup.add(api.createTexture(
SamplerType::SAMPLER_2D, kNumLevels, kSrcTexFormat, 1, kSrcTexWidth, kSrcTexHeight, 1,
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT);
TextureUsage::SAMPLEABLE | TextureUsage::UPLOADABLE | TextureUsage::COLOR_ATTACHMENT));
const bool flipY = sBackend == Backend::OPENGL;
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 0, float3(0.5, 0, 0), flipY);
createBitmap(api, srcTexture, kSrcTexWidth, kSrcTexHeight, 1, float3(0, 0, 0.5), flipY);
@@ -655,8 +630,8 @@ TEST_F(BackendTest, BlitRegionToSwapChain) {
// Create a RenderTarget for each texture's miplevel.
Handle<HwRenderTarget> srcRenderTargets[kNumLevels];
for (uint8_t level = 0; level < kNumLevels; level++) {
srcRenderTargets[level] = api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {});
srcRenderTargets[level] = cleanup.add(api.createRenderTarget( TargetBufferFlags::COLOR,
kSrcTexWidth >> level, kSrcTexHeight >> level, 1, 0, { srcTexture, level, 0 }, {}, {}));
}
// Blit one-quarter of src level 1 to dst level 0.
@@ -674,29 +649,23 @@ TEST_F(BackendTest, BlitRegionToSwapChain) {
.height = kDstTexHeight - 10,
};
api.beginFrame(0, 0, 0);
{
RenderFrame frame(api);
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
dstRect, srcRenderTargets[srcLevel],
srcRect, SamplerMagFilter::LINEAR);
api.blitDEPRECATED(TargetBufferFlags::COLOR0, dstRenderTarget,
dstRect, srcRenderTargets[srcLevel],
srcRect, SamplerMagFilter::LINEAR);
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain.png" };
dumpScreenshot(api, dstRenderTarget, &params);
ScreenshotParams params { kDstTexWidth, kDstTexHeight, "BlitRegionToSwapChain.png" };
dumpScreenshot(api, dstRenderTarget, &params);
api.commit(swapChain);
api.endFrame(0);
api.commit(swapChain);
}
// Wait for the ReadPixels result to come back.
api.finish();
executeCommands();
getDriver().purge();
// Cleanup.
api.destroyTexture(srcTexture);
api.destroySwapChain(swapChain);
for (auto rt : srcRenderTargets) api.destroyRenderTarget(rt);
executeCommands();
}
} // namespace test
+2
View File
@@ -62,6 +62,8 @@ constexpr std::pair<ChunkType, ChunkType> shaderLanguageToTags(ShaderLanguage co
return { MaterialEssl1, DictionaryText };
case ShaderLanguage::MSL:
return { MaterialMetal, DictionaryText };
case ShaderLanguage::WGSL:
return { MaterialWgsl, DictionaryText };
case ShaderLanguage::SPIRV:
return { MaterialSpirv, DictionarySpirv };
case ShaderLanguage::METAL_LIBRARY:
+4 -3
View File
@@ -388,12 +388,13 @@ void FMaterial::terminate(FEngine& engine) {
<< "destroying material \"" << this->getName().c_str_safe() << "\" but "
<< pos->second.size() << " instances still alive.";
} else {
utils::slog.e << "destroying material \"" << this->getName().c_str_safe() << "\" but "
<< pos->second.size() << " instances still alive.";
if (UTILS_UNLIKELY(!pos->second.empty())) {
slog.e << "destroying material \"" << this->getName().c_str_safe() << "\" but "
<< pos->second.size() << " instances still alive." << io::endl;
}
}
}
#if FILAMENT_ENABLE_MATDBG
// Unregister the material with matdbg.
matdbg::DebugServer* server = engine.debug.server;
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.58.1"
spec.version = "1.58.0"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.58.1/filament-v1.58.1-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.58.0/filament-v1.58.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
+4
View File
@@ -52,6 +52,10 @@ include_directories(${PUBLIC_HDR_DIR})
# we're building a library
add_library(${TARGET} STATIC ${PUBLIC_HDRS} ${SRCS})
if(FILAMENT_SUPPORTS_OSMESA)
target_compile_options(${TARGET} PRIVATE -I${FILAMENT_OSMESA_PATH}/include/GL)
endif()
# specify where the public headers of this library are
target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR})
set_target_properties(${TARGET} PROPERTIES FOLDER Libs)
+11 -2
View File
@@ -14,16 +14,21 @@
* limitations under the License.
*/
#include <GL/gl.h>
#include <dlfcn.h>
#include <string.h>
#include <osmesa.h>
namespace bluegl {
namespace {
using ProcAddressFunc = void*(*)(char const* funcName);
}
// This is to ensure that linking during compilation will not fail even if
// OSMesaGetProcAddress is not linked.
__attribute__((weak)) OSMESAproc OSMesaGetProcAddress(char const*);
struct Driver {
ProcAddressFunc OSMesaGetProcAddress;
void* library;
@@ -31,7 +36,7 @@ struct Driver {
bool initBinder() {
constexpr char const* libraryNames[] = {"libOSMesa.so", "libosmesa.so"};
for (char const* name : libraryNames) {
for (char const* name: libraryNames) {
g_driver.library = dlopen(name, RTLD_GLOBAL | RTLD_NOW);
if (g_driver.library) {
break;
@@ -45,6 +50,10 @@ bool initBinder() {
(ProcAddressFunc) dlsym(g_driver.library, "OSMesaGetProcAddress");
}
if (!g_driver.OSMesaGetProcAddress) {
g_driver.OSMesaGetProcAddress = (ProcAddressFunc) OSMesaGetProcAddress;
}
return g_driver.OSMesaGetProcAddress;
}
+9 -9
View File
@@ -346,7 +346,7 @@ class FrameGraphTable extends LitElement {
super();
this.frameGraphData = null;
this.selectedResourceId = -1;
this.collapsedResources = new Set();
this.expandedResourceSet = new Set();
}
updated(props) {
@@ -365,11 +365,11 @@ class FrameGraphTable extends LitElement {
}
_toggleCollapse(resourceIndex) {
if (this.collapsedResources.has(resourceIndex)) {
this.collapsedResources.delete(resourceIndex);
if (this.expandedResourceSet.has(resourceIndex)) {
this.expandedResourceSet.delete(resourceIndex);
}
else {
this.collapsedResources.add(resourceIndex);
this.expandedResourceSet.add(resourceIndex);
}
this.requestUpdate();
}
@@ -430,9 +430,9 @@ class FrameGraphTable extends LitElement {
.map(subresource => subresource.id);
const hasSubresources = subresourceIds.length > 0;
const isCollapsed = this.collapsedResources.has(resourceIndex);
const isExpanded = this.expandedResourceSet.has(resourceIndex);
// Show the aggregated resource usage when the subresources are collapsed.
const resourceIds = isCollapsed ? [resource.id, ...subresourceIds]:[resource.id];
const resourceIds = isExpanded ? [resource.id]:[resource.id, ...subresourceIds];
const onClickResource = () => this._handleResourceClick(resource.id);
const selectedStyle = resource.id === this.selectedResourceId ? "selected" : "";
@@ -444,15 +444,15 @@ class FrameGraphTable extends LitElement {
? html`
<span class="toggle-icon"
@click="${(e) => { e.stopPropagation(); this._toggleCollapse(resourceIndex); }}">
${isCollapsed ? '▶' : '▼'}
${isExpanded ? '▼' : '▶'}
</span>`
: nothing}
${resource.name}
${hasSubresources && isCollapsed ? html`(${subresourceIds.length})` : nothing}
${hasSubresources && isExpanded ? nothing : html`(${subresourceIds.length})`}
</th>
${this._renderResourceUsage(allPasses, resourceIds, DEFAULT_COLOR)}
</tr>
${!isCollapsed ? this._renderSubresourceRows(resources, resource, resourceIndex, allPasses) : nothing}
${isExpanded ? this._renderSubresourceRows(resources, resource, resourceIndex, allPasses) : nothing}
`;
}
@@ -45,6 +45,7 @@ enum UTILS_PUBLIC ChunkType : uint64_t {
MaterialEssl1 = charTo64bitNum("MAT_ESS1"),
MaterialSpirv = charTo64bitNum("MAT_SPIR"),
MaterialMetal = charTo64bitNum("MAT_METL"),
MaterialWgsl = charTo64bitNum("MAT_WGSL"),
MaterialMetalLibrary = charTo64bitNum("MAT_MLIB"),
MaterialShaderModels = charTo64bitNum("MAT_SMDL"),
MaterialBindingUniformInfo = charTo64bitNum("MAT_UFRM"),
@@ -28,7 +28,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
static constexpr size_t MATERIAL_VERSION = 58;
static constexpr size_t MATERIAL_VERSION = 57;
/**
* Supported shading models
+1
View File
@@ -183,6 +183,7 @@ bool MaterialChunk::getShader(ShaderContent& shaderContent, BlobDictionary const
switch (mMaterialTag) {
case filamat::ChunkType::MaterialGlsl:
case filamat::ChunkType::MaterialEssl1:
case filamat::ChunkType::MaterialWgsl:
case filamat::ChunkType::MaterialMetal:
return getTextShader(mUnflattener, dictionary, shaderContent, shaderModel, variant, stage);
case filamat::ChunkType::MaterialSpirv:
@@ -81,7 +81,11 @@ public:
VULKAN = 0x02u,
METAL = 0x04u,
WEBGPU = 0x08u,
#ifdef FILAMENT_SUPPORTS_WEBGPU
ALL = OPENGL | VULKAN | METAL | WEBGPU
#else
ALL = OPENGL | VULKAN | METAL
#endif
};
/*
+10 -9
View File
@@ -527,11 +527,14 @@ bool GLSLPostProcessor::spirvToWgsl(const SpirvBlob *spirv, std::string *outWsl)
tint::Program tintRead = tint::spirv::reader::Read(*spirv, readerOpts);
if (tintRead.Diagnostics().ContainsErrors()) {
slog.w << "This tint reader error is currently ignored during WebGPU bringup:" << tintRead.Diagnostics().Str() << io::endl;
//TODO: We should actually return false here, but for initial debugging
// let it slide until combined image sampler is resolved
// return false;
//TODO remove this if block once combined image sampler conversion works.
if (tintRead.Diagnostics().Str().rfind("error: WGSL does not support combined image-samplers") != std::string::npos) {
slog.w << "This tint reader error is currently ignored during WebGPU bringup: " << tintRead.Diagnostics().Str() << io::endl;
}
else {
slog.e << "Tint Reader Error: " << tintRead.Diagnostics().Str() << io::endl;
return false;
}
}
tint::Result<tint::wgsl::writer::Output> wgslOut = tint::wgsl::writer::Generate(tintRead,writerOpts);
@@ -539,10 +542,8 @@ bool GLSLPostProcessor::spirvToWgsl(const SpirvBlob *spirv, std::string *outWsl)
tint::SuccessType tintSuccess;
if (wgslOut != tintSuccess) {
slog.w << "This tint writer error is currently ignored during WebGPU bringup:" << wgslOut.Failure().reason.Str() << io::endl;
//TODO: We should actually return false here, but for initial debugging
// let it slide until combined image sampler is resolved
// return false;
slog.e << "Tint writer error: " << wgslOut.Failure().reason.Str() << io::endl;
return false;
}
*outWsl = wgslOut->wgsl;
return true;
+30 -1
View File
@@ -179,6 +179,14 @@ void MaterialBuilderBase::prepare(bool vulkanSemantics,
effectiveFeatureLevel,
});
}
if (any(mTargetApi & TargetApi::WEBGPU)) {
mCodeGenPermutations.push_back({
shaderModel,
TargetApi::WEBGPU,
TargetLanguage::SPIRV,
effectiveFeatureLevel,
});
}
}
}
@@ -836,6 +844,7 @@ static void showErrorMessage(const char* materialName, filament::Variant variant
break;
case TargetApi::WEBGPU:
targetApiString = "WebGPU.\n";
break;
case TargetApi::ALL:
assert(0); // Unreachable.
break;
@@ -879,6 +888,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
std::vector<TextEntry> essl1Entries;
std::vector<BinaryEntry> spirvEntries;
std::vector<TextEntry> metalEntries;
std::vector<TextEntry> wgslEntries;
LineDictionary textDictionary;
BlobDictionary spirvDictionary;
// End: must be protected by lock
@@ -907,7 +917,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
// Metal Shading Language is cross-compiled from Vulkan.
const bool targetApiNeedsSpirv =
(targetApi == TargetApi::VULKAN || targetApi == TargetApi::METAL);
(targetApi == TargetApi::VULKAN || targetApi == TargetApi::METAL || targetApi == TargetApi::WEBGPU);
const bool targetApiNeedsMsl = targetApi == TargetApi::METAL;
const bool targetApiNeedsWgsl = targetApi == TargetApi::WEBGPU;
const bool targetApiNeedsGlsl = targetApi == TargetApi::OPENGL;
@@ -933,14 +943,17 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
TextEntry glslEntry{};
BinaryEntry spirvEntry{};
TextEntry metalEntry{};
TextEntry wgslEntry{};
glslEntry.shaderModel = params.shaderModel;
spirvEntry.shaderModel = params.shaderModel;
metalEntry.shaderModel = params.shaderModel;
wgslEntry.shaderModel = params.shaderModel;
glslEntry.variant = v.variant;
spirvEntry.variant = v.variant;
metalEntry.variant = v.variant;
wgslEntry.variant = v.variant;
// Generate raw shader code.
// The quotes in Google-style line directives cause problems with certain drivers. These
@@ -1038,6 +1051,12 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
switch (targetApi) {
// TODO: Handle webgpu here
case TargetApi::WEBGPU:
assert(!spirv.empty());
assert(wgsl.length() > 0);
wgslEntry.stage = v.stage;
wgslEntry.shader = wgsl;
wgslEntries.push_back(wgslEntry);
break;
case TargetApi::ALL:
// should never happen
break;
@@ -1099,6 +1118,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
std::sort(essl1Entries.begin(), essl1Entries.end(), compare);
std::sort(spirvEntries.begin(), spirvEntries.end(), compare);
std::sort(metalEntries.begin(), metalEntries.end(), compare);
std::sort(wgslEntries.begin(), wgslEntries.end(), compare);
// Generate the dictionaries.
for (const auto& s : glslEntries) {
@@ -1114,6 +1134,9 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
for (const auto& s : metalEntries) {
textDictionary.addText(s.shader);
}
for (const auto& s : wgslEntries) {
textDictionary.addText(s.shader);
}
// Emit dictionary chunk (TextDictionaryReader and DictionaryTextChunk)
const auto& dictionaryChunk = container.push<filamat::DictionaryTextChunk>(
@@ -1144,6 +1167,12 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
dictionaryChunk.getDictionary(), ChunkType::MaterialMetal);
}
// Emit WGSL chunk (MaterialTextChunk).
if (!wgslEntries.empty()) {
container.push<MaterialTextChunk>(std::move(wgslEntries),
dictionaryChunk.getDictionary(), ChunkType::MaterialWgsl);
}
return true;
}
+6 -1
View File
@@ -525,6 +525,11 @@ EShMessages GLSLTools::glslangFlagsFromTargetApi(
// choke on gl_VertexIndex.
msg |= (Type)EShMessages::EShMsgVulkanRules;
}
if (targetApi == TargetApi::WEBGPU) {
// FIXME: We have to use EShMsgVulkanRules for WEBGPU, otherwise compilation will
// choke on gl_VertexIndex.
msg |= (Type)EShMessages::EShMsgVulkanRules;
}
return (EShMessages)msg;
}
}
@@ -540,13 +545,13 @@ void GLSLTools::prepareShaderParser(MaterialBuilder::TargetApi targetApi,
shader.setEnvInput(EShSourceGlsl, stage, EShClientOpenGL, version);
shader.setEnvClient(EShClientOpenGL, EShTargetOpenGL_450);
break;
case MaterialBuilderBase::TargetApi::WEBGPU:
case MaterialBuilderBase::TargetApi::VULKAN:
case MaterialBuilderBase::TargetApi::METAL:
shader.setEnvInput(EShSourceGlsl, stage, EShClientVulkan, version);
shader.setEnvClient(EShClientVulkan, EShTargetVulkan_1_1);
break;
// TODO: Handle webgpu here
case MaterialBuilderBase::TargetApi::WEBGPU:
case MaterialBuilderBase::TargetApi::ALL:
// can't happen
break;
@@ -175,6 +175,10 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
break;
// TODO: Handle webgpu here
case TargetApi::WEBGPU:
//For now, no differences so inherit the same changes.
// TODO Define a separte environment, OR relevant checks
out << "#define TARGET_VULKAN_ENVIRONMENT\n";
break;
case TargetApi::ALL:
// invalid should never happen
break;
@@ -190,6 +194,7 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
}
if (mTargetApi == TargetApi::VULKAN ||
mTargetApi == TargetApi::WEBGPU ||
mTargetApi == TargetApi::METAL ||
(mTargetApi == TargetApi::OPENGL && mShaderModel == ShaderModel::DESKTOP) ||
mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_2) {
@@ -725,6 +730,8 @@ io::sstream& CodeGenerator::generateBufferInterfaceBlock(io::sstream& out, Shade
break;
// TODO: Handle webgpu here
case TargetApi::WEBGPU:
out << "set = " << +set << ", binding = " << +binding << ", ";
break;
case TargetApi::ALL:
// nonsensical, shouldn't happen.
break;
@@ -813,6 +820,8 @@ io::sstream& CodeGenerator::generateCommonSamplers(utils::io::sstream& out,
break;
// TODO: Handle webgpu here
case TargetApi::WEBGPU:
out << "layout(binding = " << +info.binding << ", set = " << +set << ") ";
break;
case TargetApi::ALL:
// should not happen
break;
+72 -1
View File
@@ -891,7 +891,78 @@ TEST_F(MaterialCompiler, FeatureLevel0Ess3CallFails) {
EXPECT_FALSE(result.isValid());
}
#if FILAMENT_SUPPORTS_WEBGPU
TEST_F(MaterialCompiler, WgslConversionBakedColor) {
std::string bakedColorCodeFrag(R"(
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor = getColor();
}
)");
filamat::MaterialBuilder builder;
builder.targetApi(filamat::MaterialBuilderBase::TargetApi::WEBGPU);
builder.material(bakedColorCodeFrag.c_str());
builder.shading(filamat::MaterialBuilder::Shading::UNLIT);
builder.name("BakedColor");
builder.culling(CullingMode::NONE);
builder.featureLevel(FeatureLevel::FEATURE_LEVEL_0);
builder.require(filament::VertexAttribute::COLOR);
builder.variantFilter(static_cast<filament::UserVariantFilterMask>(
filament::UserVariantFilterBit::SKINNING | filament::UserVariantFilterBit::STE));
filamat::Package result = builder.build(*jobSystem);
EXPECT_TRUE(result.isValid());
}
TEST_F(MaterialCompiler, WgslConversionSandboxLitTransparent) {
std::string litTransparentCodeFrag(R"(
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.baseColor.rgb = materialParams.baseColor * materialParams.alpha;
material.baseColor.a = materialParams.alpha;
material.roughness = materialParams.roughness;
material.metallic = materialParams.metallic;
material.reflectance = materialParams.reflectance;
material.sheenColor = materialParams.sheenColor;
material.sheenRoughness = materialParams.sheenRoughness;
material.clearCoat = materialParams.clearCoat;
material.clearCoatRoughness = materialParams.clearCoatRoughness;
material.anisotropy = materialParams.anisotropy;
material.emissive = materialParams.emissive;
}
)");
filamat::MaterialBuilder builder;
builder.targetApi(filamat::MaterialBuilderBase::TargetApi::WEBGPU);
builder.material(litTransparentCodeFrag.c_str());
builder.shading(filamat::MaterialBuilder::Shading::LIT);
builder.name("LitTransparent");
builder.blending(filament::BlendingMode::TRANSPARENT);
builder.specularAntiAliasing(true);
builder.parameter("alpha", 1, UniformType::FLOAT);
builder.parameter("baseColor", 1, UniformType::FLOAT3);
builder.parameter("roughness", 1, UniformType::FLOAT);
builder.parameter("metallic", 1, UniformType::FLOAT);
builder.parameter("reflectance", 1, UniformType::FLOAT);
builder.parameter("sheenColor", 1, UniformType::FLOAT3);
builder.parameter("sheenRoughness", 1, UniformType::FLOAT);
builder.parameter("clearCoat", 1, UniformType::FLOAT);
builder.parameter("clearCoatRoughness", 1, UniformType::FLOAT);
builder.parameter("anisotropy", 1, UniformType::FLOAT);
builder.parameter("emissive", 1, UniformType::FLOAT4);
builder.variantFilter(static_cast<filament::UserVariantFilterMask>(
filament::UserVariantFilterBit::SKINNING | filament::UserVariantFilterBit::STE));
filamat::Package result = builder.build(*jobSystem);
EXPECT_TRUE(result.isValid());
}
#endif
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
const int rv = RUN_ALL_TESTS();
if (testing::UnitTest::GetInstance()->test_to_run_count() == 0) {
//If you run a test filter that contains 0 tests that was likely not intentional. Fail in that scenario.
return 1;
}
return rv;
}
+4
View File
@@ -254,6 +254,10 @@ bool JsonWriter::writeActiveInfo(const filaflat::ChunkContainer& package,
json << "metal";
chunkType = ChunkType::MaterialMetal;
break;
case ShaderLanguage::WGSL:
json << "webgpu";
chunkType = ChunkType::MaterialWgsl;
break;
default:
return false;
}
+4
View File
@@ -57,6 +57,10 @@ ShaderExtractor::ShaderExtractor(backend::ShaderLanguage target, const void* dat
mMaterialTag = ChunkType::MaterialMetalLibrary;
mDictionaryTag = ChunkType::DictionaryMetalLibrary;
break;
case backend::ShaderLanguage::WGSL:
mMaterialTag = ChunkType::MaterialWgsl;
mDictionaryTag = ChunkType::DictionaryText;
break;
case backend::ShaderLanguage::SPIRV:
mMaterialTag = ChunkType::MaterialSpirv;
mDictionaryTag = ChunkType::DictionarySpirv;
+4
View File
@@ -135,6 +135,10 @@ ShaderReplacer::ShaderReplacer(Backend backend, ShaderLanguage language,
mMaterialTag = ChunkType::MaterialMetal;
mDictionaryTag = ChunkType::DictionaryText;
break;
case backend::ShaderLanguage::WGSL:
mMaterialTag = ChunkType::MaterialWgsl;
mDictionaryTag = ChunkType::DictionaryText;
break;
case backend::ShaderLanguage::SPIRV:
mMaterialTag = ChunkType::MaterialSpirv;
mDictionaryTag = ChunkType::DictionarySpirv;
+7 -1
View File
@@ -429,7 +429,10 @@ static bool printShaderInfo(ostream& text, const ChunkContainer& container, Chun
break;
case ChunkType::MaterialMetalLibrary:
text << "Metal precompiled shader libraries:" << endl;
break;
break;
case ChunkType::MaterialWgsl:
text << "WGSL precompiled shader libraries:" << endl;
break;
default:
assert(false && "Invalid shader ChunkType");
break;
@@ -467,6 +470,9 @@ bool TextWriter::writeMaterialInfo(const filaflat::ChunkContainer& container) {
if (!printShaderInfo(text, container, ChunkType::MaterialMetalLibrary)) {
return false;
}
if (!printShaderInfo(text, container, ChunkType::MaterialWgsl)) {
return false;
}
printChunks(text, container);
+5 -2
View File
@@ -35,10 +35,13 @@ if [[ "$GITHUB_WORKFLOW" ]]; then
sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-${GITHUB_CLANG_VERSION} 100
else
set +e
apt-get -y build-dep mesa
sudo apt -y remove llvm-18 llvm-18-*
sudo apt-get -y build-dep mesa
sudo apt -y remove llvm-18 llvm-18-* llvm-19 llvm-19-*
set -e
CURRENT_CLANG_VERSION=$(clang --version | head -n 1 | awk '{ print $4 }' | awk 'BEGIN { FS="\\." } { print $1 }')
GITHUB_CLANG_VERSION=${GITHUB_CLANG_VERSION:-${CURRENT_CLANG_VERSION}}
sudo apt-get -y install clang-${GITHUB_CLANG_VERSION} \
libc++-${GITHUB_CLANG_VERSION}-dev \
libc++abi-${GITHUB_CLANG_VERSION}-dev \
@@ -262,6 +262,8 @@ bool CommandlineConfig::parse() {
mTargetApi |= TargetApi::VULKAN;
} else if (arg == "metal") {
mTargetApi |= TargetApi::METAL;
} else if (arg == "webgpu") {
mTargetApi |= TargetApi::WEBGPU;
} else if (arg == "all") {
mTargetApi |= TargetApi::ALL;
} else {
+1 -1
View File
@@ -532,7 +532,7 @@ static bool parseChunks(Config config, void* data, size_t size) {
return true;
}
//TODO Include a printWgsl logic here
if (config.printMetal) {
const filament::backend::ShaderLanguage language = config.binary
? filament::backend::ShaderLanguage::METAL_LIBRARY
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.58.1",
"version": "1.58.0",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",