Compare commits

..
Author SHA1 Message Date
bridgewaterrobbie 4a77d03d02 Try to handle non-transient attatchment supporting GPUs 2025-05-09 13:22:19 -04:00
104 changed files with 776 additions and 1080 deletions
+1 -1
View File
@@ -26,4 +26,4 @@ jobs:
path: out/filament-release-darwin.tgz
- name: Check public headers
run: |
test/check-headers/test.sh out/release/filament/include
build/common/check-headers.sh out/release/filament/include
+3 -3
View File
@@ -131,7 +131,7 @@ jobs:
- name: Prerequisites
id: prereqs
run: |
bash build/common/get-mesa.sh
bash test/utils/get_mesa.sh
pip install tifffile numpy
- name: Run Test
run: bash test/renderdiff/test.sh
@@ -153,8 +153,8 @@ jobs:
- name: Run test
run: ./out/cmake-debug/libs/filamat/test_filamat --gtest_filter=MaterialCompiler.Wgsl*
test-code-correctness:
name: test-code-correctness
code-correctness:
name: code-correctness
runs-on: 'macos-14-xlarge'
steps:
- uses: actions/checkout@v4.1.6
+1 -4
View File
@@ -163,9 +163,6 @@ jobs:
mv out/filamat-android-release.aar out/filamat-${TAG}-android.aar
mv out/gltfio-android-release.aar out/gltfio-${TAG}-android.aar
mv out/filament-utils-android-release.aar out/filament-utils-${TAG}-android.aar
cd out/android-release/filament
tar -czf ../../filament-${TAG}-android-native.tgz .
cd ../../..
- name: Sign sample-gltf-viewer
run: |
echo "${APK_KEYSTORE_BASE64}" > filament.jks.base64
@@ -187,7 +184,7 @@ jobs:
script: |
const upload = require('./build/common/upload-release-assets');
const { TAG } = process.env;
const globber = await glob.create(['out/*.aar', 'out/*.apk', 'out/*.tgz'].join('\n'));
const globber = await glob.create(['out/*.aar', 'out/*.apk'].join('\n'));
await upload({ github, context }, await globber.glob(), TAG);
build-ios:
+2 -2
View File
@@ -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 -6
View File
@@ -7,15 +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.60.1
## 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
@@ -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 -1
View File
@@ -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.
+11 -1
View File
@@ -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/common/versions | grep GITHUB_NDK_VERSION | sed s/GITHUB_NDK_VERSION=//g | 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
@@ -463,6 +463,16 @@ function ensure_android_build {
echo "Error: Android NDK side-by-side version ${FILAMENT_NDK_VERSION} or compatible must be installed, exiting"
exit 1
fi
local cmake_version=$(cmake --version)
if [[ "${cmake_version}" =~ ([0-9]+)\.([0-9]+)\.[0-9]+ ]]; then
if [[ "${BASH_REMATCH[1]}" -lt "${CMAKE_MAJOR}" ]] || \
[[ "${BASH_REMATCH[2]}" -lt "${CMAKE_MINOR}" ]]; then
echo "Error: cmake version ${CMAKE_MAJOR}.${CMAKE_MINOR}+ is required," \
"${BASH_REMATCH[1]}.${BASH_REMATCH[2]} installed, exiting"
exit 1
fi
fi
}
function build_android {
+2 -6
View File
@@ -1023,12 +1023,8 @@ samplerCubemap | Cubemap texture
[Table [materialParamsTypes]: Material parameter types]
Samplers
: Sampler types can specify additional options:
- `format`: either `int` or `float` (defaults to `float`).
- `stages`: array of strings containing the list of shader stages this
sampler can be accessed from. Each entry must be either `vertex` or
`fragment` (defaults to both).
: Sampler types can also specify a `format` which can be either `int` or `float` (defaults to
`float`).
Arrays
: A parameter can define an array of values by appending `[size]` after the type name, where
-1
View File
@@ -19,7 +19,6 @@
- [Vulkan](./notes/vulkan_debugging.md)
- [SPIR-V](./notes/spirv_debugging.md)
- [Running with ASAN and UBSAN](./notes/asan_ubsan.md)
- [Using Instruments on macOS](./notes/instruments.md)
- [Libraries](./notes/libs.md)
- [bluegl](./dup/bluegl.md)
- [bluevk](./dup/bluevk.md)
-36
View File
@@ -1,36 +0,0 @@
# Using Instruments on macOS
When running a binary under Instruments on macOS, you may run into the following issue when
launching or attaching to an executable:
```
Failed to gain authorization
Recovery Suggestion: Target binary needs to be debuggable and signed with 'get-task-allow'
```
This is a security precaution; the solution is to code sign the binary with the
`com.apple.security.get-task-allow` entitlement.
1. Create an `entitlements.plist` file with the following contents:
```
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.get-task-allow</key>
<true/>
</dict>
</plist>
```
2. Run the following command:
```
codesign -s - --entitlements entitlements.plist <binary>
```
Replace `<binary>` with the name of the binary, for example: `out/cmake-debug/samples/gltf_viewer`.
Afterwards, you should be able to successfully launch and attach to the executable using
Instruments.
@@ -20,15 +20,10 @@
#define TNT_FILAMENT_BACKEND_BUFFERDESCRIPTOR_H
#include <utils/compiler.h>
#include <utility>
#include <utils/ostream.h>
#include <stddef.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
class CallbackHandler;
@@ -94,8 +89,8 @@ public:
* @param callback A callback used to release the CPU buffer from this BufferDescriptor
* @param user An opaque user pointer passed to the callback function when it's called
*/
BufferDescriptor(void const* buffer, size_t const size,
Callback const callback = nullptr, void* user = nullptr) noexcept
BufferDescriptor(void const* buffer, size_t size,
Callback callback = nullptr, void* user = nullptr) noexcept
: buffer(const_cast<void*>(buffer)), size(size), mCallback(callback), mUser(user) {
}
@@ -103,12 +98,11 @@ public:
* Creates a BufferDescriptor that references a CPU memory-buffer
* @param buffer Memory address of the CPU buffer to reference
* @param size Size of the CPU buffer in bytes
* @param handler A custom handler for the callback
* @param callback A callback used to release the CPU buffer from this BufferDescriptor
* @param user An opaque user pointer passed to the callback function when it's called
*/
BufferDescriptor(void const* buffer, size_t const size,
CallbackHandler* handler, Callback const callback, void* user = nullptr) noexcept
BufferDescriptor(void const* buffer, size_t size,
CallbackHandler* handler, Callback callback, void* user = nullptr) noexcept
: buffer(const_cast<void*>(buffer)), size(size),
mCallback(callback), mUser(user), mHandler(handler) {
}
@@ -122,9 +116,8 @@ public:
*
* @param buffer Memory address of the CPU buffer to reference
* @param size Size of the CPU buffer in bytes
* @param data A pointer to the data
* @param handler Handler to use to dispatch the callback, or nullptr for the default handler
* @return A new BufferDescriptor
* @return a new BufferDescriptor
*/
template<typename T, void(T::*method)(void const*, size_t)>
static BufferDescriptor make(void const* buffer, size_t size, T* data,
@@ -171,7 +164,7 @@ public:
* @param callback The new callback function
* @param user An opaque user pointer passed to the callbeck function when it's called
*/
void setCallback(Callback const callback, void* user = nullptr) noexcept {
void setCallback(Callback callback, void* user = nullptr) noexcept {
this->mCallback = callback;
this->mUser = user;
this->mHandler = nullptr;
@@ -183,7 +176,7 @@ public:
* @param callback The new callback function
* @param user An opaque user pointer passed to the callbeck function when it's called
*/
void setCallback(CallbackHandler* handler, Callback const callback, void* user = nullptr) noexcept {
void setCallback(CallbackHandler* handler, Callback callback, void* user = nullptr) noexcept {
mCallback = callback;
mUser = user;
mHandler = handler;
@@ -19,6 +19,8 @@
#include <backend/DriverApiForward.h>
#include <utils/ostream.h>
#include <initializer_list>
#include <memory>
@@ -26,10 +28,6 @@
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
void* allocateFromCommandStream(DriverApi& driver, size_t size, size_t alignment) noexcept;
@@ -25,11 +25,12 @@
#include <backend/PresentCallable.h>
#include <utils/BitmaskEnum.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Invocable.h>
#include <utils/StaticString.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <utils/StaticString.h>
#include <math/vec4.h>
@@ -40,10 +41,6 @@
#include <stddef.h>
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
/**
* Types and enums used by filament's driver.
*
+3 -4
View File
@@ -17,6 +17,9 @@
#ifndef TNT_FILAMENT_BACKEND_HANDLE_H
#define TNT_FILAMENT_BACKEND_HANDLE_H
#if !defined(NDEBUG)
#include <utils/ostream.h>
#endif
#include <utils/debug.h>
#include <type_traits> // FIXME: STL headers are not allowed in public headers
@@ -24,10 +27,6 @@
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
struct HwBufferObject;
@@ -20,14 +20,12 @@
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <utils/ostream.h>
#include <array>
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
//! \privatesection
@@ -24,14 +24,11 @@
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <stddef.h>
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
/**
+2 -4
View File
@@ -20,10 +20,12 @@
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Invocable.h>
#include <utils/ostream.h>
#include <backend/DriverEnums.h>
#include <array>
#include <unordered_map>
#include <tuple>
#include <utility>
#include <variant>
@@ -31,10 +33,6 @@
#include <stddef.h>
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
class Program {
@@ -19,26 +19,22 @@
#include <backend/Handle.h>
#include <utility>
#include <utils/ostream.h>
#include <stddef.h>
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
//! \privatesection
struct TargetBufferInfo {
// note: the parameters of this constructor are not in the order of this structure's fields
TargetBufferInfo(Handle<HwTexture> handle, uint8_t const level, uint16_t const layer) noexcept
: handle(std::move(handle)), level(level), layer(layer) {
TargetBufferInfo(Handle<HwTexture> handle, uint8_t level, uint16_t layer) noexcept
: handle(handle), level(level), layer(layer) {
}
TargetBufferInfo(Handle<HwTexture> handle, uint8_t const level) noexcept
TargetBufferInfo(Handle<HwTexture> handle, uint8_t level) noexcept
: handle(handle), level(level) {
}
@@ -74,11 +70,11 @@ private:
TargetBufferInfo mInfos[MAX_SUPPORTED_RENDER_TARGET_COUNT];
public:
TargetBufferInfo const& operator[](size_t const i) const noexcept {
TargetBufferInfo const& operator[](size_t i) const noexcept {
return mInfos[i];
}
TargetBufferInfo& operator[](size_t const i) noexcept {
TargetBufferInfo& operator[](size_t i) noexcept {
return mInfos[i];
}
@@ -39,40 +39,18 @@ public:
Driver* createDriver(void* sharedContext, const Platform::DriverConfig& driverConfig) noexcept override;
int getOSVersion() const noexcept override { return 0; }
/**
* Optionally initializes the Metal platform by acquiring resources necessary for rendering.
*
* This method attempts to acquire a Metal device and command queue, returning true if both are
* successfully obtained, or false otherwise. Typically, these objects are acquired when
* the Metal backend is initialized. This method allows clients to check for their availability
* earlier.
*
* Calling initialize() is optional and safe to do so multiple times. After initialize() returns
* true, subsequent calls will continue to return true but have no effect.
*
* initialize() must be called from the main thread.
*
* @returns true if the device and command queue have been successfully obtained; false
* otherwise.
*/
bool initialize() noexcept;
/**
* Obtain the preferred Metal device object for the backend to use.
*
* On desktop platforms, there may be multiple GPUs suitable for rendering, and this method is
* free to decide which one to use. On mobile systems with a single GPU, implementations should
* simply return the result of MTLCreateSystemDefaultDevice();
*
* createDevice is called by the Metal backend from the backend thread.
*/
virtual void createDevice(MetalDevice& outDevice) noexcept;
/**
* Create a command submission queue on the Metal device object.
*
* createCommandQueue is called by the Metal backend from the backend thread.
*
* @param device The device which was returned from createDevice()
*/
virtual void createCommandQueue(
@@ -82,8 +60,6 @@ public:
* Obtain a MTLCommandBuffer enqueued on this Platform's MTLCommandQueue. The command buffer is
* guaranteed to execute before all subsequent command buffers created either by Filament, or
* further calls to this method.
*
* createAndEnqueueCommandBuffer must be called from the main thread.
*/
void createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept;
@@ -92,8 +68,6 @@ public:
*
* 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.
*
* setDrawableFailureBehavior must be called from the main thread.
*/
enum class DrawableFailureBehavior : uint8_t {
/**
@@ -27,11 +27,8 @@
#include <utils/Hash.h>
#include <utils/PrivateImplementation.h>
#include <cstddef>
#include <functional>
#include <tuple>
#include <unordered_set>
#include <string>
#include <stddef.h>
#include <stdint.h>
-1
View File
@@ -17,7 +17,6 @@
#include <backend/Platform.h>
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <atomic>
#include <utility>
@@ -19,8 +19,6 @@
#include "MetalContext.h"
#include <utils/Panic.h>
namespace filament {
namespace backend {
+1 -11
View File
@@ -721,25 +721,15 @@ const char* toString(DescriptorFlags flags) {
void MetalDriver::createDescriptorSetLayoutR(
Handle<HwDescriptorSetLayout> dslh, DescriptorSetLayout&& info) {
#if FILAMENT_METAL_DEBUG_LOG == 1
const char* labelStr = "";
std::visit([&labelStr](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (std::is_same_v<T, utils::CString> || std::is_same_v<T, utils::StaticString>) {
labelStr = arg.c_str();
}
}, info.label);
std::sort(info.bindings.begin(), info.bindings.end(),
[](const auto& a, const auto& b) { return a.binding < b.binding; });
DEBUG_LOG("createDescriptorSetLayoutR(dslh = %d, info = { label = %s,\n", dslh.getId(),
labelStr);
DEBUG_LOG("createDescriptorSetLayoutR(dslh = %d, info = {\n", dslh.getId());
for (size_t i = 0; i < info.bindings.size(); i++) {
DEBUG_LOG(" {binding = %d, type = %s, count = %d, stage = %s, flags = %s},\n",
info.bindings[i].binding, toString(info.bindings[i].type), info.bindings[i].count,
toString(info.bindings[i].stageFlags), toString(info.bindings[i].flags));
}
DEBUG_LOG("})\n");
#endif
construct_handle<MetalDescriptorSetLayout>(dslh, std::move(info));
}
@@ -1382,9 +1382,6 @@ id<MTLArgumentEncoder> MetalDescriptorSetLayout::getArgumentEncoderSlow(id<MTLDe
break;
}
}
if (arguments.count == 0) {
return nil;
}
return [device newArgumentEncoderWithArguments:arguments];
}
@@ -1445,9 +1442,6 @@ id<MTLBuffer> MetalDescriptorSet::finalizeAndGetBuffer(MetalDriver* driver, Shad
id<MTLArgumentEncoder> encoder =
layout->getArgumentEncoder(context.device, stage, textureTypes);
if (!encoder) {
return nil;
}
{
ScopedAllocationTimer timer("descriptor_set");
+5 -6
View File
@@ -20,18 +20,17 @@
#include <Metal/Metal.h>
#include "private/backend/Driver.h"
#include "backend/Program.h"
#include <backend/DriverEnums.h>
#include <backend/Program.h>
#include <utils/bitset.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Hash.h>
#include <utils/Invocable.h>
#include <tsl/robin_map.h>
#include <memory>
#include <tsl/robin_map.h>
#include <utils/Hash.h>
#include <utils/Invocable.h>
namespace filament {
namespace backend {
+19 -69
View File
@@ -24,22 +24,14 @@
#import <Foundation/Foundation.h>
#include <atomic>
#include <mutex>
namespace filament::backend {
struct PlatformMetalImpl {
std::mutex mLock; // locks mDevice and mCommandQueue
id<MTLDevice> mDevice = nil;
id<MTLCommandQueue> mCommandQueue = nil;
// read form driver thread, read/written to from client thread
std::atomic<PlatformMetal::DrawableFailureBehavior> mDrawableFailureBehavior =
PlatformMetal::DrawableFailureBehavior::PANIC;
// These methods must be called with mLock held
void createDeviceImpl(MetalDevice& outDevice);
void createCommandQueueImpl(MetalDevice& device, MetalCommandQueue& outCommandQueue);
};
Platform* createDefaultMetalPlatform() {
@@ -56,59 +48,7 @@ Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::Dri
return MetalDriverFactory::create(this, driverConfig);
}
bool PlatformMetal::initialize() noexcept {
std::lock_guard<std::mutex> lock(pImpl->mLock);
MetalDevice device{};
pImpl->createDeviceImpl(device);
if (device.device == nil) {
return false;
}
MetalCommandQueue commandQueue{};
pImpl->createCommandQueueImpl(device, commandQueue);
if (commandQueue.commandQueue == nil) {
return false;
}
return true;
}
void PlatformMetal::createDevice(MetalDevice& outDevice) noexcept {
std::lock_guard<std::mutex> lock(pImpl->mLock);
pImpl->createDeviceImpl(outDevice);
}
void PlatformMetal::createCommandQueue(
MetalDevice& device, MetalCommandQueue& outCommandQueue) noexcept {
std::lock_guard<std::mutex> lock(pImpl->mLock);
pImpl->createCommandQueueImpl(device, outCommandQueue);
}
void PlatformMetal::createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept {
std::lock_guard<std::mutex> lock(pImpl->mLock);
id<MTLCommandBuffer> commandBuffer = [pImpl->mCommandQueue commandBuffer];
[commandBuffer enqueue];
outCommandBuffer.commandBuffer = commandBuffer;
}
void PlatformMetal::setDrawableFailureBehavior(DrawableFailureBehavior behavior) noexcept {
pImpl->mDrawableFailureBehavior = behavior;
}
PlatformMetal::DrawableFailureBehavior PlatformMetal::getDrawableFailureBehavior() const noexcept {
return pImpl->mDrawableFailureBehavior;
}
// -------------------------------------------------------------------------------------------------
void PlatformMetalImpl::createDeviceImpl(MetalDevice& outDevice) {
if (mDevice) {
outDevice.device = mDevice;
return;
}
id<MTLDevice> result;
#if !defined(FILAMENT_IOS)
@@ -134,17 +74,27 @@ void PlatformMetalImpl::createDeviceImpl(MetalDevice& outDevice) {
<< utils::io::endl;
outDevice.device = result;
mDevice = result;
}
void PlatformMetalImpl::createCommandQueueImpl(MetalDevice& device, MetalCommandQueue& outCommandQueue) {
if (mCommandQueue) {
outCommandQueue.commandQueue = mCommandQueue;
return;
}
mCommandQueue = [device.device newCommandQueue];
mCommandQueue.label = @"Filament";
outCommandQueue.commandQueue = mCommandQueue;
void PlatformMetal::createCommandQueue(
MetalDevice& device, MetalCommandQueue& outCommandQueue) noexcept {
pImpl->mCommandQueue = [device.device newCommandQueue];
pImpl->mCommandQueue.label = @"Filament";
outCommandQueue.commandQueue = pImpl->mCommandQueue;
}
void PlatformMetal::createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept {
id<MTLCommandBuffer> commandBuffer = [pImpl->mCommandQueue commandBuffer];
[commandBuffer enqueue];
outCommandBuffer.commandBuffer = commandBuffer;
}
void PlatformMetal::setDrawableFailureBehavior(DrawableFailureBehavior behavior) noexcept {
pImpl->mDrawableFailureBehavior = behavior;
}
PlatformMetal::DrawableFailureBehavior PlatformMetal::getDrawableFailureBehavior() const noexcept {
return pImpl->mDrawableFailureBehavior;
}
} // namespace filament
@@ -542,13 +542,6 @@ void OpenGLContext::initBugs(Bugs* bugs, Extensions const& exts,
} else if (strstr(renderer, "Intel")) {
// Intel GPU
bugs->vao_doesnt_store_element_array_buffer_binding = true;
if (strstr(renderer, "Mesa")) {
// Mesa Intel driver on Linux/Android
// Renderer of the form [Mesa Intel(R) HD Graphics 505 (APL 3)]
// b/405252622
bugs->disable_invalidate_framebuffer = true;
}
} else if (strstr(renderer, "PowerVR")) {
// PowerVR GPU
// On PowerVR (Rogue GE8320) glFlush doesn't seem to do anything, in particular,
@@ -54,7 +54,7 @@ struct PlatformCocoaGLImpl {
CVOpenGLTextureCacheRef mTextureCache = nullptr;
std::unique_ptr<CocoaExternalImage::SharedGl> mExternalImageSharedGl;
void updateOpenGLContext(NSView *nsView, bool resetView, bool clearView);
struct ExternalImageCocoaGL final : public Platform::ExternalImage {
struct ExternalImageCocoaGL : public Platform::ExternalImage {
CVPixelBufferRef cvBuffer;
protected:
~ExternalImageCocoaGL() noexcept final;
+15 -47
View File
@@ -19,7 +19,6 @@
#include <backend/DriverEnums.h>
#include <utils/BitmaskEnum.h>
#include <utils/Panic.h>
#include <webgpu/webgpu_cpp.h>
@@ -503,6 +502,7 @@ size_t WebGPUDescriptorSet::countEntitiesWithDynamicOffsets() const {
return mEntriesByBindingWithDynamicOffsets.count();
}
// From createTextureR
WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat format, uint8_t samples,
uint32_t width, uint32_t height, uint32_t depth, TextureUsage usage,
wgpu::Device const& device) noexcept {
@@ -514,9 +514,8 @@ 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);
mAspect = fToWGPUTextureViewAspect(usage, format);
wgpu::TextureDescriptor textureDescriptor{
.label = getUserTextureLabel(target),
.usage = mUsage,
@@ -561,12 +560,14 @@ WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat forma
mTexView = makeTextureView(0, levels, target);
}
// From createTextureViewR
WGPUTexture::WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept {
mTexture = src->mTexture;
mTexView = makeTextureView(baseLevel, levelCount, target);
}
wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(TextureUsage const& 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(TextureUsage const& 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.
@@ -614,9 +616,8 @@ wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(TextureUsage const& fUsage)
// PROTECTED
return retUsage;
}
wgpu::TextureFormat WGPUTexture::fToWGPUTextureFormat(TextureFormat const& fFormat) {
switch (fFormat) {
wgpu::TextureFormat WGPUTexture::fToWGPUTextureFormat(const TextureFormat& fUsage) {
switch (fUsage) {
case filament::backend::TextureFormat::R8:
return wgpu::TextureFormat::R8Unorm;
case filament::backend::TextureFormat::R8_SNORM:
@@ -855,57 +856,24 @@ wgpu::TextureFormat WGPUTexture::fToWGPUTextureFormat(TextureFormat const& fForm
}
}
wgpu::TextureAspect WGPUTexture::fToWGPUTextureViewAspect(TextureUsage const& fUsage,
TextureFormat const& fFormat) {
const bool isDepth = any(fUsage & TextureUsage::DEPTH_ATTACHMENT);
const bool isStencil = any(fUsage & TextureUsage::STENCIL_ATTACHMENT);
const bool isColor = any(fUsage & TextureUsage::COLOR_ATTACHMENT);
const bool isSample = (fUsage == TextureUsage::SAMPLEABLE);
if (isDepth && !isColor && !isStencil) {
return wgpu::TextureAspect::DepthOnly;
}
if (isStencil && !isColor && !isDepth) {
return wgpu::TextureAspect::StencilOnly;
}
if (fFormat == filament::backend::TextureFormat::DEPTH32F ||
fFormat == filament::backend::TextureFormat::DEPTH24 ||
fFormat == filament::backend::TextureFormat::DEPTH16) {
return wgpu::TextureAspect::DepthOnly;
}
if (fFormat == filament::backend::TextureFormat::STENCIL8) {
return wgpu::TextureAspect::StencilOnly;
}
if (fFormat == filament::backend::TextureFormat::DEPTH24_STENCIL8 ||
fFormat == filament::backend::TextureFormat::DEPTH32F_STENCIL8) {
if (isSample) {
return wgpu::TextureAspect::DepthOnly;
}
}
return wgpu::TextureAspect::All;
}
wgpu::TextureView WGPUTexture::makeTextureView(const uint8_t& baseLevel, const uint8_t& levelCount,
SamplerType target) {
// starting with the defaults/basic configuration
wgpu::TextureViewDescriptor textureViewDescriptor{
.label = getUserTextureViewLabel(target),
.format = mFormat,
// dimension depends on target and is set below
.baseMipLevel = baseLevel,
.mipLevelCount = levelCount,
// TODO: check if this baseArrayLayer assumption is correct
// baseArrayLayer is required, making a guess
.baseArrayLayer = 0,
.arrayLayerCount = mArrayLayerCount,
.aspect = mAspect,
// Have not found an analog to aspect in other drivers, but ALL should be unrestrictive.
// TODO Can we make this better?
.aspect = wgpu::TextureAspect::All,
.usage = mUsage
};
// adjust for specific cases
switch (target) {
case SamplerType::SAMPLER_2D:
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;
+13 -16
View File
@@ -74,8 +74,8 @@ public:
private:
// TODO: can we do better in terms on heap management.
std::vector<wgpu::VertexBufferLayout> mVertexBufferLayout{};
std::vector<std::vector<wgpu::VertexAttribute>> mAttributes{};
std::vector<wgpu::VertexBufferLayout> mVertexBufferLayout {};
std::vector<std::vector<wgpu::VertexAttribute>> mAttributes {};
};
struct WGPUVertexBuffer : public HwVertexBuffer {
@@ -145,8 +145,8 @@ public:
[[nodiscard]] uint32_t const* setDynamicOffsets(uint32_t const* offsets);
[[nodiscard]] bool getIsLocked() const { return mBindGroup != nullptr; }
[[nodiscard]] size_t countEntitiesWithDynamicOffsets() const;
private:
static wgpu::Buffer sDummyUniformBuffer;
static wgpu::Texture sDummyTexture;
static wgpu::TextureView sDummyTextureView;
@@ -161,10 +161,10 @@ private:
// Also storing the wgpu ObjectBase takes care of ownership challenges in theory
wgpu::BindGroupLayout mLayout = nullptr;
static constexpr uint8_t INVALID_INDEX = MAX_DESCRIPTOR_COUNT + 1;
std::array<uint8_t, MAX_DESCRIPTOR_COUNT> mEntryIndexByBinding{};
std::array<uint8_t, MAX_DESCRIPTOR_COUNT> mEntryIndexByBinding {};
std::vector<wgpu::BindGroupEntry> mEntriesSortedByBinding;
std::bitset<MAX_DESCRIPTOR_COUNT> mEntriesByBindingWithDynamicOffsets{};
std::bitset<MAX_DESCRIPTOR_COUNT> mEntriesByBindingAdded{};
std::bitset<MAX_DESCRIPTOR_COUNT> mEntriesByBindingWithDynamicOffsets {};
std::bitset<MAX_DESCRIPTOR_COUNT> mEntriesByBindingAdded {};
std::vector<uint32_t> mDynamicOffsets;
wgpu::BindGroup mBindGroup = nullptr;
};
@@ -177,14 +177,11 @@ public:
WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
[[nodiscard]] const wgpu::Texture& getTexture() const { return mTexture; }
[[nodiscard]] const wgpu::TextureView& getTexView() const { return mTexView; }
const wgpu::Texture& getTexture() const { return mTexture; }
const wgpu::TextureView& getTexView() const { return mTexView; }
static wgpu::TextureFormat fToWGPUTextureFormat(
filament::backend::TextureFormat const& fFormat);
static wgpu::TextureAspect fToWGPUTextureViewAspect(
filament::backend::TextureUsage const& fUsage,
filament::backend::TextureFormat const& fFormat);
// Public to allow checking for support of a texture format
static wgpu::TextureFormat fToWGPUTextureFormat(const filament::backend::TextureFormat& fUsage);
private:
wgpu::TextureView makeTextureView(const uint8_t& baseLevel, const uint8_t& levelCount,
@@ -195,10 +192,10 @@ private:
wgpu::Texture mTexture = nullptr;
wgpu::TextureUsage mUsage = wgpu::TextureUsage::None;
wgpu::TextureFormat mFormat = wgpu::TextureFormat::Undefined;
wgpu::TextureAspect mAspect = wgpu::TextureAspect::Undefined;
uint32_t mArrayLayerCount = 1;
wgpu::TextureView mTexView = nullptr;
wgpu::TextureUsage fToWGPUTextureUsage(filament::backend::TextureUsage const& fUsage);
wgpu::TextureUsage fToWGPUTextureUsage(const filament::backend::TextureUsage& fUsage,
const bool supportsTransientAttachment);
};
struct WGPURenderPrimitive : public HwRenderPrimitive {
@@ -256,7 +253,7 @@ private:
Attachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
math::uint2 attachmentSize = {};
std::vector<wgpu::RenderPassColorAttachment> colorAttachments{};
std::vector<wgpu::RenderPassColorAttachment> colorAttachments {};
};
}// namespace filament::backend
@@ -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);
@@ -211,10 +211,6 @@ uint32_t RenderTargetDump::hash() const {
return mInternal->hash();
}
const std::vector<unsigned char>& RenderTargetDump::bytes() const {
return mInternal->bytes;
}
bool RenderTargetDump::bytesFilled() const {
return mInternal->bytesFilled;
}
@@ -85,12 +85,6 @@ public:
* @return The hash of the stored bytes.
*/
uint32_t hash() const;
/**
* Gets the bytes of the render target. The hash should usually be preferable for comparisons
* but this is available for debugging.
* @return The stored bytes.
*/
const std::vector<unsigned char>& bytes() const;
/**
* Thread safe as this is backed by an atomic.
* Once this returns true it will never return false.
@@ -17,6 +17,7 @@
#include "ShaderGenerator.h"
#include <GlslangToSpv.h>
#include <SPVRemapper.h>
#include <spirv_glsl.hpp>
#include <spirv_msl.hpp>
+1 -12
View File
@@ -32,24 +32,13 @@ do {
} \
} while (false)
#define NONFATAL_FAIL_IF(skipEnvironment, rationale) \
do { \
SkipEnvironment skip(skipEnvironment); \
if (skip.matches()) { \
ADD_FAILURE() \
<< "Failing test as the " << skip.describe() << "\n" \
<< " This test has a known failure where " \
<< rationale; \
} \
} while (false)
#define FAIL_IF(skipEnvironment, rationale) \
do { \
SkipEnvironment skip(skipEnvironment); \
if (skip.matches()) { \
GTEST_FAIL() \
<< "Failing test as the " << skip.describe() << "\n" \
<< " This test should be able to succeed but it needs to fail early because " \
<< " This test should be able to succeed but it needs to fail early because" \
<< rationale; \
} \
} while (false)
@@ -116,23 +116,13 @@ if __name__ == "__main__":
'Remember to sync CMake after running this to '
'move the new expected images to the binary '
'directory.')
parser.add_argument('-r', '--results_path',
help='The path with the generated images directory, which should be where '
'the test binary was run.')
parser.add_argument('-s', '--source_expected_path', default="./expected_images",
help='The directory that updated expected images should be written to, '
'which should be the source directory copy.')
parser.add_argument('-r', '--results_path')
parser.add_argument('-s', '--source_expected_path', default="./expected_images")
# The mutually exclusive options for how to process the actual images
parser.add_argument('-b', '--batch', action='extend', nargs='*',
help='If true copy all actual images to the source expected image '
'directory.')
parser.add_argument('-a', '--all', action='store_true',
help='If true, visually compare all generated images.')
parser.add_argument('-t', '--tests', action='store_true',
help='If true use a test_detail.xml file that exists in the results_path '
'directory to visually compare all images that failed a test.')
parser.add_argument('-c', '--compare', action='extend', nargs='*',
help='A list of image names to visually compare (without the .png suffix).')
parser.add_argument('-b', '--batch', action='extend', nargs='*')
parser.add_argument('-a', '--all', action='store_true')
parser.add_argument('-t', '--tests', action='store_true')
parser.add_argument('-c', '--compare', action='extend', nargs='*')
args = parser.parse_args()
if not args.results_path:
+1 -3
View File
@@ -260,8 +260,6 @@ TEST_F(BlitTest, ColorMinify) {
}
TEST_F(BlitTest, ColorResolve) {
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"Nothing is drawn, see b/417229577");
auto& api = getDriverApi();
constexpr int kSrcTexWidth = 256;
@@ -492,7 +490,7 @@ TEST_F(BlitTest, BlitRegion) {
}
TEST_F(BlitTest, BlitRegionToSwapChain) {
FAIL_IF(Backend::VULKAN, "Crashes due to not finding color attachment, see b/417481493");
FAIL_IF(Backend::VULKAN, "Crashes due to not finding color attachment");
auto& api = getDriverApi();
mCleanup.addPostCall([&]() { executeCommands(); });
@@ -20,7 +20,6 @@
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "Skip.h"
#include "TrianglePrimitive.h"
namespace test {
@@ -160,8 +159,6 @@ TEST_F(BufferUpdatesTest, VertexBufferUpdate) {
// This test renders two triangles in two separate draw calls. Between the draw calls, a uniform
// buffer object is partially updated.
TEST_F(BufferUpdatesTest, BufferObjectUpdateWithOffset) {
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"All values including alpha are written as 0, see b/417254943");
auto& api = getDriverApi();
Cleanup cleanup(api);
-2
View File
@@ -26,7 +26,6 @@ namespace test {
TEST_F(BackendTest, FrameScheduledCallback) {
SKIP_IF(Backend::OPENGL, "Frame callbacks are unsupported in OpenGL");
SKIP_IF(Backend::VULKAN, "Frame callbacks are unsupported in Vulkan, see b/417254479");
auto& api = getDriverApi();
Cleanup cleanup(api);
@@ -86,7 +85,6 @@ TEST_F(BackendTest, FrameScheduledCallback) {
TEST_F(BackendTest, FrameCompletedCallback) {
SKIP_IF(Backend::OPENGL, "Frame callbacks are unsupported in OpenGL");
SKIP_IF(Backend::VULKAN, "Frame callbacks are unsupported in Vulkan, see b/417254479");
auto& api = getDriverApi();
Cleanup cleanup(api);
@@ -99,8 +99,6 @@ struct MaterialParams {
// The problems are caused by both uploading and rendering into the same texture, since the OpenGL
// backend's readPixels does not work correctly with textures that have image data uploaded.
TEST_F(BackendTest, FeedbackLoops) {
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"Image is unexpectedly darker, see b/417226296");
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL),
"OpenGL image is upside down due to readPixels failing for texture with uploaded image "
"data");
+1 -4
View File
@@ -212,7 +212,7 @@ static SamplerFormat getSamplerFormat(TextureFormat textureFormat) {
}
TEST_F(LoadImageTest, UpdateImage2D) {
FAIL_IF(Backend::VULKAN, "Multiple test cases crash, see b/417481434");
FAIL_IF(Backend::VULKAN, "Multiple test cases crash");
// All of these test cases should result in the same rendered image, and thus the same hash.
static const uint32_t expectedHash = 3644679986;
@@ -485,9 +485,6 @@ TEST_F(LoadImageTest, UpdateImageMipLevel) {
}
TEST_F(LoadImageTest, UpdateImage3D) {
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"Checkerboard not drawn, possibly due to using wrong z value of 3d texture, "
"see b/417254499");
auto& api = getDriverApi();
Cleanup cleanup(api);
api.startCapture();
+1 -2
View File
@@ -81,8 +81,7 @@ void main() {
TEST_F(BackendTest, PushConstants) {
SKIP_IF(Backend::OPENGL, "Push constants not supported on OpenGL");
FAIL_IF(Backend::VULKAN,
"Crashing due to no program set when setting push constants, see b/417477740");
FAIL_IF(Backend::VULKAN, "Crashing due to no program set when setting push constants");
auto& api = getDriverApi();
@@ -20,7 +20,6 @@
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "Skip.h"
#include "TrianglePrimitive.h"
#include <utils/Hash.h>
@@ -75,8 +74,6 @@ public:
};
TEST_F(ReadPixelsTest, ReadPixels) {
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"Two cases fail, see b/417255941 and b/417255943");
// These test scenarios use a known hash of the result pixel buffer to decide pass / fail,
// asserting an exact pixel-for-pixel match. So far, rendering on macOS and iPhone have had
// deterministic results. Take this test with a grain of salt, however, as other platform / GPU
-3
View File
@@ -20,7 +20,6 @@
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "Skip.h"
#include "TrianglePrimitive.h"
#include <utils/Hash.h>
@@ -31,8 +30,6 @@ using namespace filament;
using namespace filament::backend;
TEST_F(BackendTest, ScissorViewportRegion) {
NONFATAL_FAIL_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"Affected area in wrong corner, see b/417229118");
auto& api = getDriverApi();
constexpr int kSrcTexWidth = 1024;
@@ -117,8 +117,6 @@ public:
};
TEST_F(BasicStencilBufferTest, StencilBuffer) {
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"Stencil not supported, see b/417230776");
auto& api = getDriverApi();
Cleanup cleanup(api);
@@ -141,8 +139,6 @@ TEST_F(BasicStencilBufferTest, StencilBuffer) {
}
TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"Stencil not supported, see b/417230776");
auto& api = getDriverApi();
Cleanup cleanup(api);
@@ -166,8 +162,6 @@ TEST_F(BasicStencilBufferTest, DepthAndStencilBuffer) {
}
TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"Stencil not supported, see b/417230776");
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::OPENGL), "Stencil isn't applied");
auto& api = getDriverApi();
Cleanup cleanup(api);
+119 -5
View File
@@ -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;
};
+7 -7
View File
@@ -21,6 +21,7 @@
#include <backend/CallbackHandler.h>
#include <backend/DriverEnums.h>
#include <backend/PresentCallable.h>
#include <utils/compiler.h>
#include <utils/Invocable.h>
@@ -34,7 +35,7 @@ class Engine;
/**
* A swap chain represents an Operating System's *native* renderable surface.
*
* Typically, it's a native window or a view. Because a SwapChain is initialized from a
* Typically it's a native window or a view. Because a SwapChain is initialized from a
* native object, it is given to filament as a `void *`, which must be of the proper type
* for each platform filament is running on.
*
@@ -157,7 +158,7 @@ public:
/**
* Requests a SwapChain with an alpha channel.
*/
static constexpr uint64_t CONFIG_TRANSPARENT = backend::SWAP_CHAIN_CONFIG_TRANSPARENT;
static const uint64_t CONFIG_TRANSPARENT = backend::SWAP_CHAIN_CONFIG_TRANSPARENT;
/**
* This flag indicates that the swap chain may be used as a source surface
@@ -167,13 +168,13 @@ public:
* @see
* Renderer.copyFrame()
*/
static constexpr uint64_t CONFIG_READABLE = backend::SWAP_CHAIN_CONFIG_READABLE;
static const uint64_t CONFIG_READABLE = backend::SWAP_CHAIN_CONFIG_READABLE;
/**
* Indicates that the native X11 window is an XCB window rather than an XLIB window.
* This is ignored on non-Linux platforms and in builds that support only one X11 API.
*/
static constexpr uint64_t CONFIG_ENABLE_XCB = backend::SWAP_CHAIN_CONFIG_ENABLE_XCB;
static const uint64_t CONFIG_ENABLE_XCB = backend::SWAP_CHAIN_CONFIG_ENABLE_XCB;
/**
* Indicates that the native window is a CVPixelBufferRef.
@@ -185,7 +186,7 @@ public:
* Filament. Filament will call CVPixelBufferRetain during Engine::createSwapChain, and
* CVPixelBufferRelease when the swap chain is destroyed.
*/
static constexpr uint64_t CONFIG_APPLE_CVPIXELBUFFER =
static const uint64_t CONFIG_APPLE_CVPIXELBUFFER =
backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER;
/**
@@ -302,7 +303,6 @@ public:
*
* @param handler Handler to dispatch the callback or nullptr for the default handler.
* @param callback Callback called when the frame is scheduled.
* @param flags
*
* @remark Only Filament's Metal backend supports PresentCallables and frame callbacks. Other
* backends ignore the callback (which will never be called) and proceed normally.
@@ -314,7 +314,7 @@ public:
FrameScheduledCallback&& callback = {}, uint64_t flags = 0);
/**
* Returns whether this SwapChain currently has a FrameScheduledCallback set.
* Returns whether or not this SwapChain currently has a FrameScheduledCallback set.
*
* @return true, if the last call to setFrameScheduledCallback set a callback
*
+26 -2
View File
@@ -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; }
};
+3 -12
View File
@@ -16,22 +16,13 @@
#include "AtlasAllocator.h"
#include <utils/compiler.h>
#include <utils/algorithm.h>
#include <utils/debug.h>
#include <utils/QuadTree.h>
#include <algorithm>
#include <utility>
#include <stddef.h>
#include <stdint.h>
namespace filament {
using namespace utils;
static constexpr std::pair<uint8_t, uint8_t> unmorton(uint16_t const m) noexcept {
static inline constexpr std::pair<uint8_t, uint8_t> unmorton(uint16_t const m) noexcept {
uint32_t r = (m | (uint32_t(m) << 15u)) & 0x55555555u;
r = (r | (r >> 1u)) & 0x33333333u;
r = (r | (r >> 2u)) & 0x0f0f0f0fu;
@@ -174,8 +165,8 @@ AtlasAllocator::NodeId AtlasAllocator::allocateInLayer(size_t const maxHeight) n
NodeId found{ -1, 0 };
QuadTree::traverse(candidate.l, candidate.code,
[this, n, &found](NodeId const& curr) -> QuadTree::TraversalResult {
size_t const j = index(curr.l, curr.code);
Node& node = mQuadTree[j];
size_t const i = index(curr.l, curr.code);
Node& node = mQuadTree[i];
if (curr.l == n) {
found = curr;
assert_invariant(!node.hasChildren());
+48 -2
View File
@@ -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; }
+143 -12
View File
@@ -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
+2
View File
@@ -51,6 +51,8 @@ private:
uint32_t mDimension;
bool mIsOneDimensional;
bool mIsLDR;
static void initializeSettings(const Builder& builder, Settings& settings) noexcept;
};
FILAMENT_DOWNCAST(ColorGrading)
+2 -2
View File
@@ -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 = {
@@ -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
@@ -52,7 +52,6 @@ public:
using Precision = backend::Precision;
using SamplerParams = backend::SamplerParams;
using Binding = backend::descriptor_binding_t;
using ShaderStageFlags = backend::ShaderStageFlags;
struct SamplerInfo { // NOLINT(cppcoreguidelines-pro-type-member-init)
utils::CString name; // name of this sampler
@@ -62,7 +61,6 @@ public:
Format format; // format of this sampler
Precision precision; // precision of this sampler
bool multisample; // multisample capable
ShaderStageFlags stages; // stages the sampler can be accessed from
};
using SamplerInfoList = utils::FixedCapacityVector<SamplerInfo>;
@@ -84,8 +82,6 @@ public:
Format format; // format of this sampler
Precision precision; // precision of this sampler
bool multisample = false; // multisample capable
ShaderStageFlags stages =
ShaderStageFlags::ALL_SHADER_STAGE_FLAGS; // shader stages using this sampler
};
// Give a name to this sampler interface block
@@ -95,8 +91,8 @@ public:
// Add a sampler
Builder& add(std::string_view samplerName, Binding binding, Type type, Format format,
Precision precision = Precision::MEDIUM, bool multisample = false,
ShaderStageFlags stages = ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) noexcept;
Precision precision = Precision::MEDIUM,
bool multisample = false) noexcept;
// Add multiple samplers
Builder& add(std::initializer_list<ListEntry> list) noexcept;
@@ -49,13 +49,13 @@ SamplerInterfaceBlock::Builder::stageFlags(backend::ShaderStageFlags stageFlags)
return *this;
}
SamplerInterfaceBlock::Builder& SamplerInterfaceBlock::Builder::add(std::string_view samplerName,
Binding binding, Type type, Format format, Precision precision, bool multisample,
ShaderStageFlags stages) noexcept {
SamplerInterfaceBlock::Builder& SamplerInterfaceBlock::Builder::add(
std::string_view samplerName, Binding binding, Type type, Format format,
Precision precision, bool multisample) noexcept {
mEntries.push_back({
{ samplerName.data(), samplerName.size() }, // name
{ }, // uniform name
binding, type, format, precision, multisample, stages });
binding, type, format, precision, multisample });
return *this;
}
@@ -66,7 +66,7 @@ SamplerInterfaceBlock SamplerInterfaceBlock::Builder::build() {
SamplerInterfaceBlock::Builder& SamplerInterfaceBlock::Builder::add(
std::initializer_list<ListEntry> list) noexcept {
for (auto& e : list) {
add(e.name, e.binding, e.type, e.format, e.precision, e.multisample, e.stages);
add(e.name, e.binding, e.type, e.format, e.precision, e.multisample);
}
return *this;
}
@@ -91,7 +91,6 @@ SamplerInterfaceBlock::SamplerInterfaceBlock(Builder const& builder) noexcept
size_t const i = std::distance(builder.mEntries.data(), &e);
SamplerInfo& info = samplersInfoList[i];
info = e;
info.stages &= builder.mStageFlags;
info.uniformName = generateUniformName(mName.c_str(), e.name.c_str());
infoMap[{ info.name.data(), info.name.size() }] = i; // info.name.c_str() guaranteed constant
}
+1 -3
View File
@@ -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
-2
View File
@@ -35,7 +35,6 @@ using OutputTarget = MaterialBuilder::OutputTarget;
using OutputQualifier = MaterialBuilder::VariableQualifier;
using OutputType = MaterialBuilder::OutputType;
using ConstantType = MaterialBuilder::ConstantType;
using ShaderStageType = MaterialBuilder::ShaderStageFlags;
// Convenience methods to convert std::string to Enum and also iterate over Enum values.
class Enums {
@@ -80,7 +79,6 @@ private:
static std::unordered_map<std::string, OutputQualifier> mStringToOutputQualifier;
static std::unordered_map<std::string, OutputType> mStringToOutputType;
static std::unordered_map<std::string, ConstantType> mStringToConstantType;
static std::unordered_map<std::string, ShaderStageType> mStringToShaderStageType;
};
template<typename T>
@@ -254,7 +254,6 @@ public:
using FeatureLevel = filament::backend::FeatureLevel;
using StereoscopicType = filament::backend::StereoscopicType;
using ShaderStage = filament::backend::ShaderStage;
using ShaderStageFlags = filament::backend::ShaderStageFlags;
enum class VariableQualifier : uint8_t {
OUT
@@ -323,9 +322,9 @@ public:
*/
MaterialBuilder& parameter(const char* name, SamplerType samplerType,
SamplerFormat format = SamplerFormat::FLOAT,
ParameterPrecision precision = ParameterPrecision::DEFAULT, bool multisample = false,
const char* transformName = "",
ShaderStageFlags stages = ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) noexcept;
ParameterPrecision precision = ParameterPrecision::DEFAULT,
bool multisample = false,
const char* transformName = "") noexcept;
MaterialBuilder& buffer(filament::BufferInterfaceBlock bib) noexcept;
@@ -657,10 +656,8 @@ public:
Parameter() noexcept: parameterType(INVALID) {}
// Sampler
Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p,
bool ms, const char* tn, ShaderStageFlags s)
: name(paramName), size(1), precision(p), samplerType(t), format(f),
parameterType(SAMPLER), multisample(ms), transformName(tn), stages(s) { }
Parameter(const char* paramName, SamplerType t, SamplerFormat f, ParameterPrecision p, bool ms, const char* tn)
: name(paramName), size(1), precision(p), samplerType(t), format(f), parameterType(SAMPLER), multisample(ms), transformName(tn) { }
// Uniform
Parameter(const char* paramName, UniformType t, size_t typeSize, ParameterPrecision p)
@@ -679,7 +676,6 @@ public:
SamplerFormat format;
bool multisample;
utils::CString transformName;
ShaderStageFlags stages;
enum {
INVALID,
UNIFORM,
-11
View File
@@ -175,15 +175,4 @@ std::unordered_map<std::string, ConstantType>& Enums::getMap<ConstantType>() noe
return mStringToConstantType;
};
std::unordered_map<std::string, ShaderStageType> Enums::mStringToShaderStageType = {
{ "fragment", ShaderStageType::FRAGMENT },
{ "vertex", ShaderStageType::VERTEX },
{ "compute", ShaderStageType::COMPUTE },
};
template <>
std::unordered_map<std::string, ShaderStageType>& Enums::getMap<ShaderStageType>() noexcept {
return mStringToShaderStageType;
};
} // namespace filamat
+27 -9
View File
@@ -17,6 +17,7 @@
#include "GLSLPostProcessor.h"
#include <GlslangToSpv.h>
#include <SPVRemapper.h>
#include <spirv-tools/libspirv.hpp>
#include <spirv_glsl.hpp>
@@ -144,12 +145,12 @@ DescriptorSetLayout getPerMaterialDescriptorSet(SamplerInterfaceBlock const& sib
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT,
+PerMaterialBindingPoints::MATERIAL_PARAMS, DescriptorFlags::NONE, 0 });
for (auto const& sampler: samplers) {
layout.bindings.push_back(DescriptorSetLayoutBinding{
(sampler.type == SamplerInterfaceBlock::Type::SAMPLER_EXTERNAL)
? DescriptorType::SAMPLER_EXTERNAL
: DescriptorType::SAMPLER,
sampler.stages, sampler.binding, DescriptorFlags::NONE, 0 });
for (auto const& sampler : samplers) {
layout.bindings.push_back(DescriptorSetLayoutBinding {
(sampler.type == SamplerInterfaceBlock::Type::SAMPLER_EXTERNAL) ?
DescriptorType::SAMPLER_EXTERNAL : DescriptorType::SAMPLER,
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, sampler.binding,
DescriptorFlags::NONE, 0 });
}
return layout;
@@ -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(
-1
View File
@@ -23,7 +23,6 @@
#include <private/filament/SamplerInterfaceBlock.h>
#include "ShaderMinifier.h"
#include "SpirvRemapWrapper.h"
#include <spirv-tools/optimizer.hpp>
+4 -5
View File
@@ -302,8 +302,7 @@ MaterialBuilder& MaterialBuilder::parameter(const char* name, UniformType const
MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType samplerType,
SamplerFormat format, ParameterPrecision precision, bool multisample,
const char* transformName, ShaderStageFlags stages) noexcept {
SamplerFormat format, ParameterPrecision precision, bool multisample, const char* transformName) noexcept {
FILAMENT_CHECK_PRECONDITION(!multisample ||
(format != SamplerFormat::SHADOW &&
(samplerType == SamplerType::SAMPLER_2D ||
@@ -312,7 +311,7 @@ MaterialBuilder& MaterialBuilder::parameter(const char* name, SamplerType sample
" as long as type is not SHADOW";
FILAMENT_CHECK_POSTCONDITION(mParameterCount < MAX_PARAMETERS_COUNT) << "Too many parameters";
mParameters[mParameterCount++] = { name, samplerType, format, precision, multisample, transformName, stages };
mParameters[mParameterCount++] = { name, samplerType, format, precision, multisample, transformName };
return *this;
}
@@ -638,8 +637,8 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept {
auto const& param = mParameters[i];
assert_invariant(!param.isSubpass());
if (param.isSampler()) {
sbb.add({ param.name.data(), param.name.size() }, binding, param.samplerType,
param.format, param.precision, param.multisample, param.stages);
sbb.add({ param.name.data(), param.name.size() },
binding, param.samplerType, param.format, param.precision, param.multisample);
if (!param.transformName.empty()) {
ibb.add({{{ param.transformName.data(), param.transformName.size() }, uint8_t(binding),
0, UniformType::MAT3, Precision::DEFAULT, FeatureLevel::FEATURE_LEVEL_0 }});
-54
View File
@@ -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
-30
View File
@@ -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
@@ -233,7 +233,7 @@ void MaterialDescriptorSetLayoutChunk::flatten(Flattener& f) {
} else {
f.writeUint8(uint8_t(DescriptorType::SAMPLER));
}
f.writeUint8(uint8_t(entry.stages));
f.writeUint8(uint8_t(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT));
f.writeUint8(entry.binding);
f.writeUint8(uint8_t(DescriptorFlags::NONE));
f.writeUint16(0);
-1
View File
@@ -26,7 +26,6 @@
#include <filamat/MaterialBuilder.h>
#include <utils/JobSystem.h>
#include <utils/Panic.h>
#include <memory>
-1
View File
@@ -21,7 +21,6 @@
#include <utils/JobSystem.h>
#include <utils/Log.h>
#include <utils/Panic.h>
using namespace filament;
using namespace filament::math;
@@ -24,7 +24,6 @@
#include <geometry/TangentSpaceMesh.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/StructureOfArrays.h>
#include <cstring>
-1
View File
@@ -22,7 +22,6 @@
#include <string_view>
#include <utils/Log.h>
#include <utils/Panic.h>
using namespace utils;
using namespace std::literals;
-2
View File
@@ -65,8 +65,6 @@ set(SRCS
src/CyclicBarrier.cpp
src/EntityManager.cpp
src/EntityManagerImpl.h
src/FixedCapacityVectorBase.cpp
src/Invocable.cpp
src/JobSystem.cpp
src/Log.cpp
src/NameComponentManager.cpp
+19 -76
View File
@@ -20,9 +20,9 @@
// NOTE: this header should not include STL headers
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <string_view>
#include <utility>
#include <assert.h>
#include <stddef.h>
@@ -31,9 +31,6 @@
#include <string.h>
namespace utils {
namespace io {
class ostream;
}
//! \privatesection
struct hashCStrings {
@@ -41,7 +38,7 @@ struct hashCStrings {
typedef size_t result_type;
result_type operator()(argument_type cstr) const noexcept {
size_t hash = 5381;
while (int const c = static_cast<unsigned char>(*cstr++)) {
while (int const c = *cstr++) {
hash = (hash * 33u) ^ size_t(c);
}
return hash;
@@ -100,7 +97,11 @@ public:
return *this;
}
~CString() noexcept;
~CString() noexcept {
if (mData) {
free(mData - 1);
}
}
void swap(CString& other) noexcept {
// don't use std::swap(), we don't want an STL dependency in this file
@@ -109,8 +110,8 @@ public:
other.mCStr = temp;
}
const_pointer c_str() const noexcept { return mCStr; }
pointer c_str() noexcept { return mCStr; }
const_pointer c_str() const noexcept { return const_cast<CString*>(this)->c_str(); }
const_pointer c_str_safe() const noexcept { return mData ? c_str() : ""; }
const_pointer data() const noexcept { return c_str(); }
pointer data() noexcept { return c_str(); }
@@ -118,89 +119,33 @@ public:
size_type length() const noexcept { return size(); }
bool empty() const noexcept { return size() == 0; }
iterator begin() noexcept { return c_str(); }
iterator begin() noexcept { return mCStr; }
iterator end() noexcept { return begin() + length(); }
const_iterator begin() const noexcept { return data(); }
const_iterator end() const noexcept { return begin() + length(); }
const_iterator cbegin() const noexcept { return begin(); }
const_iterator cend() const noexcept { return end(); }
// replace
template<size_t N>
CString& replace(size_type const pos,
size_type const len, const StringLiteral<N>& str) & noexcept {
return replace(pos, len, str, N - 1);
}
CString& replace(size_type pos, size_type len, const CString& str) noexcept;
CString& insert(size_type pos, const CString& str) noexcept { return replace(pos, 0, str); }
CString& append(const CString& str) noexcept { return insert(length(), str); }
CString& replace(size_type const pos, size_type const len, const CString& str) & noexcept {
return replace(pos, len, str.c_str_safe(), str.size());
}
template<size_t N>
CString&& replace(size_type const pos,
size_type const len, const StringLiteral<N>& str) && noexcept {
return std::move(replace(pos, len, str));
}
CString&& replace(size_type const pos, size_type const len, const CString& str) && noexcept {
return std::move(replace(pos, len, str));
}
// insert
template<size_t N>
CString& insert(size_type const pos, const StringLiteral<N>& str) & noexcept {
return replace(pos, 0, str);
}
CString& insert(size_type const pos, const CString& str) & noexcept {
return replace(pos, 0, str);
}
template<size_t N>
CString&& insert(size_type const pos, const StringLiteral<N>& str) && noexcept {
return std::move(*this).replace(pos, 0, str);
}
CString&& insert(size_type const pos, const CString& str) && noexcept {
return std::move(*this).replace(pos, 0, str);
}
// append
template<size_t N>
CString& append(const StringLiteral<N>& str) & noexcept {
return insert(length(), str);
}
CString& append(const CString& str) & noexcept {
return insert(length(), str);
}
template<size_t N>
CString&& append(const StringLiteral<N>& str) && noexcept {
return std::move(*this).insert(length(), str);
}
CString&& append(const CString& str) && noexcept {
return std::move(*this).insert(length(), str);
}
const_reference operator[](size_type const pos) const noexcept {
const_reference operator[](size_type pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
reference operator[](size_type const pos) noexcept {
reference operator[](size_type pos) noexcept {
assert(pos < size());
return begin()[pos];
}
const_reference at(size_type const pos) const noexcept {
const_reference at(size_type pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
reference at(size_type const pos) noexcept {
reference at(size_type pos) noexcept {
assert(pos < size());
return begin()[pos];
}
@@ -226,7 +171,7 @@ public:
}
// placement new declared as "throw" to avoid the compiler's null-check
void* operator new(size_t, void* ptr) {
inline void* operator new(size_t, void* ptr) {
assert(ptr);
return ptr;
}
@@ -240,8 +185,6 @@ public:
};
private:
CString& replace(size_type pos, size_type len, char const* str, size_t l) & noexcept;
#if !defined(NDEBUG)
friend io::ostream& operator<<(io::ostream& out, const CString& rhs);
#endif
@@ -282,7 +225,7 @@ private:
}
};
// Implement this for your type for automatic conversion to CString. Failing to do so leads
// implement this for your type for automatic conversion to CString. Failing to do so leads
// to a compile-time failure.
template<typename T>
CString to_string(T value) noexcept;
@@ -306,7 +249,7 @@ public:
pointer c_str() noexcept { return mData; }
private:
value_type mData[N] = {};
value_type mData[N] = {0};
};
} // namespace utils
+6 -8
View File
@@ -23,11 +23,9 @@
#include <utils/CString.h>
#include <utils/compiler.h>
#include <utils/ostream.h>
namespace utils {
namespace io {
class ostream;
}
/**
* CallStack captures the current's thread call stack.
@@ -90,23 +88,23 @@ public:
bool operator <(const CallStack& rhs) const;
bool operator >(const CallStack& rhs) const {
inline bool operator >(const CallStack& rhs) const {
return rhs < *this;
}
bool operator !=(const CallStack& rhs) const {
inline bool operator !=(const CallStack& rhs) const {
return *this < rhs || rhs < *this;
}
bool operator >=(const CallStack& rhs) const {
inline bool operator >=(const CallStack& rhs) const {
return !operator <(rhs);
}
bool operator <=(const CallStack& rhs) const {
inline bool operator <=(const CallStack& rhs) const {
return !operator >(rhs);
}
bool operator ==(const CallStack& rhs) const {
inline bool operator ==(const CallStack& rhs) const {
return !operator !=(rhs);
}
+12 -17
View File
@@ -19,8 +19,8 @@
#include <utils/compiler.h>
#include <utils/compressed_pair.h>
#include <utils/Panic.h>
#include <algorithm>
#include <initializer_list>
#include <iterator>
#include <limits>
@@ -40,11 +40,6 @@
namespace utils {
class FixedCapacityVectorBase {
protected:
UTILS_NORETURN static void capacityCheckFailed(size_t capacity, size_t size);
};
/**
* FixedCapacityVector is (almost) a drop-in replacement for std::vector<> except it has a
* fixed capacity decided at runtime. The vector storage is never reallocated unless reserve()
@@ -61,7 +56,7 @@ protected:
* the optional value argument, e.g. FixedCapacityVector<int>(4, 0) or foo.resize(4, 0).
*/
template<typename T, typename A = std::allocator<T>, bool CapacityCheck = true>
class UTILS_PUBLIC FixedCapacityVector : protected FixedCapacityVectorBase {
class UTILS_PUBLIC FixedCapacityVector {
public:
using allocator_type = A;
using value_type = T;
@@ -271,7 +266,7 @@ public:
mSize = 0;
}
void resize(size_type const count) {
void resize(size_type count) {
assertCapacityForSize(count);
if constexpr(std::is_trivially_constructible_v<value_type> &&
std::is_trivially_destructible_v<value_type>) {
@@ -282,12 +277,12 @@ public:
}
}
void resize(size_type const count, const_reference v) {
void resize(size_type count, const_reference v) {
assertCapacityForSize(count);
resize_non_trivial(count, v);
}
void swap(FixedCapacityVector& other) noexcept {
void swap(FixedCapacityVector& other) {
using std::swap;
swap(mData, other.mData);
swap(mSize, other.mSize);
@@ -331,16 +326,16 @@ private:
return mCapacityAllocator.second();
}
iterator assertCapacityForSize(size_type const s) {
iterator assertCapacityForSize(size_type s) {
if constexpr(CapacityCheck || FILAMENT_FORCE_CAPACITY_CHECK) {
if (UTILS_VERY_UNLIKELY(capacity() < s)) {
capacityCheckFailed(capacity(), s);
}
FILAMENT_CHECK_PRECONDITION(capacity() >= s)
<< "capacity exceeded: requested size " << (unsigned long)s
<< "u, available capacity " << (unsigned long)capacity() << "u.";
}
return end();
}
void construct(iterator const first, iterator const last) noexcept {
inline void construct(iterator first, iterator last) noexcept {
// we check for triviality here so that the implementation could be non-inline
if constexpr(!std::is_trivially_constructible_v<value_type>) {
construct_non_trivial(first, last);
@@ -363,7 +358,7 @@ private:
}
void destroy(iterator const first, iterator const last) noexcept {
inline void destroy(iterator first, iterator last) noexcept {
// we check for triviality here so that the implementation could be non-inline
if constexpr(!std::is_trivially_destructible_v<value_type>) {
destroy_non_trivial(first, last);
@@ -424,7 +419,7 @@ private:
explicit SizeTypeWrapper(TYPE value) noexcept : value(value) { }
SizeTypeWrapper& operator=(TYPE rhs) noexcept { value = rhs; return *this; }
SizeTypeWrapper& operator=(SizeTypeWrapper& rhs) noexcept = delete;
operator TYPE() const noexcept { return value; } // NOLINT(*-explicit-constructor)
operator TYPE() const noexcept { return value; }
};
pointer mData{};
+7 -13
View File
@@ -14,8 +14,10 @@
* limitations under the License.
*/
#ifndef TNT_UTILS_INVOCABLE_H
#define TNT_UTILS_INVOCABLE_H
#ifndef TNT_UTILS_INVOKABLE_H
#define TNT_UTILS_INVOKABLE_H
#include <utils/ostream.h>
#include <type_traits>
#include <utility>
@@ -23,9 +25,6 @@
#include <assert.h>
namespace utils {
namespace io {
class ostream;
}
/*
* Invocable is a move-only general purpose function wrapper. Instances can
@@ -51,16 +50,11 @@ template<typename Fn, typename R, typename... Args>
using EnableIfFnMatchesInvocable = std::enable_if_t<true, int>;
#endif
class InvocableBase {
protected:
static io::ostream& printInvocable(io::ostream& out, const char* name);
};
template<typename Signature>
class Invocable;
template<typename R, typename... Args>
class Invocable<R(Args...)> : protected InvocableBase {
class Invocable<R(Args...)> {
public:
// Creates an Invocable that does not contain a functor.
// Will evaluate to false.
@@ -91,7 +85,7 @@ public:
private:
#if !defined(NDEBUG)
friend io::ostream& operator<<(io::ostream& out, const Invocable&) {
return printInvocable(out, "Invocable<>"); // TODO: is there a way to do better here?
return out << "Invocable<>"; // TODO: is there a way to do better here?
}
#endif
void* mInvocable = nullptr;
@@ -162,4 +156,4 @@ Invocable<R(Args...)>::operator bool() const noexcept {
} // namespace utils
#endif // TNT_UTILS_INVOCABLE_H
#endif // TNT_UTILS_INVOKABLE_H
+14 -16
View File
@@ -353,7 +353,7 @@ public:
* The TPanic<> class implements the std::exception protocol as well as the Panic
* interface common to all exceptions thrown by the framework.
*/
template <typename>
template <typename T>
class UTILS_PUBLIC TPanic : public Panic {
public:
// std::exception protocol
@@ -398,11 +398,12 @@ public:
* @see PANIC_PRECONDITION, PANIC_POSTCONDITION, PANIC_ARITHMETIC
* @see setMode()
*/
static void panic(
static inline void panic(
char const* function, char const* file, int line, char const* literal,
std::string reason) UTILS_NORETURN;
private:
protected:
/**
* Creates a Panic with extra information about the error-site.
* @param function the name of the function where the error was detected
@@ -414,14 +415,11 @@ private:
TPanic(char const* function, char const* file, int line, char const* literal,
std::string reason);
friend class PreconditionPanic;
friend class PostconditionPanic;
friend class ArithmeticPanic;
protected:
~TPanic() override;
private:
void buildMessage();
char const* const mFile = nullptr; // file where the panic happened
char const* const mFunction = nullptr; // function where the panic happened
int const mLine = -1; // line where the panic happened
@@ -445,7 +443,7 @@ void panicLog(
* ASSERT_PRECONDITION uses this Panic to report a precondition failure.
* @see ASSERT_PRECONDITION
*/
class UTILS_PUBLIC PreconditionPanic final : public TPanic<PreconditionPanic> {
class UTILS_PUBLIC PreconditionPanic : public TPanic<PreconditionPanic> {
// Programming error, can be avoided
// e.g.: invalid arguments
using TPanic<PreconditionPanic>::TPanic;
@@ -459,9 +457,9 @@ class UTILS_PUBLIC PreconditionPanic final : public TPanic<PreconditionPanic> {
* ASSERT_POSTCONDITION uses this Panic to report a postcondition failure.
* @see ASSERT_POSTCONDITION
*/
class UTILS_PUBLIC PostconditionPanic final : public TPanic<PostconditionPanic> {
class UTILS_PUBLIC PostconditionPanic : public TPanic<PostconditionPanic> {
// Usually only detectable at runtime
// e.g.: deadlock would occur, arithmetic errors
// e.g.: dead-lock would occur, arithmetic errors
using TPanic<PostconditionPanic>::TPanic;
friend class TPanic<PostconditionPanic>;
constexpr static auto type = "Postcondition";
@@ -473,7 +471,7 @@ class UTILS_PUBLIC PostconditionPanic final : public TPanic<PostconditionPanic>
* ASSERT_ARITHMETIC uses this Panic to report an arithmetic (postcondition) failure.
* @see ASSERT_ARITHMETIC
*/
class UTILS_PUBLIC ArithmeticPanic final : public TPanic<ArithmeticPanic> {
class UTILS_PUBLIC ArithmeticPanic : public TPanic<ArithmeticPanic> {
// A common case of post-condition error
// e.g.: underflow, overflow, internal computations errors
using TPanic<ArithmeticPanic>::TPanic;
@@ -521,11 +519,11 @@ public:
PanicStream& operator<<(const void* value) noexcept;
PanicStream& operator<<(const char* value) noexcept;
PanicStream& operator<<(const unsigned char* value) noexcept;
PanicStream& operator<<(const char* string) noexcept;
PanicStream& operator<<(const unsigned char* string) noexcept;
PanicStream& operator<<(std::string const& value) noexcept;
PanicStream& operator<<(std::string_view const& value) noexcept;
PanicStream& operator<<(std::string const& s) noexcept;
PanicStream& operator<<(std::string_view const& s) noexcept;
protected:
io::sstream mStream;
+5 -2
View File
@@ -19,8 +19,11 @@
#include <utils/compiler.h>
#include <type_traits>
#include <string_view>
#include <stddef.h>
namespace utils {
/**
@@ -38,7 +41,7 @@ public:
using const_iterator = std::string_view::const_iterator;
// Constructor from string literal
template<unsigned int M>
template <size_t M>
constexpr StaticString(const char (&str)[M]) noexcept : mString(str, M - 1) {} // NOLINT(*-explicit-constructor)
constexpr StaticString() noexcept = default;
@@ -58,7 +61,7 @@ public:
return mString[pos];
}
constexpr const_reference at(size_type const pos) const {
constexpr const_reference at(size_type pos) const {
return mString[pos];
}
+16 -25
View File
@@ -20,30 +20,27 @@
#include <utils/ostream.h>
#include <algorithm>
#include <cstdlib>
#include <memory>
namespace utils {
UTILS_NOINLINE
CString::CString(const char* cstr, size_t const length) {
CString::CString(const char* cstr, size_t length) {
if (length && cstr) {
Data* const p = static_cast<Data*>(std::malloc(sizeof(Data) + length + 1));
p->length = size_type(length);
mCStr = reinterpret_cast<value_type*>(p + 1);
Data* p = (Data*)malloc(sizeof(Data) + length + 1);
p->length = (size_type)length;
mCStr = (value_type*)(p + 1);
// we don't use memcpy here to avoid a call to libc, the generated code is pretty good.
std::uninitialized_copy_n(cstr, length, mCStr);
mCStr[length] = '\0';
}
}
CString::CString(size_t const length) {
CString::CString(size_t length) {
if (length) {
Data* const p = static_cast<Data*>(std::malloc(sizeof(Data) + length + 1));
p->length = size_type(length);
mCStr = reinterpret_cast<value_type*>(p + 1);
Data* p = (Data*)malloc(sizeof(Data) + length + 1);
p->length = (size_type)length;
mCStr = (value_type*)(p + 1);
std::fill_n(mCStr, length, 0);
mCStr[length] = '\0';
}
@@ -61,30 +58,24 @@ CString& CString::operator=(const CString& rhs) {
if (this != &rhs) {
auto *const p = mData ? mData - 1 : nullptr;
new(this) CString(rhs);
std::free(p);
free(p);
}
return *this;
}
CString::~CString() noexcept {
if (mData) {
std::free(mData - 1);
}
}
CString& CString::replace(size_type const pos, size_type len, char const* str, size_t const l) & noexcept {
CString& CString::replace(size_type pos, size_type len, const CString& str) noexcept {
assert(pos <= size());
len = std::min(len, size() - pos);
// The new size of the string, after the replacement.
const size_type newSize = size() - len + l;
const size_type newSize = size() - len + str.size();
// Allocate enough memory to hold the new string.
Data* const p = static_cast<Data*>(std::malloc(sizeof(Data) + newSize + 1));
Data* p = (Data*) malloc(sizeof(Data) + newSize + 1);
assert(p);
p->length = newSize;
value_type* newStr = reinterpret_cast<value_type*>(p + 1);
value_type* newStr = (value_type*) (p + 1);
const value_type* beginning = mCStr;
const value_type* replacementStart = mCStr + pos;
@@ -93,7 +84,7 @@ CString& CString::replace(size_type const pos, size_type len, char const* str, s
value_type* ptr = newStr;
ptr = std::uninitialized_copy(beginning, replacementStart, ptr);
ptr = std::uninitialized_copy_n(str, l, ptr);
ptr = std::uninitialized_copy_n(str.c_str_safe(), str.length(), ptr);
ptr = std::uninitialized_copy(replacementEnd, end, ptr);
// null-terminator
@@ -101,14 +92,14 @@ CString& CString::replace(size_type const pos, size_type len, char const* str, s
std::swap(mCStr, newStr);
if (newStr) {
std::free(reinterpret_cast<Data*>(newStr) - 1);
free((Data*) newStr - 1);
}
return *this;
}
#if !defined(NDEBUG)
io::ostream& operator<<(io::ostream& out, const CString& rhs) {
io::ostream& operator<<(io::ostream& out, const utils::CString& rhs) {
return out << rhs.c_str_safe();
}
#endif
@@ -1,36 +0,0 @@
/*
* 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 <utils/FixedCapacityVector.h>
#include <utils/compiler.h>
#include <utils/Panic.h>
#include <stddef.h>
#include <stdlib.h>
namespace utils {
void FixedCapacityVectorBase::capacityCheckFailed(size_t const capacity, size_t const size) {
UTILS_ASSUME(capacity < size);
FILAMENT_CHECK_PRECONDITION(capacity >= size)
<< "capacity exceeded: requested size " << size
<< "u, available capacity " << capacity << "u.";
// In practice, we will never reach this.
abort();
}
} // namespace utils
-24
View File
@@ -1,24 +0,0 @@
/*
* 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 <utils/Invocable.h>
#include <utils/ostream.h>
namespace utils {
io::ostream& InvocableBase::printInvocable(io::ostream& out, const char* name) {
return out << name;
}
} // namespace utils
+22 -23
View File
@@ -18,7 +18,6 @@
#include "ostream_.h"
#include <utils/CallStack.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/ostream.h>
@@ -69,7 +68,7 @@ public:
getCallback().call(panic);
}
void set(Panic::PanicHandlerCallback const handler, void* user) noexcept {
void set(Panic::PanicHandlerCallback handler, void* user) noexcept {
std::lock_guard const lock(mLock);
mCallBack = { handler, user };
}
@@ -97,7 +96,7 @@ static std::string sprintfToString(const char* format, va_list args) noexcept {
return s;
}
static std::string sprintfToString(const char* format, ...) noexcept {
static inline std::string sprintfToString(const char* format, ...) noexcept {
va_list args;
va_start(args, format);
std::string const s{ sprintfToString(format, args) };
@@ -121,14 +120,14 @@ static std::string buildPanicString(
Panic::~Panic() noexcept = default;
void Panic::setPanicHandler(PanicHandlerCallback const handler, void* user) noexcept {
void Panic::setPanicHandler(PanicHandlerCallback handler, void* user) noexcept {
UserPanicHandler::get().set(handler, user);
}
// ------------------------------------------------------------------------------------------------
template<typename T>
TPanic<T>::TPanic(const char* function, const char* file, int const line, char const* literal,
TPanic<T>::TPanic(const char* function, const char* file, int line, char const* literal,
std::string reason)
: mFile(file),
mFunction(function),
@@ -189,13 +188,13 @@ void TPanic<T>::log() const noexcept {
}
UTILS_ALWAYS_INLINE
static const char* formatFile(char const* file) noexcept {
inline static const char* formatFile(char const* file) noexcept {
const char * p = std::strstr(file, "filament/");
return p ? p : file;
}
template<typename T>
void TPanic<T>::panic(char const* function, char const* file, int const line, char const* literal,
void TPanic<T>::panic(char const* function, char const* file, int line, char const* literal,
const char* format, ...) {
va_list args;
va_start(args, format);
@@ -234,7 +233,7 @@ void TPanic<T>::panic(char const* function, char const* file, int line, char con
namespace details {
void panicLog(char const* function, char const* file, int const line, const char* format, ...) noexcept {
void panicLog(char const* function, char const* file, int line, const char* format, ...) noexcept {
va_list args;
va_start(args, format);
std::string const reason{ sprintfToString(format, args) };
@@ -250,79 +249,79 @@ void panicLog(char const* function, char const* file, int const line, const char
PanicStream::PanicStream(
char const* function,
char const* file,
int const line,
int line,
char const* condition) noexcept
: mFunction(function), mFile(file), mLine(line), mLiteral(condition) {
}
PanicStream::~PanicStream() = default;
PanicStream& PanicStream::operator<<(short const value) noexcept {
PanicStream& PanicStream::operator<<(short value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned short const value) noexcept {
PanicStream& PanicStream::operator<<(unsigned short value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(char const value) noexcept {
PanicStream& PanicStream::operator<<(char value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned char const value) noexcept {
PanicStream& PanicStream::operator<<(unsigned char value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(int const value) noexcept {
PanicStream& PanicStream::operator<<(int value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned int const value) noexcept {
PanicStream& PanicStream::operator<<(unsigned int value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(long const value) noexcept {
PanicStream& PanicStream::operator<<(long value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned long const value) noexcept {
PanicStream& PanicStream::operator<<(unsigned long value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(long long int const value) noexcept {
PanicStream& PanicStream::operator<<(long long int value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned long long int const value) noexcept {
PanicStream& PanicStream::operator<<(unsigned long long int value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(float const value) noexcept {
PanicStream& PanicStream::operator<<(float value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(double const value) noexcept {
PanicStream& PanicStream::operator<<(double value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(long double const value) noexcept {
PanicStream& PanicStream::operator<<(long double value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(bool const value) noexcept {
PanicStream& PanicStream::operator<<(bool value) noexcept {
mStream << value;
return *this;
}
-2
View File
@@ -18,8 +18,6 @@
#define TNT_UTILS_OSTREAM__H
#include <utils/ostream.h>
#include <utility>
#include <mutex>
namespace utils::io {
@@ -17,7 +17,6 @@
#include <gtest/gtest.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Panic.h>
using namespace utils;
+2 -2
View File
@@ -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;
}
+3 -3
View File
@@ -65,7 +65,7 @@ static const Vertex TRIANGLE_VERTICES[3] = {
{{cos(M_PI * 4 / 3), sin(M_PI * 4 / 3)}, 0xff0000ffu},
};
static constexpr uint16_t TRIANGLE_INDICES[4] = { 0, 1, 2 , 0};
static constexpr uint16_t TRIANGLE_INDICES[3] = { 0, 1, 2 };
static void printUsage(char* name) {
std::string exec_name(utils::Path(name).getName());
@@ -147,11 +147,11 @@ int main(int argc, char** argv) {
app.vb->setBufferAt(*engine, 0,
VertexBuffer::BufferDescriptor(TRIANGLE_VERTICES, 36, nullptr));
app.ib = IndexBuffer::Builder()
.indexCount(4)
.indexCount(3)
.bufferType(IndexBuffer::IndexType::USHORT)
.build(*engine);
app.ib->setBuffer(*engine,
IndexBuffer::BufferDescriptor(TRIANGLE_INDICES, 8, nullptr));
IndexBuffer::BufferDescriptor(TRIANGLE_INDICES, 6, nullptr));
app.mat = Material::Builder()
.package(RESOURCES_BAKEDCOLOR_DATA, RESOURCES_BAKEDCOLOR_SIZE)
.build(*engine);
-59
View File
@@ -1,59 +0,0 @@
# 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.
#!/usr/bin/bash
source `dirname $0`/src/preamble.sh
function start_render_() {
start_
if [[ ! "$GITHUB_WORKFLOW" ]]; then
if [ ! -d ${MESA_LIB_DIR} ]; then
bash ${BUILD_COMMON_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
mkdir -p ${OUTPUT_DIR}
CXX=`which clang++` CC=`which clang` ./build.sh -f -X ${MESA_DIR} -p desktop debug gltf_viewer
}
function end_render_() {
if [[ ! "$GITHUB_WORKFLOW" ]]; then
deactivate # End python virtual env
fi
end_
}
# Following steps are taken:
# - Get and build mesa
# - Build gltf_viewer
# - Run a test
start_render_ && \
python3 ${RENDERDIFF_TEST_DIR}/src/render.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} && \
end_render_
-54
View File
@@ -1,54 +0,0 @@
import glob
import os
import sys
import pprint
import json
from utils import execute, ArgParseImpl, important_print
from image_diff import same_image
from results import RESULT_OK, RESULT_FAILED, RESULT_MISSING
def _compare_goldens(base_dir, comparison_dir):
render_results = {}
base_files = glob.glob(os.path.join(base_dir, "./**/*.tif"))
for golden_file in base_files:
base_fname = os.path.abspath(golden_file)
test_case = base_fname.replace(f'{os.path.abspath(base_dir)}/', '')
comp_fname = os.path.abspath(os.path.join(comparison_dir, test_case))
if not os.path.exists(comp_fname):
print(f'file name not found: {comp_fname}')
render_results[test_case] = RESULT_MISSING
continue
if not same_image(base_fname, comp_fname):
render_results[test_case] = RESULT_FAILED
else:
render_results[test_case] = RESULT_OK
return render_results
if __name__ == '__main__':
parser = ArgParseImpl()
parser.add_argument('--src', help='Directory of the base of the diff.', required=True)
parser.add_argument('--dest', help='Directory of the comparison of the diff.')
parser.add_argument('--out', help='Directory of output for the result of the diff.')
args, _ = parser.parse_known_args(sys.argv[1:])
dest = args.dest
if not dest:
print('Assume the default renderdiff output folder')
dest = os.path.join(os.getcwd(), './out/renderdiff_tests')
assert os.path.exists(dest), f"Destination folder={dest} does not exist."
results = _compare_goldens(args.src, dest)
if args.out:
assert os.path.exists(arg.out), f"Output folder={dest} does not exist."
with open(os.path.join(args.out, "compare_results.json", 'w')) as f:
f.write(json.dumps(results))
failed = [f" {k}" for k in results.keys() if results[k] != RESULT_OK]
success_count = len(results) - len(failed)
important_print(f'Successfully compared {success_count} / {len(results)} images' +
('\nFailed:\n' + ('\n'.join(failed)) if len(failed) > 0 else ''))
if len(failed) > 0:
exit(1)
+7 -15
View File
@@ -15,7 +15,6 @@
import os
import shutil
import re
import sys
from utils import execute, ArgParseImpl, mkdir_p
@@ -133,19 +132,12 @@ class GoldenManager:
rdiff_dir = os.path.join(assets_dir, GOLDENS_DIR)
shutil.copytree(rdiff_dir, dest_dir, dirs_exist_ok=True)
# The main entry point will enable download content of a branch to a directory
# For testing only
if __name__ == "__main__":
parser = ArgParseImpl()
parser.add_argument('--branch', type=str, help='Branch of the golden repo', default='main')
parser.add_argument('--output', type=str, help='Directory to download to', required=True)
args, _ = parser.parse_known_args(sys.argv[1:])
# prepare goldens working directory
golden_dir = args.output
assert os.path.isdir(golden_dir),\
f"Output directory {golden_dir} does not exist"
# Download the golden repo into the current working directory
golden_manager = GoldenManager(os.getcwd())
golden_manager.download_to(golden_dir, branch=args.branch)
# golden_manager.source_from_and_commit(
# os.path.join(os.getcwd(), 'out/renderdiff_tests'),
# 'First commit (local)',
# branch='branch-test')
# golden_manager.merge_to_main('branch-test', push_to_remote=True)
# golden_manager.download_to(os.path.join(os.getcwd(), 'tmp/goldens'))
@@ -129,7 +129,7 @@ def _remove_comments_from_json_txt(json_txt):
res.append(line)
return '\n'.join(res)
def parse_from_path(config_path):
def parse_test_config_from_path(config_path):
with open(config_path, 'r') as f:
json_txt = json.loads(_remove_comments_from_json_txt(f.read()))
return RenderTestConfig(json_txt)
-45
View File
@@ -1,45 +0,0 @@
# 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.
#!/usr/bin/bash
# Sets up the environment for scripts in test/renderdiff/
OUTPUT_DIR="$(pwd)/out/renderdiff_tests"
RENDERDIFF_TEST_DIR="$(pwd)/test/renderdiff"
MESA_DIR="$(pwd)/mesa/out/"
VENV_DIR="$(pwd)/venv"
BUILD_COMMON_DIR="$(pwd)/build/common"
os_name=$(uname -s)
if [[ "$os_name" == "Linux" ]]; then
MESA_LIB_DIR="${MESA_DIR}lib/x86_64-linux-gnu"
elif [[ "$os_name" == "Darwin" ]]; then
MESA_LIB_DIR="${MESA_DIR}lib"
else
echo "Unsupported platform for renderdiff tests"
exit 1
fi
function start_() {
if [[ "$GITHUB_WORKFLOW" ]]; then
set -ex
fi
}
function end_() {
if [[ "$GITHUB_WORKFLOW" ]]; then
set +ex
fi
}
-3
View File
@@ -1,3 +0,0 @@
RESULT_OK = 'ok'
RESULT_FAILED = 'failed'
RESULT_MISSING = 'missing'
@@ -18,18 +18,31 @@ import json
import glob
import shutil
from utils import execute, ArgParseImpl, mkdir_p, mv_f, important_print
import test_config
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
from results import RESULT_OK, RESULT_FAILED
def _render_test_config(gltf_viewer,
test_config,
output_dir,
opengl_lib=None,
vk_icd=None):
def important_print(msg):
lines = msg.split('\n')
max_len = max([len(l) for l in lines])
print('-' * (max_len + 8))
for line in lines:
diff = max_len - len(line)
information = f'--- {line} ' + (' ' * diff) + '---'
print(information)
print('-' * (max_len + 8))
RESULT_OK = 'ok'
RESULT_FAILED_TO_RENDER = 'failed-to-render'
RESULT_FAILED_IMAGE_DIFF = 'failed-image-diff'
RESULT_FAILED_NO_GOLDEN = 'failed-no-golden'
def run_test(gltf_viewer,
test_config,
output_dir,
opengl_lib=None,
vk_icd=None):
assert os.path.isdir(output_dir), f"output directory {output_dir} does not exist"
assert os.access(gltf_viewer, os.X_OK)
@@ -73,7 +86,7 @@ def _render_test_config(gltf_viewer,
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
result = RESULT_FAILED_TO_RENDER
important_print(f'{test_desc} rendering failed with error={out_code}')
results.append({
@@ -83,6 +96,23 @@ def _render_test_config(gltf_viewer,
})
return named_output_dir, results
def compare_goldens(render_results, output_dir, goldens):
for result in render_results:
if result['result'] != RESULT_OK:
continue
out_tif_basename = f"{result['name']}.tif"
out_tif_name = f'{output_dir}/{out_tif_basename}'
golden_path = goldens.get(out_tif_basename)
if not golden_path:
result['result'] = RESULT_FAILED_NO_GOLDEN
result['result_code'] = 1
elif not same_image(golden_path, out_tif_name):
result['result'] = RESULT_FAILED_IMAGE_DIFF
result['result_code'] = 1
return render_results
if __name__ == "__main__":
parser = ArgParseImpl()
parser.add_argument('--test', help='Configuration of the test', required=True)
@@ -90,26 +120,43 @@ if __name__ == "__main__":
parser.add_argument('--output_dir', help='Output Directory', required=True)
parser.add_argument('--opengl_lib', help='Path to the folder containing OpenGL driver lib (for LD_LIBRARY_PATH)')
parser.add_argument('--vk_icd', help='Path to VK ICD file')
parser.add_argument('--golden_branch', help='Branch of the golden repo to compare against')
args, _ = parser.parse_known_args(sys.argv[1:])
test = test_config.parse_from_path(args.test)
test = parse_test_config_from_path(args.test)
output_dir, results = \
_render_test_config(args.gltf_viewer,
test,
args.output_dir,
opengl_lib=args.opengl_lib,
vk_icd=args.vk_icd)
run_test(args.gltf_viewer,
test,
args.output_dir,
opengl_lib=args.opengl_lib,
vk_icd=args.vk_icd)
with open(f'{output_dir}/render_results.json', 'w') as f:
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'
mkdir_p(tmp_golden_dir)
# Download the golden repo into the current working directory
golden_manager = GoldenManager(os.getcwd())
golden_manager.download_to(tmp_golden_dir, branch=args.golden_branch)
goldens = {
os.path.basename(fpath) : fpath for fpath in \
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))
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 rendered {success_count} / {len(results)} tests' +
('\nFailed:\n' + ('\n'.join(failed)) if len(failed) > 0 else ''))
if len(failed) > 0:
exit(1)
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 ''))
+1 -1
View File
@@ -90,7 +90,7 @@ def _interactive_mode(base_golden_dir):
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/generate.sh',
code, res = execute('bash ./test/renderdiff/test.sh generate',
capture_output=False)
if code != 0:
print('Failed to generate new goldens')
-10
View File
@@ -106,13 +106,3 @@ def mkdir_p(path_str):
def mv_f(src_str, dst_str):
src = pathlib.Path(src_str)
src.replace(dst_str)
def important_print(msg):
lines = msg.split('\n')
max_len = max([len(l) for l in lines])
print('-' * (max_len + 8))
for line in lines:
diff = max_len - len(line)
information = f'--- {line} ' + (' ' * diff) + '---'
print(information)
print('-' * (max_len + 8))
+64 -8
View File
@@ -14,15 +14,71 @@
#!/usr/bin/bash
source `dirname $0`/src/preamble.sh
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"
GOLDEN_DIR=$(pwd)/golden_images
os_name=$(uname -s)
if [[ "$os_name" == "Linux" ]]; then
MESA_LIB_DIR="${MESA_DIR}lib/x86_64-linux-gnu"
elif [[ "$os_name" == "Darwin" ]]; then
MESA_LIB_DIR="${MESA_DIR}lib"
else
echo "Unsupported platform for renderdiff tests"
exit 1
fi
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
}
# Following steps are taken:
# - Get and build mesa
# - Build gltf_viewer
# - Run the python script that runs the test
# - Zip up the result
GOLDEN_BRANCH_PARAM='--golden_branch=main'
if [ "$1" == "generate" ]; then
GOLDEN_BRANCH_PARAM=''
fi
start_ && \
bash `dirname $0`/generate.sh && \
mkdir -p ${GOLDEN_DIR} && \
python3 ${RENDERDIFF_TEST_DIR}/src/golden_manager.py --output=${GOLDEN_DIR} && \
python3 ${RENDERDIFF_TEST_DIR}/src/compare.py \
--src=${GOLDEN_DIR} \
--dest=${OUTPUT_DIR} && \
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 \
--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_PARAM} && \
end_

Some files were not shown because too many files have changed in this diff Show More