Compare commits

...

25 Commits

Author SHA1 Message Date
bridgewaterrobbie
43ab60521c Quick change to hello triangle to comply with WebGPU requirements on alignment. Long term it would check for which backend is in use 2025-05-14 19:59:34 -04:00
bridgewaterrobbie
b7c685647a Revert "Dawn change to enable writeBuffer for size != multiple of 4"
This reverts commit b94c802076.
2025-05-14 19:58:11 -04:00
Ben Doherty
d45a5cc926 Add documentation on using Instruments (#8737) 2025-05-14 14:53:12 -07:00
Powei Feng
361ba2afea renderdiff: separate comparison from rendering (#8733)
- Move the comparison logic into its own script
- Add entry point bash script to generate the renderings
- Separate the preamble bash logic into its own file
2025-05-14 09:48:16 -07:00
Ben Doherty
78419cd992 Add initialize method to PlatformMetal (#8708) 2025-05-13 14:30:02 -07:00
Sungun Park
cc7361dba5 Release Filament 1.60.0 2025-05-13 21:27:27 +00:00
Powei Feng
5c0841ff56 Update release note after cherry-pick 2025-05-13 14:26:13 -07:00
Matthew Hoffman
10af183756 Document which vulkan tests have issues with associated bugs. (#8731)
BUGS=[409100093]
2025-05-13 21:10:34 +00:00
David Neto
56e0e9a424 Move calls to SPV remapper to another .cpp file (#8729) 2025-05-13 20:33:49 +00:00
Mathias Agopian
86a500c846 clean public header dependencies (#8725)
* remove io::ostream dependency from CString.h + clang tidy

* remove ostream.h dependency from Invocable.h

* remove Panic.h dependency from FixedCapacityVector.h

* remove ostream.h dependency from backend/ headers

* clang tidy Panic.h/Panic.cpp
2025-05-13 11:32:45 -07:00
Sungun Park
5b3f13fc1d Revert "Add getter functions for settings to build ColorGrading object (#8699)"
This reverts commit f10d226565.
2025-05-13 11:22:33 -07:00
Powei Feng
1b1dfaa57c build.sh ndk version fix again (#8726)
Fix again after #8717
2025-05-13 06:44:21 +00:00
Powei Feng
bf8c84bbe5 Clean-up build.sh android (#8722)
Failed to build android when using CMake >= 4.0.

Remove a no longer required check for the android build. This check
should be covered by the cmake_minimum_required() call, which is
present in relevant CMakeLists.txts.
2025-05-13 06:29:17 +00:00
Jeremy Nelson
fb179cabbc Using usage and format from filament 2025-05-12 16:15:21 -07:00
Jeremy Nelson
a80ea743e8 Simplifying aspect cases 2025-05-12 16:15:21 -07:00
Jeremy Nelson
97f8106909 removing duplicate return 2025-05-12 16:15:21 -07:00
Jeremy Nelson
d455899b93 Simplify cases and default to All 2025-05-12 16:15:21 -07:00
Jeremy Nelson
63fe439a5e Infer TextureView Aspect from format and usage
Picks an appropriate aspect based on formate and usage.
2025-05-12 16:15:21 -07:00
Ben Doherty
7847220aba Materials: allow specifying the shader stages for samplers (#8720) 2025-05-12 14:20:11 -07:00
Matthew Hoffman
d76ae4395b Mark ExternalImageCocoaGL as final not just its destructor. (#8719)
This is necessary to build on MacOS with homebrew's llvm as opposed to Apple's.
2025-05-12 19:43:30 +00:00
Powei Feng
f994cb58ce gl: add invalidate_framebuffer workaround for Mesa+Intel (#8706)
FIXES=405252622
2025-05-12 18:18:13 +00:00
Sungun Park
52eb682498 Update MATERIAL_VERSION to 60 2025-05-12 16:45:36 +00:00
Mathias Agopian
5a5168a191 CString tidy cleanup and rvalue methods
We add rvalue version of append/insert/replace so that calling
those on a temporary yields to a temporary.

Also add string literal versions of those so that we can 
append/insert/replace a literal without allocation, e.g.:

`CString foo = CString{ bar }.append("baz");`

This will not end-up creating a temporary CString for "baz".
2025-05-09 16:14:01 -07:00
Powei Feng
e85dfe75c8 github: move test script to /test and same for /build (#8714)
- Moved get_mesa.sh to build/common/get-mesa.sh
- Moved check-headers.sh to test/check-headers/test.sh
- Moved check-metal-shaders.sh to test/check-metal-shaders/test.sh
2025-05-09 22:35:18 +00:00
Logan Lawrence
6beb40b0a1 Update release.yml to include native android build files (#8694) 2025-05-09 17:25:00 +00:00
103 changed files with 1077 additions and 768 deletions

View File

@@ -26,4 +26,4 @@ jobs:
path: out/filament-release-darwin.tgz
- name: Check public headers
run: |
build/common/check-headers.sh out/release/filament/include
test/check-headers/test.sh out/release/filament/include

View File

@@ -131,7 +131,7 @@ jobs:
- name: Prerequisites
id: prereqs
run: |
bash test/utils/get_mesa.sh
bash build/common/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*
code-correctness:
name: code-correctness
test-code-correctness:
name: test-code-correctness
runs-on: 'macos-14-xlarge'
steps:
- uses: actions/checkout@v4.1.6

View File

@@ -163,6 +163,9 @@ 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
@@ -184,7 +187,7 @@ jobs:
script: |
const upload = require('./build/common/upload-release-assets');
const { TAG } = process.env;
const globber = await glob.create(['out/*.aar', 'out/*.apk'].join('\n'));
const globber = await glob.create(['out/*.aar', 'out/*.apk', 'out/*.tgz'].join('\n'));
await upload({ github, context }, await globber.glob(), TAG);
build-ios:

View File

@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.59.5'
implementation 'com.google.android.filament:filament-android:1.60.0'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.59.5'
pod 'Filament', '~> 1.60.0'
```
## Documentation

View File

@@ -7,10 +7,15 @@ 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**]
- `ColorGrading::Builder::toneMapper` now takes a `shared_ptr<ToneMapper>`
- matc non-functional change: Update GLSL postprocessor to
isolate calls to SPVRemap from calls to SPIRV-Cross.
## v1.59.5

View File

@@ -18,7 +18,6 @@
#include <filament/ColorGrading.h>
#include <filament/ToneMapper.h>
#include <memory>
#include <math/vec3.h>
#include <math/vec4.h>
@@ -68,9 +67,8 @@ extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_ColorGrading_nBuilderToneMapper(JNIEnv*, jclass,
jlong nativeBuilder, jlong toneMapper_) {
ColorGrading::Builder* builder = (ColorGrading::Builder*) nativeBuilder;
ToneMapper* toneMapper = reinterpret_cast<ToneMapper*>(toneMapper_);
std::shared_ptr<ToneMapper> toneMapperCopy(toneMapper->clone());
builder->toneMapper(toneMapperCopy);
const ToneMapper* toneMapper = (const ToneMapper*) toneMapper_;
builder->toneMapper(toneMapper);
}
#pragma clang diagnostic push

View File

@@ -214,9 +214,9 @@ public class ColorGrading {
*
* The default tone mapping operator is {@link ToneMapper.ACESLegacy}.
*
* 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.
* 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.
*
* @param toneMapper The tone mapping operator to apply to the HDR color buffer
*

View File

@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.59.5
VERSION_NAME=1.60.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.

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 | cut -f 1 -d ".")}
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 ".")}
# Requirements
CMAKE_MAJOR=3
@@ -463,16 +463,6 @@ 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 {

View File

@@ -1023,8 +1023,12 @@ samplerCubemap | Cubemap texture
[Table [materialParamsTypes]: Material parameter types]
Samplers
: Sampler types can also specify a `format` which can be either `int` or `float` (defaults to
`float`).
: 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).
Arrays
: A parameter can define an array of values by appending `[size]` after the type name, where

View File

@@ -19,6 +19,7 @@
- [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)

View File

@@ -0,0 +1,36 @@
# 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.

View File

@@ -20,10 +20,15 @@
#define TNT_FILAMENT_BACKEND_BUFFERDESCRIPTOR_H
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <utility>
#include <stddef.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
class CallbackHandler;
@@ -89,8 +94,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 size,
Callback callback = nullptr, void* user = nullptr) noexcept
BufferDescriptor(void const* buffer, size_t const size,
Callback const callback = nullptr, void* user = nullptr) noexcept
: buffer(const_cast<void*>(buffer)), size(size), mCallback(callback), mUser(user) {
}
@@ -98,11 +103,12 @@ 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 size,
CallbackHandler* handler, Callback callback, void* user = nullptr) noexcept
BufferDescriptor(void const* buffer, size_t const size,
CallbackHandler* handler, Callback const callback, void* user = nullptr) noexcept
: buffer(const_cast<void*>(buffer)), size(size),
mCallback(callback), mUser(user), mHandler(handler) {
}
@@ -116,8 +122,9 @@ 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,
@@ -164,7 +171,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 callback, void* user = nullptr) noexcept {
void setCallback(Callback const callback, void* user = nullptr) noexcept {
this->mCallback = callback;
this->mUser = user;
this->mHandler = nullptr;
@@ -176,7 +183,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 callback, void* user = nullptr) noexcept {
void setCallback(CallbackHandler* handler, Callback const callback, void* user = nullptr) noexcept {
mCallback = callback;
mUser = user;
mHandler = handler;

View File

@@ -19,8 +19,6 @@
#include <backend/DriverApiForward.h>
#include <utils/ostream.h>
#include <initializer_list>
#include <memory>
@@ -28,6 +26,10 @@
#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;

View File

@@ -25,12 +25,11 @@
#include <backend/PresentCallable.h>
#include <utils/BitmaskEnum.h>
#include <utils/CString.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Invocable.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <utils/StaticString.h>
#include <utils/debug.h>
#include <math/vec4.h>
@@ -41,6 +40,10 @@
#include <stddef.h>
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
/**
* Types and enums used by filament's driver.
*

View File

@@ -17,9 +17,6 @@
#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
@@ -27,6 +24,10 @@
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
struct HwBufferObject;

View File

@@ -20,12 +20,14 @@
#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

View File

@@ -24,11 +24,14 @@
#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 {
/**

View File

@@ -20,12 +20,10 @@
#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>
@@ -33,6 +31,10 @@
#include <stddef.h>
#include <stdint.h>
namespace utils::io {
class ostream;
} // namespace utils::io
namespace filament::backend {
class Program {

View File

@@ -19,22 +19,26 @@
#include <backend/Handle.h>
#include <utils/ostream.h>
#include <utility>
#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 level, uint16_t layer) noexcept
: handle(handle), level(level), layer(layer) {
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) noexcept
TargetBufferInfo(Handle<HwTexture> handle, uint8_t const level) noexcept
: handle(handle), level(level) {
}
@@ -70,11 +74,11 @@ private:
TargetBufferInfo mInfos[MAX_SUPPORTED_RENDER_TARGET_COUNT];
public:
TargetBufferInfo const& operator[](size_t i) const noexcept {
TargetBufferInfo const& operator[](size_t const i) const noexcept {
return mInfos[i];
}
TargetBufferInfo& operator[](size_t i) noexcept {
TargetBufferInfo& operator[](size_t const i) noexcept {
return mInfos[i];
}

View File

@@ -39,18 +39,40 @@ 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(
@@ -60,6 +82,8 @@ 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;
@@ -68,6 +92,8 @@ 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 {
/**

View File

@@ -27,8 +27,11 @@
#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>

View File

@@ -17,6 +17,7 @@
#include <backend/Platform.h>
#include <utils/compiler.h>
#include <utils/ostream.h>
#include <atomic>
#include <utility>

View File

@@ -19,6 +19,8 @@
#include "MetalContext.h"
#include <utils/Panic.h>
namespace filament {
namespace backend {

View File

@@ -721,15 +721,25 @@ 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 = {\n", dslh.getId());
DEBUG_LOG("createDescriptorSetLayoutR(dslh = %d, info = { label = %s,\n", dslh.getId(),
labelStr);
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));
}

View File

@@ -1382,6 +1382,9 @@ id<MTLArgumentEncoder> MetalDescriptorSetLayout::getArgumentEncoderSlow(id<MTLDe
break;
}
}
if (arguments.count == 0) {
return nil;
}
return [device newArgumentEncoderWithArguments:arguments];
}
@@ -1442,6 +1445,9 @@ id<MTLBuffer> MetalDescriptorSet::finalizeAndGetBuffer(MetalDriver* driver, Shad
id<MTLArgumentEncoder> encoder =
layout->getArgumentEncoder(context.device, stage, textureTypes);
if (!encoder) {
return nil;
}
{
ScopedAllocationTimer timer("descriptor_set");

View File

@@ -20,18 +20,19 @@
#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 <memory>
#include <tsl/robin_map.h>
#include <utils/Hash.h>
#include <utils/Invocable.h>
#include <tsl/robin_map.h>
#include <memory>
namespace filament {
namespace backend {

View File

@@ -24,14 +24,22 @@
#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() {
@@ -48,7 +56,59 @@ 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)
@@ -74,27 +134,17 @@ void PlatformMetal::createDevice(MetalDevice& outDevice) noexcept {
<< utils::io::endl;
outDevice.device = result;
mDevice = result;
}
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;
void PlatformMetalImpl::createCommandQueueImpl(MetalDevice& device, MetalCommandQueue& outCommandQueue) {
if (mCommandQueue) {
outCommandQueue.commandQueue = mCommandQueue;
return;
}
mCommandQueue = [device.device newCommandQueue];
mCommandQueue.label = @"Filament";
outCommandQueue.commandQueue = mCommandQueue;
}
} // namespace filament

View File

@@ -542,6 +542,13 @@ 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,

View File

@@ -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 : public Platform::ExternalImage {
struct ExternalImageCocoaGL final : public Platform::ExternalImage {
CVPixelBufferRef cvBuffer;
protected:
~ExternalImageCocoaGL() noexcept final;

View File

@@ -19,6 +19,7 @@
#include <backend/DriverEnums.h>
#include <utils/BitmaskEnum.h>
#include <utils/Panic.h>
#include <webgpu/webgpu_cpp.h>
@@ -502,7 +503,6 @@ 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 {
@@ -516,6 +516,7 @@ WGPUTexture::WGPUTexture(SamplerType target, uint8_t levels, TextureFormat forma
// First, the texture aspect, starting with the defaults/basic configuration
mUsage = fToWGPUTextureUsage(usage);
mFormat = fToWGPUTextureFormat(format);
mAspect = fToWGPUTextureViewAspect(usage, format);
wgpu::TextureDescriptor textureDescriptor{
.label = getUserTextureLabel(target),
.usage = mUsage,
@@ -560,13 +561,12 @@ 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(const TextureUsage& fUsage) {
wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(TextureUsage const& fUsage) {
wgpu::TextureUsage retUsage = wgpu::TextureUsage::None;
// Basing this mapping off of VulkanTexture.cpp's getUsage func and suggestions from Gemini
@@ -614,8 +614,9 @@ wgpu::TextureUsage WGPUTexture::fToWGPUTextureUsage(const TextureUsage& fUsage)
// PROTECTED
return retUsage;
}
wgpu::TextureFormat WGPUTexture::fToWGPUTextureFormat(const TextureFormat& fUsage) {
switch (fUsage) {
wgpu::TextureFormat WGPUTexture::fToWGPUTextureFormat(TextureFormat const& fFormat) {
switch (fFormat) {
case filament::backend::TextureFormat::R8:
return wgpu::TextureFormat::R8Unorm;
case filament::backend::TextureFormat::R8_SNORM:
@@ -854,24 +855,57 @@ wgpu::TextureFormat WGPUTexture::fToWGPUTextureFormat(const TextureFormat& fUsag
}
}
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,
// baseArrayLayer is required, making a guess
// TODO: check if this baseArrayLayer assumption is correct
.baseArrayLayer = 0,
.arrayLayerCount = mArrayLayerCount,
// 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,
.aspect = mAspect,
.usage = mUsage
};
// adjust for specific cases
switch (target) {
case SamplerType::SAMPLER_2D:
textureViewDescriptor.dimension = wgpu::TextureViewDimension::e2D;

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:
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,11 +177,14 @@ public:
WGPUTexture(WGPUTexture* src, uint8_t baseLevel, uint8_t levelCount) noexcept;
const wgpu::Texture& getTexture() const { return mTexture; }
const wgpu::TextureView& getTexView() const { return mTexView; }
[[nodiscard]] const wgpu::Texture& getTexture() const { return mTexture; }
[[nodiscard]] const wgpu::TextureView& getTexView() const { return mTexView; }
// Public to allow checking for support of a texture format
static wgpu::TextureFormat fToWGPUTextureFormat(const filament::backend::TextureFormat& fUsage);
static wgpu::TextureFormat fToWGPUTextureFormat(
filament::backend::TextureFormat const& fFormat);
static wgpu::TextureAspect fToWGPUTextureViewAspect(
filament::backend::TextureUsage const& fUsage,
filament::backend::TextureFormat const& fFormat);
private:
wgpu::TextureView makeTextureView(const uint8_t& baseLevel, const uint8_t& levelCount,
@@ -192,9 +195,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(const filament::backend::TextureUsage& fUsage);
wgpu::TextureUsage fToWGPUTextureUsage(filament::backend::TextureUsage const& fUsage);
};
struct WGPURenderPrimitive : public HwRenderPrimitive {
@@ -252,7 +256,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

View File

@@ -211,6 +211,10 @@ 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;
}

View File

@@ -85,6 +85,12 @@ 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.

View File

@@ -17,7 +17,6 @@
#include "ShaderGenerator.h"
#include <GlslangToSpv.h>
#include <SPVRemapper.h>
#include <spirv_glsl.hpp>
#include <spirv_msl.hpp>

View File

@@ -32,13 +32,24 @@ 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)

View File

@@ -116,13 +116,23 @@ 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')
parser.add_argument('-s', '--source_expected_path', default="./expected_images")
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.')
# The mutually exclusive options for how to process the actual images
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='*')
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).')
args = parser.parse_args()
if not args.results_path:

View File

@@ -260,6 +260,8 @@ 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;
@@ -490,7 +492,7 @@ TEST_F(BlitTest, BlitRegion) {
}
TEST_F(BlitTest, BlitRegionToSwapChain) {
FAIL_IF(Backend::VULKAN, "Crashes due to not finding color attachment");
FAIL_IF(Backend::VULKAN, "Crashes due to not finding color attachment, see b/417481493");
auto& api = getDriverApi();
mCleanup.addPostCall([&]() { executeCommands(); });

View File

@@ -20,6 +20,7 @@
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "Skip.h"
#include "TrianglePrimitive.h"
namespace test {
@@ -159,6 +160,8 @@ 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);

View File

@@ -26,6 +26,7 @@ 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);
@@ -85,6 +86,7 @@ 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);

View File

@@ -99,6 +99,8 @@ 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");

View File

@@ -212,7 +212,7 @@ static SamplerFormat getSamplerFormat(TextureFormat textureFormat) {
}
TEST_F(LoadImageTest, UpdateImage2D) {
FAIL_IF(Backend::VULKAN, "Multiple test cases crash");
FAIL_IF(Backend::VULKAN, "Multiple test cases crash, see b/417481434");
// All of these test cases should result in the same rendered image, and thus the same hash.
static const uint32_t expectedHash = 3644679986;
@@ -485,6 +485,9 @@ 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();

View File

@@ -81,7 +81,8 @@ 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");
FAIL_IF(Backend::VULKAN,
"Crashing due to no program set when setting push constants, see b/417477740");
auto& api = getDriverApi();

View File

@@ -20,6 +20,7 @@
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "Skip.h"
#include "TrianglePrimitive.h"
#include <utils/Hash.h>
@@ -74,6 +75,8 @@ 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

View File

@@ -20,6 +20,7 @@
#include "Lifetimes.h"
#include "Shader.h"
#include "SharedShaders.h"
#include "Skip.h"
#include "TrianglePrimitive.h"
#include <utils/Hash.h>
@@ -30,6 +31,8 @@ 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;

View File

@@ -117,6 +117,8 @@ public:
};
TEST_F(BasicStencilBufferTest, StencilBuffer) {
SKIP_IF(SkipEnvironment(OperatingSystem::APPLE, Backend::VULKAN),
"Stencil not supported, see b/417230776");
auto& api = getDriverApi();
Cleanup cleanup(api);
@@ -139,6 +141,8 @@ 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);
@@ -162,6 +166,8 @@ 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);

View File

@@ -21,17 +21,13 @@
#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 {
@@ -205,14 +201,15 @@ public:
*
* The default tone mapping operator is ACESLegacyToneMapper.
*
* The ownership of the specified tone mapper is shared with the builder and built
* ColorGrading object.
* 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.
*
* @param toneMapper The tone mapping operator to apply to the HDR color buffer
*
* @return This Builder, for chaining calls
*/
Builder& toneMapper(std::shared_ptr<ToneMapper> toneMapper) noexcept;
Builder& toneMapper(ToneMapper const* UTILS_NULLABLE toneMapper) noexcept;
/**
* Selects the tone mapping operator to apply to the HDR color buffer as the last
@@ -490,118 +487,7 @@ public:
friend class FColorGrading;
};
/** 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;
protected:
// prevent heap allocation
~ColorGrading() = default;
};

View File

@@ -21,7 +21,6 @@
#include <backend/CallbackHandler.h>
#include <backend/DriverEnums.h>
#include <backend/PresentCallable.h>
#include <utils/compiler.h>
#include <utils/Invocable.h>
@@ -35,7 +34,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.
*
@@ -158,7 +157,7 @@ public:
/**
* Requests a SwapChain with an alpha channel.
*/
static const uint64_t CONFIG_TRANSPARENT = backend::SWAP_CHAIN_CONFIG_TRANSPARENT;
static constexpr uint64_t CONFIG_TRANSPARENT = backend::SWAP_CHAIN_CONFIG_TRANSPARENT;
/**
* This flag indicates that the swap chain may be used as a source surface
@@ -168,13 +167,13 @@ public:
* @see
* Renderer.copyFrame()
*/
static const uint64_t CONFIG_READABLE = backend::SWAP_CHAIN_CONFIG_READABLE;
static constexpr 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 const uint64_t CONFIG_ENABLE_XCB = backend::SWAP_CHAIN_CONFIG_ENABLE_XCB;
static constexpr uint64_t CONFIG_ENABLE_XCB = backend::SWAP_CHAIN_CONFIG_ENABLE_XCB;
/**
* Indicates that the native window is a CVPixelBufferRef.
@@ -186,7 +185,7 @@ public:
* Filament. Filament will call CVPixelBufferRetain during Engine::createSwapChain, and
* CVPixelBufferRelease when the swap chain is destroyed.
*/
static const uint64_t CONFIG_APPLE_CVPIXELBUFFER =
static constexpr uint64_t CONFIG_APPLE_CVPIXELBUFFER =
backend::SWAP_CHAIN_CONFIG_APPLE_CVPIXELBUFFER;
/**
@@ -303,6 +302,7 @@ 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 or not this SwapChain currently has a FrameScheduledCallback set.
* Returns whether this SwapChain currently has a FrameScheduledCallback set.
*
* @return true, if the last call to setFrameScheduledCallback set a callback
*

View File

@@ -54,8 +54,6 @@ 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
@@ -71,13 +69,6 @@ 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))
*
@@ -101,10 +92,8 @@ 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; }
};
@@ -117,10 +106,8 @@ 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; }
};
@@ -134,10 +121,8 @@ 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; }
};
@@ -151,10 +136,8 @@ 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; }
};
@@ -167,10 +150,8 @@ 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; }
};
@@ -192,10 +173,8 @@ 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; }
@@ -236,13 +215,12 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper {
) noexcept;
~GenericToneMapper() noexcept final;
GenericToneMapper(GenericToneMapper const& rhs) noexcept;
GenericToneMapper& operator=(GenericToneMapper const& rhs) noexcept;
GenericToneMapper(GenericToneMapper const&) = delete;
GenericToneMapper& operator=(GenericToneMapper const&) = delete;
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; }
@@ -305,10 +283,8 @@ 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; }
};

View File

@@ -16,13 +16,22 @@
#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 inline constexpr std::pair<uint8_t, uint8_t> unmorton(uint16_t const m) noexcept {
static 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;
@@ -165,8 +174,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 i = index(curr.l, curr.code);
Node& node = mQuadTree[i];
size_t const j = index(curr.l, curr.code);
Node& node = mQuadTree[j];
if (curr.l == n) {
found = curr;
assert_invariant(!node.hasChildren());

View File

@@ -188,8 +188,7 @@ float3 ACES(float3 color, float brightness) noexcept {
#define DEFAULT_CONSTRUCTORS(A) \
A::A() noexcept = default; \
A::~A() noexcept = default; \
A::A(A const&) noexcept = default;
A::~A() noexcept = default;
DEFAULT_CONSTRUCTORS(ToneMapper)
@@ -203,10 +202,6 @@ float3 LinearToneMapper::operator()(float3 const v) const noexcept {
return saturate(v);
}
LinearToneMapper* LinearToneMapper::clone() const noexcept {
return new LinearToneMapper(*this);
}
//------------------------------------------------------------------------------
// ACES tone mappers
//------------------------------------------------------------------------------
@@ -217,20 +212,12 @@ 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 {
@@ -243,10 +230,6 @@ 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
//------------------------------------------------------------------------------
@@ -273,17 +256,12 @@ 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
@@ -378,10 +356,6 @@ float3 AgxToneMapper::operator()(float3 v) const noexcept {
return v;
}
AgxToneMapper* AgxToneMapper::clone() const noexcept {
return new AgxToneMapper(*this);
}
//------------------------------------------------------------------------------
// Display range tone mapper
//------------------------------------------------------------------------------
@@ -420,10 +394,6 @@ 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
//------------------------------------------------------------------------------
@@ -484,19 +454,7 @@ GenericToneMapper::~GenericToneMapper() noexcept {
delete 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) {
GenericToneMapper::GenericToneMapper(GenericToneMapper&& rhs) noexcept : mOptions(rhs.mOptions) {
rhs.mOptions = nullptr;
}
@@ -511,10 +469,6 @@ 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; }

View File

@@ -49,7 +49,7 @@ using namespace backend;
//------------------------------------------------------------------------------
struct ColorGrading::BuilderDetails {
std::shared_ptr<ToneMapper> toneMapper = nullptr;
const 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(std::shared_ptr<ToneMapper> toneMapper) noexcept {
ColorGrading::Builder& ColorGrading::Builder::toneMapper(const ToneMapper* toneMapper) noexcept {
mImpl->toneMapper = toneMapper;
return *this;
}
@@ -271,110 +271,6 @@ 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"
@@ -391,25 +287,30 @@ ColorGrading* ColorGrading::Builder::build(Engine& engine) {
if (needToneMapper) {
switch (mImpl->toneMapping) {
case ToneMapping::LINEAR:
mImpl->toneMapper = std::make_shared<LinearToneMapper>();
mImpl->toneMapper = new LinearToneMapper();
break;
case ToneMapping::ACES_LEGACY:
mImpl->toneMapper = std::make_shared<ACESLegacyToneMapper>();
mImpl->toneMapper = new ACESLegacyToneMapper();
break;
case ToneMapping::ACES:
mImpl->toneMapper = std::make_shared<ACESToneMapper>();
mImpl->toneMapper = new ACESToneMapper();
break;
case ToneMapping::FILMIC:
mImpl->toneMapper = std::make_shared<FilmicToneMapper>();
mImpl->toneMapper = new FilmicToneMapper();
break;
case ToneMapping::DISPLAY_RANGE:
mImpl->toneMapper = std::make_shared<DisplayRangeToneMapper>();
mImpl->toneMapper = new DisplayRangeToneMapper();
break;
}
}
FColorGrading* colorGrading = downcast(engine).createColorGrading(*this);
if (needToneMapper) {
delete mImpl->toneMapper;
mImpl->toneMapper = nullptr;
}
return colorGrading;
}
@@ -985,9 +886,6 @@ 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;
@@ -997,33 +895,4 @@ 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

View File

@@ -51,8 +51,6 @@ private:
uint32_t mDimension;
bool mIsOneDimensional;
bool mIsLDR;
static void initializeSettings(const Builder& builder, Settings& settings) noexcept;
};
FILAMENT_DOWNCAST(ColorGrading)

View File

@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.59.5"
spec.version = "1.60.0"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.59.5/filament-v1.59.5-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.60.0/filament-v1.60.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {

View File

@@ -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 = 59;
static constexpr size_t MATERIAL_VERSION = 60;
/**
* Supported shading models

View File

@@ -52,6 +52,7 @@ 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
@@ -61,6 +62,7 @@ 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>;
@@ -82,6 +84,8 @@ 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
@@ -91,8 +95,8 @@ public:
// Add a sampler
Builder& add(std::string_view samplerName, Binding binding, Type type, Format format,
Precision precision = Precision::MEDIUM,
bool multisample = false) noexcept;
Precision precision = Precision::MEDIUM, bool multisample = false,
ShaderStageFlags stages = ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) noexcept;
// Add multiple samplers
Builder& add(std::initializer_list<ListEntry> list) noexcept;

View File

@@ -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) noexcept {
SamplerInterfaceBlock::Builder& SamplerInterfaceBlock::Builder::add(std::string_view samplerName,
Binding binding, Type type, Format format, Precision precision, bool multisample,
ShaderStageFlags stages) noexcept {
mEntries.push_back({
{ samplerName.data(), samplerName.size() }, // name
{ }, // uniform name
binding, type, format, precision, multisample });
binding, type, format, precision, multisample, stages });
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);
add(e.name, e.binding, e.type, e.format, e.precision, e.multisample, e.stages);
}
return *this;
}
@@ -91,6 +91,7 @@ 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
}

View File

@@ -56,6 +56,7 @@ 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)
@@ -71,7 +72,8 @@ set(SRCS
src/sca/GLSLTools.cpp
src/GLSLPostProcessor.cpp
src/ShaderMinifier.cpp
src/SpirvFixup.cpp)
src/SpirvFixup.cpp
src/SpirvRemapWrapper.cpp)
# ==================================================================================================
# Include and target definitions

View File

@@ -35,6 +35,7 @@ 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 {
@@ -79,6 +80,7 @@ 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>

View File

@@ -254,6 +254,7 @@ 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
@@ -322,9 +323,9 @@ public:
*/
MaterialBuilder& parameter(const char* name, SamplerType samplerType,
SamplerFormat format = SamplerFormat::FLOAT,
ParameterPrecision precision = ParameterPrecision::DEFAULT,
bool multisample = false,
const char* transformName = "") noexcept;
ParameterPrecision precision = ParameterPrecision::DEFAULT, bool multisample = false,
const char* transformName = "",
ShaderStageFlags stages = ShaderStageFlags::ALL_SHADER_STAGE_FLAGS) noexcept;
MaterialBuilder& buffer(filament::BufferInterfaceBlock bib) noexcept;
@@ -656,8 +657,10 @@ public:
Parameter() noexcept: parameterType(INVALID) {}
// Sampler
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) { }
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) { }
// Uniform
Parameter(const char* paramName, UniformType t, size_t typeSize, ParameterPrecision p)
@@ -676,6 +679,7 @@ public:
SamplerFormat format;
bool multisample;
utils::CString transformName;
ShaderStageFlags stages;
enum {
INVALID,
UNIFORM,

View File

@@ -175,4 +175,15 @@ 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

View File

@@ -17,7 +17,6 @@
#include "GLSLPostProcessor.h"
#include <GlslangToSpv.h>
#include <SPVRemapper.h>
#include <spirv-tools/libspirv.hpp>
#include <spirv_glsl.hpp>
@@ -145,12 +144,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,
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, 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,
sampler.stages, sampler.binding, DescriptorFlags::NONE, 0 });
}
return layout;
@@ -289,24 +288,8 @@ GLSLPostProcessor::GLSLPostProcessor(MaterialBuilder::Optimization optimization,
: mOptimization(optimization),
mPrintShaders(flags & PRINT_SHADERS),
mGenerateDebugInfo(flags & GENERATE_DEBUG_INFO) {
// 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);
// This should occur only once, to avoid races.
SpirvRemapWrapperSetUp();
}
GLSLPostProcessor::~GLSLPostProcessor() = default;
@@ -988,8 +971,7 @@ void GLSLPostProcessor::optimizeSpirv(OptimizerPtr optimizer, SpirvBlob& spirv)
}
// Remove dead module-level objects: functions, types, vars
spv::spirvbin_t remapper(0);
remapper.remap(spirv, spv::spirvbin_base_t::DCE_ALL);
SpirvRemapWrapperRemap(spirv);
}
void GLSLPostProcessor::fixupClipDistance(

View File

@@ -23,6 +23,7 @@
#include <private/filament/SamplerInterfaceBlock.h>
#include "ShaderMinifier.h"
#include "SpirvRemapWrapper.h"
#include <spirv-tools/optimizer.hpp>

View File

@@ -302,7 +302,8 @@ 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) noexcept {
SamplerFormat format, ParameterPrecision precision, bool multisample,
const char* transformName, ShaderStageFlags stages) noexcept {
FILAMENT_CHECK_PRECONDITION(!multisample ||
(format != SamplerFormat::SHADOW &&
(samplerType == SamplerType::SAMPLER_2D ||
@@ -311,7 +312,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 };
mParameters[mParameterCount++] = { name, samplerType, format, precision, multisample, transformName, stages };
return *this;
}
@@ -637,8 +638,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);
sbb.add({ param.name.data(), param.name.size() }, binding, param.samplerType,
param.format, param.precision, param.multisample, param.stages);
if (!param.transformName.empty()) {
ibb.add({{{ param.transformName.data(), param.transformName.size() }, uint8_t(binding),
0, UniformType::MAT3, Precision::DEFAULT, FeatureLevel::FEATURE_LEVEL_0 }});

View File

@@ -0,0 +1,54 @@
/*
* 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

View File

@@ -0,0 +1,30 @@
/*
* 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

View File

@@ -233,7 +233,7 @@ void MaterialDescriptorSetLayoutChunk::flatten(Flattener& f) {
} else {
f.writeUint8(uint8_t(DescriptorType::SAMPLER));
}
f.writeUint8(uint8_t(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT));
f.writeUint8(uint8_t(entry.stages));
f.writeUint8(entry.binding);
f.writeUint8(uint8_t(DescriptorFlags::NONE));
f.writeUint16(0);

View File

@@ -26,6 +26,7 @@
#include <filamat/MaterialBuilder.h>
#include <utils/JobSystem.h>
#include <utils/Panic.h>
#include <memory>

View File

@@ -21,6 +21,7 @@
#include <utils/JobSystem.h>
#include <utils/Log.h>
#include <utils/Panic.h>
using namespace filament;
using namespace filament::math;

View File

@@ -24,6 +24,7 @@
#include <geometry/TangentSpaceMesh.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/StructureOfArrays.h>
#include <cstring>

View File

@@ -22,6 +22,7 @@
#include <string_view>
#include <utils/Log.h>
#include <utils/Panic.h>
using namespace utils;
using namespace std::literals;

View File

@@ -65,6 +65,8 @@ 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

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,6 +31,9 @@
#include <string.h>
namespace utils {
namespace io {
class ostream;
}
//! \privatesection
struct hashCStrings {
@@ -38,7 +41,7 @@ struct hashCStrings {
typedef size_t result_type;
result_type operator()(argument_type cstr) const noexcept {
size_t hash = 5381;
while (int const c = *cstr++) {
while (int const c = static_cast<unsigned char>(*cstr++)) {
hash = (hash * 33u) ^ size_t(c);
}
return hash;
@@ -97,11 +100,7 @@ public:
return *this;
}
~CString() noexcept {
if (mData) {
free(mData - 1);
}
}
~CString() noexcept;
void swap(CString& other) noexcept {
// don't use std::swap(), we don't want an STL dependency in this file
@@ -110,8 +109,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(); }
@@ -119,33 +118,89 @@ public:
size_type length() const noexcept { return size(); }
bool empty() const noexcept { return size() == 0; }
iterator begin() noexcept { return mCStr; }
iterator begin() noexcept { return c_str(); }
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(); }
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); }
// 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);
}
const_reference operator[](size_type pos) const noexcept {
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 {
assert(pos < size());
return begin()[pos];
}
reference operator[](size_type pos) noexcept {
reference operator[](size_type const pos) noexcept {
assert(pos < size());
return begin()[pos];
}
const_reference at(size_type pos) const noexcept {
const_reference at(size_type const pos) const noexcept {
assert(pos < size());
return begin()[pos];
}
reference at(size_type pos) noexcept {
reference at(size_type const pos) noexcept {
assert(pos < size());
return begin()[pos];
}
@@ -171,7 +226,7 @@ public:
}
// placement new declared as "throw" to avoid the compiler's null-check
inline void* operator new(size_t, void* ptr) {
void* operator new(size_t, void* ptr) {
assert(ptr);
return ptr;
}
@@ -185,6 +240,8 @@ 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
@@ -225,7 +282,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;
@@ -249,7 +306,7 @@ public:
pointer c_str() noexcept { return mData; }
private:
value_type mData[N] = {0};
value_type mData[N] = {};
};
} // namespace utils

View File

@@ -23,9 +23,11 @@
#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.
@@ -88,23 +90,23 @@ public:
bool operator <(const CallStack& rhs) const;
inline bool operator >(const CallStack& rhs) const {
bool operator >(const CallStack& rhs) const {
return rhs < *this;
}
inline bool operator !=(const CallStack& rhs) const {
bool operator !=(const CallStack& rhs) const {
return *this < rhs || rhs < *this;
}
inline bool operator >=(const CallStack& rhs) const {
bool operator >=(const CallStack& rhs) const {
return !operator <(rhs);
}
inline bool operator <=(const CallStack& rhs) const {
bool operator <=(const CallStack& rhs) const {
return !operator >(rhs);
}
inline bool operator ==(const CallStack& rhs) const {
bool operator ==(const CallStack& rhs) const {
return !operator !=(rhs);
}

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,6 +40,11 @@
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()
@@ -56,7 +61,7 @@ namespace utils {
* 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 {
class UTILS_PUBLIC FixedCapacityVector : protected FixedCapacityVectorBase {
public:
using allocator_type = A;
using value_type = T;
@@ -266,7 +271,7 @@ public:
mSize = 0;
}
void resize(size_type count) {
void resize(size_type const count) {
assertCapacityForSize(count);
if constexpr(std::is_trivially_constructible_v<value_type> &&
std::is_trivially_destructible_v<value_type>) {
@@ -277,12 +282,12 @@ public:
}
}
void resize(size_type count, const_reference v) {
void resize(size_type const count, const_reference v) {
assertCapacityForSize(count);
resize_non_trivial(count, v);
}
void swap(FixedCapacityVector& other) {
void swap(FixedCapacityVector& other) noexcept {
using std::swap;
swap(mData, other.mData);
swap(mSize, other.mSize);
@@ -326,16 +331,16 @@ private:
return mCapacityAllocator.second();
}
iterator assertCapacityForSize(size_type s) {
iterator assertCapacityForSize(size_type const s) {
if constexpr(CapacityCheck || FILAMENT_FORCE_CAPACITY_CHECK) {
FILAMENT_CHECK_PRECONDITION(capacity() >= s)
<< "capacity exceeded: requested size " << (unsigned long)s
<< "u, available capacity " << (unsigned long)capacity() << "u.";
if (UTILS_VERY_UNLIKELY(capacity() < s)) {
capacityCheckFailed(capacity(), s);
}
}
return end();
}
inline void construct(iterator first, iterator last) noexcept {
void construct(iterator const first, iterator const 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);
@@ -358,7 +363,7 @@ private:
}
inline void destroy(iterator first, iterator last) noexcept {
void destroy(iterator const first, iterator const 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);
@@ -419,7 +424,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; }
operator TYPE() const noexcept { return value; } // NOLINT(*-explicit-constructor)
};
pointer mData{};

View File

@@ -14,10 +14,8 @@
* limitations under the License.
*/
#ifndef TNT_UTILS_INVOKABLE_H
#define TNT_UTILS_INVOKABLE_H
#include <utils/ostream.h>
#ifndef TNT_UTILS_INVOCABLE_H
#define TNT_UTILS_INVOCABLE_H
#include <type_traits>
#include <utility>
@@ -25,6 +23,9 @@
#include <assert.h>
namespace utils {
namespace io {
class ostream;
}
/*
* Invocable is a move-only general purpose function wrapper. Instances can
@@ -50,11 +51,16 @@ 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...)> {
class Invocable<R(Args...)> : protected InvocableBase {
public:
// Creates an Invocable that does not contain a functor.
// Will evaluate to false.
@@ -85,7 +91,7 @@ public:
private:
#if !defined(NDEBUG)
friend io::ostream& operator<<(io::ostream& out, const Invocable&) {
return out << "Invocable<>"; // TODO: is there a way to do better here?
return printInvocable(out, "Invocable<>"); // TODO: is there a way to do better here?
}
#endif
void* mInvocable = nullptr;
@@ -156,4 +162,4 @@ Invocable<R(Args...)>::operator bool() const noexcept {
} // namespace utils
#endif // TNT_UTILS_INVOKABLE_H
#endif // TNT_UTILS_INVOCABLE_H

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 T>
template <typename>
class UTILS_PUBLIC TPanic : public Panic {
public:
// std::exception protocol
@@ -398,12 +398,11 @@ public:
* @see PANIC_PRECONDITION, PANIC_POSTCONDITION, PANIC_ARITHMETIC
* @see setMode()
*/
static inline void panic(
static void panic(
char const* function, char const* file, int line, char const* literal,
std::string reason) UTILS_NORETURN;
protected:
private:
/**
* Creates a Panic with extra information about the error-site.
* @param function the name of the function where the error was detected
@@ -415,11 +414,14 @@ protected:
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
@@ -443,7 +445,7 @@ void panicLog(
* ASSERT_PRECONDITION uses this Panic to report a precondition failure.
* @see ASSERT_PRECONDITION
*/
class UTILS_PUBLIC PreconditionPanic : public TPanic<PreconditionPanic> {
class UTILS_PUBLIC PreconditionPanic final : public TPanic<PreconditionPanic> {
// Programming error, can be avoided
// e.g.: invalid arguments
using TPanic<PreconditionPanic>::TPanic;
@@ -457,9 +459,9 @@ class UTILS_PUBLIC PreconditionPanic : public TPanic<PreconditionPanic> {
* ASSERT_POSTCONDITION uses this Panic to report a postcondition failure.
* @see ASSERT_POSTCONDITION
*/
class UTILS_PUBLIC PostconditionPanic : public TPanic<PostconditionPanic> {
class UTILS_PUBLIC PostconditionPanic final : public TPanic<PostconditionPanic> {
// Usually only detectable at runtime
// e.g.: dead-lock would occur, arithmetic errors
// e.g.: deadlock would occur, arithmetic errors
using TPanic<PostconditionPanic>::TPanic;
friend class TPanic<PostconditionPanic>;
constexpr static auto type = "Postcondition";
@@ -471,7 +473,7 @@ class UTILS_PUBLIC PostconditionPanic : public TPanic<PostconditionPanic> {
* ASSERT_ARITHMETIC uses this Panic to report an arithmetic (postcondition) failure.
* @see ASSERT_ARITHMETIC
*/
class UTILS_PUBLIC ArithmeticPanic : public TPanic<ArithmeticPanic> {
class UTILS_PUBLIC ArithmeticPanic final : public TPanic<ArithmeticPanic> {
// A common case of post-condition error
// e.g.: underflow, overflow, internal computations errors
using TPanic<ArithmeticPanic>::TPanic;
@@ -519,11 +521,11 @@ public:
PanicStream& operator<<(const void* value) noexcept;
PanicStream& operator<<(const char* string) noexcept;
PanicStream& operator<<(const unsigned char* string) noexcept;
PanicStream& operator<<(const char* value) noexcept;
PanicStream& operator<<(const unsigned char* value) noexcept;
PanicStream& operator<<(std::string const& s) noexcept;
PanicStream& operator<<(std::string_view const& s) noexcept;
PanicStream& operator<<(std::string const& value) noexcept;
PanicStream& operator<<(std::string_view const& value) noexcept;
protected:
io::sstream mStream;

View File

@@ -19,11 +19,8 @@
#include <utils/compiler.h>
#include <type_traits>
#include <string_view>
#include <stddef.h>
namespace utils {
/**
@@ -41,7 +38,7 @@ public:
using const_iterator = std::string_view::const_iterator;
// Constructor from string literal
template <size_t M>
template<unsigned int M>
constexpr StaticString(const char (&str)[M]) noexcept : mString(str, M - 1) {} // NOLINT(*-explicit-constructor)
constexpr StaticString() noexcept = default;
@@ -61,7 +58,7 @@ public:
return mString[pos];
}
constexpr const_reference at(size_type pos) const {
constexpr const_reference at(size_type const pos) const {
return mString[pos];
}

View File

@@ -20,27 +20,30 @@
#include <utils/ostream.h>
#include <algorithm>
#include <cstdlib>
#include <memory>
namespace utils {
UTILS_NOINLINE
CString::CString(const char* cstr, size_t length) {
CString::CString(const char* cstr, size_t const length) {
if (length && cstr) {
Data* p = (Data*)malloc(sizeof(Data) + length + 1);
p->length = (size_type)length;
mCStr = (value_type*)(p + 1);
Data* const p = static_cast<Data*>(std::malloc(sizeof(Data) + length + 1));
p->length = size_type(length);
mCStr = reinterpret_cast<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 length) {
CString::CString(size_t const length) {
if (length) {
Data* p = (Data*)malloc(sizeof(Data) + length + 1);
p->length = (size_type)length;
mCStr = (value_type*)(p + 1);
Data* const p = static_cast<Data*>(std::malloc(sizeof(Data) + length + 1));
p->length = size_type(length);
mCStr = reinterpret_cast<value_type*>(p + 1);
std::fill_n(mCStr, length, 0);
mCStr[length] = '\0';
}
@@ -58,24 +61,30 @@ CString& CString::operator=(const CString& rhs) {
if (this != &rhs) {
auto *const p = mData ? mData - 1 : nullptr;
new(this) CString(rhs);
free(p);
std::free(p);
}
return *this;
}
CString& CString::replace(size_type pos, size_type len, const CString& str) noexcept {
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 {
assert(pos <= size());
len = std::min(len, size() - pos);
// The new size of the string, after the replacement.
const size_type newSize = size() - len + str.size();
const size_type newSize = size() - len + l;
// Allocate enough memory to hold the new string.
Data* p = (Data*) malloc(sizeof(Data) + newSize + 1);
Data* const p = static_cast<Data*>(std::malloc(sizeof(Data) + newSize + 1));
assert(p);
p->length = newSize;
value_type* newStr = (value_type*) (p + 1);
value_type* newStr = reinterpret_cast<value_type*>(p + 1);
const value_type* beginning = mCStr;
const value_type* replacementStart = mCStr + pos;
@@ -84,7 +93,7 @@ CString& CString::replace(size_type pos, size_type len, const CString& str) noex
value_type* ptr = newStr;
ptr = std::uninitialized_copy(beginning, replacementStart, ptr);
ptr = std::uninitialized_copy_n(str.c_str_safe(), str.length(), ptr);
ptr = std::uninitialized_copy_n(str, l, ptr);
ptr = std::uninitialized_copy(replacementEnd, end, ptr);
// null-terminator
@@ -92,14 +101,14 @@ CString& CString::replace(size_type pos, size_type len, const CString& str) noex
std::swap(mCStr, newStr);
if (newStr) {
free((Data*) newStr - 1);
std::free(reinterpret_cast<Data*>(newStr) - 1);
}
return *this;
}
#if !defined(NDEBUG)
io::ostream& operator<<(io::ostream& out, const utils::CString& rhs) {
io::ostream& operator<<(io::ostream& out, const CString& rhs) {
return out << rhs.c_str_safe();
}
#endif

View File

@@ -0,0 +1,36 @@
/*
* 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

View File

@@ -0,0 +1,24 @@
/*
* 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

View File

@@ -18,6 +18,7 @@
#include "ostream_.h"
#include <utils/CallStack.h>
#include <utils/compiler.h>
#include <utils/Log.h>
#include <utils/ostream.h>
@@ -68,7 +69,7 @@ public:
getCallback().call(panic);
}
void set(Panic::PanicHandlerCallback handler, void* user) noexcept {
void set(Panic::PanicHandlerCallback const handler, void* user) noexcept {
std::lock_guard const lock(mLock);
mCallBack = { handler, user };
}
@@ -96,7 +97,7 @@ static std::string sprintfToString(const char* format, va_list args) noexcept {
return s;
}
static inline std::string sprintfToString(const char* format, ...) noexcept {
static std::string sprintfToString(const char* format, ...) noexcept {
va_list args;
va_start(args, format);
std::string const s{ sprintfToString(format, args) };
@@ -120,14 +121,14 @@ static std::string buildPanicString(
Panic::~Panic() noexcept = default;
void Panic::setPanicHandler(PanicHandlerCallback handler, void* user) noexcept {
void Panic::setPanicHandler(PanicHandlerCallback const handler, void* user) noexcept {
UserPanicHandler::get().set(handler, user);
}
// ------------------------------------------------------------------------------------------------
template<typename T>
TPanic<T>::TPanic(const char* function, const char* file, int line, char const* literal,
TPanic<T>::TPanic(const char* function, const char* file, int const line, char const* literal,
std::string reason)
: mFile(file),
mFunction(function),
@@ -188,13 +189,13 @@ void TPanic<T>::log() const noexcept {
}
UTILS_ALWAYS_INLINE
inline static const char* formatFile(char const* file) noexcept {
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 line, char const* literal,
void TPanic<T>::panic(char const* function, char const* file, int const line, char const* literal,
const char* format, ...) {
va_list args;
va_start(args, format);
@@ -233,7 +234,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 line, const char* format, ...) noexcept {
void panicLog(char const* function, char const* file, int const line, const char* format, ...) noexcept {
va_list args;
va_start(args, format);
std::string const reason{ sprintfToString(format, args) };
@@ -249,79 +250,79 @@ void panicLog(char const* function, char const* file, int line, const char* form
PanicStream::PanicStream(
char const* function,
char const* file,
int line,
int const line,
char const* condition) noexcept
: mFunction(function), mFile(file), mLine(line), mLiteral(condition) {
}
PanicStream::~PanicStream() = default;
PanicStream& PanicStream::operator<<(short value) noexcept {
PanicStream& PanicStream::operator<<(short const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned short value) noexcept {
PanicStream& PanicStream::operator<<(unsigned short const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(char value) noexcept {
PanicStream& PanicStream::operator<<(char const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned char value) noexcept {
PanicStream& PanicStream::operator<<(unsigned char const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(int value) noexcept {
PanicStream& PanicStream::operator<<(int const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned int value) noexcept {
PanicStream& PanicStream::operator<<(unsigned int const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(long value) noexcept {
PanicStream& PanicStream::operator<<(long const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned long value) noexcept {
PanicStream& PanicStream::operator<<(unsigned long const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(long long int value) noexcept {
PanicStream& PanicStream::operator<<(long long int const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(unsigned long long int value) noexcept {
PanicStream& PanicStream::operator<<(unsigned long long int const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(float value) noexcept {
PanicStream& PanicStream::operator<<(float const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(double value) noexcept {
PanicStream& PanicStream::operator<<(double const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(long double value) noexcept {
PanicStream& PanicStream::operator<<(long double const value) noexcept {
mStream << value;
return *this;
}
PanicStream& PanicStream::operator<<(bool value) noexcept {
PanicStream& PanicStream::operator<<(bool const value) noexcept {
mStream << value;
return *this;
}

View File

@@ -18,6 +18,8 @@
#define TNT_UTILS_OSTREAM__H
#include <utils/ostream.h>
#include <utility>
#include <mutex>
namespace utils::io {

View File

@@ -17,6 +17,7 @@
#include <gtest/gtest.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Panic.h>
using namespace utils;

View File

@@ -700,8 +700,7 @@ constexpr ToneMapper* createToneMapper(const ColorGradingSettings& settings) noe
}
ColorGrading* createColorGrading(const ColorGradingSettings& settings, Engine* engine) {
ToneMapper* toneMapperRaw = createToneMapper(settings);
std::shared_ptr<ToneMapper> toneMapper(toneMapperRaw);
ToneMapper* toneMapper = createToneMapper(settings);
ColorGrading *colorGrading = ColorGrading::Builder()
.quality(settings.quality)
.exposure(settings.exposure)
@@ -724,6 +723,7 @@ ColorGrading* createColorGrading(const ColorGradingSettings& settings, Engine* e
.gamutMapping(settings.gamutMapping)
.outputColorSpace(settings.colorspace)
.build(*engine);
delete toneMapper;
return colorGrading;
}

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[3] = { 0, 1, 2 };
static constexpr uint16_t TRIANGLE_INDICES[4] = { 0, 1, 2 , 0};
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(3)
.indexCount(4)
.bufferType(IndexBuffer::IndexType::USHORT)
.build(*engine);
app.ib->setBuffer(*engine,
IndexBuffer::BufferDescriptor(TRIANGLE_INDICES, 6, nullptr));
IndexBuffer::BufferDescriptor(TRIANGLE_INDICES, 8, nullptr));
app.mat = Material::Builder()
.package(RESOURCES_BAKEDCOLOR_DATA, RESOURCES_BAKEDCOLOR_SIZE)
.build(*engine);

59
test/renderdiff/generate.sh Executable file
View File

@@ -0,0 +1,59 @@
# 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_

View File

@@ -0,0 +1,54 @@
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)

View File

@@ -15,6 +15,7 @@
import os
import shutil
import re
import sys
from utils import execute, ArgParseImpl, mkdir_p
@@ -132,12 +133,19 @@ class GoldenManager:
rdiff_dir = os.path.join(assets_dir, GOLDENS_DIR)
shutil.copytree(rdiff_dir, dest_dir, dirs_exist_ok=True)
# For testing only
# The main entry point will enable download content of a branch to a directory
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.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'))
golden_manager.download_to(golden_dir, branch=args.branch)

View File

@@ -0,0 +1,45 @@
# 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
}

View File

@@ -18,31 +18,18 @@ import json
import glob
import shutil
from utils import execute, ArgParseImpl, mkdir_p, mv_f
from parse_test_json import parse_test_config_from_path
from utils import execute, ArgParseImpl, mkdir_p, mv_f, important_print
import test_config
from golden_manager import GoldenManager
from image_diff import same_image
from results import RESULT_OK, RESULT_FAILED
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):
def _render_test_config(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)
@@ -86,7 +73,7 @@ def run_test(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_TO_RENDER
result = RESULT_FAILED
important_print(f'{test_desc} rendering failed with error={out_code}')
results.append({
@@ -96,23 +83,6 @@ def run_test(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)
@@ -120,43 +90,26 @@ 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 = parse_test_config_from_path(args.test)
test = test_config.parse_from_path(args.test)
output_dir, results = \
run_test(args.gltf_viewer,
test,
args.output_dir,
opengl_lib=args.opengl_lib,
vk_icd=args.vk_icd)
_render_test_config(args.gltf_viewer,
test,
args.output_dir,
opengl_lib=args.opengl_lib,
vk_icd=args.vk_icd)
do_compare = False
# The presence of this argument indicates comparison against a set of goldens.
if args.golden_branch:
# prepare goldens working directory
tmp_golden_dir = '/tmp/renderdiff-goldens'
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:
with open(f'{output_dir}/render_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)
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 ''))
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)

View File

@@ -0,0 +1,3 @@
RESULT_OK = 'ok'
RESULT_FAILED = 'failed'
RESULT_MISSING = 'missing'

View File

@@ -129,7 +129,7 @@ def _remove_comments_from_json_txt(json_txt):
res.append(line)
return '\n'.join(res)
def parse_test_config_from_path(config_path):
def parse_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)

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/test.sh generate',
code, res = execute('bash ./test/renderdiff/generate.sh',
capture_output=False)
if code != 0:
print('Failed to generate new goldens')

View File

@@ -106,3 +106,13 @@ 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))

View File

@@ -14,71 +14,15 @@
#!/usr/bin/bash
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"
source `dirname $0`/src/preamble.sh
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
GOLDEN_DIR=$(pwd)/golden_images
start_ && \
mkdir -p ${OUTPUT_DIR} && \
CXX=`which clang++` CC=`which clang` ./build.sh -f -X ${MESA_DIR} -p desktop debug gltf_viewer && \
python3 ${RENDERDIFF_TEST_DIR}/src/run.py \
--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} && \
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} && \
end_

View File

@@ -229,10 +229,10 @@ MaybeError ValidateWriteBuffer(const DeviceBase* device,
uint64_t size) {
DAWN_TRY(device->ValidateObject(buffer));
// DAWN_INVALID_IF(bufferOffset % 4 != 0, "BufferOffset (%u) is not a multiple of 4.",
// bufferOffset);
//
// DAWN_INVALID_IF(size % 4 != 0, "Size (%u) is not a multiple of 4.", size);
DAWN_INVALID_IF(bufferOffset % 4 != 0, "BufferOffset (%u) is not a multiple of 4.",
bufferOffset);
DAWN_INVALID_IF(size % 4 != 0, "Size (%u) is not a multiple of 4.", size);
uint64_t bufferSize = buffer->GetSize();
DAWN_INVALID_IF(bufferOffset > bufferSize || size > (bufferSize - bufferOffset),

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