diff --git a/CMakeLists.txt b/CMakeLists.txt index 649577b396c7..b8462d29f1e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -525,11 +525,14 @@ else() option(FILAMENT_BUILD_FILAMAT "Build filamat and JNI buildings" OFF) endif() -# By default, link in matdbg for Desktop + Debug only since it pulls in filamat and a web server. +# By default, link in matdbg/fgviewer for Desktop + Debug only since it pulls in filamat and a web server. if (CMAKE_BUILD_TYPE STREQUAL "Debug" AND IS_HOST_PLATFORM) option(FILAMENT_ENABLE_MATDBG "Enable the material debugger" ON) + # TODO: Uncomment below when fgviewer is ready + # option(FILAMENT_ENABLE_FGVIEWER "Enable the frame graph viewer" ON) else() option(FILAMENT_ENABLE_MATDBG "Enable the material debugger" OFF) + option(FILAMENT_ENABLE_FGVIEWER "Enable the frame graph viewer" OFF) endif() # Only optimize materials in Release mode (so error message lines match the source code) @@ -778,6 +781,11 @@ if (FILAMENT_BUILD_FILAMAT OR IS_HOST_PLATFORM) if (FILAMENT_ENABLE_MATDBG OR IS_HOST_PLATFORM) add_subdirectory(${LIBRARIES}/matdbg) endif() + + # TODO: Uncomment below when fgviewer is ready + # if (FILAMENT_ENABLE_FGVIEWER OR IS_HOST_PLATFORM) + # add_subdirectory(${LIBRARIES}/fgviewer) + # endif() endif() if (FILAMENT_SUPPORTS_VULKAN) diff --git a/README.md b/README.md index d3c1dc1b2b58..5cd0c006c517 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.56.5' + implementation 'com.google.android.filament:filament-android:1.56.6' } ``` @@ -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.56.5' +pod 'Filament', '~> 1.56.6' ``` ## Documentation diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index a57b43591f42..8ff3e6507bf6 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,6 +7,10 @@ A new header is inserted each time a *tag* is created. Instead, if you are authoring a PR for the main branch, add your release note to [NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md). +## v1.56.6 + +fix crash: the 'target_node' of Animation Channel may be nullpointer. + ## v1.56.5 diff --git a/android/build.gradle b/android/build.gradle index 970e41fb90f9..293387890eea 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -11,6 +11,9 @@ // com.google.android.filament.exclude-vulkan // When set, support for Vulkan will be excluded. // +// com.google.android.filament.fgviewer +// When set, enables fgviewer +// // com.google.android.filament.matdbg // When set, enables matdbg // @@ -61,6 +64,11 @@ buildscript { .gradleProperty("com.google.android.filament.exclude-vulkan") .isPresent() + // TODO: Uncomment below when fgviewer is ready + // def fgviewer = providers + // .gradleProperty("com.google.android.filament.fgviewer") + // .isPresent() + def matdbg = providers .gradleProperty("com.google.android.filament.matdbg") .isPresent() @@ -115,6 +123,8 @@ buildscript { "-DANDROID_STL=c++_static", "-DFILAMENT_DIST_DIR=${filamentPath}".toString(), "-DFILAMENT_SUPPORTS_VULKAN=${excludeVulkan ? 'OFF' : 'ON'}".toString(), + // TODO: Uncomment below when fgviewer is ready + // "-DFILAMENT_ENABLE_FGVIEWER=${fgviewer ? 'ON' : 'OFF'}".toString(), "-DFILAMENT_ENABLE_MATDBG=${matdbg ? 'ON' : 'OFF'}".toString(), "-DFILAMENT_DISABLE_MATOPT=${matnopt ? 'ON' : 'OFF'}".toString() ] diff --git a/android/filament-android/CMakeLists.txt b/android/filament-android/CMakeLists.txt index 7f3122f64565..21da97e6b70b 100644 --- a/android/filament-android/CMakeLists.txt +++ b/android/filament-android/CMakeLists.txt @@ -2,6 +2,8 @@ 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_MATDBG "Enables Material debugger" OFF) option(FILAMENT_DISABLE_MATOPT "Disables material optimizations" OFF) @@ -51,6 +53,13 @@ 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_MATDBG) add_library(matdbg STATIC IMPORTED) set_target_properties(matdbg PROPERTIES IMPORTED_LOCATION @@ -117,6 +126,8 @@ target_link_libraries(filament-jni # libgeometry is PUBLIC because gltfio uses it. PUBLIC geometry + # TODO: Uncomment below when fgviewer is ready + # $<$:fgviewer> $<$:matdbg> $<$:filamat> $<$:bluevk> diff --git a/android/filament-android/src/main/cpp/Texture.cpp b/android/filament-android/src/main/cpp/Texture.cpp index ef778056c0ea..ff3fd36d2694 100644 --- a/android/filament-android/src/main/cpp/Texture.cpp +++ b/android/filament-android/src/main/cpp/Texture.cpp @@ -138,6 +138,13 @@ Java_com_google_android_filament_Texture_nBuilderImportTexture(JNIEnv*, jclass, builder->import((intptr_t)id); } +extern "C" +JNIEXPORT void JNICALL +Java_com_google_android_filament_Texture_nBuilderExternal(JNIEnv*, jclass, jlong nativeBuilder) { + Texture::Builder *builder = (Texture::Builder *) nativeBuilder; + builder->external(); +} + extern "C" JNIEXPORT jlong JNICALL Java_com_google_android_filament_Texture_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder, jlong nativeEngine) { diff --git a/android/filament-android/src/main/java/com/google/android/filament/Texture.java b/android/filament-android/src/main/java/com/google/android/filament/Texture.java index 1cb732af13f2..eaf81adc3cfd 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Texture.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Texture.java @@ -800,6 +800,19 @@ public Builder importTexture(long id) { return this; } + /** + * Creates an external texture. The content must be set using setExternalImage(). + * The sampler can be SAMPLER_EXTERNAL or SAMPLER_2D depending on the format. Generally + * YUV formats must use SAMPLER_EXTERNAL. This depends on the backend features and is not + * validated. + * @return This Builder, for chaining calls. + */ + @NonNull + public Builder external() { + nBuilderExternal(mNativeBuilder); + return this; + } + /** * Creates a new Texture instance. * @param engine The {@link Engine} to associate this Texture with. @@ -1261,6 +1274,7 @@ void clearNativeObject() { private static native void nBuilderUsage(long nativeBuilder, int flags); private static native void nBuilderSwizzle(long nativeBuilder, int r, int g, int b, int a); private static native void nBuilderImportTexture(long nativeBuilder, long id); + private static native void nBuilderExternal(long nativeBuilder); private static native long nBuilderBuild(long nativeBuilder, long nativeEngine); private static native int nGetWidth(long nativeTexture, int level); diff --git a/android/gradle.properties b/android/gradle.properties index 7082c1eab1a1..e4f778ef79e0 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.56.5 +VERSION_NAME=1.56.6 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/build.sh b/build.sh index 8c5d94a96db4..3f972d75c16e 100755 --- a/build.sh +++ b/build.sh @@ -23,6 +23,8 @@ function print_help { echo " This is sometimes needed instead of -c (which still misses some clean steps)." echo " -d" echo " Enable matdbg." + echo " -t" + echo " Enable fgviewer." echo " -f" echo " Always invoke CMake before incremental builds." echo " -g" @@ -125,6 +127,27 @@ function print_matdbg_help { echo "" } +function print_fgviewer_help { + echo "fgviewer is enabled in the build, but some extra steps are needed." + echo "" + echo "FOR DESKTOP BUILDS:" + echo "" + echo "Please set the port environment variable before launching. e.g., on macOS do:" + echo " export FILAMENT_FGVIEWER_PORT=8085" + echo "" + echo "FOR ANDROID BUILDS:" + echo "" + echo "1) For Android Studio builds, make sure to set:" + echo " -Pcom.google.android.filament.fgviewer" + echo " option in Preferences > Build > Compiler > Command line options." + echo "" + echo "2) The port number is hardcoded to 8085 so you will need to do:" + echo " adb forward tcp:8085 tcp:8085" + echo "" + echo "3) Be sure to enable INTERNET permission in your app's manifest file." + echo "" +} + # Unless explicitly specified, NDK version will be selected as highest available version within same major release chain FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION:-$(cat `dirname $0`/build/android/ndk.version | cut -f 1 -d ".")} @@ -172,6 +195,7 @@ VULKAN_ANDROID_GRADLE_OPTION="" EGL_ON_LINUX_OPTION="-DFILAMENT_SUPPORTS_EGL_ON_LINUX=OFF" MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=OFF" +FGVIEWER_OPTION="-DFILAMENT_ENABLE_FGVIEWER=OFF" MATDBG_GRADLE_OPTION="" MATOPT_OPTION="" @@ -240,6 +264,7 @@ function build_desktop_target { -DCMAKE_BUILD_TYPE="$1" \ -DCMAKE_INSTALL_PREFIX="../${lc_target}/filament" \ ${EGL_ON_LINUX_OPTION} \ + ${FGVIEWER_OPTION} \ ${MATDBG_OPTION} \ ${MATOPT_OPTION} \ ${ASAN_UBSAN_OPTION} \ @@ -376,6 +401,7 @@ function build_android_target { -DFILAMENT_NDK_VERSION="${FILAMENT_NDK_VERSION}" \ -DCMAKE_INSTALL_PREFIX="../android-${lc_target}/filament" \ -DCMAKE_TOOLCHAIN_FILE="../../build/toolchain-${arch}-linux-android.cmake" \ + ${FGVIEWER_OPTION} \ ${MATDBG_OPTION} \ ${MATOPT_OPTION} \ ${VULKAN_ANDROID_OPTION} \ @@ -613,6 +639,7 @@ function build_ios_target { -DPLATFORM_NAME="${platform}" \ -DIOS=1 \ -DCMAKE_TOOLCHAIN_FILE=../../third_party/clang/iOS.cmake \ + ${FGVIEWER_OPTION} \ ${MATDBG_OPTION} \ ${MATOPT_OPTION} \ ${STEREOSCOPIC_OPTION} \ @@ -802,7 +829,7 @@ function check_debug_release_build { pushd "$(dirname "$0")" > /dev/null -while getopts ":hacCfgijmp:q:uvslwedk:bx:S:X:" opt; do +while getopts ":hacCfgijmp:q:uvslwedtk:bx:S:X:" opt; do case ${opt} in h) print_help @@ -823,6 +850,12 @@ while getopts ":hacCfgijmp:q:uvslwedk:bx:S:X:" opt; do MATDBG_OPTION="-DFILAMENT_ENABLE_MATDBG=ON, -DFILAMENT_BUILD_FILAMAT=ON" MATDBG_GRADLE_OPTION="-Pcom.google.android.filament.matdbg" ;; + t) + # TODO: Uncomment below when fgviewer is ready + # PRINT_FGVIEWER_HELP=true + # FGVIEWER_OPTION="-DFILAMENT_ENABLE_FGVIEWER=ON" + #FGVIEWER_GRADLE_OPTION="-Pcom.google.android.filament.fgviewer" + ;; f) ISSUE_CMAKE_ALWAYS=true ;; @@ -1027,3 +1060,7 @@ fi if [[ "${PRINT_MATDBG_HELP}" == "true" ]]; then print_matdbg_help fi + +if [[ "${PRINT_FGVIEWER_HELP}" == "true" ]]; then + print_fgviewer_help +fi diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt index 82e336215dd3..d218a3917048 100644 --- a/filament/CMakeLists.txt +++ b/filament/CMakeLists.txt @@ -561,6 +561,13 @@ target_link_libraries(${TARGET} PUBLIC filaflat) target_link_libraries(${TARGET} PUBLIC filabridge) target_link_libraries(${TARGET} PUBLIC ibl-lite) +if (FILAMENT_ENABLE_FGVIEWER) + target_link_libraries(${TARGET} PUBLIC fgviewer) + add_definitions(-DFILAMENT_ENABLE_FGVIEWER=1) +else() + add_definitions(-DFILAMENT_ENABLE_FGVIEWER=0) +endif() + if (FILAMENT_ENABLE_MATDBG) target_link_libraries(${TARGET} PUBLIC matdbg) add_definitions(-DFILAMENT_ENABLE_MATDBG=1) diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc index 66556b0f41f3..6b951da975c5 100644 --- a/filament/backend/include/private/backend/DriverAPI.inc +++ b/filament/backend/include/private/backend/DriverAPI.inc @@ -214,6 +214,7 @@ DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureViewSwizzle, backend::TextureSwizzle, a) DECL_DRIVER_API_R_N(backend::TextureHandle, createTextureExternalImage, + backend::SamplerType, target, backend::TextureFormat, format, uint32_t, width, uint32_t, height, @@ -399,17 +400,6 @@ DECL_DRIVER_API_N(update3DImage, DECL_DRIVER_API_N(generateMipmaps, backend::TextureHandle, th) -// Deprecated -DECL_DRIVER_API_N(setExternalImage, - backend::TextureHandle, th, - void*, image) - -// Deprecated -DECL_DRIVER_API_N(setExternalImagePlane, - backend::TextureHandle, th, - void*, image, - uint32_t, plane) - DECL_DRIVER_API_N(setExternalStream, backend::TextureHandle, th, backend::StreamHandle, sh) diff --git a/filament/backend/src/PlatformFactory.cpp b/filament/backend/src/PlatformFactory.cpp index 86be6d231e02..cccd9439c3ec 100644 --- a/filament/backend/src/PlatformFactory.cpp +++ b/filament/backend/src/PlatformFactory.cpp @@ -24,7 +24,7 @@ #if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) #include "backend/platforms/PlatformEGLAndroid.h" #endif -#elif defined(IOS) +#elif defined(FILAMENT_IOS) #if defined(FILAMENT_SUPPORTS_OPENGL) && !defined(FILAMENT_USE_EXTERNAL_GLES3) #include "backend/platforms/PlatformCocoaTouchGL.h" #endif @@ -88,7 +88,7 @@ Platform* PlatformFactory::create(Backend* backend) noexcept { *backend = Backend::OPENGL; #elif defined(__ANDROID__) *backend = Backend::OPENGL; -#elif defined(IOS) || defined(__APPLE__) +#elif defined(FILAMENT_IOS) || defined(__APPLE__) *backend = Backend::METAL; #elif defined(FILAMENT_DRIVER_SUPPORTS_VULKAN) *backend = Backend::VULKAN; @@ -119,7 +119,7 @@ Platform* PlatformFactory::create(Backend* backend) noexcept { return nullptr; #elif defined(__ANDROID__) return new PlatformEGLAndroid(); - #elif defined(IOS) + #elif defined(FILAMENT_IOS) return new PlatformCocoaTouchGL(); #elif defined(__APPLE__) return new PlatformCocoaGL(); diff --git a/filament/backend/src/metal/MetalBuffer.h b/filament/backend/src/metal/MetalBuffer.h index f6fa90c1990b..ec724a9f0bdc 100644 --- a/filament/backend/src/metal/MetalBuffer.h +++ b/filament/backend/src/metal/MetalBuffer.h @@ -250,7 +250,7 @@ class MetalRingBuffer { // In practice, MetalRingBuffer is used for argument buffers, which are kept in the constant // address space. Constant buffers have specific alignment requirements when specifying an // offset. -#if defined(IOS) +#if defined(FILAMENT_IOS) #if TARGET_OS_SIMULATOR // The iOS simulator has differing alignment requirements. static constexpr auto METAL_CONSTANT_BUFFER_OFFSET_ALIGNMENT = 256; diff --git a/filament/backend/src/metal/MetalContext.h b/filament/backend/src/metal/MetalContext.h index 158435150820..f85e08545bf7 100644 --- a/filament/backend/src/metal/MetalContext.h +++ b/filament/backend/src/metal/MetalContext.h @@ -155,6 +155,7 @@ struct MetalContext { RenderPassFlags currentRenderPassFlags; MetalRenderTarget* currentRenderTarget = nullptr; + bool validPipelineBound = false; // State trackers. PipelineStateTracker pipelineState; diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index d787c0cdb265..46dcb3716ea8 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -482,7 +482,9 @@ mContext->textures.insert(construct_handle(th, *mContext, src, r, g, b, a)); } -void MetalDriver::createTextureExternalImageR(Handle th, backend::TextureFormat format, +void MetalDriver::createTextureExternalImageR(Handle th, + backend::SamplerType target, + backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, void* image) { mContext->textures.insert(construct_handle( th, *mContext, format, width, height, usage, (CVPixelBufferRef)image)); @@ -907,7 +909,7 @@ } ShaderModel MetalDriver::getShaderModel() const noexcept { -#if defined(IOS) +#if defined(FILAMENT_IOS) return ShaderModel::MOBILE; #else return ShaderModel::DESKTOP; @@ -981,7 +983,7 @@ case MTLPixelFormatRGBA16Float: return true; -#if !defined(IOS) +#if !defined(FILAMENT_IOS) // Mipmappable only on desktop: case MTLPixelFormatR32Float: case MTLPixelFormatRG32Float: @@ -989,7 +991,7 @@ return true; #endif -#if defined(IOS) +#if defined(FILAMENT_IOS) // Mipmappable only on iOS: case MTLPixelFormatRGB9E5Float: return true; @@ -1191,10 +1193,6 @@ CVPixelBufferRetain(pixelBuffer); } -void MetalDriver::setExternalImage(Handle th, void* image) {} - -void MetalDriver::setExternalImagePlane(Handle th, void* image, uint32_t plane) {} - void MetalDriver::setExternalStream(Handle th, Handle sh) { } @@ -1366,7 +1364,7 @@ if (@available(iOS 13, *)) { MTLCaptureDescriptor* descriptor = [MTLCaptureDescriptor new]; descriptor.captureObject = mContext->device; -#if defined(IOS) +#if defined(FILAMENT_IOS) descriptor.destination = MTLCaptureDestinationDeveloperTools; #else descriptor.destination = MTLCaptureDestinationGPUTraceDocument; @@ -1430,7 +1428,7 @@ width:srcTextureSize.width height:srcTextureSize.height mipmapped:NO]; -#if defined(IOS) +#if defined(FILAMENT_IOS) textureDescriptor.storageMode = MTLStorageModeShared; #else textureDescriptor.storageMode = MTLStorageModeManaged; @@ -1449,7 +1447,7 @@ mContext->blitter->blit(getPendingCommandBuffer(mContext), args, "readPixels blit"); -#if !defined(IOS) +#if !defined(FILAMENT_IOS) // Managed textures on macOS require explicit synchronization between GPU / CPU. id blitEncoder = [getPendingCommandBuffer(mContext) blitCommandEncoder]; [blitEncoder synchronizeResource:readPixelsTexture]; @@ -1657,6 +1655,7 @@ // during the draw call when the program is invalid. The shader compile error has already been // dumped to the console at this point, so it's fine to simply return early. if (FILAMENT_ENABLE_MATDBG && UTILS_UNLIKELY(!functions)) { + mContext->validPipelineBound = false; return; } @@ -1789,6 +1788,8 @@ clamp:0.0]; mContext->currentPolygonOffset = ps.polygonOffset; } + + mContext->validPipelineBound = true; } void MetalDriver::bindRenderPrimitive(Handle rph) { @@ -1886,6 +1887,10 @@ << "draw() without a valid command encoder."; DEBUG_LOG("draw2(...)\n"); + if (FILAMENT_ENABLE_MATDBG && UTILS_UNLIKELY(!mContext->validPipelineBound)) { + return; + } + // Bind the offset data. if (mContext->dynamicOffsets.isDirty()) { const auto [size, data] = mContext->dynamicOffsets.getOffsets(); diff --git a/filament/backend/src/metal/MetalEnums.h b/filament/backend/src/metal/MetalEnums.h index 10899baf95fc..5a2038ed1037 100644 --- a/filament/backend/src/metal/MetalEnums.h +++ b/filament/backend/src/metal/MetalEnums.h @@ -196,7 +196,7 @@ inline MTLPixelFormat getMetalFormatLinear(MTLPixelFormat format) { case MTLPixelFormatRG8Unorm_sRGB: return MTLPixelFormatRG8Unorm; case MTLPixelFormatRGBA8Unorm_sRGB: return MTLPixelFormatRGBA8Unorm; case MTLPixelFormatBGRA8Unorm_sRGB: return MTLPixelFormatBGRA8Unorm; -#if !defined(IOS) +#if !defined(FILAMENT_IOS) case MTLPixelFormatBC1_RGBA_sRGB: return MTLPixelFormatBC1_RGBA; case MTLPixelFormatBC2_RGBA_sRGB: return MTLPixelFormatBC2_RGBA; case MTLPixelFormatBC3_RGBA_sRGB: return MTLPixelFormatBC3_RGBA; @@ -268,7 +268,7 @@ constexpr inline bool isMetalFormatStencil(MTLPixelFormat format) { case MTLPixelFormatStencil8: case MTLPixelFormatDepth32Float_Stencil8: case MTLPixelFormatX32_Stencil8: -#if !defined(IOS) +#if !defined(FILAMENT_IOS) case MTLPixelFormatDepth24Unorm_Stencil8: case MTLPixelFormatX24_Stencil8: #endif diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index 60cf484c86ea..fc12fe0a9a6f 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -157,7 +157,7 @@ static inline MTLTextureUsage getMetalTextureUsage(TextureUsage usage) { textureDescriptor.height = headlessHeight; // Specify MTLTextureUsageShaderRead so the headless surface can be blitted from. textureDescriptor.usage = MTLTextureUsageRenderTarget | MTLTextureUsageShaderRead; -#if defined(IOS) +#if defined(FILAMENT_IOS) textureDescriptor.storageMode = MTLStorageModeShared; #else textureDescriptor.storageMode = MTLStorageModeManaged; @@ -520,7 +520,7 @@ static void func(void* user) { const BOOL mipmapped = levels > 1; const BOOL multisampled = samples > 1; -#if defined(IOS) +#if defined(FILAMENT_IOS) const BOOL textureArray = target == SamplerType::SAMPLER_2D_ARRAY; FILAMENT_CHECK_PRECONDITION(!textureArray || !multisampled) << "iOS does not support multisampled texture arrays."; @@ -533,7 +533,7 @@ static void func(void* user) { switch (target) { case SamplerType::SAMPLER_2D: return MTLTextureType2DMultisample; -#if !defined(IOS) +#if !defined(FILAMENT_IOS) case SamplerType::SAMPLER_2D_ARRAY: return MTLTextureType2DMultisampleArray; #endif @@ -856,7 +856,7 @@ static void func(void* user) { descriptor.height = region.size.height; descriptor.depth = region.size.depth; -#if defined(IOS) +#if defined(FILAMENT_IOS) descriptor.storageMode = MTLStorageModeShared; #else descriptor.storageMode = MTLStorageModeManaged; diff --git a/filament/backend/src/metal/MetalPlatform.mm b/filament/backend/src/metal/MetalPlatform.mm index 31ce2a8070a5..4979e602ba1f 100644 --- a/filament/backend/src/metal/MetalPlatform.mm +++ b/filament/backend/src/metal/MetalPlatform.mm @@ -37,7 +37,7 @@ id MetalPlatform::createDevice() noexcept { id result; -#if !defined(IOS) +#if !defined(FILAMENT_IOS) const bool forceIntegrated = NSProcessInfo.processInfo.environment[@"FILAMENT_FORCE_INTEGRATED_GPU"] != nil; if (forceIntegrated) { diff --git a/filament/backend/src/metal/MetalState.mm b/filament/backend/src/metal/MetalState.mm index 065e2b62cce3..c790e03c3048 100644 --- a/filament/backend/src/metal/MetalState.mm +++ b/filament/backend/src/metal/MetalState.mm @@ -150,7 +150,7 @@ MTLCompareFunctionNever : getCompareFunction(params.compareFunc); samplerDescriptor.supportArgumentBuffers = YES; -#if defined(IOS) +#if defined(FILAMENT_IOS) // Older Apple devices (and the simulator) don't support setting a comparison function in // MTLSamplerDescriptor. // In practice, this means shadows are not supported when running in the simulator. diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp index afc5ac808dfc..af3ad70fbb69 100644 --- a/filament/backend/src/noop/NoopDriver.cpp +++ b/filament/backend/src/noop/NoopDriver.cpp @@ -32,7 +32,7 @@ Dispatcher NoopDriver::getDispatcher() const noexcept { } ShaderModel NoopDriver::getShaderModel() const noexcept { -#if defined(__ANDROID__) || defined(IOS) || defined(__EMSCRIPTEN__) +#if defined(__ANDROID__) || defined(FILAMENT_IOS) || defined(__EMSCRIPTEN__) return ShaderModel::MOBILE; #else return ShaderModel::DESKTOP; @@ -265,12 +265,6 @@ TimerQueryResult NoopDriver::getTimerQueryValue(Handle tqh, uint64 return TimerQueryResult::ERROR; } -void NoopDriver::setExternalImage(Handle th, void* image) { -} - -void NoopDriver::setExternalImagePlane(Handle th, void* image, uint32_t plane) { -} - void NoopDriver::setExternalStream(Handle th, Handle sh) { } diff --git a/filament/backend/src/opengl/GLDescriptorSet.cpp b/filament/backend/src/opengl/GLDescriptorSet.cpp index 1584f6aff774..79c65d9a125a 100644 --- a/filament/backend/src/opengl/GLDescriptorSet.cpp +++ b/filament/backend/src/opengl/GLDescriptorSet.cpp @@ -173,6 +173,7 @@ void GLDescriptorSet::update(OpenGLContext& gl, arg.target = t ? t->gl.target : 0; arg.id = t ? t->gl.id : 0; + arg.external = t ? t->gl.external : false; if constexpr (std::is_same_v || std::is_same_v) { if constexpr (std::is_same_v) { @@ -285,7 +286,7 @@ void GLDescriptorSet::bind( } else if constexpr (std::is_same_v) { GLuint const unit = p.getTextureUnit(set, binding); if (arg.target) { - gl.bindTexture(unit, arg.target, arg.id); + gl.bindTexture(unit, arg.target, arg.id, arg.external); gl.bindSampler(unit, arg.sampler); if (UTILS_UNLIKELY(arg.ref)) { updateTextureView(gl, handleAllocator, unit, arg); @@ -296,7 +297,7 @@ void GLDescriptorSet::bind( } else if constexpr (std::is_same_v) { GLuint const unit = p.getTextureUnit(set, binding); if (arg.target) { - gl.bindTexture(unit, arg.target, arg.id); + gl.bindTexture(unit, arg.target, arg.id, arg.external); gl.bindSampler(unit, arg.sampler); if (UTILS_UNLIKELY(arg.ref)) { updateTextureView(gl, handleAllocator, unit, arg); @@ -314,7 +315,7 @@ void GLDescriptorSet::bind( // in ES2 the sampler parameters need to be set on the texture itself GLuint const unit = p.getTextureUnit(set, binding); if (arg.target) { - gl.bindTexture(unit, arg.target, arg.id); + gl.bindTexture(unit, arg.target, arg.id, arg.external); SamplerParams const params = arg.params; glTexParameteri(arg.target, GL_TEXTURE_MIN_FILTER, (GLint)GLUtils::getTextureFilter(params.filterMin)); diff --git a/filament/backend/src/opengl/GLDescriptorSet.h b/filament/backend/src/opengl/GLDescriptorSet.h index 51ac2afd884e..59b6b6ace57a 100644 --- a/filament/backend/src/opengl/GLDescriptorSet.h +++ b/filament/backend/src/opengl/GLDescriptorSet.h @@ -111,7 +111,9 @@ struct GLDescriptorSet : public HwDescriptorSet { // A sampler descriptor struct Sampler { - GLenum target = 0; // 4 + uint16_t target; // 2 (GLenum) + bool external = false; // 1 + bool reserved = false; // 1 GLuint id = 0; // 4 GLuint sampler = 0; // 4 Handle ref; // 4 @@ -126,7 +128,9 @@ struct GLDescriptorSet : public HwDescriptorSet { }; struct SamplerWithAnisotropyWorkaround { - GLenum target = 0; // 4 + uint16_t target; // 2 (GLenum) + bool external = false; // 1 + bool reserved = false; // 1 GLuint id = 0; // 4 GLuint sampler = 0; // 4 Handle ref; // 4 @@ -143,7 +147,9 @@ struct GLDescriptorSet : public HwDescriptorSet { // A sampler descriptor for ES2 struct SamplerGLES2 { - GLenum target = 0; // 4 + uint16_t target; // 2 (GLenum) + bool external = false; // 1 + bool reserved = false; // 1 GLuint id = 0; // 4 SamplerParams params{}; // 4 float anisotropy = 1.0f; // 4 diff --git a/filament/backend/src/opengl/GLTexture.h b/filament/backend/src/opengl/GLTexture.h index 5d721d3c2148..26784d156448 100644 --- a/filament/backend/src/opengl/GLTexture.h +++ b/filament/backend/src/opengl/GLTexture.h @@ -35,7 +35,7 @@ struct GLTextureRef { GLTextureRef() = default; // view reference counter uint16_t count = 1; - // current per-view values of the texture (in GL we can only have a single View active at + // Current per-view values of the texture (in GL we can only have a single View active at // a time, and this tracks that state). It's used to avoid unnecessarily change state. int8_t baseLevel = 127; int8_t maxLevel = -1; @@ -50,7 +50,7 @@ struct GLTextureRef { struct GLTexture : public HwTexture { using HwTexture::HwTexture; struct GL { - GL() noexcept : imported(false), sidecarSamples(1), reserved1(0) {} + GL() noexcept : imported(false), external(false), sidecarSamples(1), reserved1(0) {} GLuint id = 0; // texture or renderbuffer id GLenum target = 0; GLenum internalFormat = 0; @@ -62,7 +62,8 @@ struct GLTexture : public HwTexture { int8_t maxLevel = -1; uint8_t reserved0 = 0; bool imported : 1; - uint8_t sidecarSamples : 4; + bool external : 1; + uint8_t sidecarSamples : 3; uint8_t reserved1 : 3; std::array swizzle{ TextureSwizzle::CHANNEL_0, diff --git a/filament/backend/src/opengl/OpenGLContext.cpp b/filament/backend/src/opengl/OpenGLContext.cpp index d664e9e89ac2..f652d9265962 100644 --- a/filament/backend/src/opengl/OpenGLContext.cpp +++ b/filament/backend/src/opengl/OpenGLContext.cpp @@ -408,7 +408,7 @@ void OpenGLContext::initProcs(Procs* procs, procs->maxShaderCompilerThreadsKHR = +[](GLuint) {}; #ifdef BACKEND_OPENGL_VERSION_GLES -# ifndef IOS // IOS is guaranteed to have ES3.x +# ifndef FILAMENT_IOS // FILAMENT_IOS is guaranteed to have ES3.x # ifndef __EMSCRIPTEN__ if (UTILS_UNLIKELY(major == 2)) { // Runtime OpenGL version is ES 2.x @@ -438,7 +438,7 @@ void OpenGLContext::initProcs(Procs* procs, procs->maxShaderCompilerThreadsKHR = glMaxShaderCompilerThreadsKHR; } # endif // __EMSCRIPTEN__ -# endif // IOS +# endif // FILAMENT_IOS #else procs->maxShaderCompilerThreadsKHR = glMaxShaderCompilerThreadsARB; #endif @@ -576,14 +576,14 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts, } #ifdef BACKEND_OPENGL_VERSION_GLES -# ifndef IOS // IOS is guaranteed to have ES3.x +# ifndef FILAMENT_IOS // FILAMENT_IOS is guaranteed to have ES3.x if (UTILS_UNLIKELY(major == 2)) { if (UTILS_UNLIKELY(!exts.OES_vertex_array_object)) { // we activate this workaround path, which does the reset of array buffer bugs->vao_doesnt_store_element_array_buffer_binding = true; } } -# endif // IOS +# endif // FILAMENT_IOS #else // feedback loops are allowed on GL desktop as long as writes are disabled bugs->allow_read_only_ancillary_feedback_loop = true; @@ -622,7 +622,7 @@ FeatureLevel OpenGLContext::resolveFeatureLevel(GLint major, GLint minor, } } } -# ifndef IOS // IOS is guaranteed to have ES3.x +# ifndef FILAMENT_IOS // FILAMENT_IOS is guaranteed to have ES3.x else if (UTILS_UNLIKELY(major == 2)) { // Runtime OpenGL version is ES 2.x // note: mandatory extensions (all supported by Mali-400 and Adreno 304) @@ -634,7 +634,7 @@ FeatureLevel OpenGLContext::resolveFeatureLevel(GLint major, GLint minor, // OES_texture_npot featureLevel = FeatureLevel::FEATURE_LEVEL_0; } -# endif // IOS +# endif // FILAMENT_IOS #else assert_invariant(gets.max_texture_image_units >= 16); assert_invariant(gets.max_combined_texture_image_units >= 32); diff --git a/filament/backend/src/opengl/OpenGLContext.h b/filament/backend/src/opengl/OpenGLContext.h index 39ceb346787e..72e3f8f2b124 100644 --- a/filament/backend/src/opengl/OpenGLContext.h +++ b/filament/backend/src/opengl/OpenGLContext.h @@ -131,7 +131,7 @@ class OpenGLContext final : public TimerQueryFactoryInterface { } inline bool isES2() const noexcept { -#if defined(BACKEND_OPENGL_VERSION_GLES) && !defined(IOS) +#if defined(BACKEND_OPENGL_VERSION_GLES) && !defined(FILAMENT_IOS) # ifndef BACKEND_OPENGL_LEVEL_GLES30 return true; # else @@ -153,7 +153,7 @@ class OpenGLContext final : public TimerQueryFactoryInterface { void pixelStore(GLenum, GLint) noexcept; inline void activeTexture(GLuint unit) noexcept; - inline void bindTexture(GLuint unit, GLuint target, GLuint texId) noexcept; + inline void bindTexture(GLuint unit, GLuint target, GLuint texId, bool external) noexcept; void unbindTexture(GLenum target, GLuint id) noexcept; void unbindTextureUnit(GLuint unit) noexcept; @@ -761,7 +761,9 @@ void OpenGLContext::bindBufferRange(GLenum target, GLuint index, GLuint buffer, #endif } -void OpenGLContext::bindTexture(GLuint unit, GLuint target, GLuint texId) noexcept { +void OpenGLContext::bindTexture(GLuint unit, GLuint target, GLuint texId, bool external) noexcept { + // another texture is bound to the same unit with a different target, + // unbind the texture from the current target update_state(state.textures.units[unit].target, target, [&]() { activeTexture(unit); glBindTexture(state.textures.units[unit].target, 0); @@ -769,7 +771,7 @@ void OpenGLContext::bindTexture(GLuint unit, GLuint target, GLuint texId) noexce update_state(state.textures.units[unit].id, texId, [&]() { activeTexture(unit); glBindTexture(target, texId); - }, target == GL_TEXTURE_EXTERNAL_OES); + }, external); } void OpenGLContext::useProgram(GLuint program) noexcept { diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 79580fc7c46e..736fa0d6769b 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -402,7 +402,7 @@ void OpenGLDriver::setPushConstant(backend::ShaderStage stage, uint8_t index, void OpenGLDriver::bindTexture(GLuint unit, GLTexture const* t) noexcept { assert_invariant(t != nullptr); - mContext.bindTexture(unit, t->gl.target, t->gl.id); + mContext.bindTexture(unit, t->gl.target, t->gl.id, t->gl.external); } bool OpenGLDriver::useProgram(OpenGLProgram* p) noexcept { @@ -1052,17 +1052,49 @@ void OpenGLDriver::createTextureViewSwizzleR(Handle th, Handle th, backend::TextureFormat format, - uint32_t width, uint32_t height, backend::TextureUsage usage, void* image) { - createTextureR(th, SamplerType::SAMPLER_EXTERNAL, 1, format, 1, width, height, 1, usage); - setExternalImage(th, image); +void OpenGLDriver::createTextureExternalImageR(Handle th, SamplerType target, + TextureFormat format, uint32_t width, uint32_t height, TextureUsage usage, void* image) { + DEBUG_MARKER() + + usage |= TextureUsage::SAMPLEABLE; + usage &= ~TextureUsage::UPLOADABLE; + + auto& gl = mContext; + GLenum internalFormat = getInternalFormat(format); + if (UTILS_UNLIKELY(gl.isES2())) { + // on ES2, format and internal format must match + // FIXME: handle compressed texture format + internalFormat = textureFormatToFormatAndType(format).first; + } + assert_invariant(internalFormat); + + GLTexture* const t = construct(th, target, 1, 1, width, height, 1, format, usage); + assert_invariant(t); + + t->externalTexture = mPlatform.createExternalImageTexture(); + if (t->externalTexture) { + t->gl.target = t->externalTexture->target; + t->gl.id = t->externalTexture->id; + // internalFormat actually depends on the external image, but it doesn't matter + // because it's not used anywhere for anything important. + t->gl.internalFormat = internalFormat; + t->gl.baseLevel = 0; + t->gl.maxLevel = 0; + t->gl.external = true; // forces bindTexture() call (they're never cached) + } + + bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t); + if (mPlatform.setExternalImage(image, t->externalTexture)) { + // the target and id can be reset each time + t->gl.target = t->externalTexture->target; + t->gl.id = t->externalTexture->id; + } } void OpenGLDriver::createTextureExternalImagePlaneR(Handle th, backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, void* image, uint32_t plane) { - createTextureR(th, SamplerType::SAMPLER_EXTERNAL, 1, format, 1, width, height, 1, usage); - setExternalImagePlane(th, image, plane); + // not relevant for the OpenGL backend } void OpenGLDriver::importTextureR(Handle th, intptr_t id, @@ -1082,6 +1114,7 @@ void OpenGLDriver::importTextureR(Handle th, intptr_t id, switch (target) { case SamplerType::SAMPLER_EXTERNAL: t->gl.target = GL_TEXTURE_EXTERNAL_OES; + t->gl.external = true; // forces bindTexture() call (they're never cached) break; case SamplerType::SAMPLER_2D: t->gl.target = GL_TEXTURE_2D; @@ -1348,13 +1381,13 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo, #ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 // TODO: support multiview for iOS and WebGL -#if !defined(__EMSCRIPTEN__) && !defined(IOS) +#if !defined(__EMSCRIPTEN__) && !defined(FILAMENT_IOS) if (layerCount > 1) { // if layerCount > 1, it means we use the multiview extension. glFramebufferTextureMultiviewOVR(GL_FRAMEBUFFER, attachment, t->gl.id, 0, binfo.layer, layerCount); } else -#endif // !defined(__EMSCRIPTEN__) && !defined(IOS) +#endif // !defined(__EMSCRIPTEN__) && !defined(FILAMENT_IOS) { // GL_TEXTURE_2D_MULTISAMPLE_ARRAY is not supported in GLES glFramebufferTextureLayer(GL_FRAMEBUFFER, attachment, @@ -2774,25 +2807,6 @@ void OpenGLDriver::setupExternalImage(void* image) { mPlatform.retainExternalImage(image); } -void OpenGLDriver::setExternalImage(Handle th, void* image) { - DEBUG_MARKER() - GLTexture* t = handle_cast(th); - assert_invariant(t); - assert_invariant(t->target == SamplerType::SAMPLER_EXTERNAL); - - bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t); - if (mPlatform.setExternalImage(image, t->externalTexture)) { - // the target and id can be reset each time - t->gl.target = t->externalTexture->target; - t->gl.id = t->externalTexture->id; - bindTexture(OpenGLContext::DUMMY_TEXTURE_BINDING, t); - } -} - -void OpenGLDriver::setExternalImagePlane(Handle th, void* image, uint32_t plane) { - DEBUG_MARKER() -} - void OpenGLDriver::setExternalStream(Handle th, Handle sh) { auto& gl = mContext; if (gl.ext.OES_EGL_image_external_essl3) { @@ -3512,7 +3526,7 @@ void OpenGLDriver::endFrame(UTILS_UNUSED uint32_t frameId) { auto& gl = mContext; gl.bindVertexArray(nullptr); for (int unit = OpenGLContext::DUMMY_TEXTURE_BINDING; unit >= 0; unit--) { - gl.bindTexture(unit, GL_TEXTURE_2D, 0); + gl.bindTexture(unit, GL_TEXTURE_2D, 0, false); } gl.disable(GL_CULL_FACE); gl.depthFunc(GL_LESS); diff --git a/filament/backend/src/opengl/gl_headers.h b/filament/backend/src/opengl/gl_headers.h index 04ad63ff6acc..636bdb48241b 100644 --- a/filament/backend/src/opengl/gl_headers.h +++ b/filament/backend/src/opengl/gl_headers.h @@ -56,7 +56,7 @@ # define FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 #endif -#elif defined(IOS) +#elif defined(FILAMENT_IOS) #define GLES_SILENCE_DEPRECATION @@ -81,7 +81,7 @@ #if defined(GL_VERSION_4_5) #elif defined(GL_ES_VERSION_3_1) #elif defined(GL_ES_VERSION_3_0) -# if !defined(IOS) && !defined(__EMSCRIPTEN__) +# if !defined(FILAMENT_IOS) && !defined(__EMSCRIPTEN__) # error "GLES 3.0 headers only supported on iOS and WebGL2" # endif #elif defined(GL_ES_VERSION_2_0) @@ -98,7 +98,7 @@ #if defined(GL_ES_VERSION_2_0) // this basically means all versions of GLES -#if defined(IOS) +#if defined(FILAMENT_IOS) // iOS headers only provide prototypes, nothing to do. @@ -271,7 +271,7 @@ void glGetBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, void *d #endif #ifdef GL_ES_VERSION_2_0 -# ifndef IOS +# ifndef FILAMENT_IOS # ifndef GL_OES_vertex_array_object # error "Headers with GL_OES_vertex_array_object are mandatory unless on iOS" # endif diff --git a/filament/backend/src/opengl/platforms/PlatformEGL.cpp b/filament/backend/src/opengl/platforms/PlatformEGL.cpp index 38b8b9d50493..9e6b628f02b9 100644 --- a/filament/backend/src/opengl/platforms/PlatformEGL.cpp +++ b/filament/backend/src/opengl/platforms/PlatformEGL.cpp @@ -715,14 +715,16 @@ void PlatformEGL::destroyExternalImage(ExternalTexture* texture) noexcept { bool PlatformEGL::setExternalImage(void* externalImage, UTILS_UNUSED_IN_RELEASE ExternalTexture* texture) noexcept { - if (UTILS_LIKELY(ext.gl.OES_EGL_image_external_essl3)) { - assert_invariant(texture->target == GL_TEXTURE_EXTERNAL_OES); + + // OES_EGL_image_external_essl3 must be present if the target is TEXTURE_EXTERNAL_OES + // GL_OES_EGL_image must be present if TEXTURE_2D is used + +#if defined(GL_OES_EGL_image) || defined(GL_OES_EGL_image_external_essl3) // the texture is guaranteed to be bound here. -#ifdef GL_OES_EGL_image - glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, + glEGLImageTargetTexture2DOES(texture->target, static_cast(externalImage)); #endif - } + return true; } diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 25a2448b2918..520ea22b78ff 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -288,7 +288,7 @@ Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& cont } ShaderModel VulkanDriver::getShaderModel() const noexcept { -#if defined(__ANDROID__) || defined(IOS) +#if defined(__ANDROID__) || defined(FILAMENT_IOS) return ShaderModel::MOBILE; #else return ShaderModel::DESKTOP; @@ -543,7 +543,8 @@ void VulkanDriver::createTextureViewSwizzleR(Handle th, Handle th, backend::TextureFormat format, +void VulkanDriver::createTextureExternalImageR(Handle th, + backend::SamplerType target, backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage, void* externalImage) { FVK_SYSTRACE_SCOPE(); @@ -1189,13 +1190,6 @@ TimerQueryResult VulkanDriver::getTimerQueryValue(Handle tqh, uint return TimerQueryResult::AVAILABLE; } -void VulkanDriver::setExternalImage(Handle th, void* image) { - -} - -void VulkanDriver::setExternalImagePlane(Handle th, void* image, uint32_t plane) { -} - void VulkanDriver::setExternalStream(Handle th, Handle sh) { } diff --git a/filament/backend/src/vulkan/platform/VulkanPlatformApple.mm b/filament/backend/src/vulkan/platform/VulkanPlatformApple.mm index 6504ae3a7c47..6aa18749d335 100644 --- a/filament/backend/src/vulkan/platform/VulkanPlatformApple.mm +++ b/filament/backend/src/vulkan/platform/VulkanPlatformApple.mm @@ -32,7 +32,7 @@ #ifndef VK_MVK_macos_surface #error VK_MVK_macos_surface is not defined #endif -#elif defined(IOS) +#elif defined(FILAMENT_IOS) // Metal is not available when building for the iOS simulator on Desktop. #define METAL_AVAILABLE __has_include() #if METAL_AVAILABLE @@ -56,7 +56,7 @@ ExtensionSet const ret = { #if defined(__APPLE__) VK_MVK_MACOS_SURFACE_EXTENSION_NAME, // TODO: replace with VK_EXT_metal_surface - #elif defined(IOS) && defined(METAL_AVAILABLE) + #elif defined(FILAMENT_IOS) && defined(METAL_AVAILABLE) VK_MVK_IOS_SURFACE_EXTENSION_NAME, #endif }; @@ -90,7 +90,7 @@ VkResult result = vkCreateMacOSSurfaceMVK((VkInstance) instance, &createInfo, VKALLOC, (VkSurfaceKHR*) &surface); FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateMacOSSurfaceMVK error."; - #elif defined(IOS) && defined(METAL_AVAILABLE) + #elif defined(FILAMENT_IOS) && defined(METAL_AVAILABLE) CAMetalLayer* metalLayer = (CAMetalLayer*) nativeWindow; // Create the VkSurface. FILAMENT_CHECK_POSTCONDITION(vkCreateIOSSurfaceMVK) diff --git a/filament/backend/test/BackendTest.cpp b/filament/backend/test/BackendTest.cpp index 6eede04f00c0..ff4e8bb1cabb 100644 --- a/filament/backend/test/BackendTest.cpp +++ b/filament/backend/test/BackendTest.cpp @@ -33,7 +33,7 @@ static constexpr size_t CONFIG_COMMAND_BUFFERS_SIZE = 3 * CONFIG_MIN_COMMAND using namespace filament; using namespace filament::backend; -#ifndef IOS +#ifndef FILAMENT_IOS #include #include @@ -177,7 +177,7 @@ void BackendTest::readPixelsAndAssertHash(const char* testName, size_t width, si // Export a screenshot, if requested. if (c->exportScreenshot) { -#ifndef IOS +#ifndef FILAMENT_IOS LinearImage image(c->width, c->height, 4); image = toLinearWithAlpha(c->width, c->height, c->width * 4, (uint8_t*) buffer); diff --git a/filament/backend/test/test_Blit.cpp b/filament/backend/test/test_Blit.cpp index 8ba0be44c949..b664c864d28a 100644 --- a/filament/backend/test/test_Blit.cpp +++ b/filament/backend/test/test_Blit.cpp @@ -24,7 +24,7 @@ #include -#ifndef IOS +#ifndef FILAMENT_IOS #include #include #endif @@ -67,7 +67,7 @@ struct ScreenshotParams { uint32_t pixelHashResult; }; -#ifdef IOS +#ifdef FILAMENT_IOS static void dumpScreenshot(DriverApi& dapi, Handle rt, ScreenshotParams* params) {} #else static void dumpScreenshot(DriverApi& dapi, Handle rt, ScreenshotParams* params) { diff --git a/filament/backend/test/test_FeedbackLoops.cpp b/filament/backend/test/test_FeedbackLoops.cpp index bb7a83416aee..df1aaf5471bb 100644 --- a/filament/backend/test/test_FeedbackLoops.cpp +++ b/filament/backend/test/test_FeedbackLoops.cpp @@ -31,7 +31,7 @@ #include #include -#ifndef IOS +#ifndef FILAMENT_IOS #include #include @@ -112,7 +112,7 @@ static void dumpScreenshot(DriverApi& dapi, Handle rt) { int w = kTexWidth, h = kTexHeight; const uint32_t* texels = (uint32_t*) buffer; sPixelHashResult = utils::hash::murmur3(texels, size / 4, 0); -#ifndef IOS +#ifndef FILAMENT_IOS LinearImage image(w, h, 4); image = toLinearWithAlpha(w, h, w * 4, (uint8_t*) buffer); std::ofstream pngstrm("feedback.png", std::ios::binary | std::ios::trunc); diff --git a/filament/backend/test/test_ReadPixels.cpp b/filament/backend/test/test_ReadPixels.cpp index ee1a37cd9c28..873c34438818 100644 --- a/filament/backend/test/test_ReadPixels.cpp +++ b/filament/backend/test/test_ReadPixels.cpp @@ -28,7 +28,7 @@ using namespace filament; using namespace filament::backend; -#ifndef IOS +#ifndef FILAMENT_IOS #include #include @@ -137,7 +137,7 @@ TEST_F(ReadPixelsTest, ReadPixels) { } void exportScreenshot(void* pixelData) const { - #ifndef IOS + #ifndef FILAMENT_IOS const size_t width = readRect.width, height = readRect.height; LinearImage image(width, height, 4); if (format == PixelDataFormat::RGBA && type == PixelDataType::UBYTE) { diff --git a/filament/backend/test/test_RenderExternalImage.cpp b/filament/backend/test/test_RenderExternalImage.cpp index 414e9c414d9b..643545a2bec4 100644 --- a/filament/backend/test/test_RenderExternalImage.cpp +++ b/filament/backend/test/test_RenderExternalImage.cpp @@ -227,7 +227,8 @@ TEST_F(BackendTest, RenderExternalImage) { api.setupExternalImage(pixBuffer); backend::Handle texture = - api.createTextureExternalImage(TextureFormat::RGBA8, 1024, 1024, usage, pixBuffer); + api.createTextureExternalImage(SamplerType::SAMPLER_EXTERNAL, + TextureFormat::RGBA8, 1024, 1024, usage, pixBuffer); // We're now free to release the buffer. CVBufferRelease(pixBuffer); diff --git a/filament/include/filament/Options.h b/filament/include/filament/Options.h index 3966053ed207..93bba80cd8eb 100644 --- a/filament/include/filament/Options.h +++ b/filament/include/filament/Options.h @@ -438,7 +438,7 @@ struct MultiSampleAntiAliasingOptions { * @see setTemporalAntiAliasingOptions() */ struct TemporalAntiAliasingOptions { - float filterWidth = 1.0f; //!< reconstruction filter width typically between 0.2 (sharper, aliased) and 1.5 (smoother) + float filterWidth = 1.0f; //!< reconstruction filter width typically between 1 (sharper) and 2 (smoother) float feedback = 0.12f; //!< history feedback, between 0 (maximum temporal AA) and 1 (no temporal AA). float lodBias = -1.0f; //!< texturing lod bias (typically -1 or -2) float sharpness = 0.0f; //!< post-TAA sharpen, especially useful when upscaling is true. diff --git a/filament/include/filament/Texture.h b/filament/include/filament/Texture.h index b351c4294351..6813b13e736f 100644 --- a/filament/include/filament/Texture.h +++ b/filament/include/filament/Texture.h @@ -261,6 +261,18 @@ class UTILS_PUBLIC Texture : public FilamentAPI { */ Builder& import(intptr_t id) noexcept; + /** + * Creates an external texture. The content must be set using setExternalImage(). + * The sampler can be SAMPLER_EXTERNAL or SAMPLER_2D depending on the format. Generally + * YUV formats must use SAMPLER_EXTERNAL. This depends on the backend features and is not + * validated. + * + * If the Sampler is set to SAMPLER_EXTERNAL, external() is implied. + * + * @return + */ + Builder& external() noexcept; + private: friend class FTexture; }; @@ -395,7 +407,7 @@ class UTILS_PUBLIC Texture : public FilamentAPI { /** - * Specify the external image to associate with this Texture. Typically the external + * Specify the external image to associate with this Texture. Typically, the external * image is OS specific, and can be a video or camera frame. * There are many restrictions when using an external image as a texture, such as: * - only the level of detail (lod) 0 can be specified @@ -420,7 +432,7 @@ class UTILS_PUBLIC Texture : public FilamentAPI { void setExternalImage(Engine& engine, void* UTILS_NONNULL image) noexcept; /** - * Specify the external image and plane to associate with this Texture. Typically the external + * Specify the external image and plane to associate with this Texture. Typically, the external * image is OS specific, and can be a video or camera frame. When using this method, the * external image must be a planar type (such as a YUV camera frame). The plane parameter * selects which image plane is bound to this texture. diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 90c743e58459..1a96335f2a72 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -2622,7 +2622,7 @@ void PostProcessManager::TaaJitterCamera( current.projection = inoutCameraInfo->projection * inoutCameraInfo->getUserViewMatrix(); current.frameId = previous.frameId + 1; - auto jitterPosition = [pattern = taaOptions.jitterPattern](size_t frameIndex){ + auto jitterPosition = [pattern = taaOptions.jitterPattern](size_t frameIndex) -> float2 { using JitterPattern = TemporalAntiAliasingOptions::JitterPattern; switch (pattern) { case JitterPattern::RGSS_X4: @@ -2636,6 +2636,7 @@ void PostProcessManager::TaaJitterCamera( case JitterPattern::HALTON_23_X32: return sHaltonSamples(frameIndex); } + return { 0.0f, 0.0f }; }; // sample position within a pixel [-0.5, 0.5] @@ -2759,15 +2760,31 @@ FrameGraphId PostProcessManager::taa(FrameGraph& fg, }}; constexpr float2 sampleOffsets[9] = { - { -1.0f, -1.0f }, { 0.0f, -1.0f }, { 1.0f, -1.0f }, - { -1.0f, 0.0f }, { 0.0f, 0.0f }, { 1.0f, 0.0f }, - { -1.0f, 1.0f }, { 0.0f, 1.0f }, { 1.0f, 1.0f }, + { -1.0f, -1.0f }, { 0.0f, -1.0f }, { 1.0f, -1.0f }, { -1.0f, 0.0f }, + { 0.0f, 0.0f }, + { 1.0f, 0.0f }, { -1.0f, 1.0f }, { 0.0f, 1.0f }, { 1.0f, 1.0f }, }; constexpr float2 subSampleOffsets[4] = { - { -0.25f, 0.25f }, { 0.25f, 0.25f }, { 0.25f, -0.25f }, { -0.25f, -0.25f } + { -0.25f, 0.25f }, + { 0.25f, 0.25f }, + { 0.25f, -0.25f }, + { -0.25f, -0.25f } }; + UTILS_UNUSED + auto const lanczos = [](float x, float a) -> float { + if (x <= std::numeric_limits::epsilon()) { + return 1.0f; + } + if (std::abs(x) <= a) { + return (a * std::sin(f::PI * x) * std::sin(f::PI * x / a)) + / ((f::PI * f::PI) * (x * x)); + } + return 0.0f; + }; + + float const filterWidth = std::clamp(taaOptions.filterWidth, 1.0f, 2.0f); float4 sum = 0.0; float4 weights[9]; @@ -2777,11 +2794,9 @@ FrameGraphId PostProcessManager::taa(FrameGraph& fg, for (size_t i = 0; i < 9; i++) { float2 const o = sampleOffsets[i]; for (size_t j = 0; j < 4; j++) { - float2 const s = taaOptions.upscaling ? subSampleOffsets[j] : float2{ 0 }; - float2 const d = (o - current.jitter - s) / taaOptions.filterWidth; - // This is a gaussian fit of a 3.3-wide Blackman-Harris window - // see: "High Quality Temporal Supersampling" by Brian Karis - weights[i][j] = std::exp(-2.29f * (d.x * d.x + d.y * d.y)); + float2 const subPixelOffset = taaOptions.upscaling ? subSampleOffsets[j] : float2{ 0 }; + float2 const d = (o - (current.jitter - subPixelOffset)) / filterWidth; + weights[i][j] = lanczos(length(d), filterWidth); } sum += weights[i]; } diff --git a/filament/src/PostProcessManager.h b/filament/src/PostProcessManager.h index e7c9b142c770..7ed439589403 100644 --- a/filament/src/PostProcessManager.h +++ b/filament/src/PostProcessManager.h @@ -428,7 +428,7 @@ class PostProcessManager { template struct JitterSequence { - auto operator()(size_t i) const noexcept { return positions[i % SIZE] - 0.5f; } + math::float2 operator()(size_t i) const noexcept { return positions[i % SIZE] - 0.5f; } const std::array positions; }; diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index 7a4de35a3cc1..5b9d94613eae 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -680,8 +680,6 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla cmd.info.hasMorphing = (bool)morphing.handle; cmd.info.hasSkinning = (bool)skinning.handle; - assert_invariant(cmd.info.hasHybridInstancing || cmd.info.instanceCount <= 1); - // soaInstanceInfo[i].count is the number of instances the user has requested, either for // manual or hybrid instancing. Instanced stereo multiplies the number of instances by the // eye count. diff --git a/filament/src/details/Material.cpp b/filament/src/details/Material.cpp index 651b88cf0213..be8ca04be2ec 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -611,13 +611,10 @@ void FMaterial::createAndCacheProgram(Program&& p, Variant variant) const noexce FEngine const& engine = mEngine; DriverApi& driverApi = mEngine.getDriverApi(); - bool const isSharedVariant = - (mMaterialDomain == MaterialDomain::SURFACE) && - !mIsDefaultMaterial && !mHasCustomDepthShader && - Variant::isValidDepthVariant(variant); + bool const isShared = isSharedVariant(variant); // Check if the default material has this program cached - if (isSharedVariant) { + if (isShared) { FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial(); if (pDefaultMaterial) { auto program = pDefaultMaterial->mCachedPrograms[variant.key]; @@ -636,7 +633,7 @@ void FMaterial::createAndCacheProgram(Program&& p, Variant variant) const noexce // If the default material doesn't already have this program cached, and all caching conditions // are met (Surface Domain and no custom depth shader), cache it now. // New Materials will inherit these program automatically. - if (isSharedVariant) { + if (isShared) { FMaterial const* const pDefaultMaterial = engine.getDefaultMaterial(); if (pDefaultMaterial && !pDefaultMaterial->mCachedPrograms[variant.key]) { // set the tag to the default material name diff --git a/filament/src/details/Material.h b/filament/src/details/Material.h index eb578be3a3a7..5dd55e733ec8 100644 --- a/filament/src/details/Material.h +++ b/filament/src/details/Material.h @@ -156,6 +156,13 @@ class FMaterial : public Material { std::unique_lock lock(mActiveProgramsLock); mActivePrograms.set(variant.key); lock.unlock(); + + if (isSharedVariant(variant)) { + FMaterial const* const pDefaultMaterial = mEngine.getDefaultMaterial(); + if (pDefaultMaterial && pDefaultMaterial->mCachedPrograms[variant.key]) { + return pDefaultMaterial->getProgram(variant); + } + } #endif assert_invariant(mCachedPrograms[variant.key]); return mCachedPrograms[variant.key]; @@ -290,6 +297,11 @@ class FMaterial : public Material { void createAndCacheProgram(backend::Program&& p, Variant variant) const noexcept; + inline bool isSharedVariant(Variant variant) const { + return (mMaterialDomain == MaterialDomain::SURFACE) && !mIsDefaultMaterial && + !mHasCustomDepthShader && Variant::isValidDepthVariant(variant); + } + // try to order by frequency of use mutable std::array, VARIANT_COUNT> mCachedPrograms; DescriptorSetLayout mPerViewDescriptorSetLayout; diff --git a/filament/src/details/Texture.cpp b/filament/src/details/Texture.cpp index 176907345b72..cff6d7e42a91 100644 --- a/filament/src/details/Texture.cpp +++ b/filament/src/details/Texture.cpp @@ -81,6 +81,7 @@ struct Texture::BuilderDetails { Usage mUsage = Usage::NONE; bool mHasBlitSrc = false; bool mTextureIsSwizzled = false; + bool mExternal = false; std::array mSwizzle = { Swizzle::CHANNEL_0, Swizzle::CHANNEL_1, Swizzle::CHANNEL_2, Swizzle::CHANNEL_3 }; @@ -136,6 +137,11 @@ Texture::Builder& Texture::Builder::import(intptr_t id) noexcept { return *this; } +Texture::Builder& Texture::Builder::external() noexcept { + mImpl->mExternal = true; + return *this; +} + Texture::Builder& Texture::Builder::swizzle(Swizzle r, Swizzle g, Swizzle b, Swizzle a) noexcept { mImpl->mTextureIsSwizzled = true; mImpl->mSwizzle = { r, g, b, a }; @@ -154,6 +160,11 @@ Texture* Texture::Builder::build(Engine& engine) { (isProtectedTexturesSupported && useProtectedMemory) || !useProtectedMemory) << "Texture is PROTECTED but protected textures are not supported"; + // SAMPLER_EXTERNAL implies imported. + if (mImpl->mTarget == SamplerType::SAMPLER_EXTERNAL) { + mImpl->mExternal = true; + } + uint8_t maxLevelCount; switch (mImpl->mTarget) { case SamplerType::SAMPLER_2D: @@ -174,7 +185,7 @@ Texture* Texture::Builder::build(Engine& engine) { mImpl->mUsage = TextureUsage::DEFAULT; if (mImpl->mLevels > 1 && (mImpl->mWidth > 1 || mImpl->mHeight > 1) && - mImpl->mTarget != SamplerType::SAMPLER_EXTERNAL) { + !mImpl->mExternal) { const bool formatMipmappable = downcast(engine).getDriverApi().isTextureFormatMipmappable(mImpl->mFormat); if (formatMipmappable) { @@ -243,9 +254,10 @@ FTexture::FTexture(FEngine& engine, const Builder& builder) { mSwizzle = builder->mSwizzle; mTextureIsSwizzled = builder->mTextureIsSwizzled; mHasBlitSrc = builder->mHasBlitSrc; + mExternal = builder->mExternal; bool const isImported = builder->mImportedId != 0; - if (mTarget == SamplerType::SAMPLER_EXTERNAL && !isImported) { + if (mExternal && !isImported) { // mHandle and mHandleForSampling will be created in setExternalImage() // If this Texture is used for sampling before setExternalImage() is called, // we'll lazily create a 1x1 placeholder texture. @@ -318,8 +330,8 @@ void FTexture::setImage(FEngine& engine, size_t level, << "level=" << unsigned(level) << " is >= to levelCount=" << unsigned(mLevelCount) << "."; - FILAMENT_CHECK_PRECONDITION(mTarget != SamplerType::SAMPLER_EXTERNAL) - << "Texture SamplerType::SAMPLER_EXTERNAL not supported for this operation."; + FILAMENT_CHECK_PRECONDITION(!mExternal) + << "External Texture not supported for this operation."; FILAMENT_CHECK_PRECONDITION(mSampleCount <= 1) << "Operation not supported with multisample (" << unsigned(mSampleCount) << ") texture."; @@ -454,15 +466,14 @@ void FTexture::setImage(FEngine& engine, size_t level, } void FTexture::setExternalImage(FEngine& engine, void* image) noexcept { - if (mTarget != Sampler::SAMPLER_EXTERNAL) { - return; - } + FILAMENT_CHECK_PRECONDITION(mExternal) << "The texture must be external."; + // The call to setupExternalImage is synchronous, and allows the driver to take ownership of the // external image on this thread, if necessary. auto& api = engine.getDriverApi(); api.setupExternalImage(image); - auto texture = api.createTextureExternalImage(mFormat, mWidth, mHeight, mUsage, image); + auto texture = api.createTextureExternalImage(mTarget, mFormat, mWidth, mHeight, mUsage, image); if (mTextureIsSwizzled) { auto const& s = mSwizzle; @@ -475,9 +486,8 @@ void FTexture::setExternalImage(FEngine& engine, void* image) noexcept { } void FTexture::setExternalImage(FEngine& engine, void* image, size_t plane) noexcept { - if (mTarget != Sampler::SAMPLER_EXTERNAL) { - return; - } + FILAMENT_CHECK_PRECONDITION(mExternal) << "The texture must be external."; + // The call to setupExternalImage is synchronous, and allows the driver to take ownership of // the external image on this thread, if necessary. auto& api = engine.getDriverApi(); @@ -497,9 +507,7 @@ void FTexture::setExternalImage(FEngine& engine, void* image, size_t plane) noex } void FTexture::setExternalStream(FEngine& engine, FStream* stream) noexcept { - if (mTarget != Sampler::SAMPLER_EXTERNAL) { - return; - } + FILAMENT_CHECK_PRECONDITION(mExternal) << "The texture must be external."; auto& api = engine.getDriverApi(); auto texture = api.createTexture( @@ -524,7 +532,7 @@ void FTexture::setExternalStream(FEngine& engine, FStream* stream) noexcept { } void FTexture::generateMipmaps(FEngine& engine) const noexcept { - FILAMENT_CHECK_PRECONDITION(mTarget != SamplerType::SAMPLER_EXTERNAL) + FILAMENT_CHECK_PRECONDITION(!mExternal) << "External Textures are not mipmappable."; FILAMENT_CHECK_PRECONDITION(mTarget != SamplerType::SAMPLER_3D) @@ -544,12 +552,11 @@ void FTexture::generateMipmaps(FEngine& engine) const noexcept { } bool FTexture::textureHandleCanMutate() const noexcept { - return (any(mUsage & Usage::SAMPLEABLE) && mLevelCount > 1) || - mTarget == SamplerType::SAMPLER_EXTERNAL; + return (any(mUsage & Usage::SAMPLEABLE) && mLevelCount > 1) || mExternal; } void FTexture::updateLodRange(uint8_t baseLevel, uint8_t levelCount) noexcept { - assert_invariant(mTarget != SamplerType::SAMPLER_EXTERNAL); + assert_invariant(!mExternal); if (any(mUsage & Usage::SAMPLEABLE) && mLevelCount > 1) { auto& range = mLodRange; uint8_t const last = int8_t(baseLevel + levelCount); @@ -601,7 +608,7 @@ backend::Handle FTexture::createPlaceholderTexture( } backend::Handle FTexture::getHwHandleForSampling() const noexcept { - if (UTILS_UNLIKELY(mTarget == SamplerType::SAMPLER_EXTERNAL && !mHandleForSampling)) { + if (UTILS_UNLIKELY(mExternal && !mHandleForSampling)) { return setHandleForSampling(createPlaceholderTexture(*mDriver)); } auto const& range = mLodRange; @@ -610,9 +617,10 @@ backend::Handle FTexture::getHwHandleForSampling() const noe if (UTILS_UNLIKELY(lodRangeChanged)) { activeRange = range; if (range.empty() || hasAllLods(range)) { - setHandleForSampling(mHandle); + std::ignore = setHandleForSampling(mHandle); } else { - setHandleForSampling(mDriver->createTextureView(mHandle, range.first, range.size())); + std::ignore = setHandleForSampling(mDriver->createTextureView( + mHandle, range.first, range.size())); } } return mHandleForSampling; diff --git a/filament/src/details/Texture.h b/filament/src/details/Texture.h index 6bf04dae4ac2..2131988c84c9 100644 --- a/filament/src/details/Texture.h +++ b/filament/src/details/Texture.h @@ -173,7 +173,8 @@ class FTexture : public Texture { // Indicates whether the user has set the TextureUsage::BLIT_SRC usage. This will be used to // temporarily validate whether this texture can be used for readPixels. bool mHasBlitSrc = false; - // there is 5 bytes of padding here + bool mExternal = false; + // there is 4 bytes of padding here FStream* mStream = nullptr; // only needed for streaming textures }; diff --git a/filament/src/ds/DescriptorSet.cpp b/filament/src/ds/DescriptorSet.cpp index 9d41b20a33f6..caa433ad46ca 100644 --- a/filament/src/ds/DescriptorSet.cpp +++ b/filament/src/ds/DescriptorSet.cpp @@ -149,7 +149,7 @@ void DescriptorSet::setSampler( DescriptorSet DescriptorSet::duplicate(DescriptorSetLayout const& layout) const noexcept { DescriptorSet set{layout}; - memcpy(set.mDescriptors.data(), mDescriptors.data(), mDescriptors.size() * sizeof(Desc)); + set.mDescriptors = mDescriptors; // Use the vector's assignment operator set.mDirty = mDirty; set.mValid = mValid; return set; diff --git a/filament/src/materials/antiAliasing/taa.mat b/filament/src/materials/antiAliasing/taa.mat index b5430378c70f..86f55508e472 100644 --- a/filament/src/materials/antiAliasing/taa.mat +++ b/filament/src/materials/antiAliasing/taa.mat @@ -127,7 +127,8 @@ float lumaYCoCg(const vec3 c) { } float luma(const vec3 c) { - return materialConstants_useYCoCg ? lumaYCoCg(c) : lumaRGB(c); + return (materialConstants_useYCoCg && materialConstants_boxClipping != BOX_CLIPPING_NONE) ? + lumaYCoCg(c) : lumaRGB(c); } vec3 tonemap(const vec3 c) { @@ -278,10 +279,6 @@ void postProcess(inout PostProcessInputs postProcess) { history = textureLod(materialParams_history, uv.zw, 0.0); } - if (materialConstants_useYCoCg) { - history.rgb = RGB_YCoCg(history.rgb); - } - highp vec2 size = vec2(textureSize(materialParams_color, 0)); highp vec2 p = (floor(uv.xy * size) + 0.5) / size; vec4 filtered = textureLod(materialParams_color, p, 0.0); @@ -297,47 +294,46 @@ void postProcess(inout PostProcessInputs postProcess) { s[6] = textureLodOffset(materialParams_color, p, 0.0, ivec2(-1, 1)).rgb; s[7] = textureLodOffset(materialParams_color, p, 0.0, ivec2( 0, 1)).rgb; s[8] = textureLodOffset(materialParams_color, p, 0.0, ivec2( 1, 1)).rgb; - if (materialConstants_useYCoCg) { - for (int i = 0; i < 9; i++) { - s[i] = RGB_YCoCg(s[i]); - } - } } - vec2 subPixelOffset = p - uv.xy; // +/- [0.25, 0.25] - float confidence = materialConstants_upscaling ? 0.0 : 1.0; + int j = 0; + float confidence = 1.0; + if (materialConstants_upscaling) { + highp vec2 subPixelOffset = (p - uv.xy) * size; // +/- [0.25, 0.25] + + // we reduce the contribution of a sample based on the distance + // to the high resolution pixel center + const float cutoff = 0.5; + highp float l = length(materialParams.jitter - subPixelOffset) / cutoff; + confidence = saturate(1.0 - l * l); + + if (materialConstants_filterInput) { + int jxp = subPixelOffset.y > 0.0 ? 1 : 2; + int jxn = subPixelOffset.y > 0.0 ? 0 : 3; + j = subPixelOffset.x > 0.0 ? jxp : jxn; + } + } if (materialConstants_filterInput) { // unjitter/filter input - // figure out which set of coeficients to use - filtered = vec4(0, 0, 0, filtered.a); - if (materialConstants_upscaling) { - int jxp = subPixelOffset.y > 0.0 ? 3 : 0; - int jxn = subPixelOffset.y > 0.0 ? 2 : 1; - int j = subPixelOffset.x > 0.0 ? jxp : jxn; - for (int i = 0; i < 9; i++) { - float w = materialParams.filterWeights[i][j]; - filtered.rgb += s[i] * w; - confidence = max(confidence, w); - } - } else { - for (int i = 0; i < 9; i++) { - float w = materialParams.filterWeights[i][0]; - filtered.rgb += s[i] * w; - } - } - } else { - if (materialConstants_useYCoCg) { - filtered.rgb = RGB_YCoCg(filtered.rgb); - } - if (materialConstants_upscaling) { - confidence = float(materialParams.jitter.x * subPixelOffset.x > 0.0 && - materialParams.jitter.y * subPixelOffset.y > 0.0); + filtered = vec4(vec3(0), filtered.a); + for (int i = 0; i < 9; i++) { + float w = materialParams.filterWeights[i][j]; + filtered.rgb += s[i] * w; } + filtered.rgb = max(filtered.rgb, vec3(0)); } // build the history clamping box if (materialConstants_boxClipping != BOX_CLIPPING_NONE) { + if (materialConstants_useYCoCg) { + history.rgb = RGB_YCoCg(history.rgb); + filtered.rgb = RGB_YCoCg(filtered.rgb); + for (int i = 0; i < 9; i++) { + s[i] = RGB_YCoCg(s[i]); + } + } + vec3 boxmin; vec3 boxmax; if (materialConstants_boxType == BOX_TYPE_AABB || @@ -346,7 +342,7 @@ void postProcess(inout PostProcessInputs postProcess) { boxmax = max(s[4], max(max(s[1], s[3]), max(s[5], s[7]))); vec3 box9min = min(boxmin, min(min(s[0], s[2]), min(s[6], s[8]))); vec3 box9max = max(boxmax, max(max(s[0], s[2]), max(s[6], s[8]))); - // round the corners of the 3x3 box + // round the corners of the 3x3 box, giving less importance to the corner samples boxmin = (boxmin + box9min) * 0.5; boxmax = (boxmax + box9max) * 0.5; } @@ -388,9 +384,11 @@ void postProcess(inout PostProcessInputs postProcess) { } // go back to RGB space before tonemapping - if (materialConstants_useYCoCg) { - filtered.rgb = YCoCg_RGB(filtered.rgb); - history.rgb = YCoCg_RGB(history.rgb); + if (materialConstants_boxClipping != BOX_CLIPPING_NONE) { + if (materialConstants_useYCoCg) { + filtered.rgb = YCoCg_RGB(filtered.rgb); + history.rgb = YCoCg_RGB(history.rgb); + } } // tonemap before mixing diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index c6378573e361..1133525c5a70 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.56.5" + spec.version = "1.56.6" 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.56.5/filament-v1.56.5-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.56.6/filament-v1.56.6-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/libs/bluevk/include/bluevk/BlueVK.h b/libs/bluevk/include/bluevk/BlueVK.h index a57c528a2542..4fb61191a321 100644 --- a/libs/bluevk/include/bluevk/BlueVK.h +++ b/libs/bluevk/include/bluevk/BlueVK.h @@ -40,7 +40,7 @@ #if defined(__ANDROID__) #define VK_USE_PLATFORM_ANDROID_KHR 1 - #elif defined(IOS) + #elif defined(FILAMENT_IOS) #define VK_USE_PLATFORM_IOS_MVK 1 #elif defined(__linux__) #if defined(FILAMENT_SUPPORTS_XCB) diff --git a/libs/fgviewer/CMakeLists.txt b/libs/fgviewer/CMakeLists.txt new file mode 100644 index 000000000000..cf6bdb526df7 --- /dev/null +++ b/libs/fgviewer/CMakeLists.txt @@ -0,0 +1,71 @@ +cmake_minimum_required(VERSION 3.19) +project(fgviewer C ASM) + +set(TARGET fgviewer) +set(PUBLIC_HDR_DIR include) + +if (CMAKE_CROSSCOMPILING) + include(${IMPORT_EXECUTABLES}) +endif() + +# ================================================================================================== +# Sources and headers +# ================================================================================================== + +set(PUBLIC_HDRS + include/fgviewer/DebugServer.h + include/fgviewer/JsonWriter.h +) + +set(SRCS + src/ApiHandler.cpp + src/ApiHandler.h + src/DebugServer.cpp +) + +# ================================================================================================== +# Include and target definitions +# ================================================================================================== + +include_directories(${PUBLIC_HDR_DIR}) + +add_library(${TARGET} STATIC ${PUBLIC_HDRS} ${SRCS}) + +target_link_libraries(${TARGET} PUBLIC + civetweb + utils +) + +target_include_directories(${TARGET} PRIVATE ${filamat_SOURCE_DIR}/src) + +target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR}) +set_target_properties(${TARGET} PROPERTIES FOLDER Libs) + +# ================================================================================================== +# Compiler flags +# ================================================================================================== + +if (MSVC) +else() + target_compile_options(${TARGET} PRIVATE -Wno-deprecated-register) +endif() + +# ================================================================================================== +# Installation +# ================================================================================================== + +# matdbg 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. +set(FGVIEWER_DEPS + fgviewer + civetweb + ) + +set(FGVIEWER_COMBINED_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/libfgviewer_combined.a") +combine_static_libs(fgviewer "${FGVIEWER_COMBINED_OUTPUT}" "${FGVIEWER_DEPS}") + +set(FGVIEWER_LIB_NAME ${CMAKE_STATIC_LIBRARY_PREFIX}fgviewer${CMAKE_STATIC_LIBRARY_SUFFIX}) +install(FILES "${FGVIEWER_COMBINED_OUTPUT}" DESTINATION lib/${DIST_DIR} RENAME ${FGVIEWER_LIB_NAME}) +# We do not need fgviewer headers in the install directory +# install(DIRECTORY ${PUBLIC_HDR_DIR}/fgviewer DESTINATION include) diff --git a/libs/fgviewer/include/fgviewer/DebugServer.h b/libs/fgviewer/include/fgviewer/DebugServer.h new file mode 100644 index 000000000000..ef0544833347 --- /dev/null +++ b/libs/fgviewer/include/fgviewer/DebugServer.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2024 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 FGVIEWER_DEBUGSERVER_H +#define FGVIEWER_DEBUGSERVER_H + +#include +#include + +#include +#include + +class CivetServer; + +namespace filament::fgviewer { + +using FrameGraphInfoKey = uint32_t; + +struct FrameGraphPassInfo { + utils::CString pass_name; + // TODO: Add struct detail properties +}; + +struct FrameGraphInfo { + utils::CString view_name; + std::vector passes; +}; + +/** + * Server-side frame graph debugger. + * + * This class manages an HTTP server. It receives frame graph packages from the Filament C++ engine or + * from a standalone tool such as fginfo. + */ +class DebugServer { +public: + static std::string_view const kSuccessHeader; + static std::string_view const kErrorHeader; + + DebugServer(int port); + ~DebugServer(); + + /** + * Notifies the debugger that a new view has been added. + */ + void addView(const utils::CString& name, FrameGraphInfo info); + + /** + * Notifies the debugger that the given view has been deleted. + */ + void removeView(const utils::CString& name); + + /** + * Updates the information for a given view. + */ + void updateView(const utils::CString& name, FrameGraphInfo info); + + bool isReady() const { return mServer; } + +private: + CivetServer* mServer; + + std::unordered_map mViews; + mutable utils::Mutex mViewsMutex; + + class FileRequestHandler* mFileHandler = nullptr; + class ApiHandler* mApiHandler = nullptr; + + friend class FileRequestHandler; + friend class ApiHandler; +}; + +} // namespace filament::fgviewer + +#endif // FGVIEWER_DEBUGSERVER_H diff --git a/libs/fgviewer/include/fgviewer/JsonWriter.h b/libs/fgviewer/include/fgviewer/JsonWriter.h new file mode 100644 index 000000000000..de9c188eee2b --- /dev/null +++ b/libs/fgviewer/include/fgviewer/JsonWriter.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2024 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 FGVIEWER_JSONWRITER_H +#define FGVIEWER_JSONWRITER_H + +#include + +namespace filament::fgviewer { + +struct FrameGraphInfo; + +// This class generates portions of JSON messages that are sent to the web client. +// Note that some portions of these JSON strings are generated by directly in DebugServer, +// as well as CommonWriter. +class JsonWriter { +public: + + // Retrieves the most recently generated string. + const char* getJsonString() const; + size_t getJsonSize() const; + + // Generates a JSON string describing the given FrameGraphInfo. + bool writeFrameGraphInfo(const FrameGraphInfo& frameGraph); + +private: + utils::CString mJsonString; +}; + +} // namespace filament::fgviewer + +#endif // FGVIEWER_JSONWRITER_H diff --git a/libs/fgviewer/src/ApiHandler.cpp b/libs/fgviewer/src/ApiHandler.cpp new file mode 100644 index 000000000000..729820d4047c --- /dev/null +++ b/libs/fgviewer/src/ApiHandler.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2024 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 "ApiHandler.h" + +#include + +#include +#include + +#include + +namespace filament::fgviewer { + +using namespace std::chrono_literals; + +namespace { + +auto const& kSuccessHeader = DebugServer::kSuccessHeader; +auto const& kErrorHeader = DebugServer::kErrorHeader; + +auto const error = [](int line, std::string const& uri) { + utils::slog.e << "[fgviewer] DebugServer: 404 at line " << line << ": " << uri << utils::io::endl; + return false; +}; + +} // anonymous + +bool ApiHandler::handleGetApiFgInfo(struct mg_connection* conn, + struct mg_request_info const* request) { + auto const softError = [conn, request](char const* msg) { + utils::slog.e << "[fgviewer] DebugServer: " << msg << ": " << request->query_string << utils::io::endl; + mg_printf(conn, kErrorHeader.data(), "application/txt"); + mg_write(conn, msg, strlen(msg)); + return true; + }; + + // TODO: Implement the method + return true; +} + +void ApiHandler::addFrameGraph(FrameGraphInfo const* framegraph) { + // TODO: Implement the method +} + + +bool ApiHandler::handleGet(CivetServer* server, struct mg_connection* conn) { + struct mg_request_info const* request = mg_get_request_info(conn); + std::string const& uri = request->local_uri; + + // TODO: Implement the method + return true; +} + +} // filament::fgviewer diff --git a/libs/fgviewer/src/ApiHandler.h b/libs/fgviewer/src/ApiHandler.h new file mode 100644 index 000000000000..b0ed9e785d39 --- /dev/null +++ b/libs/fgviewer/src/ApiHandler.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 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 FGVIEWER_APIHANDLER_H +#define FGVIEWER_APIHANDLER_H + +#include + +namespace filament::fgviewer { + +class DebugServer; +struct FrameGraphInfo; + +// Handles the following REST requests, where {id} is an 8-digit hex string. +// +// GET /api/framegraphs +// GET /api/framegraph?fg={viewname} +// +class ApiHandler : public CivetHandler { +public: + explicit ApiHandler(DebugServer* server) + : mServer(server) {} + ~ApiHandler() = default; + + bool handleGet(CivetServer* server, struct mg_connection* conn); + void addFrameGraph(FrameGraphInfo const* frameGraph); + +private: + bool handleGetApiFgInfo(struct mg_connection* conn, struct mg_request_info const* request); + + DebugServer* mServer; +}; + +} // filament::fgviewer + +#endif // FGVIEWER_APIHANDLER_H diff --git a/libs/fgviewer/src/DebugServer.cpp b/libs/fgviewer/src/DebugServer.cpp new file mode 100644 index 000000000000..ce1ccaa36b2e --- /dev/null +++ b/libs/fgviewer/src/DebugServer.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2024 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 + +#include "ApiHandler.h" + +#include + +#include +#include +#include + +#include +#include + +namespace filament::fgviewer { + +namespace { +std::string const BASE_URL = "libs/fgviewer/web"; + +FrameGraphInfoKey getKeybyString(const utils::CString &input, + uint32_t seed) { + return utils::hash::murmurSlow(reinterpret_cast( + input.c_str()), input.size(), 0); +} +} // anonymous + +using namespace utils; + +std::string_view const DebugServer::kSuccessHeader = + "HTTP/1.1 200 OK\r\nContent-Type: %s\r\n" + "Connection: close\r\n\r\n"; + +std::string_view const DebugServer::kErrorHeader = + "HTTP/1.1 404 Not Found\r\nContent-Type: %s\r\n" + "Connection: close\r\n\r\n"; + +class FileRequestHandler : public CivetHandler { +public: + FileRequestHandler(DebugServer* server) : mServer(server) {} + bool handleGet(CivetServer *server, struct mg_connection *conn) { + auto const& kSuccessHeader = DebugServer::kSuccessHeader; + struct mg_request_info const* request = mg_get_request_info(conn); + std::string uri(request->request_uri); + if (uri == "/") { + uri = "/index.html"; + } + + if (uri == "/index.html" || uri == "/app.js" || uri == "/api.js") { + mg_send_file(conn, (BASE_URL + uri).c_str()); + return true; + } + slog.e << "[fgviewer] DebugServer: bad request at line " << __LINE__ << ": " << uri << io::endl; + return false; + } +private: + DebugServer* mServer; +}; + +DebugServer::DebugServer(int port) { + // 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 + // (CSS, JavaScript, images, ...)." If this count is too small, the web app basically hangs. + const char* kServerOptions[] = { + "listening_ports", "8085", + "num_threads", "10", + "error_log_file", "civetweb.txt", + nullptr + }; + std::string portString = std::to_string(port); + kServerOptions[1] = portString.c_str(); + + mServer = new CivetServer(kServerOptions); + if (!mServer->getContext()) { + delete mServer; + mServer = nullptr; + slog.e << "[fgviewer] Unable to start DebugServer, see civetweb.txt for details." << io::endl; + return; + } + + mFileHandler = new FileRequestHandler(this); + mApiHandler = new ApiHandler(this); + + mServer->addHandler("/api", mApiHandler); + mServer->addHandler("", mFileHandler); + + slog.i << "[fgviewer] DebugServer listening at http://localhost:" << port << io::endl; +} + +DebugServer::~DebugServer() { + mServer->close(); + + delete mFileHandler; + delete mApiHandler; + delete mServer; +} + +void DebugServer::addView(const utils::CString &name, FrameGraphInfo info) { + std::unique_lock lock(mViewsMutex); + const FrameGraphInfoKey key = getKeybyString(name, 0); + mViews.insert({key, info}); +} + +void DebugServer::removeView(const utils::CString& name) { + std::unique_lock lock(mViewsMutex); + const FrameGraphInfoKey key = getKeybyString(name, 0); + mViews.erase(key); +} + +void DebugServer::updateView(const utils::CString& name, FrameGraphInfo info) { + std::unique_lock lock(mViewsMutex); + const FrameGraphInfoKey key = getKeybyString(name, 0); + mViews[key] = info; +} + +} // namespace filament::fgviewer diff --git a/libs/filamentapp/src/FilamentApp.cpp b/libs/filamentapp/src/FilamentApp.cpp index 4b7bbd8c33d3..e47c95c79a1a 100644 --- a/libs/filamentapp/src/FilamentApp.cpp +++ b/libs/filamentapp/src/FilamentApp.cpp @@ -645,7 +645,7 @@ FilamentApp::Window::Window(FilamentApp* filamentApp, // This mirrors the logic for choosing a backend given compile-time flags and client having // provided DEFAULT as the backend (see PlatformFactory.cpp) - #if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !defined(IOS) && \ + #if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !defined(FILAMENT_IOS) && \ !defined(__APPLE__) && defined(FILAMENT_DRIVER_SUPPORTS_VULKAN) if (backend == Engine::Backend::DEFAULT) { backend = Engine::Backend::VULKAN; diff --git a/libs/gltfio/src/Animator.cpp b/libs/gltfio/src/Animator.cpp index 0d8b667644b1..7b1bd6eb2508 100644 --- a/libs/gltfio/src/Animator.cpp +++ b/libs/gltfio/src/Animator.cpp @@ -408,6 +408,9 @@ void AnimatorImpl::addChannels(const FixedCapacityVector& nodeMap, const Sampler* samplers = dst.samplers.data(); for (cgltf_size j = 0, nchans = srcAnim.channels_count; j < nchans; ++j) { const cgltf_animation_channel& srcChannel = srcChannels[j]; + if (!srcChannel.target_node) { + continue; + } Entity targetEntity = nodeMap[srcChannel.target_node - nodes]; if (UTILS_UNLIKELY(!targetEntity)) { if (GLTFIO_VERBOSE) { diff --git a/libs/gltfio/src/AssetLoader.cpp b/libs/gltfio/src/AssetLoader.cpp index cf3007a67f64..2e3439a9a7da 100644 --- a/libs/gltfio/src/AssetLoader.cpp +++ b/libs/gltfio/src/AssetLoader.cpp @@ -1733,7 +1733,7 @@ void FAssetLoader::importSkins(FFilamentInstance* instance, const cgltf_data* gl } bool AssetConfigurationExtended::isSupported() { -#if defined(__ANDROID__) || defined(IOS) || defined(__EMSCRIPTEN__) +#if defined(__ANDROID__) || defined(FILAMENT_IOS) || defined(__EMSCRIPTEN__) return false; #else return true; diff --git a/libs/gltfio/src/FFilamentAsset.h b/libs/gltfio/src/FFilamentAsset.h index a1401c37ccb0..1648ad0d2c38 100644 --- a/libs/gltfio/src/FFilamentAsset.h +++ b/libs/gltfio/src/FFilamentAsset.h @@ -60,7 +60,7 @@ #define GLTFIO_WARN(msg) slog.w << msg << io::endl #endif -#if defined(__EMSCRIPTEN__) || defined(__ANDROID__) || defined(IOS) +#if defined(__EMSCRIPTEN__) || defined(__ANDROID__) || defined(FILAMENT_IOS) #define GLTFIO_USE_FILESYSTEM 0 #else #define GLTFIO_USE_FILESYSTEM 1 diff --git a/libs/matdbg/CMakeLists.txt b/libs/matdbg/CMakeLists.txt index e5805854808d..566aec3fd31e 100644 --- a/libs/matdbg/CMakeLists.txt +++ b/libs/matdbg/CMakeLists.txt @@ -31,6 +31,8 @@ set(SRCS src/ShaderReplacer.cpp src/ShaderExtractor.cpp src/ShaderInfo.cpp + src/SourceFormatter.cpp + src/SourceFormatter.h src/TextWriter.cpp ) diff --git a/libs/matdbg/src/ApiHandler.cpp b/libs/matdbg/src/ApiHandler.cpp index 82e27fa53ac7..c9ef42c0ee0b 100644 --- a/libs/matdbg/src/ApiHandler.cpp +++ b/libs/matdbg/src/ApiHandler.cpp @@ -43,19 +43,6 @@ namespace { auto const& kSuccessHeader = DebugServer::kSuccessHeader; auto const& kErrorHeader = DebugServer::kErrorHeader; -void spirvToAsm(struct mg_connection* conn, uint32_t const* spirv, size_t size) { - auto spirvDisassembly = ShaderExtractor::spirvToText(spirv, size / 4); - mg_printf(conn, kSuccessHeader.data(), "application/txt"); - mg_write(conn, spirvDisassembly.c_str(), spirvDisassembly.size()); -} - -void spirvToGlsl(ShaderModel shaderModel, struct mg_connection* conn, uint32_t const* spirv, - size_t size) { - auto glsl = ShaderExtractor::spirvToGLSL(shaderModel, spirv, size / 4); - mg_printf(conn, kSuccessHeader.data(), "application/txt"); - mg_printf(conn, glsl.c_str(), glsl.size()); -} - } // anonymous using filaflat::ChunkContainer; @@ -145,8 +132,10 @@ bool ApiHandler::handleGetApiShader(struct mg_connection* conn, filaflat::ShaderContent content; extractor.getShader(item.shaderModel, item.variant, item.pipelineStage, content); + std::string const shader = mFormatter.format((char const*) content.data()); mg_printf(conn, kSuccessHeader.data(), "application/txt"); - mg_write(conn, content.data(), content.size() - 1); + mg_write(conn, shader.c_str(), shader.size()); + return true; } @@ -171,12 +160,19 @@ bool ApiHandler::handleGetApiShader(struct mg_connection* conn, extractor.getShader(item.shaderModel, item.variant, item.pipelineStage, content); if (language == spirv) { - spirvToAsm(conn, (uint32_t const*) content.data(), content.size()); + auto spirvDisassembly = ShaderExtractor::spirvToText((uint32_t const*) content.data(), + content.size() / 4); + mg_printf(conn, kSuccessHeader.data(), "application/txt"); + mg_write(conn, spirvDisassembly.c_str(), spirvDisassembly.size()); return true; } if (language == glsl) { - spirvToGlsl(item.shaderModel, conn, (uint32_t const*) content.data(), content.size()); + auto glsl = ShaderExtractor::spirvToGLSL(item.shaderModel, + (uint32_t const*) content.data(), content.size() / 4); + std::string const shader = mFormatter.format((char const*) glsl.c_str()); + mg_printf(conn, kSuccessHeader.data(), "application/txt"); + mg_printf(conn, shader.c_str(), shader.size()); return true; } @@ -204,8 +200,9 @@ bool ApiHandler::handleGetApiShader(struct mg_connection* conn, extractor.getShader(item.shaderModel, item.variant, item.pipelineStage, content); if (language == msl) { + std::string const shader = mFormatter.format((char const*) content.data()); mg_printf(conn, kSuccessHeader.data(), "application/txt"); - mg_write(conn, content.data(), content.size() - 1); + mg_write(conn, shader.c_str(), shader.size()); return true; } diff --git a/libs/matdbg/src/ApiHandler.h b/libs/matdbg/src/ApiHandler.h index 81aaa7c734c5..353326a7dc7d 100644 --- a/libs/matdbg/src/ApiHandler.h +++ b/libs/matdbg/src/ApiHandler.h @@ -17,6 +17,8 @@ #ifndef MATDBG_APIHANDLER_H #define MATDBG_APIHANDLER_H +#include "SourceFormatter.h" + #include #include #include @@ -63,6 +65,8 @@ class ApiHandler : public CivetHandler { // will always block until statusMaterialId is updated again. The client is expected to keep // calling /api/status (a constant "pull" to simulate a push). std::atomic mCurrentStatus = 0; + + SourceFormatter mFormatter; }; } // filament::matdbg diff --git a/libs/matdbg/src/DebugServer.cpp b/libs/matdbg/src/DebugServer.cpp index 96c9538620e7..e8efb0b22745 100644 --- a/libs/matdbg/src/DebugServer.cpp +++ b/libs/matdbg/src/DebugServer.cpp @@ -193,8 +193,12 @@ DebugServer::addMaterial(const CString& name, const void* data, size_t size, voi return {}; } - const uint32_t seed = 42; - const MaterialKey key = utils::hash::murmurSlow((const uint8_t*) data, size, seed); + // Note that it's possible to have two materials with the exact same content (however wasteful), + // but they refer to different instantiation of FMaterial. Hence we hash on userdata and the + // material data. + constexpr uint32_t seed = 42; + uint64_t dataSpace[2] = {(uint64_t) data, (uint64_t) userdata}; + uint32_t const key = utils::hash::murmurSlow((uint8_t const*) dataSpace, sizeof(dataSpace), seed); // Retain a copy of the package to permit queries after the client application has // freed up the original material package. diff --git a/libs/matdbg/src/ShaderReplacer.cpp b/libs/matdbg/src/ShaderReplacer.cpp index 2a99ee0db567..8a457e598c98 100644 --- a/libs/matdbg/src/ShaderReplacer.cpp +++ b/libs/matdbg/src/ShaderReplacer.cpp @@ -377,7 +377,7 @@ void BlobIndex::writeChunks(ostream& stream) { for (auto& record : mShaderRecords) { const auto& src = mDataBlobs[record.dictionaryIndex]; assert(src.size() % 4 == 0); - const uint32_t* ptr = (const uint32_t*) src.data(); + uint8_t const* ptr = (uint8_t const*) src.data(); record.dictionaryIndex = blobs.addBlob(vector(ptr, ptr + src.size())); } diff --git a/libs/matdbg/src/SourceFormatter.cpp b/libs/matdbg/src/SourceFormatter.cpp new file mode 100644 index 000000000000..c068a34f4b43 --- /dev/null +++ b/libs/matdbg/src/SourceFormatter.cpp @@ -0,0 +1,80 @@ +/* + * 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 "SourceFormatter.h" + +#include + +#include +#include + +namespace filament::matdbg { + +#if defined(__linux__) || defined(__APPLE__) +std::string SourceFormatter::format(char const* source) { + std::string const TMP_FILENAME = "/tmp/matdbg-tmp-src.cpp"; + + std::string original(source); + FILE* fp; + fp = fopen(TMP_FILENAME.c_str(), "w"); + if (!fp) { + return original; + } + fputs(original.c_str(), fp); + fflush(fp); + pclose(fp); + + std::string const CLANG_FORMAT_OPTIONS = + "-style='{" + "BasedOnStyle: Google, " + "IndentWidth: 4, " + "MaxEmptyLinesToKeep: 2" + "}'"; + + fp = popen(("clang-format " + CLANG_FORMAT_OPTIONS + "< " + TMP_FILENAME).c_str(), "r"); + + if (!fp) { + std::call_once(mClangWarningFlag, []() { + utils::slog.w << "[matdbg] unable to run clang-format to format shader file. " + << "Please make sure it's installed."; + }); + return original; + } + + char output[1024]; + std::stringstream outStream; + while (fgets(output, 1024, fp) != NULL) { + outStream << output; + } + + int status = pclose(fp); + if (WEXITSTATUS(status)) { + utils::slog.w << "[matdbg] clang-format failed with code=" << WEXITSTATUS(status) + << utils::io::endl; + } + return outStream.str(); +} +#else +std::string SourceFormatter::format(char const* source) { + std::call_once(mClangWarningFlag, []() { + utils::slog.w <<"[matdbg]: source formatting is not available on this platform" << + utils::io::endl; + }); + return ""; +} +#endif + +} // filament::matdbg diff --git a/libs/matdbg/src/SourceFormatter.h b/libs/matdbg/src/SourceFormatter.h new file mode 100644 index 000000000000..5793474d7168 --- /dev/null +++ b/libs/matdbg/src/SourceFormatter.h @@ -0,0 +1,37 @@ +/* + * 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 MATDBG_SOURCE_FORMATTER_H +#define MATDBG_SOURCE_FORMATTER_H + +#include + +namespace filament::matdbg { + +class SourceFormatter { +public: + SourceFormatter() = default; + ~SourceFormatter() = default; + std::string format(char const* source); + +private: + + std::once_flag mClangWarningFlag; +}; + +} // filament::matdbg + +#endif // MATDBG_SOURCE_FORMATTER_H diff --git a/libs/matdbg/web/app.js b/libs/matdbg/web/app.js index 4a7c7df4e444..325d47487fd6 100644 --- a/libs/matdbg/web/app.js +++ b/libs/matdbg/web/app.js @@ -452,6 +452,7 @@ class AdvancedOptions extends LitElement { static get properties() { return { currentBackend: {type: String, attribute: 'current-backend'}, + hideInactiveVariants: {type: Boolean, attribute: 'hide-inactive-variants'}, availableBackends: {type: Array, state: true}, }; } @@ -470,6 +471,9 @@ class AdvancedOptions extends LitElement { justify-content: center; align-items: flex-start; } + .borderless { + border: 1px solid rgba(0,0,0,0); + } label { display: flex; flex-direction: row; @@ -486,6 +490,35 @@ class AdvancedOptions extends LitElement { .option-heading { margin-bottom: 5px; } + + /* Below is a custom checkbox */ + input[type="checkbox"] { + -webkit-appearance: none; + appearance: none; + background-color: var(--form-background); + margin-right: 5px; + font: inherit; + width: 1.15em; + height: 1.15em; + border: 0.15em solid ${unsafeCSS(UNSELECTED_COLOR)}; + border-radius: 0.15em; + display: grid; + place-content: center; + } + input[type="checkbox"]::before { + content: ""; + width: 0.65em; + height: 0.65em; + clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%); + transform: scale(0); + transform-origin: bottom left; + box-shadow: inset 1em 1em var(--form-control-color); + /* Windows High Contrast Mode */ + background-color: ${unsafeCSS(FOREGROUND_COLOR)}; + } + input[type="checkbox"]:checked::before { + transform: scale(1); + } `; } @@ -536,6 +569,25 @@ class AdvancedOptions extends LitElement { `; } + _hideInactiveVariantsOption() { + const onChange = (ev) => { + this.dispatchEvent( + new CustomEvent( + 'option-hide-inactive-variants', + {detail: null, bubbles: true, composed: true})); + } + return html` +
+ +
+ + Hide Inactive Variants +
+
+ `; + } + constructor() { super(); this.availableBackends = []; @@ -544,6 +596,7 @@ class AdvancedOptions extends LitElement { render() { return html` + ${this._hideInactiveVariantsOption() ?? nothing} ${this._backendOption() ?? nothing} `; @@ -613,11 +666,11 @@ class MaterialSidePanel extends LitElement { currentShaderIndex: {type: Number, attribute: 'current-shader-index'}, currentBackend: {type: String, attribute: 'current-backend'}, currentLanguage: {type: String, attribute: 'current-language'}, + hideInactiveVariants: {type: Boolean, attribute: 'hide-inactive-variants'}, database: {type: Object, state: true}, materials: {type: Array, state: true}, activeShaders: {type: Object, state: true}, - variants: {type: Array, state: true}, } } @@ -662,8 +715,8 @@ class MaterialSidePanel extends LitElement { let name = material.name || kUntitledPlaceholder; if (name in duplicatedLabels) { const index = duplicatedLabels[name]; - name = `${name} (${index})`; duplicatedLabels[name] = index + 1; + name = `${name} (${index})`; } return { matid: matid, @@ -747,6 +800,7 @@ class MaterialSidePanel extends LitElement { if (b.active && !a.active) return 1; return 0; }) + .filter((variant) => (!this.hideInactiveVariants || variant.shader.active)) .map((variant) => { let divClass = 'material_variant_language'; const shaderIndex = +variant.shader.index; @@ -778,7 +832,13 @@ class MaterialSidePanel extends LitElement { render() { const sections = (title, domain) => { - const mats = this.materials.filter((m) => m.domain == domain).map((mat) => { + const mats = this.materials + .filter((m) => m.domain == domain) + .filter((mat) => { + const material = this.database[mat.matid]; + return !this.hideInactiveVariants || material.active; + }) + .map((mat) => { const material = this.database[mat.matid]; const onClick = this._handleMaterialClick.bind(this, mat.matid); let divClass = 'material_variant_language'; @@ -806,15 +866,13 @@ class MaterialSidePanel extends LitElement { } return null; }; - let advancedOptions = null; - // Currently we only have one advanced option and it's only for when we're in matinfo - if (_isMatInfoMode(this.database)) { - advancedOptions = - (() => html` - - `)(); - } + const advancedOptions = + (() => html` + + `)(); return html` @@ -874,6 +932,12 @@ class MatdbgViewer extends LitElement { let materials = await fetchMaterials(); this.database = materials; + + // This is the user preferences stored in localStorage + let hideInactiveVariantsVal = localStorage.getItem('option-hide-inactive-variants'); + if (hideInactiveVariantsVal != null) { + this.hideInactiveVariants = hideInactiveVariantsVal == 'true'; + } } _getShader() { @@ -907,6 +971,7 @@ class MatdbgViewer extends LitElement { this.currentMaterial = null; this.currentLanguage = null; this.currentBackend = null; + this.hideInactiveVariants = false; this.init(); this.addEventListener('select-material', @@ -957,6 +1022,12 @@ class MatdbgViewer extends LitElement { } ); + this.addEventListener('option-hide-inactive-variants', + (ev) => { + this.hideInactiveVariants = !this.hideInactiveVariants; + localStorage.setItem('option-hide-inactive-variants', "" + this.hideInactiveVariants); + } + ); addEventListener('resize', this._onResize.bind(this)); } @@ -972,6 +1043,8 @@ class MatdbgViewer extends LitElement { currentBackend: {type: String, state: true}, codeViewerExpectedWidth: {type: Number, state: true}, codeViewerExpectedHeight: {type: Number, state: true}, + + hideInactiveVariants: {type: Boolean, state: true}, } } @@ -1085,7 +1158,8 @@ class MatdbgViewer extends LitElement { current-language="${this.currentLanguage}" current-shader-index="${this.currentShaderIndex}" current-material="${this.currentMaterial}" - current-backend="${this.currentBackend}" > + current-backend="${this.currentBackend}" + ?hide-inactive-variants="${this.hideInactiveVariants}" > ("Texture$Builder") return &builder->sampler(target); }) .BUILDER_FUNCTION("format", TexBuilder, (TexBuilder* builder, Texture::InternalFormat fmt), { return &builder->format(fmt); }) + .BUILDER_FUNCTION("external", TexBuilder, (TexBuilder* builder), { + return &builder->external(); }) // This takes a bitfield that can be composed by or'ing constants. // - JS clients should use the value member, as in: "Texture$Usage.SAMPLEABLE.value". diff --git a/web/filament-js/package.json b/web/filament-js/package.json index e78ccf5f932d..3a8340976cb8 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.56.5", + "version": "1.56.6", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js",