Merge branch 'google:main' into main

This commit is contained in:
Serge Metral
2025-01-13 12:07:40 -08:00
committed by GitHub
64 changed files with 991 additions and 196 deletions
+9 -1
View File
@@ -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)
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.56.4'
implementation 'com.google.android.filament:filament-android:1.56.5'
}
```
@@ -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.4'
pod 'Filament', '~> 1.56.5'
```
## Documentation
+4
View File
@@ -7,6 +7,10 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.56.6
fix crash: the 'target_node' of Animation Channel may be nullpointer.
## v1.56.5
+10
View File
@@ -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()
]
+11
View File
@@ -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
# $<$<STREQUAL:${FILAMENT_ENABLE_FGVIEWER},ON>:fgviewer>
$<$<STREQUAL:${FILAMENT_ENABLE_MATDBG},ON>:matdbg>
$<$<STREQUAL:${FILAMENT_ENABLE_MATDBG},ON>:filamat>
$<$<STREQUAL:${FILAMENT_SUPPORTS_VULKAN},ON>:bluevk>
@@ -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) {
@@ -800,6 +800,19 @@ public class Texture {
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 <code>Texture</code> instance.
* @param engine The {@link Engine} to associate this <code>Texture</code> with.
@@ -1261,6 +1274,7 @@ public class Texture {
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);
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.56.4
VERSION_NAME=1.56.5
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+38 -1
View File
@@ -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
+7
View File
@@ -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)
@@ -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)
+3 -3
View File
@@ -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();
+1 -1
View File
@@ -250,7 +250,7 @@ public:
// 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;
@@ -155,6 +155,7 @@ struct MetalContext {
RenderPassFlags currentRenderPassFlags;
MetalRenderTarget* currentRenderTarget = nullptr;
bool validPipelineBound = false;
// State trackers.
PipelineStateTracker pipelineState;
+16 -11
View File
@@ -482,7 +482,9 @@ void MetalDriver::createTextureViewSwizzleR(Handle<HwTexture> th, Handle<HwTextu
mContext->textures.insert(construct_handle<MetalTexture>(th, *mContext, src, r, g, b, a));
}
void MetalDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::TextureFormat format,
void MetalDriver::createTextureExternalImageR(Handle<HwTexture> th,
backend::SamplerType target,
backend::TextureFormat format,
uint32_t width, uint32_t height, backend::TextureUsage usage, void* image) {
mContext->textures.insert(construct_handle<MetalTexture>(
th, *mContext, format, width, height, usage, (CVPixelBufferRef)image));
@@ -907,7 +909,7 @@ void MetalDriver::terminate() {
}
ShaderModel MetalDriver::getShaderModel() const noexcept {
#if defined(IOS)
#if defined(FILAMENT_IOS)
return ShaderModel::MOBILE;
#else
return ShaderModel::DESKTOP;
@@ -981,7 +983,7 @@ bool MetalDriver::isTextureFormatMipmappable(TextureFormat format) {
case MTLPixelFormatRGBA16Float:
return true;
#if !defined(IOS)
#if !defined(FILAMENT_IOS)
// Mipmappable only on desktop:
case MTLPixelFormatR32Float:
case MTLPixelFormatRG32Float:
@@ -989,7 +991,7 @@ bool MetalDriver::isTextureFormatMipmappable(TextureFormat format) {
return true;
#endif
#if defined(IOS)
#if defined(FILAMENT_IOS)
// Mipmappable only on iOS:
case MTLPixelFormatRGB9E5Float:
return true;
@@ -1191,10 +1193,6 @@ void MetalDriver::setupExternalImage(void* image) {
CVPixelBufferRetain(pixelBuffer);
}
void MetalDriver::setExternalImage(Handle<HwTexture> th, void* image) {}
void MetalDriver::setExternalImagePlane(Handle<HwTexture> th, void* image, uint32_t plane) {}
void MetalDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh) {
}
@@ -1366,7 +1364,7 @@ void MetalDriver::startCapture(int) {
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 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
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 @@ void MetalDriver::readPixels(Handle<HwRenderTarget> src, uint32_t x, uint32_t y,
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 <MTLBlitCommandEncoder> blitEncoder = [getPendingCommandBuffer(mContext) blitCommandEncoder];
[blitEncoder synchronizeResource:readPixelsTexture];
@@ -1657,6 +1655,7 @@ void MetalDriver::bindPipeline(PipelineState const& ps) {
// 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 @@ void MetalDriver::bindPipeline(PipelineState const& ps) {
clamp:0.0];
mContext->currentPolygonOffset = ps.polygonOffset;
}
mContext->validPipelineBound = true;
}
void MetalDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
@@ -1886,6 +1887,10 @@ void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t inst
<< "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();
+2 -2
View File
@@ -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
+4 -4
View File
@@ -157,7 +157,7 @@ id<MTLTexture> MetalSwapChain::acquireDrawable() {
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 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
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 @@ MetalTexture::MetalTexture(MetalContext& context, SamplerType target, uint8_t le
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 @@ void MetalTexture::loadWithBlit(uint32_t level, uint32_t slice, MTLRegion region
descriptor.height = region.size.height;
descriptor.depth = region.size.depth;
#if defined(IOS)
#if defined(FILAMENT_IOS)
descriptor.storageMode = MTLStorageModeShared;
#else
descriptor.storageMode = MTLStorageModeManaged;
+1 -1
View File
@@ -37,7 +37,7 @@ Driver* MetalPlatform::createDriver(void* /*sharedContext*/, const Platform::Dri
id<MTLDevice> MetalPlatform::createDevice() noexcept {
id<MTLDevice> result;
#if !defined(IOS)
#if !defined(FILAMENT_IOS)
const bool forceIntegrated =
NSProcessInfo.processInfo.environment[@"FILAMENT_FORCE_INTEGRATED_GPU"] != nil;
if (forceIntegrated) {
+1 -1
View File
@@ -150,7 +150,7 @@ id<MTLSamplerState> SamplerStateCreator::operator()(id<MTLDevice> device,
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.
+1 -7
View File
@@ -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<HwTimerQuery> tqh, uint64
return TimerQueryResult::ERROR;
}
void NoopDriver::setExternalImage(Handle<HwTexture> th, void* image) {
}
void NoopDriver::setExternalImagePlane(Handle<HwTexture> th, void* image, uint32_t plane) {
}
void NoopDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh) {
}
@@ -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<T, Sampler> ||
std::is_same_v<T, SamplerWithAnisotropyWorkaround>) {
if constexpr (std::is_same_v<T, SamplerWithAnisotropyWorkaround>) {
@@ -285,7 +286,7 @@ void GLDescriptorSet::bind(
} else if constexpr (std::is_same_v<T, Sampler>) {
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<T, SamplerWithAnisotropyWorkaround>) {
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));
@@ -111,7 +111,9 @@ private:
// 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<GLTextureRef> ref; // 4
@@ -126,7 +128,9 @@ private:
};
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<GLTextureRef> ref; // 4
@@ -143,7 +147,9 @@ private:
// 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
+4 -3
View File
@@ -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<TextureSwizzle, 4> swizzle{
TextureSwizzle::CHANNEL_0,
@@ -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);
+6 -4
View File
@@ -131,7 +131,7 @@ public:
}
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 @@ public:
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 {
+43 -29
View File
@@ -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<HwTexture> th, Handle<HwText
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::createTextureExternalImageR(Handle<HwTexture> 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<HwTexture> 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<GLTexture>(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<HwTexture> 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<HwTexture> th, intptr_t id,
@@ -1082,6 +1114,7 @@ void OpenGLDriver::importTextureR(Handle<HwTexture> 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<HwTexture> th, void* image) {
DEBUG_MARKER()
GLTexture* t = handle_cast<GLTexture*>(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<HwTexture> th, void* image, uint32_t plane) {
DEBUG_MARKER()
}
void OpenGLDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> 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);
+4 -4
View File
@@ -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
@@ -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<GLeglImageOES>(externalImage));
#endif
}
return true;
}
+3 -9
View File
@@ -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<HwTexture> th, Handle<HwText
texture.inc();
}
void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::TextureFormat format,
void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th,
backend::SamplerType target, backend::TextureFormat format,
uint32_t width, uint32_t height, backend::TextureUsage usage, void* externalImage) {
FVK_SYSTRACE_SCOPE();
@@ -1189,13 +1190,6 @@ TimerQueryResult VulkanDriver::getTimerQueryValue(Handle<HwTimerQuery> tqh, uint
return TimerQueryResult::AVAILABLE;
}
void VulkanDriver::setExternalImage(Handle<HwTexture> th, void* image) {
}
void VulkanDriver::setExternalImagePlane(Handle<HwTexture> th, void* image, uint32_t plane) {
}
void VulkanDriver::setExternalStream(Handle<HwTexture> th, Handle<HwStream> sh) {
}
@@ -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(<QuartzCore/CAMetalLayer.h>)
#if METAL_AVAILABLE
@@ -56,7 +56,7 @@ VulkanPlatform::ExtensionSet VulkanPlatform::getSwapchainInstanceExtensions() {
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 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin
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)
+2 -2
View File
@@ -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 <imageio/ImageEncoder.h>
#include <image/ColorTransform.h>
@@ -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<uint8_t>(c->width, c->height, c->width * 4,
(uint8_t*) buffer);
+2 -2
View File
@@ -24,7 +24,7 @@
#include <fstream>
#ifndef IOS
#ifndef FILAMENT_IOS
#include <imageio/ImageEncoder.h>
#include <image/ColorTransform.h>
#endif
@@ -67,7 +67,7 @@ struct ScreenshotParams {
uint32_t pixelHashResult;
};
#ifdef IOS
#ifdef FILAMENT_IOS
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt, ScreenshotParams* params) {}
#else
static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> rt, ScreenshotParams* params) {
+2 -2
View File
@@ -31,7 +31,7 @@
#include <stddef.h>
#include <stdint.h>
#ifndef IOS
#ifndef FILAMENT_IOS
#include <imageio/ImageEncoder.h>
#include <image/ColorTransform.h>
@@ -112,7 +112,7 @@ static void dumpScreenshot(DriverApi& dapi, Handle<HwRenderTarget> 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<uint8_t>(w, h, w * 4, (uint8_t*) buffer);
std::ofstream pngstrm("feedback.png", std::ios::binary | std::ios::trunc);
+2 -2
View File
@@ -28,7 +28,7 @@
using namespace filament;
using namespace filament::backend;
#ifndef IOS
#ifndef FILAMENT_IOS
#include <imageio/ImageEncoder.h>
#include <image/ColorTransform.h>
@@ -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) {
@@ -227,7 +227,8 @@ TEST_F(BackendTest, RenderExternalImage) {
api.setupExternalImage(pixBuffer);
backend::Handle<HwTexture> 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);
+14 -2
View File
@@ -261,6 +261,18 @@ public:
*/
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 @@ public:
/**
* 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 @@ public:
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.
+3 -6
View File
@@ -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
+12
View File
@@ -156,6 +156,13 @@ public:
std::unique_lock<utils::Mutex> 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 @@ private:
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<backend::Handle<backend::HwProgram>, VARIANT_COUNT> mCachedPrograms;
DescriptorSetLayout mPerViewDescriptorSetLayout;
+29 -21
View File
@@ -81,6 +81,7 @@ struct Texture::BuilderDetails {
Usage mUsage = Usage::NONE;
bool mHasBlitSrc = false;
bool mTextureIsSwizzled = false;
bool mExternal = false;
std::array<Swizzle, 4> 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<backend::HwTexture> FTexture::createPlaceholderTexture(
}
backend::Handle<backend::HwTexture> 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<backend::HwTexture> 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;
+2 -1
View File
@@ -173,7 +173,8 @@ private:
// 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
};
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.56.4"
spec.version = "1.56.5"
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.4/filament-v1.56.4-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.56.5/filament-v1.56.5-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
+1 -1
View File
@@ -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)
+71
View File
@@ -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)
@@ -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 <utils/CString.h>
#include <utils/Mutex.h>
#include <unordered_map>
#include <vector>
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<FrameGraphPassInfo> 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<FrameGraphInfoKey, FrameGraphInfo> 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
@@ -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 <utils/CString.h>
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
+69
View File
@@ -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 <fgviewer/DebugServer.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Log.h>
#include <CivetServer.h>
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
+49
View File
@@ -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 <CivetServer.h>
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
+131
View File
@@ -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 <fgviewer/DebugServer.h>
#include "ApiHandler.h"
#include <CivetServer.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Hash.h>
#include <utils/Log.h>
#include <string>
#include <string_view>
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<uint8_t const*>(
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<utils::Mutex> lock(mViewsMutex);
const FrameGraphInfoKey key = getKeybyString(name, 0);
mViews.insert({key, info});
}
void DebugServer::removeView(const utils::CString& name) {
std::unique_lock<utils::Mutex> lock(mViewsMutex);
const FrameGraphInfoKey key = getKeybyString(name, 0);
mViews.erase(key);
}
void DebugServer::updateView(const utils::CString& name, FrameGraphInfo info) {
std::unique_lock<utils::Mutex> lock(mViewsMutex);
const FrameGraphInfoKey key = getKeybyString(name, 0);
mViews[key] = info;
}
} // namespace filament::fgviewer
+1 -1
View File
@@ -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;
+3
View File
@@ -408,6 +408,9 @@ void AnimatorImpl::addChannels(const FixedCapacityVector<Entity>& 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) {
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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
+2
View File
@@ -31,6 +31,8 @@ set(SRCS
src/ShaderReplacer.cpp
src/ShaderExtractor.cpp
src/ShaderInfo.cpp
src/SourceFormatter.cpp
src/SourceFormatter.h
src/TextWriter.cpp
)
+14 -17
View File
@@ -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;
}
+4
View File
@@ -17,6 +17,8 @@
#ifndef MATDBG_APIHANDLER_H
#define MATDBG_APIHANDLER_H
#include "SourceFormatter.h"
#include <mutex>
#include <condition_variable>
#include <atomic>
@@ -63,6 +65,8 @@ private:
// 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<uint64_t> mCurrentStatus = 0;
SourceFormatter mFormatter;
};
} // filament::matdbg
+6 -2
View File
@@ -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.
+1 -1
View File
@@ -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<uint8_t>(ptr, ptr + src.size()));
}
+80
View File
@@ -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 <utils/Log.h>
#include <cstdio>
#include <sstream>
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
+37
View File
@@ -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 <mutex>
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
+87 -13
View File
@@ -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`
<div class="option borderless">
<!--<div class="option-heading">Current Backend</div>-->
<form action="" id="hide-inactive-variants-form">
<input type="checkbox" name="InactiveVariants"
?checked=${this.hideInactiveVariants} @change=${onChange}/>
Hide Inactive Variants
</form>
</div>
`;
}
constructor() {
super();
this.availableBackends = [];
@@ -544,6 +596,7 @@ class AdvancedOptions extends LitElement {
render() {
return html`
<menu-section title="Advanced Options">
${this._hideInactiveVariantsOption() ?? nothing}
${this._backendOption() ?? nothing}
</menu-section>
`;
@@ -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`
<advanced-options id="advanced-options"
current-backend=${this.currentBackend}></advanced-options>
`)();
}
const advancedOptions =
(() => html`
<advanced-options id="advanced-options"
current-backend=${this.currentBackend}
?hide-inactive-variants=${this.hideInactiveVariants}
></advanced-options>
`)();
return html`
<style>${this.dynamicStyle()}</style>
@@ -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}" >
</material-sidepanel>
<code-viewer id="code-viewer"
?active=${shader && shader.active}
+1 -1
View File
@@ -158,7 +158,7 @@ static void printUsage(char* name) {
" Specify the backend API: "
// Matches logic in filament/backend/src/PlatformFactory.cpp for Backend::DEFAULT
#if defined(IOS) || defined(__APPLE__)
#if defined(FILAMENT_IOS) || defined(__APPLE__)
"opengl, vulkan, or metal (default)"
#elif defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
"opengl, vulkan (default), or metal"
+1 -1
View File
@@ -13,7 +13,7 @@ set(CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE STRING "Build architecture for iOS
# Necessary for correct install location
set(DIST_ARCH ${IOS_ARCH})
add_definitions(-DIOS)
add_definitions(-DFILAMENT_IOS)
set(IOS_MIN_TARGET "11.0")
+2
View File
@@ -1489,6 +1489,8 @@ class_<TexBuilder>("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".
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.56.4",
"version": "1.56.5",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",