Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4a77d03d02 | ||
|
|
5cb96e5732 | ||
|
|
927aa57a4e | ||
|
|
36e775902d | ||
|
|
53e28f3b33 |
@@ -65,13 +65,9 @@ jobs:
|
||||
|
||||
build-mac:
|
||||
name: build-mac
|
||||
runs-on: ${{ matrix.os }}
|
||||
runs-on: macos-14-xlarge
|
||||
if: github.event_name == 'release' || github.event.inputs.platform == 'desktop'
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-14-xlarge, ubuntu-22.04-32core]
|
||||
|
||||
steps:
|
||||
- name: Decide Git ref
|
||||
id: git_ref
|
||||
|
||||
@@ -7,4 +7,3 @@ for next branch cut* header.
|
||||
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
|
||||
|
||||
## Release notes for next branch cut
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.60.0'
|
||||
implementation 'com.google.android.filament:filament-android:1.59.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.60.0'
|
||||
pod 'Filament', '~> 1.59.5'
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
+1
-3
@@ -10,9 +10,7 @@ Instead, if you are authoring a PR for the main branch, add your release note to
|
||||
## v1.60.0
|
||||
|
||||
- materials: remove dependence on per-view descset layout from filamat. [⚠️ **New Material Version**]
|
||||
- matc non-functional change: Update GLSL postprocessor to
|
||||
isolate calls to SPVRemap from calls to SPIRV-Cross.
|
||||
|
||||
- `ColorGrading::Builder::toneMapper` now takes a `shared_ptr<ToneMapper>`
|
||||
|
||||
## v1.59.5
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
|
||||
#include <filament/ColorGrading.h>
|
||||
#include <filament/ToneMapper.h>
|
||||
#include <memory>
|
||||
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
@@ -67,8 +68,9 @@ extern "C" JNIEXPORT void JNICALL
|
||||
Java_com_google_android_filament_ColorGrading_nBuilderToneMapper(JNIEnv*, jclass,
|
||||
jlong nativeBuilder, jlong toneMapper_) {
|
||||
ColorGrading::Builder* builder = (ColorGrading::Builder*) nativeBuilder;
|
||||
const ToneMapper* toneMapper = (const ToneMapper*) toneMapper_;
|
||||
builder->toneMapper(toneMapper);
|
||||
ToneMapper* toneMapper = reinterpret_cast<ToneMapper*>(toneMapper_);
|
||||
std::shared_ptr<ToneMapper> toneMapperCopy(toneMapper->clone());
|
||||
builder->toneMapper(toneMapperCopy);
|
||||
}
|
||||
|
||||
#pragma clang diagnostic push
|
||||
|
||||
+3
-3
@@ -214,9 +214,9 @@ public class ColorGrading {
|
||||
*
|
||||
* The default tone mapping operator is {@link ToneMapper.ACESLegacy}.
|
||||
*
|
||||
* The specified tone mapper must have a lifecycle that exceeds the lifetime of
|
||||
* this builder. Since the build(Engine&) method is synchronous, it is safe to
|
||||
* delete the tone mapper object after that finishes executing.
|
||||
* The copy of the specified tone mapper is set to this builder. It is safe to delete
|
||||
* the original tone mapper object while the copied one is held by the built ColorGrading
|
||||
* object.
|
||||
*
|
||||
* @param toneMapper The tone mapping operator to apply to the HDR color buffer
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.60.0
|
||||
VERSION_NAME=1.59.5
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
@@ -151,7 +151,7 @@ function print_fgviewer_help {
|
||||
}
|
||||
|
||||
# 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 ".")}
|
||||
FILAMENT_NDK_VERSION=${FILAMENT_NDK_VERSION:-$(cat `dirname $0`/build/common/versions | grep GITHUB_NDK_VERSION | cut -f 1 -d ".")}
|
||||
|
||||
# Requirements
|
||||
CMAKE_MAJOR=3
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
- [Metal](./notes/metal_debugging.md)
|
||||
- [Vulkan](./notes/vulkan_debugging.md)
|
||||
- [SPIR-V](./notes/spirv_debugging.md)
|
||||
- [Running with ASAN and UBSAN](./notes/asan_ubsan.md)
|
||||
- [Libraries](./notes/libs.md)
|
||||
- [bluegl](./dup/bluegl.md)
|
||||
- [bluevk](./dup/bluevk.md)
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
# Running with ASAN/UBSAN
|
||||
|
||||
## Enabling
|
||||
|
||||
When building though build.sh, pass the `-b` flag. This sets the cmake variable
|
||||
`FILAMENT_ENABLE_ASAN_UBSAN=ON` which eventually passes `"-fsanitize=address -fsanitize=undefined"`
|
||||
to all compile and link operations.
|
||||
|
||||
If building through CMake directly, or an IDE like CLion that doesn't use build.sh, instead pass
|
||||
`-DFILAMENT_ENABLE_ASAN_UBSAN=ON` to cmake in order to get the same result.
|
||||
|
||||
## Getting memory leak detection on Mac
|
||||
|
||||
Memory leak detection isn't enabled by default on MacOS. There are two issues to address, first is
|
||||
using a version of clang that supports memory leak detection and second is enabling it at runtime.
|
||||
|
||||
The version of clang distributed by Apple (with a version like "Apple clang version 16.0.0") doesn't
|
||||
currently support leak detection at all. Instead you will need to get or build a different LLVM,
|
||||
such as the one distributed through homebrew and get CMake to use that instead.
|
||||
|
||||
Then during runtime you'll need to have the environment variable `ASAN_OPTIONS` include the option
|
||||
`detect_leaks=1`. Multiple `ASAN_OPTIONS` values are concatenated with `:`.
|
||||
|
||||
## Getting memory leak output in CLion
|
||||
|
||||
### Setting variables
|
||||
|
||||
Under `Settings | Build, Execution, Deployment | Dynamic Analysis Tools | Sanitizers` there is an
|
||||
ASAN Settings field that overrides whatever other `ASAN_OPTIONS` you might set elsewhere, so you
|
||||
must use that instead of setting it through your Run/Debug Configuration.
|
||||
|
||||
To pass `-DFILAMENT_ENABLE_ASAN_UBSAN=ON` to CMake you'll want to create a new CMake Profile and
|
||||
pass it as a CMake argument.
|
||||
|
||||
### Avoiding losing output
|
||||
|
||||
CMake will consume ASAN output and display it through a separate "Sanitizers" tab. Unfortunately
|
||||
certain leak detection errors that interrupt the executable seem to not show up in this tab, but are
|
||||
still removed from the user-visible console output. If this is happening and you need to see the
|
||||
unfiltered console output you'll need to go to `Settings | Build, Execution, Deployment | Dynamic
|
||||
Analysis Tools | Sanitizers` and uncheck "Use visual representation for Sanitizer's output".
|
||||
@@ -5,18 +5,6 @@ set(TARGET backend)
|
||||
set(PUBLIC_HDR_DIR include)
|
||||
set(GENERATION_ROOT ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# ==================================================================================================
|
||||
# Compilation options
|
||||
# ==================================================================================================
|
||||
#
|
||||
set(BACKEND_SANITIZATION "" CACHE STRING "Sanitization option")
|
||||
set_property(CACHE BACKEND_SANITIZATION PROPERTY STRINGS ";ASAN")
|
||||
|
||||
set(BACKEND_SANITIZERS)
|
||||
if (BACKEND_SANITIZATION STREQUAL "ASAN")
|
||||
set(BACKEND_SANITIZERS -fsanitize=address)
|
||||
endif()
|
||||
|
||||
# ==================================================================================================
|
||||
# Sources and headers
|
||||
# ==================================================================================================
|
||||
@@ -484,7 +472,6 @@ target_compile_options(${TARGET} PRIVATE
|
||||
${OSMESA_COMPILE_FLAGS}
|
||||
$<$<CONFIG:Release>:${OPTIMIZATION_FLAGS}>
|
||||
$<$<AND:$<PLATFORM_ID:Darwin>,$<CONFIG:Release>>:${DARWIN_OPTIMIZATION_FLAGS}>
|
||||
${BACKEND_SANITIZERS}
|
||||
)
|
||||
|
||||
if (FILAMENT_SUPPORTS_METAL)
|
||||
@@ -495,8 +482,6 @@ if (FILAMENT_SUPPORTS_WEBGPU)
|
||||
target_compile_definitions(${TARGET} PRIVATE $<$<BOOL:${FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING}>:FILAMENT_WEBGPU_IMMEDIATE_ERROR_HANDLING>)
|
||||
endif()
|
||||
|
||||
target_link_options(${TARGET} PRIVATE ${BACKEND_SANITIZERS})
|
||||
|
||||
target_link_libraries(${TARGET} PRIVATE
|
||||
${OSMESA_LINKER_FLAGS}
|
||||
$<$<AND:$<PLATFORM_ID:Linux>,$<CONFIG:Release>>:${LINUX_LINKER_OPTIMIZATION_FLAGS}>
|
||||
@@ -566,8 +551,6 @@ if (APPLE AND NOT IOS)
|
||||
test/test_RenderExternalImage.cpp)
|
||||
add_library(backend_test STATIC ${BACKEND_TEST_SRC})
|
||||
target_link_libraries(backend_test PUBLIC ${BACKEND_TEST_LIBS})
|
||||
target_compile_options(backend_test PRIVATE ${BACKEND_SANITIZERS})
|
||||
target_link_options(backend_test PRIVATE ${BACKEND_SANITIZERS})
|
||||
|
||||
set(BACKEND_TEST_DEPS
|
||||
OSDependent
|
||||
@@ -606,7 +589,6 @@ if (APPLE AND NOT IOS)
|
||||
# linker from removing "unused" symbols.
|
||||
target_link_libraries(backend_test_mac PRIVATE -force_load backend_test)
|
||||
set_target_properties(backend_test_mac PROPERTIES FOLDER Tests)
|
||||
target_link_options(backend_test_mac PRIVATE ${BACKEND_SANITIZERS})
|
||||
|
||||
# This is needed after XCode 15.3
|
||||
set_target_properties(backend_test_mac PROPERTIES BUILD_WITH_INSTALL_RPATH TRUE)
|
||||
@@ -616,8 +598,6 @@ endif()
|
||||
|
||||
if (LINUX)
|
||||
add_executable(backend_test_linux test/linux_runner.cpp ${BACKEND_TEST_SRC})
|
||||
target_compile_options(backend_test_linux PRIVATE ${BACKEND_SANITIZERS})
|
||||
target_link_options(backend_test_linux PRIVATE ${BACKEND_SANITIZERS})
|
||||
target_link_libraries(backend_test_linux PRIVATE ${BACKEND_TEST_LIBS})
|
||||
set_target_properties(backend_test_linux PROPERTIES FOLDER Tests)
|
||||
endif()
|
||||
|
||||
@@ -149,13 +149,6 @@ public:
|
||||
* - PlatformEGLAndroid
|
||||
*/
|
||||
bool assertNativeWindowIsValid = false;
|
||||
|
||||
/**
|
||||
* The action to take if a Drawable cannot be acquired. If true, the
|
||||
* frame is aborted instead of panic. This is only supported for:
|
||||
* - PlatformMetal
|
||||
*/
|
||||
bool metalDisablePanicOnDrawableFailure = false;
|
||||
};
|
||||
|
||||
Platform() noexcept;
|
||||
|
||||
@@ -45,9 +45,6 @@ PlatformMetal::~PlatformMetal() noexcept {
|
||||
}
|
||||
|
||||
Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::DriverConfig& driverConfig) noexcept {
|
||||
pImpl->mDrawableFailureBehavior = driverConfig.metalDisablePanicOnDrawableFailure
|
||||
? DrawableFailureBehavior::ABORT_FRAME
|
||||
: DrawableFailureBehavior::PANIC;
|
||||
return MetalDriverFactory::create(this, driverConfig);
|
||||
}
|
||||
|
||||
|
||||
@@ -514,7 +514,7 @@ WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat forma
|
||||
"the spec. See https://www.w3.org/TR/webgpu/#texture-creation or "
|
||||
"https://gpuweb.github.io/gpuweb/#multisample-state");
|
||||
// First, the texture aspect, starting with the defaults/basic configuration
|
||||
mUsage = fToWGPUTextureUsage(usage);
|
||||
mUsage = fToWGPUTextureUsage(usage, device.HasFeature(wgpu::FeatureName::TransientAttachments));
|
||||
mFormat = fToWGPUTextureFormat(format);
|
||||
wgpu::TextureDescriptor textureDescriptor{
|
||||
.label = getUserTextureLabel(target),
|
||||
@@ -566,7 +566,8 @@ WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount
|
||||
mTexView = makeTextureView(baseLevel, levelCount, target);
|
||||
}
|
||||
|
||||
wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(const TextureUsage& fUsage) {
|
||||
wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(const TextureUsage& fUsage,
|
||||
const bool supportsTransientAttachment) {
|
||||
wgpu::TextureUsage retUsage = wgpu::TextureUsage::None;
|
||||
|
||||
// Basing this mapping off of VulkanTexture.cpp's getUsage func and suggestions from Gemini
|
||||
@@ -592,6 +593,7 @@ wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(const TextureUsage& fUsage)
|
||||
// This is from Vulkan logic- if there are any issues try disabling this first, allows perf
|
||||
// benefit though
|
||||
const bool useTransientAttachment =
|
||||
supportsTransientAttachment &&
|
||||
// Usage consists of attachment flags only.
|
||||
none(fUsage & ~TextureUsage::ALL_ATTACHMENTS) &&
|
||||
// Usage contains at least one attachment flag.
|
||||
|
||||
@@ -194,7 +194,8 @@ private:
|
||||
wgpu::TextureFormat mFormat = wgpu::TextureFormat::Undefined;
|
||||
uint32_t mArrayLayerCount = 1;
|
||||
wgpu::TextureView mTexView = nullptr;
|
||||
wgpu::TextureUsage fToWGPUTextureUsage(const filament::backend::TextureUsage& fUsage);
|
||||
wgpu::TextureUsage fToWGPUTextureUsage(const filament::backend::TextureUsage& fUsage,
|
||||
const bool supportsTransientAttachment);
|
||||
};
|
||||
|
||||
struct WGPURenderPrimitive : public HwRenderPrimitive {
|
||||
|
||||
@@ -100,9 +100,11 @@ wgpu::Adapter WebGPUPlatform::requestAdapter(wgpu::Surface const& surface) {
|
||||
wgpu::Device WebGPUPlatform::requestDevice(wgpu::Adapter const& adapter) {
|
||||
// TODO consider passing limits
|
||||
constexpr std::array optionalFeatures = { wgpu::FeatureName::DepthClipControl,
|
||||
wgpu::FeatureName::Depth32FloatStencil8, wgpu::FeatureName::CoreFeaturesAndLimits };
|
||||
wgpu::FeatureName::Depth32FloatStencil8, wgpu::FeatureName::CoreFeaturesAndLimits,
|
||||
wgpu::FeatureName::TransientAttachments };
|
||||
|
||||
constexpr std::array requiredFeatures = { wgpu::FeatureName::TransientAttachments };
|
||||
// Currently no required features, but logic to check them is available
|
||||
constexpr std::array<wgpu::FeatureName, 0> requiredFeatures = {};
|
||||
|
||||
wgpu::SupportedFeatures supportedFeatures;
|
||||
adapter.GetFeatures(&supportedFeatures);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "ShaderGenerator.h"
|
||||
|
||||
#include <GlslangToSpv.h>
|
||||
#include <SPVRemapper.h>
|
||||
|
||||
#include <spirv_glsl.hpp>
|
||||
#include <spirv_msl.hpp>
|
||||
|
||||
@@ -21,13 +21,17 @@
|
||||
|
||||
#include <filament/FilamentAPI.h>
|
||||
#include <filament/ToneMapper.h>
|
||||
#include <filament/ColorSpace.h>
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <math/mathfwd.h>
|
||||
#include <math/vec3.h>
|
||||
#include <math/vec4.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <memory>
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -201,15 +205,14 @@ public:
|
||||
*
|
||||
* The default tone mapping operator is ACESLegacyToneMapper.
|
||||
*
|
||||
* The specified tone mapper must have a lifecycle that exceeds the lifetime of
|
||||
* this builder. Since the build(Engine&) method is synchronous, it is safe to
|
||||
* delete the tone mapper object after that finishes executing.
|
||||
* The ownership of the specified tone mapper is shared with the builder and built
|
||||
* ColorGrading object.
|
||||
*
|
||||
* @param toneMapper The tone mapping operator to apply to the HDR color buffer
|
||||
*
|
||||
* @return This Builder, for chaining calls
|
||||
*/
|
||||
Builder& toneMapper(ToneMapper const* UTILS_NULLABLE toneMapper) noexcept;
|
||||
Builder& toneMapper(std::shared_ptr<ToneMapper> toneMapper) noexcept;
|
||||
|
||||
/**
|
||||
* Selects the tone mapping operator to apply to the HDR color buffer as the last
|
||||
@@ -487,7 +490,118 @@ public:
|
||||
friend class FColorGrading;
|
||||
};
|
||||
|
||||
protected:
|
||||
/** Returns the quality level used to create this ColorGrading object. */
|
||||
QualityLevel getQuality() const noexcept;
|
||||
|
||||
/** Returns the LUT format used to create this ColorGrading object. */
|
||||
LutFormat getLutFormat() const noexcept;
|
||||
|
||||
/** Returns the LUT dimensions used to create this ColorGrading object. */
|
||||
uint8_t getLutDimensions() const noexcept;
|
||||
|
||||
/** Returns the tone mapper used to create this ColorGrading object. */
|
||||
const ToneMapper& getToneMapper() const noexcept;
|
||||
|
||||
/** Returns whether luminance scaling was enabled during creation. */
|
||||
bool isLuminanceScalingEnabled() const noexcept;
|
||||
|
||||
/** Returns whether gamut mapping was enabled during creation. */
|
||||
bool isGamutMappingEnabled() const noexcept;
|
||||
|
||||
/** Returns the exposure value used to create this ColorGrading object. */
|
||||
float getExposure() const noexcept;
|
||||
|
||||
/** Returns the night adaptation value used to create this ColorGrading object. */
|
||||
float getNightAdaptation() const noexcept;
|
||||
|
||||
/** Returns the white balance temperature used to create this ColorGrading object. */
|
||||
float getWhiteBalanceTemperature() const noexcept;
|
||||
|
||||
/** Returns the white balance tint used to create this ColorGrading object. */
|
||||
float getWhiteBalanceTint() const noexcept;
|
||||
|
||||
/** Returns the channel mixer output for the red channel. */
|
||||
math::float3 getChannelMixerOutRed() const noexcept;
|
||||
|
||||
/** Returns the channel mixer output for the green channel. */
|
||||
math::float3 getChannelMixerOutGreen() const noexcept;
|
||||
|
||||
/** Returns the channel mixer output for the blue channel. */
|
||||
math::float3 getChannelMixerOutBlue() const noexcept;
|
||||
|
||||
/** Returns the shadows adjustment used to create this ColorGrading object. */
|
||||
math::float3 getShadows() const noexcept;
|
||||
|
||||
/** Returns the midtones adjustment used to create this ColorGrading object. */
|
||||
math::float3 getMidtones() const noexcept;
|
||||
|
||||
/** Returns the highlights adjustment used to create this ColorGrading object. */
|
||||
math::float3 getHighlights() const noexcept;
|
||||
|
||||
/** Returns the shadow/midtones/highlights ranges used to create this ColorGrading object. */
|
||||
math::float4 getShadowMidtonesHighlightsRanges() const noexcept;
|
||||
|
||||
/** Returns the slope adjustment used to create this ColorGrading object. */
|
||||
math::float3 getSlope() const noexcept;
|
||||
|
||||
/** Returns the offset adjustment used to create this ColorGrading object. */
|
||||
math::float3 getOffset() const noexcept;
|
||||
|
||||
/** Returns the power adjustment used to create this ColorGrading object. */
|
||||
math::float3 getPower() const noexcept;
|
||||
|
||||
/** Returns the contrast value used to create this ColorGrading object. */
|
||||
float getContrast() const noexcept;
|
||||
|
||||
/** Returns the vibrance value used to create this ColorGrading object. */
|
||||
float getVibrance() const noexcept;
|
||||
|
||||
/** Returns the saturation value used to create this ColorGrading object. */
|
||||
float getSaturation() const noexcept;
|
||||
|
||||
/** Returns the shadow gamma curve adjustment used to create this ColorGrading object. */
|
||||
math::float3 getCurvesShadowGamma() const noexcept;
|
||||
|
||||
/** Returns the mid-point curve adjustment used to create this ColorGrading object. */
|
||||
math::float3 getCurvesMidPoint() const noexcept;
|
||||
|
||||
/** Returns the highlight scale curve adjustment used to create this ColorGrading object. */
|
||||
math::float3 getCurvesHighlightScale() const noexcept;
|
||||
|
||||
/** Returns the output color space used to create this ColorGrading object. */
|
||||
const color::ColorSpace& getOutputColorSpace() const noexcept;
|
||||
|
||||
protected :
|
||||
struct Settings {
|
||||
LutFormat lutFormat = LutFormat::INTEGER;
|
||||
uint8_t lutDimensions = 32;
|
||||
std::shared_ptr<ToneMapper> toneMapper = std::make_shared<ACESLegacyToneMapper>();
|
||||
bool luminanceScaling = false;
|
||||
bool gummapMapping = false;
|
||||
float exposure = 0.0f;
|
||||
float nightAdaptation = 0.0f;
|
||||
float whiteBalanceTemperature = 0.0f;
|
||||
float whiteBalanceTint = 0.0f;
|
||||
math::float3 channelMixerOutRed{1.0f, 0.0f, 0.0f};
|
||||
math::float3 channelMixerOutGreen{0.0f, 1.0f, 0.0f};
|
||||
math::float3 channelMixerOutBlue{0.0f, 0.0f, 1.0f};
|
||||
math::float3 shadows{1.0f, 1.0f, 1.0f};
|
||||
math::float3 midtones{1.0f, 1.0f, 1.0f};
|
||||
math::float3 highlights{1.0f, 1.0f, 1.0f};
|
||||
math::float4 ShadowMidtonesHighlightsRanges{0.0f, 0.333f, 0.55f, 1.0f};
|
||||
math::float3 slope{1.0f};
|
||||
math::float3 offset{0.0f};
|
||||
math::float3 power{1.0f};
|
||||
float contrast = 1.0f;
|
||||
float vibrance = 1.0f;
|
||||
float saturation = 1.0f;
|
||||
math::float3 curvesShadowGamma{1.0f, 1.0f, 1.0f};
|
||||
math::float3 curvesMidPoint{1.0f, 1.0f, 1.0f};
|
||||
math::float3 curvesHighlightScale{1.0f, 1.0f, 1.0f};
|
||||
color::ColorSpace colorSpace = color::Rec709 - color::sRGB - color::D65;
|
||||
};
|
||||
Settings mSettings;
|
||||
|
||||
// prevent heap allocation
|
||||
~ColorGrading() = default;
|
||||
};
|
||||
|
||||
@@ -317,15 +317,6 @@ public:
|
||||
*/
|
||||
size_t metalUploadBufferSizeBytes = 512 * 1024;
|
||||
|
||||
/**
|
||||
* The action to take if a Drawable cannot be acquired.
|
||||
*
|
||||
* Each frame rendered requires a CAMetalDrawable texture, which is
|
||||
* presented on-screen at the completion of each frame. These are
|
||||
* limited and provided round-robin style by the system.
|
||||
*/
|
||||
bool metalDisablePanicOnDrawableFailure = false;
|
||||
|
||||
/**
|
||||
* Set to `true` to forcibly disable parallel shader compilation in the backend.
|
||||
* Currently only honored by the GL and Metal backends.
|
||||
|
||||
@@ -54,6 +54,8 @@ namespace filament {
|
||||
struct UTILS_PUBLIC ToneMapper {
|
||||
ToneMapper() noexcept;
|
||||
virtual ~ToneMapper() noexcept;
|
||||
ToneMapper(ToneMapper const&) noexcept;
|
||||
ToneMapper& operator=(ToneMapper const&) noexcept = default;
|
||||
|
||||
/**
|
||||
* Maps an open domain (or "scene referred" values) color value to display
|
||||
@@ -69,6 +71,13 @@ struct UTILS_PUBLIC ToneMapper {
|
||||
*/
|
||||
virtual math::float3 operator()(math::float3 c) const noexcept = 0;
|
||||
|
||||
/**
|
||||
* Creates a copy of this tone mapper instance.
|
||||
*
|
||||
* @return A pointer to a new ToneMapper instance that is a copy of this instance.
|
||||
*/
|
||||
virtual ToneMapper* clone() const noexcept = 0;
|
||||
|
||||
/**
|
||||
* If true, then this function holds that f(x) = vec3(f(x.r), f(x.g), f(x.b))
|
||||
*
|
||||
@@ -92,8 +101,10 @@ struct UTILS_PUBLIC ToneMapper {
|
||||
struct UTILS_PUBLIC LinearToneMapper final : public ToneMapper {
|
||||
LinearToneMapper() noexcept;
|
||||
~LinearToneMapper() noexcept final;
|
||||
LinearToneMapper(LinearToneMapper const&) noexcept;
|
||||
|
||||
math::float3 operator()(math::float3 c) const noexcept override;
|
||||
LinearToneMapper* clone() const noexcept override;
|
||||
bool isOneDimensional() const noexcept override { return true; }
|
||||
bool isLDR() const noexcept override { return true; }
|
||||
};
|
||||
@@ -106,8 +117,10 @@ struct UTILS_PUBLIC LinearToneMapper final : public ToneMapper {
|
||||
struct UTILS_PUBLIC ACESToneMapper final : public ToneMapper {
|
||||
ACESToneMapper() noexcept;
|
||||
~ACESToneMapper() noexcept final;
|
||||
ACESToneMapper(ACESToneMapper const&) noexcept;
|
||||
|
||||
math::float3 operator()(math::float3 c) const noexcept override;
|
||||
ACESToneMapper* clone() const noexcept override;
|
||||
bool isOneDimensional() const noexcept override { return false; }
|
||||
bool isLDR() const noexcept override { return false; }
|
||||
};
|
||||
@@ -121,8 +134,10 @@ struct UTILS_PUBLIC ACESToneMapper final : public ToneMapper {
|
||||
struct UTILS_PUBLIC ACESLegacyToneMapper final : public ToneMapper {
|
||||
ACESLegacyToneMapper() noexcept;
|
||||
~ACESLegacyToneMapper() noexcept final;
|
||||
ACESLegacyToneMapper(ACESLegacyToneMapper const&) noexcept;
|
||||
|
||||
math::float3 operator()(math::float3 c) const noexcept override;
|
||||
ACESLegacyToneMapper* clone() const noexcept override;
|
||||
bool isOneDimensional() const noexcept override { return false; }
|
||||
bool isLDR() const noexcept override { return false; }
|
||||
};
|
||||
@@ -136,8 +151,10 @@ struct UTILS_PUBLIC ACESLegacyToneMapper final : public ToneMapper {
|
||||
struct UTILS_PUBLIC FilmicToneMapper final : public ToneMapper {
|
||||
FilmicToneMapper() noexcept;
|
||||
~FilmicToneMapper() noexcept final;
|
||||
FilmicToneMapper(FilmicToneMapper const&) noexcept;
|
||||
|
||||
math::float3 operator()(math::float3 x) const noexcept override;
|
||||
FilmicToneMapper* clone() const noexcept override;
|
||||
bool isOneDimensional() const noexcept override { return true; }
|
||||
bool isLDR() const noexcept override { return false; }
|
||||
};
|
||||
@@ -150,8 +167,10 @@ struct UTILS_PUBLIC FilmicToneMapper final : public ToneMapper {
|
||||
struct UTILS_PUBLIC PBRNeutralToneMapper final : public ToneMapper {
|
||||
PBRNeutralToneMapper() noexcept;
|
||||
~PBRNeutralToneMapper() noexcept final;
|
||||
PBRNeutralToneMapper(PBRNeutralToneMapper const&) noexcept;
|
||||
|
||||
math::float3 operator()(math::float3 x) const noexcept override;
|
||||
virtual PBRNeutralToneMapper* clone() const noexcept override;
|
||||
bool isOneDimensional() const noexcept override { return false; }
|
||||
bool isLDR() const noexcept override { return false; }
|
||||
};
|
||||
@@ -173,8 +192,10 @@ struct UTILS_PUBLIC AgxToneMapper final : public ToneMapper {
|
||||
*/
|
||||
explicit AgxToneMapper(AgxLook look = AgxLook::NONE) noexcept;
|
||||
~AgxToneMapper() noexcept final;
|
||||
AgxToneMapper(AgxToneMapper const&) noexcept;
|
||||
|
||||
math::float3 operator()(math::float3 x) const noexcept override;
|
||||
virtual AgxToneMapper* clone() const noexcept override;
|
||||
bool isOneDimensional() const noexcept override { return false; }
|
||||
bool isLDR() const noexcept override { return false; }
|
||||
|
||||
@@ -215,12 +236,13 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper {
|
||||
) noexcept;
|
||||
~GenericToneMapper() noexcept final;
|
||||
|
||||
GenericToneMapper(GenericToneMapper const&) = delete;
|
||||
GenericToneMapper& operator=(GenericToneMapper const&) = delete;
|
||||
GenericToneMapper(GenericToneMapper const& rhs) noexcept;
|
||||
GenericToneMapper& operator=(GenericToneMapper const& rhs) noexcept;
|
||||
GenericToneMapper(GenericToneMapper&& rhs) noexcept;
|
||||
GenericToneMapper& operator=(GenericToneMapper&& rhs) noexcept;
|
||||
|
||||
math::float3 operator()(math::float3 x) const noexcept override;
|
||||
GenericToneMapper* clone() const noexcept override;
|
||||
bool isOneDimensional() const noexcept override { return true; }
|
||||
bool isLDR() const noexcept override { return false; }
|
||||
|
||||
@@ -283,8 +305,10 @@ private:
|
||||
struct UTILS_PUBLIC DisplayRangeToneMapper final : public ToneMapper {
|
||||
DisplayRangeToneMapper() noexcept;
|
||||
~DisplayRangeToneMapper() noexcept override;
|
||||
DisplayRangeToneMapper(DisplayRangeToneMapper const&) noexcept;
|
||||
|
||||
math::float3 operator()(math::float3 c) const noexcept override;
|
||||
DisplayRangeToneMapper* clone() const noexcept override;
|
||||
bool isOneDimensional() const noexcept override { return false; }
|
||||
bool isLDR() const noexcept override { return false; }
|
||||
};
|
||||
|
||||
@@ -188,7 +188,8 @@ float3 ACES(float3 color, float brightness) noexcept {
|
||||
|
||||
#define DEFAULT_CONSTRUCTORS(A) \
|
||||
A::A() noexcept = default; \
|
||||
A::~A() noexcept = default;
|
||||
A::~A() noexcept = default; \
|
||||
A::A(A const&) noexcept = default;
|
||||
|
||||
DEFAULT_CONSTRUCTORS(ToneMapper)
|
||||
|
||||
@@ -202,6 +203,10 @@ float3 LinearToneMapper::operator()(float3 const v) const noexcept {
|
||||
return saturate(v);
|
||||
}
|
||||
|
||||
LinearToneMapper* LinearToneMapper::clone() const noexcept {
|
||||
return new LinearToneMapper(*this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// ACES tone mappers
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -212,12 +217,20 @@ float3 ACESToneMapper::operator()(float3 const c) const noexcept {
|
||||
return aces::ACES(c, 1.0f);
|
||||
}
|
||||
|
||||
ACESToneMapper* ACESToneMapper::clone() const noexcept {
|
||||
return new ACESToneMapper(*this);
|
||||
}
|
||||
|
||||
DEFAULT_CONSTRUCTORS(ACESLegacyToneMapper)
|
||||
|
||||
float3 ACESLegacyToneMapper::operator()(float3 const c) const noexcept {
|
||||
return aces::ACES(c, 1.0f / 0.6f);
|
||||
}
|
||||
|
||||
ACESLegacyToneMapper* ACESLegacyToneMapper::clone() const noexcept {
|
||||
return new ACESLegacyToneMapper(*this);
|
||||
}
|
||||
|
||||
DEFAULT_CONSTRUCTORS(FilmicToneMapper)
|
||||
|
||||
float3 FilmicToneMapper::operator()(float3 const x) const noexcept {
|
||||
@@ -230,6 +243,10 @@ float3 FilmicToneMapper::operator()(float3 const x) const noexcept {
|
||||
return (x * (a * x + b)) / (x * (c * x + d) + e);
|
||||
}
|
||||
|
||||
FilmicToneMapper* FilmicToneMapper::clone() const noexcept {
|
||||
return new FilmicToneMapper(*this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// PBR Neutral tone mapper
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -256,12 +273,17 @@ float3 PBRNeutralToneMapper::operator()(float3 color) const noexcept {
|
||||
return mix(color, float3(newPeak), g);
|
||||
}
|
||||
|
||||
PBRNeutralToneMapper* PBRNeutralToneMapper::clone() const noexcept {
|
||||
return new PBRNeutralToneMapper(*this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// AgX tone mapper
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
AgxToneMapper::AgxToneMapper(AgxLook const look) noexcept : look(look) {}
|
||||
AgxToneMapper::~AgxToneMapper() noexcept = default;
|
||||
AgxToneMapper::AgxToneMapper(AgxToneMapper const& rhs) noexcept = default;
|
||||
|
||||
// These matrices taken from Blender's implementation of AgX, which works with Rec.2020 primaries.
|
||||
// https://github.com/EaryChow/AgX_LUT_Gen/blob/main/AgXBaseRec2020.py
|
||||
@@ -356,6 +378,10 @@ float3 AgxToneMapper::operator()(float3 v) const noexcept {
|
||||
return v;
|
||||
}
|
||||
|
||||
AgxToneMapper* AgxToneMapper::clone() const noexcept {
|
||||
return new AgxToneMapper(*this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Display range tone mapper
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -394,6 +420,10 @@ float3 DisplayRangeToneMapper::operator()(float3 const c) const noexcept {
|
||||
return mix(debugColors[index], debugColors[index + 1], saturate(v - float(index)));
|
||||
}
|
||||
|
||||
DisplayRangeToneMapper* DisplayRangeToneMapper::clone() const noexcept {
|
||||
return new DisplayRangeToneMapper(*this);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Generic tone mapper
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -454,7 +484,19 @@ GenericToneMapper::~GenericToneMapper() noexcept {
|
||||
delete mOptions;
|
||||
}
|
||||
|
||||
GenericToneMapper::GenericToneMapper(GenericToneMapper&& rhs) noexcept : mOptions(rhs.mOptions) {
|
||||
GenericToneMapper::GenericToneMapper(GenericToneMapper const& rhs) noexcept {
|
||||
mOptions = new Options(*rhs.mOptions);
|
||||
}
|
||||
|
||||
GenericToneMapper& GenericToneMapper::operator=(GenericToneMapper const& rhs) noexcept {
|
||||
if (this != &rhs) {
|
||||
delete mOptions;
|
||||
mOptions = new Options(*rhs.mOptions);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
GenericToneMapper::GenericToneMapper(GenericToneMapper&& rhs) noexcept : mOptions(rhs.mOptions) {
|
||||
rhs.mOptions = nullptr;
|
||||
}
|
||||
|
||||
@@ -469,6 +511,10 @@ float3 GenericToneMapper::operator()(float3 x) const noexcept {
|
||||
return mOptions->outputScale * x / (x + mOptions->inputScale);
|
||||
}
|
||||
|
||||
GenericToneMapper* GenericToneMapper::clone() const noexcept {
|
||||
return new GenericToneMapper(*this);
|
||||
}
|
||||
|
||||
float GenericToneMapper::getContrast() const noexcept { return mOptions->contrast; }
|
||||
float GenericToneMapper::getMidGrayIn() const noexcept { return mOptions->midGrayIn; }
|
||||
float GenericToneMapper::getMidGrayOut() const noexcept { return mOptions->midGrayOut; }
|
||||
|
||||
@@ -49,7 +49,7 @@ using namespace backend;
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
struct ColorGrading::BuilderDetails {
|
||||
const ToneMapper* toneMapper = nullptr;
|
||||
std::shared_ptr<ToneMapper> toneMapper = nullptr;
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
@@ -173,7 +173,7 @@ ColorGrading::Builder& ColorGrading::Builder::dimensions(uint8_t const dim) noex
|
||||
return *this;
|
||||
}
|
||||
|
||||
ColorGrading::Builder& ColorGrading::Builder::toneMapper(const ToneMapper* toneMapper) noexcept {
|
||||
ColorGrading::Builder& ColorGrading::Builder::toneMapper(std::shared_ptr<ToneMapper> toneMapper) noexcept {
|
||||
mImpl->toneMapper = toneMapper;
|
||||
return *this;
|
||||
}
|
||||
@@ -271,6 +271,110 @@ ColorGrading::Builder& ColorGrading::Builder::outputColorSpace(
|
||||
return *this;
|
||||
}
|
||||
|
||||
ColorGrading::LutFormat ColorGrading::getLutFormat() const noexcept {
|
||||
return mSettings.lutFormat;
|
||||
}
|
||||
|
||||
uint8_t ColorGrading::getLutDimensions() const noexcept {
|
||||
return mSettings.lutDimensions;
|
||||
}
|
||||
|
||||
const ToneMapper& ColorGrading::getToneMapper() const noexcept {
|
||||
return *mSettings.toneMapper;
|
||||
}
|
||||
|
||||
bool ColorGrading::isLuminanceScalingEnabled() const noexcept {
|
||||
return mSettings.luminanceScaling;
|
||||
}
|
||||
|
||||
bool ColorGrading::isGamutMappingEnabled() const noexcept {
|
||||
return mSettings.gummapMapping;
|
||||
}
|
||||
|
||||
float ColorGrading::getExposure() const noexcept {
|
||||
return mSettings.exposure;
|
||||
}
|
||||
|
||||
float ColorGrading::getNightAdaptation() const noexcept {
|
||||
return mSettings.nightAdaptation;
|
||||
}
|
||||
|
||||
float ColorGrading::getWhiteBalanceTemperature() const noexcept {
|
||||
return mSettings.whiteBalanceTemperature;
|
||||
}
|
||||
|
||||
float ColorGrading::getWhiteBalanceTint() const noexcept {
|
||||
return mSettings.whiteBalanceTint;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getChannelMixerOutRed() const noexcept {
|
||||
return mSettings.channelMixerOutRed;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getChannelMixerOutGreen() const noexcept {
|
||||
return mSettings.channelMixerOutGreen;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getChannelMixerOutBlue() const noexcept {
|
||||
return mSettings.channelMixerOutBlue;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getShadows() const noexcept {
|
||||
return mSettings.shadows;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getMidtones() const noexcept {
|
||||
return mSettings.midtones;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getHighlights() const noexcept {
|
||||
return mSettings.highlights;
|
||||
}
|
||||
|
||||
math::float4 ColorGrading::getShadowMidtonesHighlightsRanges() const noexcept {
|
||||
return mSettings.ShadowMidtonesHighlightsRanges;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getSlope() const noexcept {
|
||||
return mSettings.slope;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getOffset() const noexcept {
|
||||
return mSettings.offset;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getPower() const noexcept {
|
||||
return mSettings.power;
|
||||
}
|
||||
|
||||
float ColorGrading::getContrast() const noexcept {
|
||||
return mSettings.contrast;
|
||||
}
|
||||
|
||||
float ColorGrading::getVibrance() const noexcept {
|
||||
return mSettings.vibrance;
|
||||
}
|
||||
|
||||
float ColorGrading::getSaturation() const noexcept {
|
||||
return mSettings.saturation;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getCurvesShadowGamma() const noexcept {
|
||||
return mSettings.curvesShadowGamma;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getCurvesMidPoint() const noexcept {
|
||||
return mSettings.curvesMidPoint;
|
||||
}
|
||||
|
||||
math::float3 ColorGrading::getCurvesHighlightScale() const noexcept {
|
||||
return mSettings.curvesHighlightScale;
|
||||
}
|
||||
|
||||
const color::ColorSpace& ColorGrading::getOutputColorSpace() const noexcept {
|
||||
return mSettings.colorSpace;
|
||||
}
|
||||
|
||||
#if defined(__clang__)
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
@@ -287,30 +391,25 @@ ColorGrading* ColorGrading::Builder::build(Engine& engine) {
|
||||
if (needToneMapper) {
|
||||
switch (mImpl->toneMapping) {
|
||||
case ToneMapping::LINEAR:
|
||||
mImpl->toneMapper = new LinearToneMapper();
|
||||
mImpl->toneMapper = std::make_shared<LinearToneMapper>();
|
||||
break;
|
||||
case ToneMapping::ACES_LEGACY:
|
||||
mImpl->toneMapper = new ACESLegacyToneMapper();
|
||||
mImpl->toneMapper = std::make_shared<ACESLegacyToneMapper>();
|
||||
break;
|
||||
case ToneMapping::ACES:
|
||||
mImpl->toneMapper = new ACESToneMapper();
|
||||
mImpl->toneMapper = std::make_shared<ACESToneMapper>();
|
||||
break;
|
||||
case ToneMapping::FILMIC:
|
||||
mImpl->toneMapper = new FilmicToneMapper();
|
||||
mImpl->toneMapper = std::make_shared<FilmicToneMapper>();
|
||||
break;
|
||||
case ToneMapping::DISPLAY_RANGE:
|
||||
mImpl->toneMapper = new DisplayRangeToneMapper();
|
||||
mImpl->toneMapper = std::make_shared<DisplayRangeToneMapper>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FColorGrading* colorGrading = downcast(engine).createColorGrading(*this);
|
||||
|
||||
if (needToneMapper) {
|
||||
delete mImpl->toneMapper;
|
||||
mImpl->toneMapper = nullptr;
|
||||
}
|
||||
|
||||
return colorGrading;
|
||||
}
|
||||
|
||||
@@ -886,6 +985,9 @@ FColorGrading::FColorGrading(FEngine& engine, const Builder& builder) {
|
||||
data, lutElementCount * elementSize, format, type,
|
||||
[](void* buffer, size_t, void*) { free(buffer); }
|
||||
});
|
||||
|
||||
// Initialize settings from builder
|
||||
initializeSettings(builder, mSettings);
|
||||
}
|
||||
|
||||
FColorGrading::~FColorGrading() noexcept = default;
|
||||
@@ -895,4 +997,33 @@ void FColorGrading::terminate(FEngine& engine) {
|
||||
driver.destroyTexture(mLutHandle);
|
||||
}
|
||||
|
||||
void FColorGrading::initializeSettings(const Builder& builder, Settings& settings) noexcept {
|
||||
settings.lutFormat = builder->format;
|
||||
settings.lutDimensions = builder->dimension;
|
||||
settings.toneMapper = builder->toneMapper;
|
||||
settings.luminanceScaling = builder->luminanceScaling;
|
||||
settings.gummapMapping = builder->gamutMapping;
|
||||
settings.exposure = builder->exposure;
|
||||
settings.nightAdaptation = builder->nightAdaptation;
|
||||
settings.whiteBalanceTemperature = builder->whiteBalance.x;
|
||||
settings.whiteBalanceTint = builder->whiteBalance.y;
|
||||
settings.channelMixerOutRed = builder->outRed;
|
||||
settings.channelMixerOutGreen = builder->outGreen;
|
||||
settings.channelMixerOutBlue = builder->outBlue;
|
||||
settings.shadows = builder->shadows;
|
||||
settings.midtones = builder->midtones;
|
||||
settings.highlights = builder->highlights;
|
||||
settings.ShadowMidtonesHighlightsRanges = builder->tonalRanges;
|
||||
settings.slope = builder->slope;
|
||||
settings.offset = builder->offset;
|
||||
settings.power = builder->power;
|
||||
settings.contrast = builder->contrast;
|
||||
settings.vibrance = builder->vibrance;
|
||||
settings.saturation = builder->saturation;
|
||||
settings.curvesShadowGamma = builder->shadowGamma;
|
||||
settings.curvesMidPoint = builder->midPoint;
|
||||
settings.curvesHighlightScale = builder->highlightScale;
|
||||
settings.colorSpace = builder->outputColorSpace;
|
||||
}
|
||||
|
||||
} //namespace filament
|
||||
|
||||
@@ -51,6 +51,8 @@ private:
|
||||
uint32_t mDimension;
|
||||
bool mIsOneDimensional;
|
||||
bool mIsLDR;
|
||||
|
||||
static void initializeSettings(const Builder& builder, Settings& settings) noexcept;
|
||||
};
|
||||
|
||||
FILAMENT_DOWNCAST(ColorGrading)
|
||||
|
||||
@@ -138,7 +138,6 @@ Engine* FEngine::create(Builder const& builder) {
|
||||
.forceGLES2Context = instance->getConfig().forceGLES2Context,
|
||||
.stereoscopicType = instance->getConfig().stereoscopicType,
|
||||
.assertNativeWindowIsValid = instance->features.backend.opengl.assert_native_window_is_valid,
|
||||
.metalDisablePanicOnDrawableFailure = instance->getConfig().metalDisablePanicOnDrawableFailure,
|
||||
};
|
||||
instance->mDriver = platform->createDriver(sharedContext, driverConfig);
|
||||
|
||||
@@ -734,7 +733,6 @@ int FEngine::loop() {
|
||||
.forceGLES2Context = mConfig.forceGLES2Context,
|
||||
.stereoscopicType = mConfig.stereoscopicType,
|
||||
.assertNativeWindowIsValid = features.backend.opengl.assert_native_window_is_valid,
|
||||
.metalDisablePanicOnDrawableFailure = mConfig.metalDisablePanicOnDrawableFailure,
|
||||
};
|
||||
mDriver = mPlatform->createDriver(mSharedGLContext, driverConfig);
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.60.0"
|
||||
spec.version = "1.59.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.60.0/filament-v1.60.0-ios.tgz" }
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.59.5/filament-v1.59.5-ios.tgz" }
|
||||
|
||||
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
||||
spec.pod_target_xcconfig = {
|
||||
|
||||
@@ -102,7 +102,8 @@ project in Xcode to see changes take effect.
|
||||
|
||||
## Building iOS Samples with ASan / UBSan
|
||||
|
||||
1. Turn on ASan / UBSan in Filament's top-level CMakeLists.txt by uncommenting the following line:
|
||||
1. Turn on ASan / UBSan in Filament's top-level CMakeLists.txt by passing
|
||||
`-DFILAMENT_ENABLE_ASAN_UBSAN=1` to trigger the following line:
|
||||
|
||||
```
|
||||
set(EXTRA_SANITIZE_OPTIONS "-fsanitize=undefined -fsanitize=address")
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
namespace filament {
|
||||
|
||||
// update this when a new version of filament wouldn't work with older materials
|
||||
static constexpr size_t MATERIAL_VERSION = 60;
|
||||
static constexpr size_t MATERIAL_VERSION = 59;
|
||||
|
||||
/**
|
||||
* Supported shading models
|
||||
|
||||
@@ -56,7 +56,6 @@ set(PRIVATE_HDRS
|
||||
src/MetalArgumentBuffer.h
|
||||
src/ShaderMinifier.h
|
||||
src/SpirvFixup.h
|
||||
src/SpirvRemapWrapper.h
|
||||
src/sca/ASTHelpers.h
|
||||
src/sca/GLSLTools.h
|
||||
src/sca/builtinResource.h)
|
||||
@@ -72,8 +71,7 @@ set(SRCS
|
||||
src/sca/GLSLTools.cpp
|
||||
src/GLSLPostProcessor.cpp
|
||||
src/ShaderMinifier.cpp
|
||||
src/SpirvFixup.cpp
|
||||
src/SpirvRemapWrapper.cpp)
|
||||
src/SpirvFixup.cpp)
|
||||
|
||||
# ==================================================================================================
|
||||
# Include and target definitions
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "GLSLPostProcessor.h"
|
||||
|
||||
#include <GlslangToSpv.h>
|
||||
#include <SPVRemapper.h>
|
||||
#include <spirv-tools/libspirv.hpp>
|
||||
|
||||
#include <spirv_glsl.hpp>
|
||||
@@ -288,8 +289,24 @@ GLSLPostProcessor::GLSLPostProcessor(MaterialBuilder::Optimization optimization,
|
||||
: mOptimization(optimization),
|
||||
mPrintShaders(flags & PRINT_SHADERS),
|
||||
mGenerateDebugInfo(flags & GENERATE_DEBUG_INFO) {
|
||||
// This should occur only once, to avoid races.
|
||||
SpirvRemapWrapperSetUp();
|
||||
// SPIRV error handler registration needs to occur only once. To avoid a race we do it up here
|
||||
// in the constructor, which gets invoked before MaterialBuilder kicks off jobs.
|
||||
spv::spirvbin_t::registerErrorHandler([](const std::string& str) {
|
||||
slog.e << str << io::endl;
|
||||
});
|
||||
|
||||
// Similar to above, we need to do a no-op remap to init a static table in the remapper before
|
||||
// the jobs start using remap().
|
||||
spv::spirvbin_t remapper(0);
|
||||
// We need to provide at least a valid header to not crash.
|
||||
SpirvBlob spirv {
|
||||
0x07230203,// MAGIC
|
||||
0, // VERSION
|
||||
0, // GENERATOR
|
||||
0, // BOUND
|
||||
0 // SCHEMA, must be 0
|
||||
};
|
||||
remapper.remap(spirv, 0);
|
||||
}
|
||||
|
||||
GLSLPostProcessor::~GLSLPostProcessor() = default;
|
||||
@@ -971,7 +988,8 @@ void GLSLPostProcessor::optimizeSpirv(OptimizerPtr optimizer, SpirvBlob& spirv)
|
||||
}
|
||||
|
||||
// Remove dead module-level objects: functions, types, vars
|
||||
SpirvRemapWrapperRemap(spirv);
|
||||
spv::spirvbin_t remapper(0);
|
||||
remapper.remap(spirv, spv::spirvbin_base_t::DCE_ALL);
|
||||
}
|
||||
|
||||
void GLSLPostProcessor::fixupClipDistance(
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include <private/filament/SamplerInterfaceBlock.h>
|
||||
|
||||
#include "ShaderMinifier.h"
|
||||
#include "SpirvRemapWrapper.h"
|
||||
|
||||
#include <spirv-tools/optimizer.hpp>
|
||||
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 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 "SpirvRemapWrapper.h"
|
||||
|
||||
#include <SPVRemapper.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace filamat {
|
||||
|
||||
void SpirvRemapWrapperSetUp() {
|
||||
// SPIRV error handler registration should occur only once.
|
||||
// Construct this SpirvRemapWrapper object only once.
|
||||
spv::spirvbin_t::registerErrorHandler([](const std::string& str) {
|
||||
utils::slog.e << str << utils::io::endl;
|
||||
});
|
||||
|
||||
// Similar to above, we need to do a no-op remap to init a static
|
||||
// table in the remapper before the jobs start using remap().
|
||||
spv::spirvbin_t remapper(0);
|
||||
// We need to provide at least a valid header to not crash.
|
||||
std::vector<uint32_t> spirv {
|
||||
0x07230203,// MAGIC
|
||||
0, // VERSION
|
||||
0, // GENERATOR
|
||||
0, // BOUND
|
||||
0 // SCHEMA, must be 0
|
||||
};
|
||||
remapper.remap(spirv, 0);
|
||||
}
|
||||
|
||||
void SpirvRemapWrapperRemap(std::vector<uint32_t>& spirv) {
|
||||
// Remove dead module-level objects: functions, types, vars
|
||||
spv::spirvbin_t remapper(0);
|
||||
remapper.remap(spirv, spv::spirvbin_base_t::DCE_ALL);
|
||||
}
|
||||
|
||||
} // namespace filamat
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_SPIRVREMAPWRAPPER_H
|
||||
#define TNT_SPIRVREMAPWRAPPER_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace filamat {
|
||||
|
||||
void SpirvRemapWrapperSetUp();
|
||||
void SpirvRemapWrapperRemap(std::vector<uint32_t>& spirv);
|
||||
|
||||
} // namespace filamat
|
||||
|
||||
#endif //TNT_SPIRVREMAPWRAPPER_H
|
||||
@@ -700,7 +700,8 @@ constexpr ToneMapper* createToneMapper(const ColorGradingSettings& settings) noe
|
||||
}
|
||||
|
||||
ColorGrading* createColorGrading(const ColorGradingSettings& settings, Engine* engine) {
|
||||
ToneMapper* toneMapper = createToneMapper(settings);
|
||||
ToneMapper* toneMapperRaw = createToneMapper(settings);
|
||||
std::shared_ptr<ToneMapper> toneMapper(toneMapperRaw);
|
||||
ColorGrading *colorGrading = ColorGrading::Builder()
|
||||
.quality(settings.quality)
|
||||
.exposure(settings.exposure)
|
||||
@@ -723,7 +724,6 @@ ColorGrading* createColorGrading(const ColorGradingSettings& settings, Engine* e
|
||||
.gamutMapping(settings.gamutMapping)
|
||||
.outputColorSpace(settings.colorspace)
|
||||
.build(*engine);
|
||||
delete toneMapper;
|
||||
return colorGrading;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,42 @@
|
||||
# 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.
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import re
|
||||
|
||||
from utils import execute, ArgParseImpl
|
||||
from utils import execute, ArgParseImpl, mkdir_p
|
||||
|
||||
GOLDENS_DIR = 'renderdiff'
|
||||
|
||||
ACCESS_TYPE_TOKEN = 'token'
|
||||
ACCESS_TYPE_SSH = 'ssh'
|
||||
ACCESS_TYPE_READ_ONLY = 'read-only'
|
||||
|
||||
def _read_git_config(curdir):
|
||||
with open(os.path.join(curdir, './.git/config'), 'r') as f:
|
||||
return f.read()
|
||||
|
||||
def _write_git_config(curdir, config_str):
|
||||
with open(os.path.join(curdir, './.git/config'), 'w') as f:
|
||||
return f.write(config_str)
|
||||
|
||||
class GoldenManager:
|
||||
def __init__(self, working_dir, access_token=None):
|
||||
def __init__(self, working_dir, access_type=ACCESS_TYPE_READ_ONLY, access_token=None):
|
||||
self.working_dir_ = working_dir
|
||||
self.access_token_ = access_token
|
||||
self.access_type_ = access_type
|
||||
assert os.path.isdir(self.working_dir_),\
|
||||
f"working directory {self.working_dir_} does not exist"
|
||||
self._prepare()
|
||||
@@ -16,16 +44,36 @@ class GoldenManager:
|
||||
def _assets_dir(self):
|
||||
return os.path.join(self.working_dir_, "filament-assets")
|
||||
|
||||
# Returns the directory containing the goldens
|
||||
def directory(self):
|
||||
return os.path.join(self._assets_dir(), GOLDENS_DIR)
|
||||
|
||||
def _get_repo_url(self):
|
||||
protocol = ''
|
||||
protocol_separator = ''
|
||||
if self.access_type_ == ACCESS_TYPE_SSH:
|
||||
protocol = 'git@'
|
||||
protocol_separator = ':'
|
||||
else:
|
||||
protocol = 'https://' + \
|
||||
(f'x-access-token:{self.access_token_}@' if self.access_token_ else '')
|
||||
protocol_separator = '/'
|
||||
return f'{protocol}github.com{protocol_separator}google/filament-assets.git'
|
||||
|
||||
def _prepare(self):
|
||||
assets_dir = self._assets_dir()
|
||||
if not os.path.exists(assets_dir):
|
||||
access_token_part = ''
|
||||
if self.access_token_:
|
||||
access_token_part = f'x-access-token:{self.access_token_}@'
|
||||
execute(
|
||||
f'git clone --depth=1 https://{access_token_part}github.com/google/filament-assets.git',
|
||||
cwd=self.working_dir_)
|
||||
f'git clone --depth=1 {self._get_repo_url()}',
|
||||
cwd=self.working_dir_,
|
||||
capture_output=False
|
||||
)
|
||||
else:
|
||||
if self.access_type_ == ACCESS_TYPE_SSH:
|
||||
config = _read_git_config(self._assets_dir())
|
||||
https_url = r'https://github\.com\/google\/filament\.git'
|
||||
config = re.sub(https_url, self._get_repo_url(), config)
|
||||
_write_git_config(self._assets_dir(), config)
|
||||
self.update()
|
||||
|
||||
def update(self):
|
||||
@@ -41,31 +89,46 @@ class GoldenManager:
|
||||
assets_dir = self._assets_dir()
|
||||
self._git_exec(f'checkout main')
|
||||
self._git_exec(f'merge --no-ff {branch}')
|
||||
if push_to_remote and self.access_token_:
|
||||
if push_to_remote and \
|
||||
(self.access_token_ or self.access_type_ == ACCESS_TYPE_SSH):
|
||||
self._git_exec(f'push origin main')
|
||||
self.update()
|
||||
|
||||
def source_from_and_commit(self, src_dir, commit_msg, branch, push_to_remote=False):
|
||||
def source_from(self, src_dir, commit_msg, branch,
|
||||
updates=[], deletes=[], push_to_remote=False):
|
||||
assets_dir = self._assets_dir()
|
||||
self._git_exec(f'checkout main')
|
||||
# Force create the branch (note will overwrite the old branch)
|
||||
self._git_exec(f'switch -C {branch}')
|
||||
rdiff_dir = os.path.join(assets_dir, GOLDENS_DIR)
|
||||
execute(f'rm -rf {rdiff_dir}')
|
||||
execute(f'mkdir -p {rdiff_dir}')
|
||||
shutil.copytree(src_dir, rdiff_dir, dirs_exist_ok=True)
|
||||
self._git_exec(f'add {GOLDENS_DIR}')
|
||||
if len(updates) == 0 and len(deletes) == 0:
|
||||
shutil.rmtree(rdiff_dir, ignore_errors=True)
|
||||
mkdir_p(rdiff_dir)
|
||||
shutil.copytree(src_dir, rdiff_dir, dirs_exist_ok=True)
|
||||
self._git_exec(f'add {GOLDENS_DIR}')
|
||||
else:
|
||||
for f in deletes:
|
||||
self._git_exec(f'remove {os.path.join(GOLDENS_DIR, f)}')
|
||||
for f in updates:
|
||||
shutil.copy2(
|
||||
os.path.join(src_dir, f),
|
||||
os.path.join(rdiff_dir, f))
|
||||
self._git_exec(f'add {os.path.join(GOLDENS_DIR, f)}')
|
||||
|
||||
TMP_GOLDEN_COMMIT_FILE = '/tmp/golden_commit.txt'
|
||||
|
||||
with open(TMP_GOLDEN_COMMIT_FILE, 'w') as f:
|
||||
f.write(commit_msg)
|
||||
self._git_exec(f'commit -F {TMP_GOLDEN_COMMIT_FILE}')
|
||||
if push_to_remote and self.access_token_:
|
||||
self._git_exec(f'push -f origin ${branch}')
|
||||
self._git_exec(f'commit -a -F {TMP_GOLDEN_COMMIT_FILE}')
|
||||
if push_to_remote and \
|
||||
(self.access_token_ or self.access_type_ == ACCESS_TYPE_SSH):
|
||||
self._git_exec(f'push -f origin {branch}')
|
||||
self.update()
|
||||
|
||||
def download_to(self, dest_dir, branch='main'):
|
||||
self._git_exec(f'checkout {branch}')
|
||||
assets_dir = self._assets_dir()
|
||||
execute(f'mkdir -p {dest_dir}')
|
||||
mkdir_p(dest_dir)
|
||||
rdiff_dir = os.path.join(assets_dir, GOLDENS_DIR)
|
||||
shutil.copytree(rdiff_dir, dest_dir, dirs_exist_ok=True)
|
||||
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
# 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.
|
||||
|
||||
import tifffile
|
||||
import numpy
|
||||
|
||||
|
||||
@@ -16,8 +16,9 @@ import sys
|
||||
import os
|
||||
import json
|
||||
import glob
|
||||
import shutil
|
||||
|
||||
from utils import execute, ArgParseImpl
|
||||
from utils import execute, ArgParseImpl, mkdir_p, mv_f
|
||||
from parse_test_json import parse_test_config_from_path
|
||||
from golden_manager import GoldenManager
|
||||
from image_diff import same_image
|
||||
@@ -46,7 +47,7 @@ def run_test(gltf_viewer,
|
||||
assert os.access(gltf_viewer, os.X_OK)
|
||||
|
||||
named_output_dir = os.path.join(output_dir, test_config.name)
|
||||
execute(f'mkdir -p {named_output_dir}')
|
||||
mkdir_p(named_output_dir)
|
||||
|
||||
results = []
|
||||
for test in test_config.tests:
|
||||
@@ -82,9 +83,8 @@ def run_test(gltf_viewer,
|
||||
result = RESULT_OK
|
||||
out_tif_basename = f'{out_name}.tif'
|
||||
out_tif_name = f'{named_output_dir}/{out_tif_basename}'
|
||||
execute(f'mv -f {test.name}0.tif {out_tif_name}', capture_output=False)
|
||||
execute(f'mv -f {test.name}0.json {named_output_dir}/{test.name}.json',
|
||||
capture_output=False)
|
||||
mv_f(f'{test.name}0.tif', out_tif_name)
|
||||
mv_f(f'{test.name}0.json', f'{named_output_dir}/{test.name}.json')
|
||||
else:
|
||||
result = RESULT_FAILED_TO_RENDER
|
||||
important_print(f'{test_desc} rendering failed with error={out_code}')
|
||||
@@ -132,11 +132,12 @@ if __name__ == "__main__":
|
||||
opengl_lib=args.opengl_lib,
|
||||
vk_icd=args.vk_icd)
|
||||
|
||||
do_compare = False
|
||||
# The presence of this argument indicates comparison against a set of goldens.
|
||||
if args.golden_branch:
|
||||
# prepare goldens working directory
|
||||
tmp_golden_dir = '/tmp/renderdiff-goldens'
|
||||
execute(f'mkdir -p {tmp_golden_dir}')
|
||||
mkdir_p(tmp_golden_dir)
|
||||
|
||||
# Download the golden repo into the current working directory
|
||||
golden_manager = GoldenManager(os.getcwd())
|
||||
@@ -147,13 +148,15 @@ if __name__ == "__main__":
|
||||
glob.glob(f'{os.path.join(tmp_golden_dir, test.name)}/**/*.tif', recursive=True)
|
||||
}
|
||||
results = compare_goldens(results, output_dir, goldens)
|
||||
|
||||
do_compare = True
|
||||
|
||||
with open(f'{output_dir}/results.json', 'w') as f:
|
||||
f.write(json.dumps(results))
|
||||
execute(f'cp {args.test} {output_dir}/test.json')
|
||||
|
||||
shutil.copy2(args.test, f'{output_dir}/test.json')
|
||||
|
||||
failed = [f" {k['name']}" for k in results if k['result'] != RESULT_OK]
|
||||
success_count = len(results) - len(failed)
|
||||
important_print(f'Successfully tested {success_count} / {len(results)}' +
|
||||
op = 'tested' if do_compare else 'rendered'
|
||||
important_print(f'Successfully {op} {success_count} / {len(results)}' +
|
||||
('\nFailed:\n' + ('\n'.join(failed)) if len(failed) > 0 else ''))
|
||||
|
||||
@@ -0,0 +1,171 @@
|
||||
# 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.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import glob
|
||||
import time
|
||||
|
||||
from golden_manager import GoldenManager, ACCESS_TYPE_SSH, ACCESS_TYPE_TOKEN
|
||||
from image_diff import same_image
|
||||
|
||||
from utils import execute, ArgParseImpl
|
||||
from utils import prompt_helper, PROMPT_YES, PROMPT_NO
|
||||
|
||||
def line_prompt(prompt, validator=lambda a:True):
|
||||
while True:
|
||||
res = input(f'{prompt} => ').strip()
|
||||
if validator(res):
|
||||
return res
|
||||
return None
|
||||
|
||||
CONFIG_NEW_SRC_DIR = 'goldens_dir'
|
||||
CONFIG_GOLDENS_BRANCH = 'goldens_branch'
|
||||
CONFIG_GOLDENS_UPDATES = 'goldens_updates'
|
||||
CONFIG_GOLDENS_DELETES = 'goldens_deletes'
|
||||
CONFIG_AUTO_COMMIT = 'auto-commit'
|
||||
CONFIG_COMMIT_MSG = 'commit_msg'
|
||||
|
||||
def _get_current_branch():
|
||||
code, res = execute('git branch --show-current')
|
||||
return res.strip()
|
||||
|
||||
def _file_as_str(fpath):
|
||||
with open(fpath, 'r') as f:
|
||||
return f.read()
|
||||
|
||||
def _do_update(golden_manager, config):
|
||||
deletes = config[CONFIG_GOLDENS_DELETES]
|
||||
updates = config[CONFIG_GOLDENS_UPDATES]
|
||||
if len(deletes) == 0 and len(updates) == 0:
|
||||
print('Nothing to update. Exiting...')
|
||||
exit(0)
|
||||
|
||||
branch = config[CONFIG_GOLDENS_BRANCH]
|
||||
src_dir = config[CONFIG_NEW_SRC_DIR]
|
||||
auto_commit = config[CONFIG_AUTO_COMMIT]
|
||||
commit_msg = config[CONFIG_COMMIT_MSG]
|
||||
golden_manager.source_from(src_dir, commit_msg, branch,
|
||||
updates=updates,
|
||||
deletes=deletes,
|
||||
push_to_remote=auto_commit)
|
||||
|
||||
def _get_deletes_updates(update_dir, golden_dir):
|
||||
ret_delete = []
|
||||
ret_update = []
|
||||
for ext in ['tif', 'json']:
|
||||
base = set(glob.glob(f'./**/*.{ext}', root_dir=golden_dir, recursive=True))
|
||||
new = set(glob.glob(f'./**/*.{ext}', root_dir=update_dir, recursive=True))
|
||||
|
||||
delete = list(base - new)
|
||||
update = list(new - base)
|
||||
|
||||
for fpath in base.intersection(new):
|
||||
base_fpath = os.path.join(golden_dir, fpath)
|
||||
new_fpath = os.path.join(update_dir, fpath)
|
||||
if (ext == 'tif' and not same_image(new_fpath, base_fpath)) or \
|
||||
(ext == 'json' and _file_as_str(new_fpath) != _file_as_str(base_fpath)):
|
||||
update.append(fpath)
|
||||
|
||||
ret_update += update
|
||||
ret_delete += delete
|
||||
|
||||
return ret_delete, ret_update
|
||||
|
||||
# Ask a bunch of questions to gather the configuration for the update
|
||||
def _interactive_mode(base_golden_dir):
|
||||
config = {}
|
||||
cur_branch = _get_current_branch()
|
||||
if prompt_helper(
|
||||
f'Generate the new goldens from your local ' \
|
||||
f'Filament branch? (branch={cur_branch})') == PROMPT_YES:
|
||||
code, res = execute('bash ./test/renderdiff/test.sh generate',
|
||||
capture_output=False)
|
||||
if code != 0:
|
||||
print('Failed to generate new goldens')
|
||||
exit(1)
|
||||
config[CONFIG_NEW_SRC_DIR] = os.path.join(os.getcwd(), './out/renderdiff_tests/')
|
||||
else:
|
||||
def validator(src_dir):
|
||||
if not os.path.exists(src_dir):
|
||||
print(f'Cannot find directory {src_dir}. Please try again.')
|
||||
return False
|
||||
return True
|
||||
|
||||
config[CONFIG_NEW_SRC_DIR] = line_prompt(
|
||||
'Please provide path of directory containing new goldens',
|
||||
validator)
|
||||
|
||||
if prompt_helper(f'Update new goldens to branch={cur_branch}? '
|
||||
'(Note that this refers to a branch in the goldens repo, not the Filament repo.)'
|
||||
) == PROMPT_YES:
|
||||
config[CONFIG_GOLDENS_BRANCH] = cur_branch
|
||||
else:
|
||||
config[CONFIG_GOLDENS_BRANCH] = line_prompt('Please provide new branch name for update')
|
||||
|
||||
if prompt_helper(f'Provide a commit message?') == PROMPT_YES:
|
||||
config[CONFIG_COMMIT_MSG] = line_prompt('Message:')
|
||||
else:
|
||||
config[CONFIG_COMMIT_MSG] = f'Update {time.time()} from filament ({cur_branch})'
|
||||
|
||||
new_golden_dir = config[CONFIG_NEW_SRC_DIR]
|
||||
deletes, updates = _get_deletes_updates(new_golden_dir, base_golden_dir)
|
||||
if len(deletes) + len(updates) != 0:
|
||||
prompt = 'The following files will be changed:\n' + \
|
||||
'\n'.join([f' {fname} [delete]' for fname in deletes]) + \
|
||||
'\n'.join([f' {fname} [update]' for fname in updates]) + \
|
||||
'\nIs that ok?'
|
||||
if prompt_helper(prompt) == PROMPT_YES:
|
||||
config[CONFIG_GOLDENS_DELETES] = deletes
|
||||
config[CONFIG_GOLDENS_UPDATES] = updates
|
||||
else:
|
||||
# We cannot proceed if user answered no.
|
||||
exit(1)
|
||||
else:
|
||||
config[CONFIG_GOLDENS_DELETES] = []
|
||||
config[CONFIG_GOLDENS_UPDATES] = []
|
||||
|
||||
config[CONFIG_AUTO_COMMIT] = \
|
||||
prompt_helper(f'Commit golden repo changes to remote?') == PROMPT_YES
|
||||
return config
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = ArgParseImpl()
|
||||
parser.add_argument('--branch', help='Branch of the golden repo to write to')
|
||||
parser.add_argument('--source', help='Directory containing the new goldens')
|
||||
parser.add_argument('--commit-msg', help='Message for the commit to the golden repo')
|
||||
parser.add_argument('--golden-repo-token', help='Access token for the golden repo')
|
||||
|
||||
args, _ = parser.parse_known_args(sys.argv[1:])
|
||||
config = {}
|
||||
golden_manager = GoldenManager(
|
||||
os.getcwd(),
|
||||
access_type=ACCESS_TYPE_SSH if not args.golden_repo_token else ACCESS_TYPE_TOKEN,
|
||||
access_token=args.golden_repo_token
|
||||
)
|
||||
base_golden_dir = golden_manager.directory()
|
||||
if args.branch and args.source and args.commit_msg:
|
||||
assert os.path.exists(args.source), f'{args.source} (--source) directory not found'
|
||||
deletes, updates = _get_deletes_updates(args.source, base_golden_dir)
|
||||
config = {
|
||||
CONFIG_AUTO_COMMIT: True,
|
||||
CONFIG_GOLDENS_BRANCH: args.branch,
|
||||
CONFIG_NEW_SRC_DIR: args.source,
|
||||
CONFIG_GOLDENS_UPDATES: updates,
|
||||
CONFIG_GOLDENS_DELETES: deletes,
|
||||
CONFIG_COMMIT_MSG: args.commit_msg,
|
||||
}
|
||||
else:
|
||||
config = _interactive_mode(base_golden_dir)
|
||||
_do_update(golden_manager, config)
|
||||
@@ -16,6 +16,7 @@ import subprocess
|
||||
import os
|
||||
import argparse
|
||||
import sys
|
||||
import pathlib
|
||||
|
||||
def execute(cmd,
|
||||
cwd=None,
|
||||
@@ -66,3 +67,42 @@ class ArgParseImpl(argparse.ArgumentParser):
|
||||
sys.stderr.write('error: %s\n' % message)
|
||||
self.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
PROMPT_YES = 'y'
|
||||
PROMPT_NO = 'n'
|
||||
PROMPT_YES_NO = f'{PROMPT_YES}{PROMPT_NO}'
|
||||
|
||||
class GetCh:
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def __call__(self):
|
||||
import sys, tty, termios
|
||||
fd = sys.stdin.fileno()
|
||||
old_settings = termios.tcgetattr(fd)
|
||||
try:
|
||||
tty.setraw(sys.stdin.fileno())
|
||||
ch = sys.stdin.read(1)
|
||||
finally:
|
||||
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
||||
return ch
|
||||
|
||||
getch = GetCh()
|
||||
|
||||
def prompt_helper(prompt_str, keys=PROMPT_YES_NO):
|
||||
while True:
|
||||
print(f'{prompt_str}: [' + ', '.join(keys) + '] => ', end='', flush=True)
|
||||
val = getch()
|
||||
print(val)
|
||||
if val in keys or ord(val) == 3: # If user pressed Ctrl+c
|
||||
if ord(val) == 3:
|
||||
exit(1)
|
||||
return val
|
||||
|
||||
def mkdir_p(path_str):
|
||||
pathlib.Path(path_str).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
def mv_f(src_str, dst_str):
|
||||
src = pathlib.Path(src_str)
|
||||
src.replace(dst_str)
|
||||
|
||||
@@ -17,19 +17,29 @@ from utils import execute
|
||||
def get_last_commit():
|
||||
res, o = execute('git log -1')
|
||||
commit, author, date, _, title, *desc = o.split('\n')
|
||||
commit = commit.split(' ')[1]
|
||||
title = title.strip()
|
||||
|
||||
desc = [l.strip() for l in desc[1:]]
|
||||
if len(desc) > 0 and len(desc[0]) == 0:
|
||||
while len(desc) > 0 and len(desc[0]) == 0:
|
||||
desc = desc[1:]
|
||||
|
||||
return (
|
||||
commit.split(' ')[1],
|
||||
title.strip(),
|
||||
commit,
|
||||
title,
|
||||
desc
|
||||
)
|
||||
|
||||
def sanitized_split(line, split_atom='\n'):
|
||||
return list(filter(lambda x: len(x) > 0, map(lambda x: x.strip(), line.split(split_atom))))
|
||||
return list(
|
||||
filter(
|
||||
lambda x: len(x) > 0,
|
||||
map(
|
||||
lambda x: x.strip(),
|
||||
line.split(split_atom)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
RDIFF_UPDATE_GOLDEN_STR = 'RDIFF_UPDATE_GOLDEN'
|
||||
|
||||
|
||||
+39
-7
@@ -18,6 +18,7 @@ OUTPUT_DIR="$(pwd)/out/renderdiff_tests"
|
||||
RENDERDIFF_TEST_DIR="$(pwd)/test/renderdiff"
|
||||
TEST_UTILS_DIR="$(pwd)/test/utils"
|
||||
MESA_DIR="$(pwd)/mesa/out/"
|
||||
VENV_DIR="$(pwd)/venv"
|
||||
|
||||
os_name=$(uname -s)
|
||||
if [[ "$os_name" == "Linux" ]]; then
|
||||
@@ -29,9 +30,33 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
function prepare_mesa() {
|
||||
if [ ! -d ${MESA_LIB_DIR} ]; then
|
||||
bash ${TEST_UTILS_DIR}/get_mesa.sh
|
||||
function start_() {
|
||||
if [[ "$GITHUB_WORKFLOW" ]]; then
|
||||
set -ex
|
||||
else
|
||||
if [ ! -d ${MESA_LIB_DIR} ]; then
|
||||
bash ${TEST_UTILS_DIR}/get_mesa.sh
|
||||
fi
|
||||
|
||||
# Install python deps
|
||||
python3 -m venv ${VENV_DIR}
|
||||
source ${VENV_DIR}/bin/activate
|
||||
|
||||
NEEDED_PYTHON_DEPS=("numpy" "tifffile")
|
||||
for cmd in "${NEEDED_PYTHON_DEPS[@]}"; do
|
||||
if ! python3 -m pip show -q "${cmd}"; then
|
||||
python3 -m pip install ${cmd}
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
function end_() {
|
||||
if [[ "$GITHUB_WORKFLOW" ]]; then
|
||||
set +ex
|
||||
else
|
||||
deactivate # End python virtual env
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -41,12 +66,19 @@ function prepare_mesa() {
|
||||
# - Run the python script that runs the test
|
||||
# - Zip up the result
|
||||
|
||||
set -ex && prepare_mesa && \
|
||||
GOLDEN_BRANCH_PARAM='--golden_branch=main'
|
||||
|
||||
if [ "$1" == "generate" ]; then
|
||||
GOLDEN_BRANCH_PARAM=''
|
||||
fi
|
||||
|
||||
start_ && \
|
||||
mkdir -p ${OUTPUT_DIR} && \
|
||||
CXX=`which clang++` CC=`which clang` ./build.sh -f -X ${MESA_DIR} -p desktop debug gltf_viewer && \
|
||||
python3 ${RENDERDIFF_TEST_DIR}/src/run.py \
|
||||
CXX=`which clang++` CC=`which clang` ./build.sh -f -X ${MESA_DIR} -p desktop debug gltf_viewer && \
|
||||
python3 ${RENDERDIFF_TEST_DIR}/src/run.py \
|
||||
--gltf_viewer="$(pwd)/out/cmake-debug/samples/gltf_viewer" \
|
||||
--test=${RENDERDIFF_TEST_DIR}/tests/presubmit.json \
|
||||
--output_dir=${OUTPUT_DIR} \
|
||||
--opengl_lib=${MESA_LIB_DIR} \
|
||||
--golden_branch=main
|
||||
${GOLDEN_BRANCH_PARAM} && \
|
||||
end_
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
|
||||
#!/usr/bin/bash
|
||||
|
||||
set -x
|
||||
set -e
|
||||
if [[ "$GITHUB_WORKFLOW" ]]; then
|
||||
set -e
|
||||
set -x
|
||||
fi
|
||||
|
||||
OS_NAME=$(uname -s)
|
||||
@@ -35,7 +35,7 @@ source ${ORIG_DIR}/venv/bin/activate
|
||||
|
||||
NEEDED_PYTHON_DEPS=("mako" "setuptools" "pyyaml")
|
||||
for cmd in "${NEEDED_PYTHON_DEPS[@]}"; do
|
||||
if ! python3 -m pip show "${cmd}" >/dev/null 2>&1; then
|
||||
if ! python3 -m pip show -q "${cmd}" >/dev/null 2>&1; then
|
||||
python3 -m pip install ${cmd}
|
||||
fi
|
||||
done
|
||||
@@ -145,6 +145,6 @@ deactivate
|
||||
popd
|
||||
|
||||
if [[ "$GITHUB_WORKFLOW" ]]; then
|
||||
set +e
|
||||
set +x
|
||||
fi
|
||||
set +x
|
||||
set +e
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "filament",
|
||||
"version": "1.60.0",
|
||||
"version": "1.59.5",
|
||||
"description": "Real-time physically based rendering engine",
|
||||
"main": "filament.js",
|
||||
"module": "filament.js",
|
||||
|
||||
Reference in New Issue
Block a user