Compare commits

..
Author SHA1 Message Date
Benjamin Doherty e9f4433324 OpenGL: handle glFenceSync returning null 2025-02-06 10:25:32 -08:00
91 changed files with 621 additions and 2099 deletions
+1 -1
View File
@@ -528,7 +528,7 @@ if (FILAMENT_SUPPORTS_METAL)
endif()
# Building filamat increases build times and isn't required for web, so turn it off by default.
if (NOT WEBGL AND NOT IOS)
if (NOT WEBGL)
option(FILAMENT_BUILD_FILAMAT "Build filamat and JNI buildings" ON)
else()
option(FILAMENT_BUILD_FILAMAT "Build filamat and JNI buildings" OFF)
+3
View File
@@ -7,3 +7,6 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- matdbg: Add support for debugging ESSL 1.0 shaders
- backend: New platform API to better handle external textures [⚠️ **New Material Version**]
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.57.2'
implementation 'com.google.android.filament:filament-android:1.56.7'
}
```
@@ -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.57.2'
pod 'Filament', '~> 1.56.7'
```
## Documentation
-12
View File
@@ -7,18 +7,6 @@ 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.57.2
- Support including PlatformMetal.h in C++ files.
## v1.57.1
## v1.57.0
- matdbg: Add support for debugging ESSL 1.0 shaders
- backend: New platform API to better handle external textures [⚠️ **New Material Version**]
## v1.56.8
@@ -329,18 +329,6 @@ Java_com_google_android_filament_MaterialInstance_nSetCullingMode(JNIEnv*,
instance->setCullingMode((MaterialInstance::CullingMode) cullingMode);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_MaterialInstance_nSetCullingModeSeparate(JNIEnv*, jclass,
jlong nativeMaterialInstance,
jlong colorPassCullingMode, jlong shadowPassCullingMode) {
MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance;
instance->setCullingMode(
(MaterialInstance::CullingMode) colorPassCullingMode,
(MaterialInstance::CullingMode) shadowPassCullingMode);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_MaterialInstance_nSetColorWrite(JNIEnv*,
@@ -517,14 +505,6 @@ Java_com_google_android_filament_MaterialInstance_nGetCullingMode(JNIEnv* env, j
return (jint)instance->getCullingMode();
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_google_android_filament_MaterialInstance_nGetShadowCullingMode(JNIEnv* env, jclass,
jlong nativeMaterialInstance) {
MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance;
return (jint)instance->getShadowCullingMode();
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_MaterialInstance_nIsColorWriteEnabled(JNIEnv* env, jclass clazz,
@@ -53,14 +53,6 @@ Java_com_google_android_filament_Texture_nIsTextureFormatSupported(JNIEnv*, jcla
(Texture::InternalFormat) internalFormat);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Texture_nIsTextureFormatMipmappable(JNIEnv*, jclass,
jlong nativeEngine, jint internalFormat) {
Engine *engine = (Engine *) nativeEngine;
return (jboolean) Texture::isTextureFormatMipmappable(*engine,
(Texture::InternalFormat) internalFormat);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Texture_nIsTextureSwizzleSupported(JNIEnv*, jclass,
jlong nativeEngine) {
@@ -68,16 +60,6 @@ Java_com_google_android_filament_Texture_nIsTextureSwizzleSupported(JNIEnv*, jcl
return (jboolean) Texture::isTextureSwizzleSupported(*engine);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Texture_nValidatePixelFormatAndType(JNIEnv*, jclass,
jint internalFormat, jint pixelDataFormat, jint pixelDataType) {
return (jboolean) Texture::validatePixelFormatAndType(
(Texture::InternalFormat) internalFormat,
(Texture::Format) pixelDataFormat,
(Texture::Type) pixelDataType
);
}
// Texture::Builder...
extern "C" JNIEXPORT jlong JNICALL
@@ -556,20 +556,6 @@ public class MaterialInstance {
nSetCullingMode(getNativeObject(), mode.ordinal());
}
/**
* Overrides the default triangle culling state that was set on the material separately for the
* color and shadow passes
*
* @see
* <a href="https://google.github.io/filament/Materials.html#materialdefinitions/materialblock/rasterization:culling">
* Rasterization: culling</a>
*/
public void setCullingMode(@NonNull Material.CullingMode colorPassCullingMode,
@NonNull Material.CullingMode shadowPassCullingMode) {
nSetCullingModeSeparate(getNativeObject(),
colorPassCullingMode.ordinal(), shadowPassCullingMode.ordinal());
}
/**
* Returns the face culling mode.
*/
@@ -578,14 +564,6 @@ public class MaterialInstance {
return sCullingModeValues[nGetCullingMode(getNativeObject())];
}
/**
* Returns the face culling mode for the shadow passes.
*/
@NonNull
public Material.CullingMode getShadowCullingMode() {
return sCullingModeValues[nGetShadowCullingMode(getNativeObject())];
}
/**
* Overrides the default color-buffer write state that was set on the material.
*
@@ -942,8 +920,6 @@ public class MaterialInstance {
private static native void nSetDoubleSided(long nativeMaterialInstance, boolean doubleSided);
private static native void nSetCullingMode(long nativeMaterialInstance, long mode);
private static native void nSetCullingModeSeparate(long nativeMaterialInstance,
long colorPassCullingMode, long shadowPassCullingMode);
private static native void nSetColorWrite(long nativeMaterialInstance, boolean enable);
private static native void nSetDepthWrite(long nativeMaterialInstance, boolean enable);
private static native void nSetStencilWrite(long nativeMaterialInstance, boolean enable);
@@ -976,7 +952,6 @@ public class MaterialInstance {
private static native float nGetSpecularAntiAliasingThreshold(long nativeMaterialInstance);
private static native boolean nIsDoubleSided(long nativeMaterialInstance);
private static native int nGetCullingMode(long nativeMaterialInstance);
private static native int nGetShadowCullingMode(long nativeMaterialInstance);
private static native boolean nIsColorWriteEnabled(long nativeMaterialInstance);
private static native boolean nIsDepthWriteEnabled(long nativeMaterialInstance);
private static native boolean nIsStencilWriteEnabled(long nativeMaterialInstance);
@@ -653,19 +653,6 @@ public class Texture {
return nIsTextureFormatSupported(engine.getNativeObject(), format.ordinal());
}
/**
* Checks whether a given texture format is supported for mipmapping in this {@link Engine}.
* This depends on the selected backend.
*
* @param engine {@link Engine} to test the {@link InternalFormat InternalFormat} against
* @param format format to check
* @return <code>true</code> if this format is supported for texturing.
*/
public static boolean isTextureFormatMipmappable(@NonNull Engine engine,
@NonNull InternalFormat format) {
return nIsTextureFormatMipmappable(engine.getNativeObject(), format.ordinal());
}
/**
* Checks whether texture swizzling is supported in this {@link Engine}.
* This depends on the selected backend.
@@ -677,20 +664,6 @@ public class Texture {
return nIsTextureSwizzleSupported(engine.getNativeObject());
}
/**
* Checks whether a given combination of texture format, pixel data and type is valid.
*
* @param internalFormat texture format
* @param pixelDataFormat pixel data format
* @param pixelDataType pixel data type
* @return <code>true</code> if the combination is valid
*/
public static boolean validatePixelFormatAndType(@NonNull InternalFormat internalFormat,
@NonNull Format pixelDataFormat, @NonNull Type pixelDataType) {
return nValidatePixelFormatAndType(internalFormat.ordinal(), pixelDataFormat.ordinal(),
pixelDataType.ordinal());
}
/**
* Use <code>Builder</code> to construct a <code>Texture</code> object instance.
*/
@@ -1287,12 +1260,8 @@ public class Texture {
}
private static native boolean nIsTextureFormatSupported(long nativeEngine, int internalFormat);
private static native boolean nIsTextureFormatMipmappable(long nativeEngine, int internalFormat);
private static native boolean nIsTextureSwizzleSupported(long nativeEngine);
private static native boolean nValidatePixelFormatAndType(int internalFormat, int pixelDataFormat,
int pixelDataType);
private static native long nCreateBuilder();
private static native void nDestroyBuilder(long nativeBuilder);
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.57.2
VERSION_NAME=1.56.7
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+1 -2
View File
@@ -460,8 +460,7 @@ if (APPLE OR LINUX)
spirv-cross-glsl)
endif()
# TODO: Disabling IOS test due to breakage wrt glslang update
if (APPLE AND NOT IOS)
if (APPLE)
# TODO: we should expand this test to Linux and other platforms.
list(APPEND BACKEND_TEST_SRC
test/test_RenderExternalImage.cpp)
+1 -12
View File
@@ -79,10 +79,6 @@ public:
void clear() noexcept;
void reset(ExternalImage* UTILS_NULLABLE p) noexcept;
private:
friend utils::io::ostream& operator<<(utils::io::ostream& out,
ExternalImageHandle const& handle);
};
using ExternalImageHandleRef = ExternalImageHandle const&;
@@ -144,13 +140,6 @@ public:
* - PlatformEGLAndroid
*/
bool assertNativeWindowIsValid = false;
/**
* The action to take if a Drawable cannot be acquired. If true, the
* frame is aborted instead of panic. This is only supported for:
* - PlatformMetal
*/
bool metalDisablePanicOnDrawableFailure = false;
};
Platform() noexcept;
@@ -176,7 +165,7 @@ public:
*
* @return nullptr on failure, or a pointer to the newly created driver.
*/
virtual Driver* UTILS_NULLABLE createDriver(void* UTILS_NULLABLE sharedContext,
virtual backend::Driver* UTILS_NULLABLE createDriver(void* UTILS_NULLABLE sharedContext,
const DriverConfig& driverConfig) noexcept = 0;
/**
@@ -23,7 +23,6 @@
#include <utils/compiler.h>
#include <utils/Invocable.h>
#include <utils/CString.h>
#include <stddef.h>
#include <stdint.h>
@@ -57,18 +56,6 @@ public:
unsigned int id; // GLuint id
};
/**
* Return the OpenGL vendor string of the specified Driver instance.
* @return The GL_VENDOR string
*/
static utils::CString getVendorString(Driver const* UTILS_NONNULL driver);
/**
* Return the OpenGL vendor string of the specified Driver instance
* @return The GL_RENDERER string
*/
static utils::CString getRendererString(Driver const* UTILS_NONNULL driver);
/**
* Called by the driver to destroy the OpenGL context. This should clean up any windows
* or buffers from initialization. This is for instance where `eglDestroyContext` would be
@@ -17,6 +17,7 @@
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_COCOA_GL_H
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGL_PLATFORM_COCOA_GL_H
#include <backend/DriverEnums.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <stdint.h>
@@ -80,7 +80,8 @@ protected:
* Initializes EGL, creates the OpenGL context and returns a concrete Driver implementation
* that supports OpenGL/OpenGL ES.
*/
Driver* createDriver(void* sharedContext, const DriverConfig& driverConfig) noexcept override;
Driver* createDriver(void* sharedContext,
const Platform::DriverConfig& driverConfig) noexcept override;
/**
* This returns zero. This method can be overridden to return something more useful.
@@ -1,38 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_OBJC_H
#define TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_OBJC_H
#import <Metal/Metal.h>
namespace filament::backend {
struct MetalDevice {
id<MTLDevice> device;
};
struct MetalCommandQueue {
id<MTLCommandQueue> commandQueue;
};
struct MetalCommandBuffer {
id<MTLCommandBuffer> commandBuffer;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_PRIVATE_PLATFORMMETAL_OBJC_H
@@ -20,17 +20,12 @@
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#import <Metal/Metal.h>
namespace filament::backend {
struct PlatformMetalImpl;
// In order for this header to be compatible with Objective-C and C++, we use these wrappers around
// id<MTL*> objects.
// See PlatformMetal-Objc.h.
struct MetalDevice;
struct MetalCommandQueue;
struct MetalCommandBuffer;
class PlatformMetal final : public Platform {
public:
PlatformMetal();
@@ -46,22 +41,21 @@ public:
* free to decide which one to use. On mobile systems with a single GPU, implementations should
* simply return the result of MTLCreateSystemDefaultDevice();
*/
virtual void createDevice(MetalDevice& outDevice) noexcept;
virtual id<MTLDevice> createDevice() noexcept;
/**
* Create a command submission queue on the Metal device object.
*
* @param device The device which was returned from createDevice()
*/
virtual void createCommandQueue(
MetalDevice& device, MetalCommandQueue& outCommandQueue) noexcept;
virtual id<MTLCommandQueue> createCommandQueue(id<MTLDevice> device) noexcept;
/**
* 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.
*/
void createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept;
id<MTLCommandBuffer> createAndEnqueueCommandBuffer() noexcept;
/**
* The action to take if a Drawable cannot be acquired.
@@ -71,9 +71,6 @@ public:
// where the gpu only has one graphics queue. Then the client needs to ensure that no
// concurrent access can occur.
uint32_t graphicsQueueIndex = 0xFFFFFFFF;
bool debugUtilsSupported = false;
bool debugMarkersSupported = false;
bool multiviewSupported = false;
};
/**
@@ -91,7 +88,6 @@ public:
VkFormat colorFormat = VK_FORMAT_UNDEFINED;
VkFormat depthFormat = VK_FORMAT_UNDEFINED;
VkExtent2D extent = {0, 0};
uint32_t layerCount = 1;
bool isProtected = false;
};
@@ -307,11 +303,6 @@ public:
*/
uint32_t height;
/**
* The layerCount of the external image
*/
uint32_t layerCount;
/**
* The layer count of the external image
*/
@@ -29,7 +29,6 @@
#include <tsl/robin_map.h>
#include <atomic>
#include <cstddef>
#include <exception>
#include <mutex>
@@ -250,8 +249,8 @@ public:
if (UTILS_LIKELY(isPoolHandle(id))) {
// Truncate the age to get the debug tag
key &= ~(HANDLE_DEBUG_TAG_MASK ^ HANDLE_AGE_MASK);
writeHandleTag(key, std::move(tag));
}
writeHandleTag(key, std::move(tag));
}
private:
@@ -427,7 +426,7 @@ private:
// Below is only used when running out of space in the HandleArena
mutable utils::Mutex mLock;
tsl::robin_map<HandleBase::HandleId, void*> mOverflowMap;
std::atomic<HandleBase::HandleId> mId = 0;
HandleBase::HandleId mId = 0;
bool mUseAfterFreeCheckDisabled = false;
};
+7 -16
View File
@@ -35,13 +35,12 @@ void Platform::ExternalImageHandle::incref(ExternalImage* p) noexcept {
void Platform::ExternalImageHandle::decref(ExternalImage* p) noexcept {
if (p) {
// When decrementing the ref-count, unless it reaches zero, there is no need to acquire
// data; we need to release all previous writes though so they can be visible to the thread
// that will actually delete the object.
// When decrementing the ref-count, unless it reaches zero, there is no need to acquire data; we need to
// release all previous writes though so they can be visible to the thread that will actually delete the
// object.
if (p->mRefCount.fetch_sub(1, std::memory_order_release) == 1) {
// if we reach zero, we're about to delete the object, we need to acquire all previous
// writes from other threads (i.e.: the memory from other threads prior to the decref()
// need to be visible now.
// if we reach zero, we're about to delete the object, we need to acquire all previous writes from other
// threads (i.e.: the memory from other threads prior to the decref() need to be visible now.
std::atomic_thread_fence(std::memory_order_acquire);
delete p;
}
@@ -69,8 +68,7 @@ Platform::ExternalImageHandle::ExternalImageHandle(ExternalImageHandle&& rhs) no
rhs.mTarget = nullptr;
}
Platform::ExternalImageHandle& Platform::ExternalImageHandle::operator=(
ExternalImageHandle const& rhs) noexcept {
Platform::ExternalImageHandle& Platform::ExternalImageHandle::operator=(ExternalImageHandle const& rhs) noexcept {
if (UTILS_LIKELY(this != &rhs)) {
incref(rhs.mTarget);
decref(mTarget);
@@ -79,8 +77,7 @@ Platform::ExternalImageHandle& Platform::ExternalImageHandle::operator=(
return *this;
}
Platform::ExternalImageHandle& Platform::ExternalImageHandle::operator=(
ExternalImageHandle&& rhs) noexcept {
Platform::ExternalImageHandle& Platform::ExternalImageHandle::operator=(ExternalImageHandle&& rhs) noexcept {
if (UTILS_LIKELY(this != &rhs)) {
decref(mTarget);
mTarget = rhs.mTarget;
@@ -100,12 +97,6 @@ void Platform::ExternalImageHandle::reset(ExternalImage* p) noexcept {
mTarget = p;
}
utils::io::ostream& operator<<(utils::io::ostream& out,
Platform::ExternalImageHandle const& handle) {
out << "ExternalImageHandle{" << handle.mTarget << "}";
return out;
}
// --------------------------------------------------------------------------------------------------------------------
Platform::ExternalImage::~ExternalImage() noexcept = default;
+3 -22
View File
@@ -31,7 +31,6 @@
#include "MetalTimerQuery.h"
#include <backend/platforms/PlatformMetal.h>
#include <backend/platforms/PlatformMetal-ObjC.h>
#include <CoreVideo/CVMetalTexture.h>
#include <CoreVideo/CVPixelBuffer.h>
@@ -121,11 +120,8 @@ MetalDriver::MetalDriver(
TrackedMetalBuffer::setPlatform(platform);
ScopedAllocationTimer::setPlatform(platform);
MetalDevice device;
device.device = nil;
mPlatform.createDevice(device);
mContext->device = device.device;
FILAMENT_CHECK_POSTCONDITION(mContext->device) << "Could not obtain Metal device.";
mContext->device = mPlatform.createDevice();
assert_invariant(mContext->device);
mContext->emptyBuffer = [mContext->device newBufferWithLength:16
options:MTLResourceStorageModePrivate];
@@ -190,13 +186,7 @@ MetalDriver::MetalDriver(
[mContext->device.name containsString:@"Apple A8 GPU"] ||
[mContext->device.name containsString:@"Apple A7 GPU"];
MetalCommandQueue commandQueue;
commandQueue.commandQueue = nil;
mPlatform.createCommandQueue(device, commandQueue);
FILAMENT_CHECK_POSTCONDITION(commandQueue.commandQueue)
<< "Could not create Metal command queue.";
mContext->commandQueue = commandQueue.commandQueue;
mContext->commandQueue = mPlatform.createCommandQueue(mContext->device);
mContext->pipelineStateCache.setDevice(mContext->device);
mContext->depthStencilStateCache.setDevice(mContext->device);
mContext->samplerStateCache.setDevice(mContext->device);
@@ -1408,9 +1398,6 @@ void MetalDriver::commit(Handle<HwSwapChain> sch) {
void MetalDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
if (UTILS_UNLIKELY(mContext->currentRenderPassAbandoned)) {
return;
}
FILAMENT_CHECK_PRECONDITION(isInRenderPass(mContext))
<< "setPushConstant must be called inside a render pass.";
assert_invariant(static_cast<size_t>(stage) < mContext->currentPushConstants.size());
@@ -1718,9 +1705,6 @@ void MetalDriver::blitDEPRECATED(TargetBufferFlags buffers,
}
void MetalDriver::bindPipeline(PipelineState const& ps) {
if (UTILS_UNLIKELY(mContext->currentRenderPassAbandoned)) {
return;
}
FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder != nullptr)
<< "bindPipeline() without a valid command encoder.";
DEBUG_LOG("bindPipeline(ps = { program = %d }))\n", ps.program.getId());
@@ -1876,9 +1860,6 @@ void MetalDriver::bindPipeline(PipelineState const& ps) {
}
void MetalDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
if (UTILS_UNLIKELY(mContext->currentRenderPassAbandoned)) {
return;
}
FILAMENT_CHECK_PRECONDITION(mContext->currentRenderPassEncoder != nullptr)
<< "bindRenderPrimitive() without a valid command encoder.";
+6 -46
View File
@@ -36,42 +36,6 @@
#include <math.h>
// A wrapper around a C void* pointer that will free the memory when deallocated, unless the pointer
// is first reset.
// ReleasablePointer serves a similar purpose as std::unique_ptr, but avoids false positives with
// TSAN and Objective-C blocks.
@interface ReleasablePointer : NSObject
@property(nonatomic, readonly) void* p;
@end
@implementation ReleasablePointer {
void* _p;
}
- (id)initWithPointer:(void*)p {
self = [super init];
if (self) {
_p = p;
}
return self;
}
- (void)dealloc {
if (_p) {
free(_p);
}
}
- (void*)p {
return _p;
}
- (void)reset {
_p = nullptr;
}
@end
namespace filament {
namespace backend {
@@ -392,17 +356,15 @@ void MetalSwapChain::scheduleFrameScheduledCallback() {
};
// This callback pointer will be captured by the block. Even if the scheduled handler is never
// called, the ReleasablePointer will still ensure we don't leak memory.
// called, the unique_ptr will still ensure we don't leak memory.
uint64_t const flags = frameScheduled.flags;
ReleasablePointer* callback = [[ReleasablePointer alloc]
initWithPointer:new Callback(frameScheduled.callback, drawable, layerDrawableMutex,
context.driver, flags)];
__block auto callback = std::make_unique<Callback>(
frameScheduled.callback, drawable, layerDrawableMutex, context.driver, flags);
backend::CallbackHandler* handler = frameScheduled.handler;
MetalDriver* driver = context.driver;
[getPendingCommandBuffer(&context) addScheduledHandler:^(id<MTLCommandBuffer> cb) {
Callback* user = static_cast<Callback*>(callback.p);
[callback reset];
Callback* user = callback.release();
if (flags & SwapChain::CALLBACK_DEFAULT_USE_METAL_COMPLETION_HANDLER) {
Callback::func(user);
} else {
@@ -428,14 +390,12 @@ void MetalSwapChain::scheduleFrameCompletedCallback() {
// This callback pointer will be captured by the block. Even if the completed handler is never
// called, the unique_ptr will still ensure we don't leak memory.
ReleasablePointer* callback =
[[ReleasablePointer alloc] initWithPointer:new Callback(frameCompleted.callback)];
__block auto callback = std::make_unique<Callback>(frameCompleted.callback);
CallbackHandler* handler = frameCompleted.handler;
MetalDriver* driver = context.driver;
[getPendingCommandBuffer(&context) addCompletedHandler:^(id<MTLCommandBuffer> cb) {
Callback* user = static_cast<Callback*>(callback.p);
[callback reset];
Callback* user = callback.release();
driver->scheduleCallback(handler, user, &Callback::func);
}];
}
+7 -12
View File
@@ -15,7 +15,6 @@
*/
#include <backend/platforms/PlatformMetal.h>
#include <backend/platforms/PlatformMetal-ObjC.h>
#include "MetalDriverFactory.h"
@@ -45,13 +44,10 @@ PlatformMetal::~PlatformMetal() noexcept {
}
Driver* PlatformMetal::createDriver(void* /*sharedContext*/, const Platform::DriverConfig& driverConfig) noexcept {
pImpl->mDrawableFailureBehavior = driverConfig.metalDisablePanicOnDrawableFailure
? DrawableFailureBehavior::ABORT_FRAME
: DrawableFailureBehavior::PANIC;
return MetalDriverFactory::create(this, driverConfig);
}
void PlatformMetal::createDevice(MetalDevice& outDevice) noexcept {
id<MTLDevice> PlatformMetal::createDevice() noexcept {
id<MTLDevice> result;
#if !defined(FILAMENT_IOS)
@@ -76,20 +72,19 @@ void PlatformMetal::createDevice(MetalDevice& outDevice) noexcept {
<< [result.name cStringUsingEncoding:NSUTF8StringEncoding] << "'"
<< utils::io::endl;
outDevice.device = result;
return result;
}
void PlatformMetal::createCommandQueue(
MetalDevice& device, MetalCommandQueue& outCommandQueue) noexcept {
pImpl->mCommandQueue = [device.device newCommandQueue];
id<MTLCommandQueue> PlatformMetal::createCommandQueue(id<MTLDevice> device) noexcept {
pImpl->mCommandQueue = [device newCommandQueue];
pImpl->mCommandQueue.label = @"Filament";
outCommandQueue.commandQueue = pImpl->mCommandQueue;
return pImpl->mCommandQueue;
}
void PlatformMetal::createAndEnqueueCommandBuffer(MetalCommandBuffer& outCommandBuffer) noexcept {
id<MTLCommandBuffer> PlatformMetal::createAndEnqueueCommandBuffer() noexcept {
id<MTLCommandBuffer> commandBuffer = [pImpl->mCommandQueue commandBuffer];
[commandBuffer enqueue];
outCommandBuffer.commandBuffer = commandBuffer;
return commandBuffer;
}
void PlatformMetal::setDrawableFailureBehavior(DrawableFailureBehavior behavior) noexcept {
-3
View File
@@ -14,9 +14,6 @@
* limitations under the License.
*/
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include "noop/NoopDriver.h"
#include "CommandStreamDispatcher.h"
+50 -55
View File
@@ -39,7 +39,6 @@
#include <backend/Program.h>
#include <backend/TargetBufferInfo.h>
#include "private/backend/CommandStream.h"
#include "private/backend/Dispatcher.h"
#include "private/backend/DriverApi.h"
@@ -47,11 +46,9 @@
#include <utils/BitmaskEnum.h>
#include <utils/FixedCapacityVector.h>
#include <utils/CString.h>
#include <utils/Invocable.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/Systrace.h>
#include <utils/Slice.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
@@ -157,8 +154,8 @@ using namespace GLUtils;
// ------------------------------------------------------------------------------------------------
UTILS_NOINLINE
OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* const platform,
void* const /*sharedGLContext*/, const Platform::DriverConfig& driverConfig) noexcept {
Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
void* const sharedGLContext, const Platform::DriverConfig& driverConfig) noexcept {
assert_invariant(platform);
OpenGLPlatform* const ec = platform;
@@ -225,7 +222,7 @@ OpenGLDriver* OpenGLDriver::create(OpenGLPlatform* const platform,
}
#endif
constexpr size_t defaultSize = FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
size_t const defaultSize = FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
Platform::DriverConfig validConfig{ driverConfig };
validConfig.handleArenaSize = std::max(driverConfig.handleArenaSize, defaultSize);
OpenGLDriver* const driver = new(std::nothrow) OpenGLDriver(ec, validConfig);
@@ -280,6 +277,7 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi
driverConfig.disableHandleUseAfterFreeCheck),
mDriverConfig(driverConfig),
mCurrentPushConstants(new(std::nothrow) PushConstantBundle{}) {
// set a reasonable default value for our stream array
mTexturesWithStreamsAttached.reserve(8);
mStreamsWithPendingAcquiredImage.reserve(8);
@@ -368,8 +366,8 @@ void OpenGLDriver::bindSampler(GLuint unit, GLuint sampler) noexcept {
mContext.bindSampler(unit, sampler);
}
void OpenGLDriver::setPushConstant(ShaderStage stage, uint8_t index,
PushConstantVariant value) {
void OpenGLDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
backend::PushConstantVariant value) {
assert_invariant(stage == ShaderStage::VERTEX || stage == ShaderStage::FRAGMENT);
#if FILAMENT_ENABLE_MATDBG
@@ -378,7 +376,7 @@ void OpenGLDriver::setPushConstant(ShaderStage stage, uint8_t index,
}
#endif
Slice<std::pair<GLint, ConstantType>> constants;
utils::Slice<std::pair<GLint, ConstantType>> constants;
if (stage == ShaderStage::VERTEX) {
constants = mCurrentPushConstants->vertexConstants;
} else if (stage == ShaderStage::FRAGMENT) {
@@ -683,7 +681,7 @@ void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh,
bo->gl.buffer = malloc(byteCount);
memset(bo->gl.buffer, 0, byteCount);
} else {
bo->gl.binding = getBufferBindingType(bindingType);
bo->gl.binding = GLUtils::getBufferBindingType(bindingType);
glGenBuffers(1, &bo->gl.id);
gl.bindBuffer(bo->gl.binding, bo->gl.id);
glBufferData(bo->gl.binding, byteCount, nullptr, getBufferUsage(usage));
@@ -760,7 +758,7 @@ void OpenGLDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
}
UTILS_NOINLINE
void OpenGLDriver::textureStorage(GLTexture* t,
void OpenGLDriver::textureStorage(OpenGLDriver::GLTexture* t,
uint32_t width, uint32_t height, uint32_t depth, bool useProtectedMemory) noexcept {
auto& gl = mContext;
@@ -896,17 +894,6 @@ void OpenGLDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
if (UTILS_UNLIKELY(t->target == SamplerType::SAMPLER_EXTERNAL)) {
t->externalTexture = mPlatform.createExternalImageTexture();
if (t->externalTexture) {
if (target == SamplerType::SAMPLER_EXTERNAL) {
if (UTILS_LIKELY(gl.ext.OES_EGL_image_external_essl3)) {
t->externalTexture->target = GL_TEXTURE_EXTERNAL_OES;
} else {
// revert to texture 2D if external is not supported; what else can we do?
t->externalTexture->target = GL_TEXTURE_2D;
}
} else {
t->externalTexture->target = getTextureTargetNotExternal(target);
}
t->gl.target = t->externalTexture->target;
t->gl.id = t->externalTexture->id;
// internalFormat actually depends on the external image, but it doesn't matter
@@ -1157,7 +1144,7 @@ void OpenGLDriver::createTextureExternalImageR(Handle<HwTexture> th, SamplerType
}
void OpenGLDriver::createTextureExternalImagePlaneR(Handle<HwTexture> th,
TextureFormat format, uint32_t width, uint32_t height, TextureUsage usage,
backend::TextureFormat format, uint32_t width, uint32_t height, backend::TextureUsage usage,
void* image, uint32_t plane) {
// not relevant for the OpenGL backend
}
@@ -1473,7 +1460,7 @@ void OpenGLDriver::framebufferTexture(TargetBufferInfo const& binfo,
&& ((gl.ext.EXT_multisampled_render_to_texture && attachment == GL_COLOR_ATTACHMENT0)
|| gl.ext.EXT_multisampled_render_to_texture2)) {
assert_invariant(rt->gl.samples > 1);
// We have a multi-sample rendertarget, and we have EXT_multisampled_render_to_texture,
// We have a multi-sample rendertarget and we have EXT_multisampled_render_to_texture,
// so, we can directly use a 1-sample texture as attachment, multi-sample resolve,
// will happen automagically and efficiently in the driver.
// This extension only exists on OpenGL ES.
@@ -1673,9 +1660,9 @@ void OpenGLDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
rt->gl.samples = samples;
rt->targets = targets;
UTILS_UNUSED_IN_RELEASE vec2<uint32_t> tmin = { std::numeric_limits<uint32_t>::max() };
UTILS_UNUSED_IN_RELEASE vec2<uint32_t> tmax = { 0 };
auto checkDimensions = [&tmin, &tmax](GLTexture const* t, uint8_t level) {
UTILS_UNUSED_IN_RELEASE math::vec2<uint32_t> tmin = { std::numeric_limits<uint32_t>::max() };
UTILS_UNUSED_IN_RELEASE math::vec2<uint32_t> tmax = { 0 };
auto checkDimensions = [&tmin, &tmax](GLTexture* t, uint8_t level) {
const auto twidth = std::max(1u, t->width >> level);
const auto theight = std::max(1u, t->height >> level);
tmin = { std::min(tmin.x, twidth), std::min(tmin.y, theight) };
@@ -2377,11 +2364,11 @@ bool OpenGLDriver::isStereoSupported() {
return false;
}
switch (mDriverConfig.stereoscopicType) {
case StereoscopicType::INSTANCED:
case backend::StereoscopicType::INSTANCED:
return mContext.ext.EXT_clip_cull_distance;
case StereoscopicType::MULTIVIEW:
case backend::StereoscopicType::MULTIVIEW:
return mContext.ext.OVR_multiview2;
case StereoscopicType::NONE:
case backend::StereoscopicType::NONE:
return false;
}
}
@@ -2394,7 +2381,7 @@ bool OpenGLDriver::isDepthStencilResolveSupported() {
return true;
}
bool OpenGLDriver::isDepthStencilBlitSupported(TextureFormat) {
bool OpenGLDriver::isDepthStencilBlitSupported(TextureFormat format) {
return true;
}
@@ -2428,12 +2415,12 @@ FeatureLevel OpenGLDriver::getFeatureLevel() {
return mContext.getFeatureLevel();
}
float2 OpenGLDriver::getClipSpaceParams() {
math::float2 OpenGLDriver::getClipSpaceParams() {
return mContext.ext.EXT_clip_control ?
// z-coordinate of virtual and physical clip-space is in [-w, 0]
float2{ 1.0f, 0.0f } :
math::float2{ 1.0f, 0.0f } :
// z-coordinate of virtual clip-space is in [-w,0], physical is in [-w, w]
float2{ 2.0f, -1.0f };
math::float2{ 2.0f, -1.0f };
}
uint8_t OpenGLDriver::getMaxDrawBuffers() {
@@ -2497,7 +2484,7 @@ void OpenGLDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain>
// Updating driver objects
// ------------------------------------------------------------------------------------------------
void OpenGLDriver::setDebugTag(HandleBase::HandleId handleId, CString tag) {
void OpenGLDriver::setDebugTag(HandleBase::HandleId handleId, utils::CString tag) {
mHandleAllocator.associateTagToHandle(handleId, std::move(tag));
}
@@ -2673,7 +2660,7 @@ void OpenGLDriver::generateMipmaps(Handle<HwTexture> th) {
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::setTextureData(GLTexture const* t, uint32_t level,
void OpenGLDriver::setTextureData(GLTexture* t, uint32_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
uint32_t width, uint32_t height, uint32_t depth,
PixelBufferDescriptor&& p) {
@@ -2782,7 +2769,7 @@ void OpenGLDriver::setTextureData(GLTexture const* t, uint32_t level,
CHECK_GL_ERROR(utils::slog.e)
}
void OpenGLDriver::setCompressedTextureData(GLTexture const* t, uint32_t level,
void OpenGLDriver::setCompressedTextureData(GLTexture* t, uint32_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
uint32_t width, uint32_t height, uint32_t depth,
PixelBufferDescriptor&& p) {
@@ -3423,8 +3410,8 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
#endif
}
void OpenGLDriver::readBufferSubData(BufferObjectHandle boh,
uint32_t offset, uint32_t size, BufferDescriptor&& p) {
void OpenGLDriver::readBufferSubData(backend::BufferObjectHandle boh,
uint32_t offset, uint32_t size, backend::BufferDescriptor&& p) {
UTILS_UNUSED_IN_RELEASE auto& gl = mContext;
assert_invariant(!gl.isES2());
@@ -3502,6 +3489,14 @@ void OpenGLDriver::whenFrameComplete(const std::function<void()>& fn) noexcept {
void OpenGLDriver::whenGpuCommandsComplete(const std::function<void()>& fn) noexcept {
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
// glFenceSync can return 0 in certain situations. For example, we've seen this happen on Web
// when the GL context has been lost.
// Unfortunately, there's not much we can do here. The fn callback will never execute, which
// could cause issues. If we've lost the WebGL context though, Filament is most likely shutting
// down anyway, so in practice this might not be a big problem.
if (!sync) {
return;
}
mGpuCommandCompleteOps.emplace_back(sync, fn);
CHECK_GL_ERROR(utils::slog.e)
}
@@ -3571,13 +3566,13 @@ void OpenGLDriver::beginFrame(
}
}
void OpenGLDriver::setFrameScheduledCallback(Handle<HwSwapChain>, CallbackHandler*,
FrameScheduledCallback&& /*callback*/, uint64_t /*flags*/) {
void OpenGLDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
FrameScheduledCallback&& callback, uint64_t flags) {
DEBUG_MARKER()
}
void OpenGLDriver::setFrameCompletedCallback(Handle<HwSwapChain>,
CallbackHandler*, Invocable<void()>&& /*callback*/) {
void OpenGLDriver::setFrameCompletedCallback(Handle<HwSwapChain> sch,
CallbackHandler* handler, utils::Invocable<void(void)>&& callback) {
DEBUG_MARKER()
}
@@ -3608,9 +3603,9 @@ void OpenGLDriver::endFrame(UTILS_UNUSED uint32_t frameId) {
}
void OpenGLDriver::updateDescriptorSetBuffer(
DescriptorSetHandle dsh,
descriptor_binding_t binding,
BufferObjectHandle boh,
backend::DescriptorSetHandle dsh,
backend::descriptor_binding_t binding,
backend::BufferObjectHandle boh,
uint32_t offset, uint32_t size) {
GLDescriptorSet* ds = handle_cast<GLDescriptorSet*>(dsh);
GLBufferObject* bo = boh ? handle_cast<GLBufferObject*>(boh) : nullptr;
@@ -3618,9 +3613,9 @@ void OpenGLDriver::updateDescriptorSetBuffer(
}
void OpenGLDriver::updateDescriptorSetTexture(
DescriptorSetHandle dsh,
descriptor_binding_t binding,
TextureHandle th,
backend::DescriptorSetHandle dsh,
backend::descriptor_binding_t binding,
backend::TextureHandle th,
SamplerParams params) {
GLDescriptorSet* ds = handle_cast<GLDescriptorSet*>(dsh);
GLTexture* t = th ? handle_cast<GLTexture*>(th) : nullptr;
@@ -3653,7 +3648,7 @@ void OpenGLDriver::finish(int) {
UTILS_NOINLINE
void OpenGLDriver::clearWithRasterPipe(TargetBufferFlags clearFlags,
float4 const& linearColor, GLfloat depth, GLint stencil) noexcept {
math::float4 const& linearColor, GLfloat depth, GLint stencil) noexcept {
if (any(clearFlags & TargetBufferFlags::COLOR_ALL)) {
mContext.colorMask(GL_TRUE);
@@ -3976,9 +3971,9 @@ void OpenGLDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
}
void OpenGLDriver::bindDescriptorSet(
DescriptorSetHandle dsh,
descriptor_set_t set,
DescriptorSetOffsetArray&& offsets) {
backend::DescriptorSetHandle dsh,
backend::descriptor_set_t set,
backend::DescriptorSetOffsetArray&& offsets) {
if (UTILS_UNLIKELY(!dsh)) {
mBoundDescriptorSets[set].dsh = dsh;
@@ -4010,7 +4005,7 @@ void OpenGLDriver::bindDescriptorSet(
}
}
void OpenGLDriver::updateDescriptors(bitset8 invalidDescriptorSets) noexcept {
void OpenGLDriver::updateDescriptors(utils::bitset8 invalidDescriptorSets) noexcept {
assert_invariant(mBoundProgram);
auto const offsetOnly = mInvalidDescriptorSetBindingOffsets & ~mInvalidDescriptorSetBindings;
invalidDescriptorSets.forEachSetBit([this, offsetOnly,
@@ -4122,7 +4117,7 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
}
}
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, uint3 workGroupCount) {
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
DEBUG_MARKER()
getShaderCompilerService().tick();
+21 -30
View File
@@ -19,7 +19,6 @@
#include "DriverBase.h"
#include "OpenGLContext.h"
#include "OpenGLDriverBase.h"
#include "OpenGLTimerQuery.h"
#include "GLBufferObject.h"
#include "GLDescriptorSet.h"
@@ -27,10 +26,11 @@
#include "GLTexture.h"
#include "ShaderCompilerService.h"
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/AcquiredImage.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/PipelineState.h>
#include <backend/Platform.h>
#include <backend/Program.h>
#include <backend/TargetBufferInfo.h>
@@ -41,7 +41,6 @@
#include <utils/bitset.h>
#include <utils/FixedCapacityVector.h>
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/debug.h>
#include <math/vec4.h>
@@ -75,14 +74,14 @@ class OpenGLProgram;
class TimerQueryFactoryInterface;
struct PushConstantBundle;
class OpenGLDriver final : public OpenGLDriverBase {
class OpenGLDriver final : public DriverBase {
inline explicit OpenGLDriver(OpenGLPlatform* platform,
const Platform::DriverConfig& driverConfig) noexcept;
~OpenGLDriver() noexcept override;
Dispatcher getDispatcher() const noexcept override;
~OpenGLDriver() noexcept final;
Dispatcher getDispatcher() const noexcept final;
public:
static OpenGLDriver* create(OpenGLPlatform* platform, void* sharedGLContext,
static Driver* create(OpenGLPlatform* platform, void* sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept;
class DebugMarker {
@@ -185,7 +184,7 @@ public:
std::condition_variable cond;
FenceStatus status{ FenceStatus::TIMEOUT_EXPIRED };
};
std::shared_ptr<State> state{ std::make_shared<State>() };
std::shared_ptr<State> state{ std::make_shared<GLFence::State>() };
};
OpenGLDriver(OpenGLDriver const&) = delete;
@@ -204,21 +203,13 @@ private:
return mShaderCompilerService;
}
ShaderModel getShaderModel() const noexcept override;
ShaderLanguage getShaderLanguage() const noexcept override;
ShaderModel getShaderModel() const noexcept final;
ShaderLanguage getShaderLanguage() const noexcept final;
/*
* OpenGLDriver interface
* Driver interface
*/
utils::CString getVendorString() const noexcept override {
return utils::CString{ mContext.state.vendor };
}
utils::CString getRendererString() const noexcept override {
return utils::CString{ mContext.state.renderer };
}
template<typename T>
friend class ConcreteDispatcher;
@@ -245,21 +236,21 @@ private:
}
template<typename D, typename B, typename ... ARGS>
std::enable_if_t<std::is_base_of_v<B, D>, D>*
typename std::enable_if<std::is_base_of<B, D>::value, D>::type*
construct(Handle<B> const& handle, ARGS&& ... args) {
return mHandleAllocator.destroyAndConstruct<D, B>(handle, std::forward<ARGS>(args) ...);
}
template<typename B, typename D,
typename = std::enable_if_t<std::is_base_of_v<B, D>, D>>
typename = typename std::enable_if<std::is_base_of<B, D>::value, D>::type>
void destruct(Handle<B>& handle, D const* p) noexcept {
return mHandleAllocator.deallocate(handle, p);
}
template<typename Dp, typename B>
std::enable_if_t<
typename std::enable_if_t<
std::is_pointer_v<Dp> &&
std::is_base_of_v<B, std::remove_pointer_t<Dp>>, Dp>
std::is_base_of_v<B, typename std::remove_pointer_t<Dp>>, Dp>
handle_cast(Handle<B>& handle) {
return mHandleAllocator.handle_cast<Dp, B>(handle);
}
@@ -270,9 +261,9 @@ private:
}
template<typename Dp, typename B>
std::enable_if_t<
inline typename std::enable_if_t<
std::is_pointer_v<Dp> &&
std::is_base_of_v<B, std::remove_pointer_t<Dp>>, Dp>
std::is_base_of_v<B, typename std::remove_pointer_t<Dp>>, Dp>
handle_cast(Handle<B> const& handle) {
return mHandleAllocator.handle_cast<Dp, B>(handle);
}
@@ -297,13 +288,13 @@ private:
void setStencilState(StencilState ss) noexcept;
void setTextureData(GLTexture const* t,
void setTextureData(GLTexture* t,
uint32_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
uint32_t width, uint32_t height, uint32_t depth,
PixelBufferDescriptor&& p);
void setCompressedTextureData(GLTexture const* t,
void setCompressedTextureData(GLTexture* t,
uint32_t level,
uint32_t xoffset, uint32_t yoffset, uint32_t zoffset,
uint32_t width, uint32_t height, uint32_t depth,
@@ -312,7 +303,7 @@ private:
void renderBufferStorage(GLuint rbo, GLenum internalformat, uint32_t width,
uint32_t height, uint8_t samples) const noexcept;
void textureStorage(GLTexture* t, uint32_t width, uint32_t height,
void textureStorage(OpenGLDriver::GLTexture* t, uint32_t width, uint32_t height,
uint32_t depth, bool useProtectedMemory) noexcept;
/* State tracking GL wrappers... */
@@ -344,9 +335,9 @@ private:
void updateDescriptors(utils::bitset8 invalidDescriptorSets) noexcept;
struct {
DescriptorSetHandle dsh;
backend::DescriptorSetHandle dsh;
std::array<uint32_t, CONFIG_UNIFORM_BINDING_COUNT> offsets;
} mBoundDescriptorSets[MAX_DESCRIPTOR_SET_COUNT] = {};
} mBoundDescriptorSets[MAX_DESCRIPTOR_SET_COUNT];
void clearWithRasterPipe(TargetBufferFlags clearFlags,
math::float4 const& linearColor, GLfloat depth, GLint stencil) noexcept;
@@ -1,37 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_OPENGL_OPENGLDRIVERBASE_H
#define TNT_FILAMENT_BACKEND_OPENGL_OPENGLDRIVERBASE_H
#include "DriverBase.h"
#include <utils/CString.h>
namespace filament::backend {
class OpenGLDriverBase : public DriverBase {
protected:
~OpenGLDriverBase() override;
public:
virtual utils::CString getVendorString() const noexcept = 0;
virtual utils::CString getRendererString() const noexcept = 0;
};
} // filament::backend
#endif //TNT_FILAMENT_BACKEND_OPENGL_OPENGLDRIVERBASE_H
+2 -27
View File
@@ -16,7 +16,6 @@
#include <backend/platforms/OpenGLPlatform.h>
#include "OpenGLDriverBase.h"
#include "OpenGLDriverFactory.h"
#include <backend/AcquiredImage.h>
@@ -24,45 +23,21 @@
#include <backend/Platform.h>
#include <utils/compiler.h>
#include <utils/Panic.h>
#include <utils/CString.h>
#include <utils/Invocable.h>
#include <utils/Invocable.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include "OpenGLDriver.h"
namespace filament::backend {
OpenGLDriverBase::~OpenGLDriverBase() = default;
Driver* OpenGLPlatform::createDefaultDriver(OpenGLPlatform* platform,
void* sharedContext, const DriverConfig& driverConfig) {
void* sharedContext, const Platform::DriverConfig& driverConfig) {
return OpenGLDriverFactory::create(platform, sharedContext, driverConfig);
}
OpenGLPlatform::~OpenGLPlatform() noexcept = default;
utils::CString OpenGLPlatform::getVendorString(Driver const* driver) {
auto const p = static_cast<OpenGLDriverBase const*>(driver);
#if UTILS_HAS_RTTI
FILAMENT_CHECK_POSTCONDITION(dynamic_cast<OpenGLDriverBase const*>(driver))
<< "Driver* has not been allocated with OpenGLPlatform";
#endif
return p->getVendorString();
}
utils::CString OpenGLPlatform::getRendererString(Driver const* driver) {
auto const p = static_cast<OpenGLDriverBase const*>(driver);
#if UTILS_HAS_RTTI
FILAMENT_CHECK_POSTCONDITION(dynamic_cast<OpenGLDriverBase const*>(driver))
<< "Driver* has not been allocated with OpenGLPlatform";
#endif
return p->getRendererString();
}
void OpenGLPlatform::makeCurrent(SwapChain* drawSwapChain, SwapChain* readSwapChain,
utils::Invocable<void()>, utils::Invocable<void(size_t)>) noexcept {
makeCurrent(getCurrentContextType(), drawSwapChain, readSwapChain);
@@ -297,7 +297,7 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const DriverConfig& drive
clearGlError();
// success!!
return createDefaultDriver(this, sharedContext, driverConfig);
return OpenGLPlatform::createDefaultDriver(this, sharedContext, driverConfig);
error:
// if we're here, we've failed
@@ -551,7 +551,7 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
return sc;
}
void PlatformEGL::destroySwapChain(SwapChain* swapChain) noexcept {
void PlatformEGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
if (swapChain) {
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
if (sc->sur != EGL_NO_SURFACE) {
@@ -564,7 +564,7 @@ void PlatformEGL::destroySwapChain(SwapChain* swapChain) noexcept {
}
}
bool PlatformEGL::isSwapChainProtected(SwapChain* swapChain) noexcept {
bool PlatformEGL::isSwapChainProtected(Platform::SwapChain* swapChain) noexcept {
if (swapChain) {
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
return bool(sc->flags & SWAP_CHAIN_CONFIG_PROTECTED_CONTENT);
@@ -585,10 +585,10 @@ bool PlatformEGL::makeCurrent(ContextType type,
return success == EGL_TRUE ? true : false;
}
void PlatformEGL::makeCurrent(SwapChain* drawSwapChain,
SwapChain* readSwapChain,
Invocable<void()> preContextChange,
Invocable<void(size_t index)> postContextChange) noexcept {
void PlatformEGL::makeCurrent(Platform::SwapChain* drawSwapChain,
Platform::SwapChain* readSwapChain,
utils::Invocable<void()> preContextChange,
utils::Invocable<void(size_t index)> postContextChange) noexcept {
assert_invariant(drawSwapChain);
assert_invariant(readSwapChain);
@@ -647,7 +647,7 @@ void PlatformEGL::makeCurrent(SwapChain* drawSwapChain,
}
}
void PlatformEGL::commit(SwapChain* swapChain) noexcept {
void PlatformEGL::commit(Platform::SwapChain* swapChain) noexcept {
if (swapChain) {
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
if (sc->sur != EGL_NO_SURFACE) {
@@ -670,7 +670,7 @@ Platform::Fence* PlatformEGL::createFence() noexcept {
return f;
}
void PlatformEGL::destroyFence(Fence* fence) noexcept {
void PlatformEGL::destroyFence(Platform::Fence* fence) noexcept {
#ifdef EGL_KHR_reusable_sync
EGLSyncKHR sync = (EGLSyncKHR) fence;
if (sync != EGL_NO_SYNC_KHR) {
@@ -680,7 +680,7 @@ void PlatformEGL::destroyFence(Fence* fence) noexcept {
}
FenceStatus PlatformEGL::waitFence(
Fence* fence, uint64_t timeout) noexcept {
Platform::Fence* fence, uint64_t timeout) noexcept {
#ifdef EGL_KHR_reusable_sync
EGLSyncKHR sync = (EGLSyncKHR) fence;
if (sync != EGL_NO_SYNC_KHR) {
@@ -746,7 +746,7 @@ void PlatformEGL::initializeGlExtensions() noexcept {
ext.gl.OES_EGL_image_external_essl3 = glExtensions.has("GL_OES_EGL_image_external_essl3");
}
EGLContext PlatformEGL::getContextForType(ContextType type) const noexcept {
EGLContext PlatformEGL::getContextForType(OpenGLPlatform::ContextType type) const noexcept {
switch (type) {
case ContextType::NONE:
return EGL_NO_CONTEXT;
+2 -13
View File
@@ -163,24 +163,13 @@ public:
return mProtectedMemorySupported;
}
inline bool isImageView2DOn3DImageSupported() const noexcept {
return mPortabilitySubsetFeatures.imageView2DOn3DImage == VK_TRUE;
}
private:
VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2
};
VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
};
VkPhysicalDevicePortabilitySubsetFeaturesKHR mPortabilitySubsetFeatures = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR,
// By default, on platforms where we don't have portability subset, then this feature must
// exists. We only fill this struct only when portability subset is needed (i.e.
// non-conformant vulkan implementation).
.imageView2DOn3DImage = VK_TRUE,
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2
};
bool mDebugMarkersSupported = false;
bool mDebugUtilsSupported = false;
+1 -1
View File
@@ -579,7 +579,7 @@ void VulkanDriver::createTextureExternalImageR(Handle<HwTexture> th, backend::Sa
auto texture = resource_ptr<VulkanTexture>::make(&mResourceManager, th, mPlatform->getDevice(),
mAllocator, &mResourceManager, &mCommands, data.first, data.second, metadata.format,
1, metadata.width, metadata.height, /*depth=*/1, usage, mStagePool);
1, metadata.width, metadata.height, usage, mStagePool);
texture.inc();
}
+4 -12
View File
@@ -128,15 +128,6 @@ fvkmemory::resource_ptr<VulkanTexture> initMsaaTexture(
return msTexture;
}
VulkanAttachment createSwapchainAttachment(const fvkmemory::resource_ptr<VulkanTexture> texture) {
return VulkanAttachment {
.texture = texture,
.level = 0,
.layerCount = static_cast<uint8_t>(texture ? texture->getPrimaryViewRange().layerCount : 1),
.layer = 0,
};
}
} // anonymous namespace
void VulkanDescriptorSet::acquire(fvkmemory::resource_ptr<VulkanTexture> texture) {
@@ -283,21 +274,22 @@ void VulkanRenderTarget::bindToSwapChain(fvkmemory::resource_ptr<VulkanSwapChain
height = extent.height;
mProtected = swapchain->isProtected();
VulkanAttachment color = createSwapchainAttachment(swapchain->getCurrentColor());
VulkanAttachment color = {};
color.texture = swapchain->getCurrentColor();
mInfo->attachments = {color};
auto& fbkey = mInfo->fbkey;
auto& rpkey = mInfo->rpkey;
rpkey.colorFormat[0] = color.getFormat();
rpkey.viewCount = color.layerCount;
fbkey.width = width;
fbkey.height = height;
fbkey.color[0] = color.getImageView();
fbkey.resolve[0] = VK_NULL_HANDLE;
if (swapchain->getDepth()) {
VulkanAttachment depth = createSwapchainAttachment(swapchain->getDepth());
VulkanAttachment depth = {};
depth.texture = swapchain->getDepth();
mInfo->attachments.push_back(depth);
mInfo->depthIndex = 1;
@@ -218,6 +218,12 @@ struct VulkanProgram : public HwProgram, fvkmemory::Resource {
mInfo->pushConstantDescription.write(cmdbuf, layout, stage, index, value);
}
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
inline utils::FixedCapacityVector<std::string> const& getBindingToName() const {
return mInfo->bindingToName;
}
#endif
// TODO: handle compute shaders.
// The expected order of shaders - from frontend to backend - is vertex, fragment, compute.
static constexpr uint8_t const MAX_SHADER_MODULES = 2;
@@ -40,8 +40,6 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
mFlushAndWaitOnResize(platform->getCustomization().flushAndWaitOnWindowResize),
mTransitionSwapChainImageLayoutForPresent(
platform->getCustomization().transitionSwapChainImageLayoutForPresent),
mLayerCount(1),
mCurrentSwapIndex(0),
mAcquired(false),
mIsFirstRenderPass(true) {
swapChain = mPlatform->createSwapChain(nativeWindow, flags, extent);
@@ -78,18 +76,17 @@ void VulkanSwapChain::update() {
for (auto const color: bundle.colors) {
auto colorTexture = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager,
device, mAllocator, mResourceManager, mCommands, color, VK_NULL_HANDLE,
bundle.colorFormat, 1, bundle.extent.width, bundle.extent.height, bundle.layerCount, colorUsage,
bundle.colorFormat, 1, bundle.extent.width, bundle.extent.height, colorUsage,
mStagePool);
mColors.push_back(colorTexture);
}
mDepth = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager, device,
mAllocator, mResourceManager, mCommands, bundle.depth, VK_NULL_HANDLE,
bundle.depthFormat, 1, bundle.extent.width, bundle.extent.height, bundle.layerCount, depthUsage,
bundle.depthFormat, 1, bundle.extent.width, bundle.extent.height, depthUsage,
mStagePool);
mExtent = bundle.extent;
mLayerCount = bundle.layerCount;
}
void VulkanSwapChain::present() {
@@ -100,7 +97,7 @@ void VulkanSwapChain::present() {
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = mLayerCount,
.layerCount = 1,
};
mColors[mCurrentSwapIndex]->transitionLayout(&commands, subresources, VulkanLayout::PRESENT);
}
@@ -96,7 +96,6 @@ private:
utils::FixedCapacityVector<fvkmemory::resource_ptr<VulkanTexture>> mColors;
fvkmemory::resource_ptr<VulkanTexture> mDepth;
VkExtent2D mExtent;
uint32_t mLayerCount;
uint32_t mCurrentSwapIndex;
std::function<void(Platform::SwapChain* handle)> mExplicitImageReadyWait = nullptr;
bool mAcquired;
+12 -31
View File
@@ -144,16 +144,6 @@ inline VulkanLayout getDefaultLayoutImpl(VkImageUsageFlags vkusage) {
return getDefaultLayoutImpl(usage);
}
SamplerType getSamplerTypeFromDepth(uint32_t const depth) {
return depth > 1 ? SamplerType::SAMPLER_2D_ARRAY
: SamplerType::SAMPLER_2D;
}
uint8_t getLayerCountFromDepth(uint32_t const depth) {
return getLayerCount(getSamplerTypeFromDepth(depth), depth);
}
} // anonymous namespace
VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator,
@@ -175,12 +165,12 @@ VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator,
VulkanTexture::VulkanTexture(VkDevice device, VmaAllocator allocator,
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image,
VkDeviceMemory memory, VkFormat format, uint8_t samples, uint32_t width,
uint32_t height, uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool)
: HwTexture(getSamplerTypeFromDepth(depth), 1, samples, width, height, depth, TextureFormat::UNUSED,
uint32_t height, TextureUsage tusage, VulkanStagePool& stagePool)
: HwTexture(SamplerType::SAMPLER_2D, 1, samples, width, height, 1, TextureFormat::UNUSED,
tusage),
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, device,
allocator, commands, stagePool, format, fvkutils::getViewType(SamplerType::SAMPLER_2D),
/*mipLevels=*/1, getLayerCountFromDepth(depth), getDefaultLayoutImpl(tusage), any(usage & TextureUsage::PROTECTED))) {
1, 1, getDefaultLayoutImpl(tusage), any(usage & TextureUsage::PROTECTED))) {
mState->mTextureImage = image;
mState->mTextureImageMemory = memory;
mPrimaryViewRange = mState->mFullViewRange;
@@ -193,6 +183,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
uint8_t levels, TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h,
uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool)
: HwTexture(target, levels, samples, w, h, depth, tformat, tusage),
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, device,
allocator, commands, stagePool, fvkutils::getVkFormat(tformat),
fvkutils::getViewType(target), levels, getLayerCount(target, depth),
@@ -208,20 +199,11 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = 0,
};
if (target == SamplerType::SAMPLER_3D && any(tusage & TextureUsage::ALL_ATTACHMENTS)) {
if (context.isImageView2DOn3DImageSupported()) {
// Note that VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT is only meant to create 2D views of
// a 3D image in the case where the image is the render target. So, for example, it's
// not meant to allow for 2D views that can be used with a sampler.
imageInfo.flags = VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT;
} else {
FVK_LOGW << "Note: creating 2D views on 3D image is not available on this platform. "
<< "i.e. we cannot render to slices of a 3D image" << utils::io::endl;
}
} else if (target == SamplerType::SAMPLER_CUBEMAP) {
if (target == SamplerType::SAMPLER_CUBEMAP) {
imageInfo.arrayLayers = 6;
imageInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
} else if (target == SamplerType::SAMPLER_2D_ARRAY) {
}
if (target == SamplerType::SAMPLER_2D_ARRAY) {
imageInfo.arrayLayers = depth;
imageInfo.extent.depth = 1;
// NOTE: We do not use VK_IMAGE_CREATE_2D_ARRAY_COMPATIBLE_BIT here because:
@@ -265,9 +247,9 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
// Validate that the format is actually sampleable.
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(physicalDevice, mState->mVkFormat, &props);
vkGetPhysicalDeviceFormatProperties(physicalDevice, state->mVkFormat, &props);
if (!(props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT)) {
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << mState->mVkFormat << " is not "
FVK_LOGW << "Texture usage is SAMPLEABLE but format " << state->mVkFormat << " is not "
"sampleable with optimal tiling." << utils::io::endl;
}
#endif
@@ -649,16 +631,15 @@ bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceR
if (hasTransitions) {
#if FVK_ENABLED(FVK_DEBUG_LAYOUT_TRANSITION)
FVK_LOGD << "transition texture=" << mState->mTextureImage << " (" << range.baseArrayLayer
FVK_LOGD << "transition texture=" << state->mTextureImage << " (" << range.baseArrayLayer
<< "," << range.baseMipLevel << ")" << " count=(" << range.layerCount << ","
<< range.levelCount << ")" << " from=" << oldLayout << " to=" << newLayout
<< " format=" << mState->mVkFormat << " depth="
<< fvkutils::isVkDepthFormat(mState->mVkFormat)
<< " format=" << state->mVkFormat << " depth=" << isVkDepthFormat(state->mVkFormat)
<< " slice-by-slice=" << transitionSliceBySlice << utils::io::endl;
#endif
} else {
#if FVK_ENABLED(FVK_DEBUG_LAYOUT_TRANSITION)
FVK_LOGD << "transition texture=" << mState->mTextureImage << " (" << range.baseArrayLayer
FVK_LOGD << "transition texture=" << state->mTextureImage << " (" << range.baseArrayLayer
<< "," << range.baseMipLevel << ")" << " count=(" << range.layerCount << ","
<< range.levelCount << ")" << " to=" << newLayout
<< " is skipped because of no change in layout" << utils::io::endl;
+1 -1
View File
@@ -94,7 +94,7 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
// The texture will never destroy the given VkImage, but it does manages its subresources.
VulkanTexture(VkDevice device, VmaAllocator allocator,
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image,
VkDeviceMemory memory, VkFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth,
VkDeviceMemory memory, VkFormat format, uint8_t samples, uint32_t width, uint32_t height,
TextureUsage tusage, VulkanStagePool& stagePool);
// Constructor for creating a texture view for wrt specific mip range
@@ -18,7 +18,6 @@
#include <backend/DriverEnums.h>
#include "vulkan/VulkanContext.h"
#include "vulkan/platform/VulkanPlatformSwapChainImpl.h"
#include "vulkan/VulkanConstants.h"
#include "vulkan/VulkanDriver.h"
@@ -45,7 +44,7 @@ namespace filament::backend {
namespace {
constexpr uint32_t INVALID_VK_INDEX = 0xFFFFFFFF;
constexpr uint32_t const INVALID_VK_INDEX = 0xFFFFFFFF;
using ExtensionSet = VulkanPlatform::ExtensionSet;
@@ -56,25 +55,25 @@ inline bool setContains(ExtensionSet const& set, utils::CString const& extension
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
// These strings need to be allocated outside a function stack
const std::string_view DESIRED_LAYERS[] = {
"VK_LAYER_KHRONOS_validation",
"VK_LAYER_KHRONOS_validation",
#if FVK_ENABLED(FVK_DEBUG_DUMP_API)
"VK_LAYER_LUNARG_api_dump",
"VK_LAYER_LUNARG_api_dump",
#endif
#if defined(ENABLE_RENDERDOC)
"VK_LAYER_RENDERDOC_Capture",
"VK_LAYER_RENDERDOC_Capture",
#endif
};
FixedCapacityVector<const char*> getEnabledLayers() {
constexpr size_t kMaxEnabledLayersCount = sizeof(DESIRED_LAYERS) / sizeof(DESIRED_LAYERS[0]);
FixedCapacityVector<VkLayerProperties> const availableLayers
= fvkutils::enumerate(vkEnumerateInstanceLayerProperties);
const FixedCapacityVector<VkLayerProperties> availableLayers
= filament::backend::enumerate(vkEnumerateInstanceLayerProperties);
auto enabledLayers = FixedCapacityVector<const char*>::with_capacity(kMaxEnabledLayersCount);
for (auto const& desired: DESIRED_LAYERS) {
for (VkLayerProperties const& layer: availableLayers) {
std::string_view const availableLayer(layer.layerName);
for (const VkLayerProperties& layer: availableLayers) {
const std::string_view availableLayer(layer.layerName);
if (availableLayer == desired) {
enabledLayers.push_back(desired.data());
break;
@@ -85,13 +84,6 @@ FixedCapacityVector<const char*> getEnabledLayers() {
}
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION)
template<typename StructA, typename StructB>
StructA* chainStruct(StructA* structA, StructB* structB) {
structB->pNext = const_cast<void*>(structA->pNext);
structA->pNext = (void*) structB;
return structA;
}
void printDeviceInfo(VkInstance instance, VkPhysicalDevice device) {
// Print some driver or MoltenVK information if it is available.
if (vkGetPhysicalDeviceProperties2) {
@@ -100,8 +92,8 @@ void printDeviceInfo(VkInstance instance, VkPhysicalDevice device) {
};
VkPhysicalDeviceProperties2 physicalDeviceProperties2 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
.pNext = &driverProperties,
};
chainStruct(&physicalDeviceProperties2, &driverProperties);
vkGetPhysicalDeviceProperties2(device, &physicalDeviceProperties2);
FVK_LOGI << "Vulkan device driver: " << driverProperties.driverName << " "
<< driverProperties.driverInfo << utils::io::endl;
@@ -127,13 +119,13 @@ void printDeviceInfo(VkInstance instance, VkPhysicalDevice device) {
// Since we don't have any vendor-specific workarounds yet, there's no need to make this
// mapping in code. The "deviceName" string informally reveals the marketing name for the
// GPU. (e.g., Quadro)
uint32_t const driverVersion = deviceProperties.driverVersion;
uint32_t const vendorID = deviceProperties.vendorID;
uint32_t const deviceID = deviceProperties.deviceID;
int const major = VK_VERSION_MAJOR(deviceProperties.apiVersion);
int const minor = VK_VERSION_MINOR(deviceProperties.apiVersion);
const uint32_t driverVersion = deviceProperties.driverVersion;
const uint32_t vendorID = deviceProperties.vendorID;
const uint32_t deviceID = deviceProperties.deviceID;
const int major = VK_VERSION_MAJOR(deviceProperties.apiVersion);
const int minor = VK_VERSION_MINOR(deviceProperties.apiVersion);
FixedCapacityVector<VkPhysicalDevice> const physicalDevices
const FixedCapacityVector<VkPhysicalDevice> physicalDevices
= fvkutils::enumerate(vkEnumeratePhysicalDevices, instance);
FVK_LOGI << "Selected physical device '" << deviceProperties.deviceName << "' from "
@@ -148,10 +140,10 @@ void printDeviceInfo(VkInstance instance, VkPhysicalDevice device) {
void printDepthFormats(VkPhysicalDevice device) {
// For diagnostic purposes, print useful information about available depth formats.
// Note that Vulkan is more constrained than OpenGL ES 3.1 in this area.
constexpr VkFormatFeatureFlags required =
VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
const VkFormatFeatureFlags required = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT
| VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
FVK_LOGI << "Sampleable depth formats: ";
for (VkFormat format : fvkutils::ALL_VK_FORMATS) {
for (VkFormat format : ALL_VK_FORMATS) {
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(device, format, &props);
if ((props.optimalTilingFeatures & required) == required) {
@@ -168,13 +160,11 @@ ExtensionSet getInstanceExtensions(ExtensionSet const& externallyRequiredExts =
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
// Request these if available.
// Request these if available.
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
#endif
#if defined(__APPLE__)
VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
#endif
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
@@ -184,8 +174,8 @@ ExtensionSet getInstanceExtensions(ExtensionSet const& externallyRequiredExts =
FixedCapacityVector<VkExtensionProperties> const availableExts =
fvkutils::enumerate(vkEnumerateInstanceExtensionProperties,
static_cast<char const*>(nullptr) /* pLayerName */);
for (auto const& extension: availableExts) {
utils::CString name { extension.extensionName };
for (auto const& extProps: availableExts) {
utils::CString name { extProps.extensionName };
// To workaround an Adreno bug where the extension name could be of 0 length.
if (name.size() == 0) {
@@ -202,24 +192,22 @@ ExtensionSet getInstanceExtensions(ExtensionSet const& externallyRequiredExts =
ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
ExtensionSet const TARGET_EXTS = {
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
VK_EXT_DEBUG_MARKER_EXTENSION_NAME,
VK_EXT_DEBUG_MARKER_EXTENSION_NAME,
#endif
// We only support external image for Android for now, but nothing bars us from
// supporting other platforms.
// We only support external image for Android for now, but nothing bars us from
// supporting other platforms.
#if defined(__ANDROID__)
VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
#endif
// MoltenVk is the only non-conformant implementation we're interested in.
#if defined(__APPLE__)
VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,
#endif
VK_KHR_MAINTENANCE1_EXTENSION_NAME,
VK_KHR_MAINTENANCE2_EXTENSION_NAME,
VK_KHR_MAINTENANCE3_EXTENSION_NAME,
VK_KHR_MULTIVIEW_EXTENSION_NAME,
VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
#endif
VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,
VK_KHR_MAINTENANCE1_EXTENSION_NAME,
VK_KHR_MAINTENANCE2_EXTENSION_NAME,
VK_KHR_MAINTENANCE3_EXTENSION_NAME,
VK_KHR_MULTIVIEW_EXTENSION_NAME,
};
ExtensionSet exts;
// Identify supported physical device extensions
@@ -242,19 +230,8 @@ ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
}
VkInstance createInstance(ExtensionSet const& requiredExts) {
// Create the Vulkan instance.
VkApplicationInfo appInfo = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pEngineName = "Filament",
.apiVersion =
VK_MAKE_API_VERSION(0, FVK_REQUIRED_VERSION_MAJOR, FVK_REQUIRED_VERSION_MINOR, 0),
};
VkInstance instance;
VkInstanceCreateInfo instanceCreateInfo = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &appInfo,
};
VkInstanceCreateInfo instanceCreateInfo = {};
bool validationFeaturesSupported = false;
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
@@ -262,7 +239,7 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
if (!enabledLayers.empty()) {
// If layers are supported, Check if VK_EXT_validation_features is supported.
FixedCapacityVector<VkExtensionProperties> const availableValidationExts
= fvkutils::enumerate(vkEnumerateInstanceExtensionProperties,
= filament::backend::enumerate(vkEnumerateInstanceExtensionProperties,
"VK_LAYER_KHRONOS_validation");
for (auto const& extProps: availableValidationExts) {
if (!strcmp(extProps.extensionName, VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME)) {
@@ -284,10 +261,10 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
}
#endif // FVK_ENABLED(FVK_DEBUG_VALIDATION)
// The Platform class can require 1 or 2 instance extensions, plus we'll request at most 6
// instance extensions here in the common code. So that's a max of 8.
constexpr uint32_t MAX_INSTANCE_EXTENSION_COUNT = 8;
char const* ppEnabledExtensions[MAX_INSTANCE_EXTENSION_COUNT];
// The Platform class can require 1 or 2 instance extensions, plus we'll request at most 5
// instance extensions here in the common code. So that's a max of 7.
static constexpr uint32_t MAX_INSTANCE_EXTENSION_COUNT = 8;
const char* ppEnabledExtensions[MAX_INSTANCE_EXTENSION_COUNT];
uint32_t enabledExtensionCount = 0;
if (validationFeaturesSupported) {
@@ -299,24 +276,30 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
ppEnabledExtensions[enabledExtensionCount++] = requiredExt.data();
}
// Create the Vulkan instance.
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pEngineName = "Filament";
appInfo.apiVersion
= VK_MAKE_API_VERSION(0, FVK_REQUIRED_VERSION_MAJOR, FVK_REQUIRED_VERSION_MINOR, 0);
instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instanceCreateInfo.pApplicationInfo = &appInfo;
instanceCreateInfo.enabledExtensionCount = enabledExtensionCount;
instanceCreateInfo.ppEnabledExtensionNames = ppEnabledExtensions;
if (setContains(requiredExts, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) {
instanceCreateInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
}
// Validation features
VkValidationFeaturesEXT features = {};
VkValidationFeatureEnableEXT enables[] = {
VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT,
VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT,
};
VkValidationFeaturesEXT features = {
.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT,
.enabledValidationFeatureCount = sizeof(enables) / sizeof(enables[0]),
.pEnabledValidationFeatures = enables,
VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT,
VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT,
};
if (validationFeaturesSupported) {
chainStruct(&instanceCreateInfo, &features);
features.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;
features.enabledValidationFeatureCount = sizeof(enables) / sizeof(enables[0]);
features.pEnabledValidationFeatures = enables;
instanceCreateInfo.pNext = &features;
}
VkResult result = vkCreateInstance(&instanceCreateInfo, VKALLOC, &instance);
@@ -327,13 +310,11 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceFeatures2 const& features, uint32_t graphicsQueueFamilyIndex,
uint32_t protectedGraphicsQueueFamilyIndex, ExtensionSet const& deviceExtensions,
bool requestImageView2DOn3DImage) {
uint32_t protectedGraphicsQueueFamilyIndex, ExtensionSet const& deviceExtensions) {
VkDevice device;
float queuePriority[] = {1.0f};
VkDeviceCreateInfo deviceCreateInfo = {
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
};
VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = {};
const float queuePriority[] = {1.0f};
VkDeviceCreateInfo deviceCreateInfo = {};
FixedCapacityVector<const char*> requestExtensions;
requestExtensions.reserve(deviceExtensions.size() + 1);
@@ -342,17 +323,18 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
for (auto const& ext: deviceExtensions) {
requestExtensions.push_back(ext.data());
}
VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = {};
deviceQueueCreateInfo[0] = {
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = graphicsQueueFamilyIndex,
.queueCount = 1,
.pQueuePriorities = &queuePriority[0],
};
deviceQueueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
deviceQueueCreateInfo[0].queueFamilyIndex = graphicsQueueFamilyIndex;
deviceQueueCreateInfo[0].queueCount = 1;
deviceQueueCreateInfo[0].pQueuePriorities = &queuePriority[0];
// Protected queue
deviceQueueCreateInfo[1] = deviceQueueCreateInfo[0];
deviceQueueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
deviceQueueCreateInfo[1].flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT;
deviceQueueCreateInfo[1].queueFamilyIndex = protectedGraphicsQueueFamilyIndex;
deviceQueueCreateInfo[1].queueCount = 1;
deviceQueueCreateInfo[1].pQueuePriorities = &queuePriority[0];
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
bool const hasProtectedQueue = protectedGraphicsQueueFamilyIndex != INVALID_VK_INDEX;
deviceCreateInfo.queueCreateInfoCount = hasProtectedQueue ? 2 : 1;
deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
@@ -360,46 +342,53 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
// We could simply enable all supported features, but since that may have performance
// consequences let's just enable the features we need.
VkPhysicalDeviceFeatures enabledFeatures{
.depthClamp = features.features.depthClamp,
.samplerAnisotropy = features.features.samplerAnisotropy,
.textureCompressionETC2 = features.features.textureCompressionETC2,
.textureCompressionBC = features.features.textureCompressionBC,
.shaderClipDistance = features.features.shaderClipDistance,
.depthClamp = features.features.depthClamp,
.samplerAnisotropy = features.features.samplerAnisotropy,
.textureCompressionETC2 = features.features.textureCompressionETC2,
.textureCompressionBC = features.features.textureCompressionBC,
.shaderClipDistance = features.features.shaderClipDistance,
};
deviceCreateInfo.pEnabledFeatures = &enabledFeatures;
deviceCreateInfo.pEnabledFeatures = &enabledFeatures;
deviceCreateInfo.enabledExtensionCount = (uint32_t) requestExtensions.size();
deviceCreateInfo.ppEnabledExtensionNames = requestExtensions.data();
void* pNext = nullptr;
VkPhysicalDevicePortabilitySubsetFeaturesKHR portability = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR,
.imageViewFormatSwizzle = VK_TRUE,
.imageView2DOn3DImage = requestImageView2DOn3DImage ? VK_TRUE : VK_FALSE,
.mutableComparisonSamplers = VK_TRUE,
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PORTABILITY_SUBSET_FEATURES_KHR,
.pNext = nullptr,
.imageViewFormatSwizzle = VK_TRUE,
.mutableComparisonSamplers = VK_TRUE,
};
if (setContains(deviceExtensions, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) {
chainStruct(&deviceCreateInfo, &portability);
portability.pNext = pNext;
pNext = &portability;
}
VkPhysicalDeviceMultiviewFeaturesKHR multiview = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR,
.multiview = VK_TRUE,
.multiviewGeometryShader = VK_FALSE,
.multiviewTessellationShader = VK_FALSE,
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES_KHR,
.pNext = nullptr,
.multiview = VK_TRUE,
.multiviewGeometryShader = VK_FALSE,
.multiviewTessellationShader = VK_FALSE
};
if (setContains(deviceExtensions, VK_KHR_MULTIVIEW_EXTENSION_NAME)) {
chainStruct(&deviceCreateInfo, &multiview);
multiview.pNext = pNext;
pNext = &multiview;
}
VkPhysicalDeviceProtectedMemoryFeatures protectedMemory = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES,
.protectedMemory = VK_TRUE,
};
if (hasProtectedQueue) {
// Enable protected memory, if requested.
chainStruct(&deviceCreateInfo, &protectedMemory);
protectedMemory.protectedMemory = VK_TRUE;
protectedMemory.pNext = pNext;
pNext = &protectedMemory;
}
deviceCreateInfo.pNext = pNext;
VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, VKALLOC, &device);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "vkCreateDevice error=" << static_cast<int32_t>(result);
@@ -410,8 +399,7 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
// This method is used to enable/disable extensions based on external factors (i.e.
// driver/device workarounds).
std::tuple<ExtensionSet, ExtensionSet> pruneExtensions(VkPhysicalDevice device,
Platform::DriverConfig const& driverConfig, ExtensionSet const& instExts,
ExtensionSet const& deviceExts) noexcept {
ExtensionSet const& instExts, ExtensionSet const& deviceExts) {
ExtensionSet newInstExts = instExts;
ExtensionSet newDeviceExts = deviceExts;
@@ -431,10 +419,6 @@ std::tuple<ExtensionSet, ExtensionSet> pruneExtensions(VkPhysicalDevice device,
}
#endif
if (driverConfig.stereoscopicType != StereoscopicType::MULTIVIEW) {
newDeviceExts.erase(VK_KHR_MULTIVIEW_EXTENSION_NAME);
}
return std::tuple(newInstExts, newDeviceExts);
}
@@ -468,17 +452,22 @@ uint32_t identifyGraphicsQueueFamilyIndex(VkPhysicalDevice physicalDevice, VkQue
// Enum based on:
// https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceType.html
inline int deviceTypeOrder(VkPhysicalDeviceType deviceType) {
constexpr std::array<VkPhysicalDeviceType, 5> TYPES = {
VK_PHYSICAL_DEVICE_TYPE_OTHER,
VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU,
VK_PHYSICAL_DEVICE_TYPE_CPU,
VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU,
VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU,
};
if (auto itr = std::find(TYPES.begin(), TYPES.end(), deviceType); itr != TYPES.end()) {
return std::distance(TYPES.begin(), itr);
switch (deviceType) {
case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU:
return 5;
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
return 4;
case VK_PHYSICAL_DEVICE_TYPE_CPU:
return 3;
case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU:
return 2;
case VK_PHYSICAL_DEVICE_TYPE_OTHER:
return 1;
default:
FVK_LOGW << "deviceTypeOrder: Unexpected deviceType: " << deviceType
<< utils::io::endl;
return -1;
}
return -1;
}
VkPhysicalDevice selectPhysicalDevice(VkInstance instance,
@@ -512,7 +501,7 @@ VkPhysicalDevice selectPhysicalDevice(VkInstance instance,
// Does the device have any command queues that support graphics?
// In theory, we should also ensure that the device supports presentation of our
// particular VkSurface, but we don't have a VkSurface yet, so we'll skip this requirement.
if (identifyGraphicsQueueFamilyIndex(candidateDevice, VK_QUEUE_GRAPHICS_BIT)
if (identifyGraphicsQueueFamilyIndex(candidateDevice, VK_QUEUE_GRAPHICS_BIT)
== INVALID_VK_INDEX) {
continue;
}
@@ -521,19 +510,20 @@ VkPhysicalDevice selectPhysicalDevice(VkInstance instance,
FixedCapacityVector<VkExtensionProperties> const extensions
= fvkutils::enumerate(vkEnumerateDeviceExtensionProperties,
candidateDevice, static_cast<char const*>(nullptr) /* pLayerName */);
bool const supportsSwapchain =
std::any_of(extensions.begin(), extensions.end(), [](auto const& ext) {
return !strcmp(ext.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME);
});
bool supportsSwapchain = false;
for (auto const& extension: extensions) {
if (!strcmp(extension.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
supportsSwapchain = true;
break;
}
}
if (!supportsSwapchain) {
continue;
}
deviceList[deviceInd] = {
.device = candidateDevice,
.deviceType = targetDeviceProperties.deviceType,
.index = (int8_t) deviceInd,
.name = targetDeviceProperties.deviceName,
};
deviceList[deviceInd].device = candidateDevice;
deviceList[deviceInd].deviceType = targetDeviceProperties.deviceType;
deviceList[deviceInd].index = deviceInd;
deviceList[deviceInd].name = targetDeviceProperties.deviceName;
}
FILAMENT_CHECK_PRECONDITION(gpuPreference.index < static_cast<int32_t>(deviceList.size()))
@@ -574,7 +564,7 @@ fvkutils::VkFormatList findAttachmentDepthStencilFormats(VkPhysicalDevice device
VkFormatFeatureFlags const features = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
// The ordering here indicates the preference of choosing depth+stencil format.
constexpr VkFormat formats[] = {
VkFormat const formats[] = {
VK_FORMAT_D32_SFLOAT,
VK_FORMAT_X8_D24_UNORM_PACK32,
@@ -596,7 +586,7 @@ fvkutils::VkFormatList findAttachmentDepthStencilFormats(VkPhysicalDevice device
fvkutils::VkFormatList findBlittableDepthStencilFormats(VkPhysicalDevice device) {
std::vector<VkFormat> selectedFormats;
constexpr VkFormatFeatureFlags required = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT |
VkFormatFeatureFlags const required = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT |
VK_FORMAT_FEATURE_BLIT_SRC_BIT | VK_FORMAT_FEATURE_BLIT_DST_BIT;
for (VkFormat format : fvkutils::ALL_VK_FORMATS) {
if (fvkutils::isVkDepthFormat(format)) {
@@ -656,7 +646,7 @@ void VulkanPlatform::terminate() {
// This is the main entry point for context creation.
Driver* VulkanPlatform::createDriver(void* sharedContext,
Platform::DriverConfig const& driverConfig) noexcept {
const Platform::DriverConfig& driverConfig) noexcept {
// Load Vulkan entry points.
FILAMENT_CHECK_POSTCONDITION(bluevk::initialize()) << "BlueVK is unable to load entry points.";
@@ -684,6 +674,7 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
}
VulkanContext context;
ExtensionSet instExts;
// If using a shared context, we do not assume any extensions.
if (!mImpl->mSharedContext) {
@@ -708,9 +699,9 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
instExts.merge(getRequiredInstanceExtensions());
}
if (mImpl->mInstance == VK_NULL_HANDLE) {
mImpl->mInstance = createInstance(instExts);
}
mImpl->mInstance
= mImpl->mInstance == VK_NULL_HANDLE ? createInstance(instExts) : mImpl->mInstance;
assert_invariant(mImpl->mInstance != VK_NULL_HANDLE);
bluevk::bindInstance(mImpl->mInstance);
@@ -720,9 +711,9 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
FILAMENT_CHECK_PRECONDITION(!(hasGPUPreference && sharedContext))
<< "Cannot both share context and indicate GPU preference";
if (mImpl->mPhysicalDevice == VK_NULL_HANDLE) {
mImpl->mPhysicalDevice = selectPhysicalDevice(mImpl->mInstance, pref);
}
mImpl->mPhysicalDevice = mImpl->mPhysicalDevice == VK_NULL_HANDLE
? selectPhysicalDevice(mImpl->mInstance, pref)
: mImpl->mPhysicalDevice;
assert_invariant(mImpl->mPhysicalDevice != VK_NULL_HANDLE);
printDeviceInfo(mImpl->mInstance, mImpl->mPhysicalDevice);
@@ -730,15 +721,14 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
VkPhysicalDeviceProtectedMemoryFeatures queryProtectedMemoryFeatures = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES,
};
// Chain to the pNext linked list
queryProtectedMemoryFeatures.pNext = context.mPhysicalDeviceFeatures.pNext;
context.mPhysicalDeviceFeatures.pNext = &queryProtectedMemoryFeatures;
VkPhysicalDeviceProtectedMemoryProperties protectedMemoryProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES,
};
chainStruct(&context.mPhysicalDeviceFeatures, &queryProtectedMemoryFeatures);
chainStruct(&context.mPhysicalDeviceProperties, &protectedMemoryProperties);
// We know we need to allocate the protected version of the VK objects
context.mProtectedMemorySupported =
static_cast<bool>(queryProtectedMemoryFeatures.protectedMemory);
protectedMemoryProperties.pNext = context.mPhysicalDeviceProperties.pNext;
context.mPhysicalDeviceProperties.pNext = &protectedMemoryProperties;
// Initialize the following fields: physicalDeviceProperties, memoryProperties,
// physicalDeviceFeatures, graphicsQueueFamilyIndex.
@@ -746,103 +736,91 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
vkGetPhysicalDeviceFeatures2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceFeatures);
vkGetPhysicalDeviceMemoryProperties(mImpl->mPhysicalDevice, &context.mMemoryProperties);
if (mImpl->mGraphicsQueueFamilyIndex == INVALID_VK_INDEX) {
mImpl->mGraphicsQueueFamilyIndex =
identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice, VK_QUEUE_GRAPHICS_BIT);
mImpl->mGraphicsQueueFamilyIndex
= mImpl->mGraphicsQueueFamilyIndex == INVALID_VK_INDEX
? identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice,
VK_QUEUE_GRAPHICS_BIT)
: mImpl->mGraphicsQueueFamilyIndex;
assert_invariant(mImpl->mGraphicsQueueFamilyIndex != INVALID_VK_INDEX);
// We know we need to allocate the protected version of the VK objects
context.mProtectedMemorySupported =
static_cast<bool>(queryProtectedMemoryFeatures.protectedMemory);
if (context.mProtectedMemorySupported) {
mImpl->mProtectedGraphicsQueueFamilyIndex
= identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice,
(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_PROTECTED_BIT));
assert_invariant(mImpl->mProtectedGraphicsQueueFamilyIndex != INVALID_VK_INDEX);
}
// Only enable shaderClipDistance if we are doing instanced stereoscopic rendering.
if (context.mPhysicalDeviceFeatures.features.shaderClipDistance == VK_TRUE
&& driverConfig.stereoscopicType != StereoscopicType::INSTANCED) {
context.mPhysicalDeviceFeatures.features.shaderClipDistance = VK_FALSE;
}
// At this point, we should have a family index that points to a family that has > 0 queues for
// graphics. In which case, we will allocate one queue for all of Filament (and assumes at least
// one has been allocated by the client if context was shared). If the index of the target queue
// within the family hasn't been provided by the client, we assume it to be 0.
if (mImpl->mGraphicsQueueIndex == INVALID_VK_INDEX) {
mImpl->mGraphicsQueueIndex = 0;
}
mImpl->mGraphicsQueueIndex
= mImpl->mGraphicsQueueIndex == INVALID_VK_INDEX ? 0 : mImpl->mGraphicsQueueIndex;
if (context.mProtectedMemorySupported) {
mImpl->mProtectedGraphicsQueueFamilyIndex = identifyGraphicsQueueFamilyIndex(
mImpl->mPhysicalDevice, (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_PROTECTED_BIT));
// Applying the same logic to the protected queue index (Not sure about shared context and
// protection)
if (mImpl->mProtectedGraphicsQueueIndex == INVALID_VK_INDEX) {
mImpl->mProtectedGraphicsQueueIndex = 0;
}
}
// Only enable shaderClipDistance if we are doing instanced stereoscopic rendering.
if (driverConfig.stereoscopicType != StereoscopicType::INSTANCED) {
context.mPhysicalDeviceFeatures.features.shaderClipDistance = VK_FALSE;
}
// Applying the same logic to the protected queue index (Not sure about shared context and protection)
mImpl->mProtectedGraphicsQueueIndex
= mImpl->mProtectedGraphicsQueueIndex == INVALID_VK_INDEX ? 0 :
mImpl->mProtectedGraphicsQueueIndex;
ExtensionSet deviceExts;
// If using a shared context, we do not assume any extensions.
if (!mImpl->mSharedContext) {
deviceExts = getDeviceExtensions(mImpl->mPhysicalDevice);
auto [prunedInstExts, prunedDeviceExts] =
pruneExtensions(mImpl->mPhysicalDevice, driverConfig, instExts, deviceExts);
auto [prunedInstExts, prunedDeviceExts]
= pruneExtensions(mImpl->mPhysicalDevice, instExts, deviceExts);
instExts = prunedInstExts;
deviceExts = prunedDeviceExts;
if (driverConfig.stereoscopicType != StereoscopicType::MULTIVIEW) {
deviceExts.erase(VK_KHR_MULTIVIEW_EXTENSION_NAME);
}
}
bool requestPortabilitySubsetImageView2DOn3DImage = false;
if (setContains(deviceExts, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME)) {
// We are on a non-conformant vulkan implementation so we need to ascertain if the features
// we need are available.
chainStruct(&context.mPhysicalDeviceFeatures, &context.mPortabilitySubsetFeatures);
vkGetPhysicalDeviceFeatures2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceFeatures);
requestPortabilitySubsetImageView2DOn3DImage =
context.mPortabilitySubsetFeatures.imageView2DOn3DImage == VK_TRUE;
}
if (mImpl->mDevice == VK_NULL_HANDLE) {
mImpl->mDevice =
createLogicalDevice(mImpl->mPhysicalDevice, context.mPhysicalDeviceFeatures,
mImpl->mGraphicsQueueFamilyIndex, mImpl->mProtectedGraphicsQueueFamilyIndex,
deviceExts, requestPortabilitySubsetImageView2DOn3DImage);
}
mImpl->mDevice
= mImpl->mDevice == VK_NULL_HANDLE ? createLogicalDevice(mImpl->mPhysicalDevice,
context.mPhysicalDeviceFeatures, mImpl->mGraphicsQueueFamilyIndex,
mImpl->mProtectedGraphicsQueueFamilyIndex, deviceExts)
: mImpl->mDevice;
assert_invariant(mImpl->mDevice != VK_NULL_HANDLE);
assert_invariant(mImpl->mGraphicsQueueFamilyIndex != INVALID_VK_INDEX);
assert_invariant(mImpl->mGraphicsQueueIndex != INVALID_VK_INDEX);
vkGetDeviceQueue(mImpl->mDevice, mImpl->mGraphicsQueueFamilyIndex, mImpl->mGraphicsQueueIndex,
&mImpl->mGraphicsQueue);
assert_invariant(mImpl->mGraphicsQueue != VK_NULL_HANDLE);
if (context.mProtectedMemorySupported) {
assert_invariant(mImpl->mProtectedGraphicsQueueFamilyIndex != INVALID_VK_INDEX);
assert_invariant(mImpl->mProtectedGraphicsQueueIndex != INVALID_VK_INDEX);
VkDeviceQueueInfo2 info = {
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2,
.flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT,
.queueFamilyIndex = mImpl->mProtectedGraphicsQueueFamilyIndex,
.queueIndex = mImpl->mProtectedGraphicsQueueIndex,
};
VkDeviceQueueInfo2 info = {};
info.queueFamilyIndex = mImpl->mProtectedGraphicsQueueFamilyIndex;
info.queueIndex = mImpl->mProtectedGraphicsQueueIndex;
info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2;
info.flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT;
vkGetDeviceQueue2(mImpl->mDevice, &info, &mImpl->mProtectedGraphicsQueue);
assert_invariant(mImpl->mProtectedGraphicsQueue != VK_NULL_HANDLE);
}
// Store the extension support in the context
if (!mImpl->mSharedContext) {
context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
context.mMultiviewEnabled = setContains(deviceExts, VK_KHR_MULTIVIEW_EXTENSION_NAME);
} else {
VulkanSharedContext const* scontext = (VulkanSharedContext const*) sharedContext;
context.mDebugUtilsSupported = scontext->debugUtilsSupported;
context.mDebugMarkersSupported = scontext->debugMarkersSupported;
context.mMultiviewEnabled = scontext->multiviewSupported;
}
context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
context.mMultiviewEnabled = setContains(deviceExts, VK_KHR_MULTIVIEW_EXTENSION_NAME);
// Check the availability of lazily allocated memory
context.mLazilyAllocatedMemorySupported = false;
for (uint32_t i = 0, typeCount = context.mMemoryProperties.memoryTypeCount; i < typeCount;
++i) {
VkMemoryType const type = context.mMemoryProperties.memoryTypes[i];
if (type.propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
context.mLazilyAllocatedMemorySupported = true;
assert_invariant(type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
break;
{
context.mLazilyAllocatedMemorySupported = false;
const uint32_t typeCount = context.mMemoryProperties.memoryTypeCount;
for(uint32_t i = 0; i < typeCount; ++i) {
const VkMemoryType type = context.mMemoryProperties.memoryTypes[i];
if (type.propertyFlags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) {
context.mLazilyAllocatedMemorySupported = true;
assert_invariant(type.propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
break;
}
}
}
+1 -1
View File
@@ -214,7 +214,7 @@ bool operator<(const VkImageSubresourceRange& a, const VkImageSubresourceRange&
case filament::backend::VulkanLayout::VALUE: { \
out << #VALUE; \
out << " [" \
<< filament::backend::fvkutils::getVkLayout( \
<< filament::backend::imgutil::getVkLayout( \
filament::backend::VulkanLayout::VALUE) \
<< "]"; \
break; \
-21
View File
@@ -17,7 +17,6 @@
#ifndef TNT_FILAMENT_ENGINE_H
#define TNT_FILAMENT_ENGINE_H
#include <filament/FilamentAPI.h>
#include <backend/DriverEnums.h>
@@ -33,7 +32,6 @@
#include <stdint.h>
#include <stddef.h>
namespace utils {
class Entity;
class EntityManager;
@@ -42,10 +40,6 @@ class JobSystem;
namespace filament {
namespace backend {
class Driver;
} // backend
class BufferObject;
class Camera;
class ColorGrading;
@@ -189,7 +183,6 @@ public:
using DriverConfig = backend::Platform::DriverConfig;
using FeatureLevel = backend::FeatureLevel;
using StereoscopicType = backend::StereoscopicType;
using Driver = backend::Driver;
/**
* Config is used to define the memory footprint used by the engine, such as the
@@ -317,15 +310,6 @@ public:
*/
size_t metalUploadBufferSizeBytes = 512 * 1024;
/**
* The action to take if a Drawable cannot be acquired.
*
* Each frame rendered requires a CAMetalDrawable texture, which is
* presented on-screen at the completion of each frame. These are
* limited and provided round-robin style by the system.
*/
bool metalDisablePanicOnDrawableFailure = false;
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL and Metal backends.
@@ -598,11 +582,6 @@ public:
static Engine* UTILS_NULLABLE getEngine(void* UTILS_NONNULL token);
#endif
/**
* @return the Driver instance used by this Engine.
* @see OpenGLPlatform
*/
backend::Driver const* UTILS_NONNULL getDriver() const noexcept;
/**
* Destroy the Engine instance and all associated resources.
+9 -20
View File
@@ -121,7 +121,7 @@ public:
/** inline helper to provide the name as a null-terminated string literal */
template<typename T, typename = is_supported_parameter_t<T>>
void setParameter(StringLiteral const name, T const& value) {
void setParameter(StringLiteral name, T const& value) {
setParameter<T>(name.data, name.size, value);
}
@@ -148,14 +148,14 @@ public:
/** inline helper to provide the name as a null-terminated string literal */
template<typename T, typename = is_supported_parameter_t<T>>
void setParameter(StringLiteral const name, const T* UTILS_NONNULL values, size_t const count) {
void setParameter(StringLiteral name, const T* UTILS_NONNULL values, size_t count) {
setParameter<T>(name.data, name.size, values, count);
}
/** inline helper to provide the name as a null-terminated C string */
template<typename T, typename = is_supported_parameter_t<T>>
void setParameter(const char* UTILS_NONNULL name,
const T* UTILS_NONNULL values, size_t const count) {
const T* UTILS_NONNULL values, size_t count) {
setParameter<T>(name, strlen(name), values, count);
}
@@ -176,7 +176,7 @@ public:
Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler);
/** inline helper to provide the name as a null-terminated string literal */
void setParameter(StringLiteral const name,
void setParameter(StringLiteral name,
Texture const* UTILS_NULLABLE texture, TextureSampler const& sampler) {
setParameter(name.data, name.size, texture, sampler);
}
@@ -202,12 +202,12 @@ public:
RgbType type, math::float3 color);
/** inline helper to provide the name as a null-terminated string literal */
void setParameter(StringLiteral const name, RgbType const type, math::float3 const color) {
void setParameter(StringLiteral name, RgbType type, math::float3 color) {
setParameter(name.data, name.size, type, color);
}
/** inline helper to provide the name as a null-terminated C string */
void setParameter(const char* UTILS_NONNULL name, RgbType const type, math::float3 const color) {
void setParameter(const char* UTILS_NONNULL name, RgbType type, math::float3 color) {
setParameter(name, strlen(name), type, color);
}
@@ -226,12 +226,12 @@ public:
RgbaType type, math::float4 color);
/** inline helper to provide the name as a null-terminated string literal */
void setParameter(StringLiteral const name, RgbaType const type, math::float4 const color) {
void setParameter(StringLiteral name, RgbaType type, math::float4 color) {
setParameter(name.data, name.size, type, color);
}
/** inline helper to provide the name as a null-terminated C string */
void setParameter(const char* UTILS_NONNULL name, RgbaType const type, math::float4 const color) {
void setParameter(const char* UTILS_NONNULL name, RgbaType type, math::float4 color) {
setParameter(name, strlen(name), type, color);
}
@@ -251,7 +251,7 @@ public:
/** inline helper to provide the name as a null-terminated C string */
template<typename T, typename = is_supported_parameter_t<T>>
T getParameter(StringLiteral const name) const {
T getParameter(StringLiteral name) const {
return getParameter<T>(name.data, name.size);
}
@@ -376,22 +376,11 @@ public:
*/
void setCullingMode(CullingMode culling) noexcept;
/**
* Overrides the default triangle culling state that was set on the material separately for the
* color and shadow passes
*/
void setCullingMode(CullingMode colorPassCullingMode, CullingMode shadowPassCullingMode) noexcept;
/**
* Returns the face culling mode.
*/
CullingMode getCullingMode() const noexcept;
/**
* Returns the face culling mode for the shadow passes.
*/
CullingMode getShadowCullingMode() const noexcept;
/**
* Overrides the default color-buffer write state that was set on the material.
*/
-5
View File
@@ -141,11 +141,6 @@ public:
*/
void removeEntities(const utils::Entity* UTILS_NONNULL entities, size_t count);
/**
* Remove all entities to the Scene.
*/
void removeAllEntities() noexcept;
/**
* Returns the total number of Entities in the Scene, whether alive or not.
* @return Total number of Entities in the Scene.
-5
View File
@@ -91,9 +91,6 @@ public:
/** @return Whether a backend supports a particular format. */
static bool isTextureFormatSupported(Engine& engine, InternalFormat format) noexcept;
/** @return Whether a backend supports mipmapping of a particular format. */
static bool isTextureFormatMipmappable(Engine& engine, InternalFormat format) noexcept;
/** @return Whether this backend supports protected textures. */
static bool isProtectedTexturesSupported(Engine& engine) noexcept;
@@ -103,8 +100,6 @@ public:
static size_t computeTextureDataSize(Format format, Type type,
size_t stride, size_t height, size_t alignment) noexcept;
/** @return Whether a combination of texture format, pixel format and type is valid. */
static bool validatePixelFormatAndType(InternalFormat internalFormat, Format format, Type type) noexcept;
/**
* Options for environment prefiltering into reflection map
-4
View File
@@ -68,10 +68,6 @@ Engine* Engine::getEngine(void* token) {
}
#endif
Driver const* Engine::getDriver() const noexcept {
return std::addressof(downcast(this)->getDriver());
}
void Engine::destroy(Engine** pEngine) {
if (pEngine) {
Engine* engine = *pEngine;
+5 -11
View File
@@ -116,12 +116,6 @@ static bool fuzzyEqual(mat4f const& UTILS_RESTRICT l, mat4f const& UTILS_RESTRIC
return result == 0;
}
size_t Froxelizer::getFroxelBufferByteCount(FEngine::DriverApi& driverApi) noexcept {
// Make sure that targetSize is 16-byte aligned so that it'll fit properly into an array of
// uvec4.
size_t const targetSize = (driverApi.getMaxUniformBufferSize() / 16) * 16;
return std::min(FROXEL_BUFFER_MAX_ENTRY_COUNT * sizeof(FroxelEntry), targetSize);
}
Froxelizer::Froxelizer(FEngine& engine)
: mArena("froxel", PER_FROXELDATA_ARENA_SIZE),
@@ -137,14 +131,14 @@ Froxelizer::Froxelizer(FEngine& engine)
return;
}
size_t const froxelBufferByteCount = getFroxelBufferByteCount(engine.getDriverApi());
mFroxelBufferEntryCount = froxelBufferByteCount / sizeof(FroxelEntry);
mFroxelBufferEntryCount = std::min(
FROXEL_BUFFER_MAX_ENTRY_COUNT,
engine.getDriverApi().getMaxUniformBufferSize() / 16u);
mRecordsBuffer = driverApi.createBufferObject(RECORD_BUFFER_ENTRY_COUNT,
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
mFroxelsBuffer = driverApi.createBufferObject(
froxelBufferByteCount,
mFroxelsBuffer = driverApi.createBufferObject(getFroxelBufferEntryCount() * 16u,
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
}
@@ -533,7 +527,7 @@ std::pair<size_t, size_t> Froxelizer::clipToIndices(float2 const& clip) const no
void Froxelizer::commit(DriverApi& driverApi) {
// send data to GPU
driverApi.updateBufferObject(mFroxelsBuffer,
{ mFroxelBufferUser.data(), getFroxelBufferEntryCount() * sizeof(FroxelEntry) }, 0);
{ mFroxelBufferUser.data(), getFroxelBufferEntryCount() * 16u }, 0);
driverApi.updateBufferObject(mRecordsBuffer,
{ mRecordBufferUser.data(), RECORD_BUFFER_ENTRY_COUNT }, 0);
+7 -11
View File
@@ -35,16 +35,15 @@
namespace filament {
// The number of froxel buffer entries is determined by max UBO size (see
// getFroxelBufferByteCount()). We also introduce the limit below because increasing the number of
// froxels adds more pressure on the "record buffer" which stores the light indices per froxel. The
// record buffer is limited to min(16K[ubo], 64K[uint16]) entries. In practice some froxels are not
// used, so we can store more.
// Max number of froxels limited by:
// - max ubo size [min 16KiB]
//
// Also, increasing the number of froxels adds more pressure on the "record buffer" which stores
// the light indices per froxel. The record buffer is limited to min(16K[ubo], 64K[uint16]) entries,
// so with 8192 froxels, we can store 2 lights per froxels assuming they're all used. In practice,
// some froxels are not used, so we can store more.
constexpr size_t FROXEL_BUFFER_MAX_ENTRY_COUNT = 8192;
// Froxel buffer UBO is an array of uvec4. Make sure that the buffer is properly aligned.
static_assert(FROXEL_BUFFER_MAX_ENTRY_COUNT % 4 == 0u);
class FEngine;
class FCamera;
class FTexture;
@@ -147,7 +146,6 @@ public:
inline uint16_t offset() const noexcept { return u32 >> 16u; }
uint32_t u32 = 0;
};
static_assert(sizeof(FroxelEntry) == 4u);
// we can't change this easily because the shader expects 16 indices per uint4
using RecordBufferType = uint8_t;
@@ -159,8 +157,6 @@ public:
// with 256 lights this implies 8 jobs (256 / 32) for froxelization.
using LightGroupType = uint32_t;
static size_t getFroxelBufferByteCount(FEngine::DriverApi& driverApi) noexcept;
private:
size_t getFroxelBufferEntryCount() const noexcept {
return mFroxelBufferEntryCount;
+3 -32
View File
@@ -17,26 +17,6 @@
#include <filament/MaterialInstance.h>
#include "details/Material.h"
#include "details/MaterialInstance.h"
#include <filament/Color.h>
#include <filament/MaterialEnums.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/mat3.h>
#include <algorithm>
#include <string_view>
#include <stddef.h>
#include <stdint.h>
namespace filament {
@@ -105,7 +85,7 @@ void MaterialInstance::setParameter(const char* name, size_t nameLength, T const
template<>
UTILS_PUBLIC void MaterialInstance::setParameter(const char* name, size_t const nameLength, bool const& v) {
// this kills tail-call optimization
setParameter(name, nameLength, uint32_t(v));
setParameter(name, nameLength, (uint32_t)v);
}
template<>
@@ -145,8 +125,8 @@ template UTILS_PUBLIC void MaterialInstance::setParameter<mat4f> (const char*
// ------------------------------------------------------------------------------------------------
template <typename T, typename>
void MaterialInstance::setParameter(const char* name, size_t nameLength, const T* values, size_t count) {
downcast(this)->setParameterImpl({ name, nameLength }, values, count);
void MaterialInstance::setParameter(const char* name, size_t nameLength, const T* value, size_t count) {
downcast(this)->setParameterImpl({ name, nameLength }, value, count);
}
template<>
@@ -292,11 +272,6 @@ void MaterialInstance::setCullingMode(CullingMode const culling) noexcept {
downcast(this)->setCullingMode(culling);
}
void MaterialInstance::setCullingMode(CullingMode const colorPassCullingMode,
CullingMode const shadowPassCullingMode) noexcept {
downcast(this)->setCullingMode(colorPassCullingMode, shadowPassCullingMode);
}
void MaterialInstance::setColorWrite(bool const enable) noexcept {
downcast(this)->setColorWrite(enable);
}
@@ -378,10 +353,6 @@ CullingMode MaterialInstance::getCullingMode() const noexcept {
return downcast(this)->getCullingMode();
}
CullingMode MaterialInstance::getShadowCullingMode() const noexcept {
return downcast(this)->getShadowCullingMode();
}
bool MaterialInstance::isColorWriteEnabled() const noexcept {
return downcast(this)->isColorWriteEnabled();
}
+4 -11
View File
@@ -807,13 +807,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(CommandTypeFlags extraFlag
cmd.key &= ~Z_BUCKET_MASK;
cmd.key |= makeField(distanceBits >> 22u, Z_BUCKET_MASK, Z_BUCKET_SHIFT);
}
*curr = cmd;
// cancel command if both front and back faces are culled
curr->key |= select(mi->getCullingMode() == CullingMode::FRONT_AND_BACK);
} else if constexpr (isDepthPass) {
const CullingMode cullingMode = hasShadowing ? mi->getShadowCullingMode() : mi->getCullingMode();
const RasterState rs = ma->getRasterState();
const TransparencyMode mode = mi->getTransparencyMode();
const BlendingMode blendingMode = ma->getBlendingMode();
@@ -822,7 +816,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(CommandTypeFlags extraFlag
const bool isPickingVariant = Variant::isPickingVariant(variant);
cmd.key |= mi->getSortingKey(); // already all set-up for direct or'ing
cmd.info.rasterState.culling = cullingMode;
cmd.info.rasterState.culling = mi->getCullingMode();
// FIXME: should writeDepthForShadowCasters take precedence over mi->getDepthWrite()?
cmd.info.rasterState.depthWrite = (1 // only keep bit 0
@@ -831,12 +825,11 @@ RenderPass::Command* RenderPass::generateCommandsImpl(CommandTypeFlags extraFlag
& !(filterTranslucentObjects & translucent)
& !(depthFilterAlphaMaskedObjects & rs.alphaToCoverage))
| writeDepthForShadowCasters;
*curr = cmd;
// cancel command if both front and back faces are culled
curr->key |= select(cullingMode == CullingMode::FRONT_AND_BACK);
}
*curr = cmd;
// cancel command if both front and back faces are culled
curr->key |= select(mi->getCullingMode() == CullingMode::FRONT_AND_BACK);
++curr;
}
}
-4
View File
@@ -55,10 +55,6 @@ void Scene::removeEntities(const Entity* entities, size_t const count) {
downcast(this)->removeEntities(entities, count);
}
void Scene::removeAllEntities() noexcept {
downcast(this)->removeAllEntities();
}
size_t Scene::getEntityCount() const noexcept {
return downcast(this)->getEntityCount();
}
-8
View File
@@ -82,10 +82,6 @@ bool Texture::isTextureFormatSupported(Engine& engine, InternalFormat const form
return FTexture::isTextureFormatSupported(downcast(engine), format);
}
bool Texture::isTextureFormatMipmappable(Engine& engine, InternalFormat const format) noexcept {
return FTexture::isTextureFormatMipmappable(downcast(engine), format);
}
bool Texture::isProtectedTexturesSupported(Engine& engine) noexcept {
return FTexture::isProtectedTexturesSupported(downcast(engine));
}
@@ -104,8 +100,4 @@ void Texture::generatePrefilterMipmap(Engine& engine, PixelBufferDescriptor&& bu
downcast(this)->generatePrefilterMipmap(downcast(engine), std::move(buffer), faceOffsets, options);
}
bool Texture::validatePixelFormatAndType(InternalFormat internalFormat, Format format, Type type) noexcept {
return FTexture::validatePixelFormatAndType(internalFormat, format, type);
}
} // namespace filament
+57 -72
View File
@@ -25,9 +25,7 @@
#include "details/Fence.h"
#include "details/IndexBuffer.h"
#include "details/IndirectLight.h"
#include "details/InstanceBuffer.h"
#include "details/Material.h"
#include "details/MorphTargetBuffer.h"
#include "details/Renderer.h"
#include "details/Scene.h"
#include "details/SkinningBuffer.h"
@@ -38,39 +36,28 @@
#include "details/VertexBuffer.h"
#include "details/View.h"
#include <filament/ColorGrading.h>
#include <filament/Engine.h>
#include <filament/MaterialEnums.h>
#include <private/filament/DescriptorSets.h>
#include <private/filament/EngineEnums.h>
#include <private/filament/Variant.h>
#include <private/backend/PlatformFactory.h>
#include <backend/DriverEnums.h>
#include <utils/Allocator.h>
#include <utils/CallStack.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Invocable.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <utils/PrivateImplementation-impl.h>
#include <utils/Systrace.h>
#include <utils/ThreadUtils.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <algorithm>
#include <chrono>
#include <initializer_list>
#include <memory>
#include <mutex>
#include <new>
#include <optional>
#include <thread>
#include <string_view>
@@ -79,7 +66,6 @@
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "generated/resources/materials.h"
@@ -115,7 +101,7 @@ Engine* FEngine::create(Builder const& builder) {
// Normally we launch a thread and create the context and Driver from there (see FEngine::loop).
// In the single-threaded case, we do so in the here and now.
if constexpr (!UTILS_HAS_THREADING) {
if (!UTILS_HAS_THREADING) {
Platform* platform = builder->mPlatform;
void* const sharedContext = builder->mSharedContext;
@@ -137,7 +123,6 @@ Engine* FEngine::create(Builder const& builder) {
.forceGLES2Context = instance->getConfig().forceGLES2Context,
.stereoscopicType = instance->getConfig().stereoscopicType,
.assertNativeWindowIsValid = instance->features.backend.opengl.assert_native_window_is_valid,
.metalDisablePanicOnDrawableFailure = instance->getConfig().metalDisablePanicOnDrawableFailure,
};
instance->mDriver = platform->createDriver(sharedContext, driverConfig);
@@ -159,7 +144,7 @@ Engine* FEngine::create(Builder const& builder) {
// now we can initialize the largest part of the engine
instance->init();
if constexpr (!UTILS_HAS_THREADING) {
if (!UTILS_HAS_THREADING) {
instance->execute();
}
@@ -178,7 +163,7 @@ void FEngine::create(Builder const& builder, Invocable<void(void*)>&& callback)
instance->mDriverThread = std::thread(&FEngine::loop, instance);
// launch a thread to call the callback -- so it can't do any damage.
std::thread callbackThread = std::thread([instance, callback = std::move(callback)] {
std::thread callbackThread = std::thread([instance, callback = std::move(callback)]() {
instance->mDriverBarrier.await();
callback(instance);
});
@@ -211,7 +196,7 @@ FEngine* FEngine::getEngine(void* token) {
#endif
// These must be static because only a pointer is copied to the render stream
// these must be static because only a pointer is copied to the render stream
// Note that these coordinates are specified in OpenGL clip space. Other backends can transform
// these in the vertex shader as needed.
static constexpr float4 sFullScreenTriangleVertices[3] = {
@@ -221,7 +206,7 @@ static constexpr float4 sFullScreenTriangleVertices[3] = {
};
// these must be static because only a pointer is copied to the render stream
static constexpr uint16_t sFullScreenTriangleIndices[3] = { 0, 1, 2 };
static const uint16_t sFullScreenTriangleIndices[3] = { 0, 1, 2 };
FEngine::FEngine(Builder const& builder) :
mBackend(builder->mBackend),
@@ -231,6 +216,7 @@ FEngine::FEngine(Builder const& builder) :
mPostProcessManager(*this),
mEntityManager(EntityManager::get()),
mRenderableManager(*this),
mTransformManager(),
mLightManager(*this),
mCameraManager(*this),
mCommandBufferQueue(
@@ -288,7 +274,7 @@ uint32_t FEngine::getJobSystemThreadPoolSize(Config const& config) noexcept {
}
// 1 thread for the user, 1 thread for the backend
int threadCount = int(std::thread::hardware_concurrency()) - 2;
int threadCount = (int)std::thread::hardware_concurrency() - 2;
// make sure we have at least 1 thread though
threadCount = std::max(1, threadCount);
return threadCount;
@@ -361,8 +347,8 @@ void FEngine::init() {
}
// initialize the dummy textures so that their contents are not undefined
static constexpr uint32_t zeroes[6] = {};
static constexpr uint32_t ones = 0xffffffff;
static const uint32_t zeroes[6] = {0};
static const uint32_t ones = 0xffffffff;
mDefaultIblTexture = downcast(Texture::Builder()
.width(1).height(1).levels(1)
@@ -374,7 +360,7 @@ void FEngine::init() {
{ zeroes, sizeof(zeroes), Texture::Format::RGBA, Texture::Type::UBYTE });
// 3 bands = 9 float3
constexpr float sh[9 * 3] = { 0.0f };
const float sh[9 * 3] = { 0.0f };
mDefaultIbl = downcast(IndirectLight::Builder()
.irradiance(3, reinterpret_cast<const float3*>(sh))
.build(*this));
@@ -441,10 +427,10 @@ void FEngine::init() {
#endif
break;
}
mDefaultMaterial = downcast(defaultMaterialBuilder.build(*this));
mDefaultMaterial = downcast(defaultMaterialBuilder.build(*const_cast<FEngine*>(this)));
constexpr float3 dummyPositions[1] = {};
constexpr short4 dummyTangents[1] = {};
float3 dummyPositions[1] = {};
short4 dummyTangents[1] = {};
mDummyMorphTargetBuffer->setPositionsAt(*this, 0, dummyPositions, 1, 0);
mDummyMorphTargetBuffer->setTangentsAt(*this, 0, dummyTangents, 1, 0);
@@ -467,7 +453,7 @@ void FEngine::init() {
mPostProcessManager.init();
mDebugRegistry.registerProperty("d.shadowmap.debug_directional_shadowmap",
&debug.shadowmap.debug_directional_shadowmap, [this] {
&debug.shadowmap.debug_directional_shadowmap, [this]() {
mMaterials.forEach([this](FMaterial* material) {
if (material->getMaterialDomain() == MaterialDomain::SURFACE) {
@@ -483,7 +469,7 @@ void FEngine::init() {
});
mDebugRegistry.registerProperty("d.lighting.debug_froxel_visualization",
&debug.lighting.debug_froxel_visualization, [this] {
&debug.lighting.debug_froxel_visualization, [this]() {
mMaterials.forEach([this](FMaterial* material) {
if (material->getMaterialDomain() == MaterialDomain::SURFACE) {
@@ -621,7 +607,7 @@ void FEngine::shutdown() {
// now wait for all pending commands to be executed and the thread to exit
mCommandBufferQueue.requestExit();
if constexpr (!UTILS_HAS_THREADING) {
if (!UTILS_HAS_THREADING) {
execute();
getDriverApi().terminate();
} else {
@@ -652,13 +638,13 @@ void FEngine::prepare() {
DriverApi& driver = getDriverApi();
for (auto& materialInstanceList: mMaterialInstances) {
materialInstanceList.second.forEach([&driver](FMaterialInstance const* item) {
materialInstanceList.second.forEach([&driver](FMaterialInstance* item) {
item->commit(driver);
});
}
mMaterials.forEach([](FMaterial* material) {
#if FILAMENT_ENABLE_MATDBG // NOLINT(*-include-cleaner)
#if FILAMENT_ENABLE_MATDBG
material->checkProgramEdits();
#endif
});
@@ -682,7 +668,7 @@ void FEngine::flushAndWait() {
flushAndWait(FENCE_WAIT_FOR_EVER);
}
bool FEngine::flushAndWait(uint64_t const timeout) {
bool FEngine::flushAndWait(uint64_t timeout) {
FILAMENT_CHECK_PRECONDITION(!mCommandBufferQueue.isPaused())
<< "Cannot call Engine::flushAndWait() when rendering thread is paused!";
@@ -731,7 +717,6 @@ int FEngine::loop() {
.forceGLES2Context = mConfig.forceGLES2Context,
.stereoscopicType = mConfig.stereoscopicType,
.assertNativeWindowIsValid = features.backend.opengl.assert_native_window_is_valid,
.metalDisablePanicOnDrawableFailure = mConfig.metalDisablePanicOnDrawableFailure,
};
mDriver = mPlatform->createDriver(mSharedGLContext, driverConfig);
@@ -764,7 +749,7 @@ int FEngine::loop() {
}
#endif
#if FILAMENT_ENABLE_FGVIEWER // NOLINT(*-include-cleaner)
#if FILAMENT_ENABLE_FGVIEWER
#ifdef __ANDROID__
const char* fgviewerPortString = "8085";
#else
@@ -794,9 +779,9 @@ int FEngine::loop() {
return 0;
}
void FEngine::flushCommandBuffer(CommandBufferQueue& commandBufferQueue) const {
void FEngine::flushCommandBuffer(CommandBufferQueue& commandQueue) {
getDriver().purge();
commandBufferQueue.flush();
commandQueue.flush();
}
const FMaterial* FEngine::getSkyboxMaterial() const noexcept {
@@ -817,7 +802,7 @@ const FMaterial* FEngine::getSkyboxMaterial() const noexcept {
*/
template<typename T, typename ... ARGS>
T* FEngine::create(ResourceList<T>& list,
inline T* FEngine::create(ResourceList<T>& list,
typename T::Builder const& builder, ARGS&& ... args) noexcept {
T* p = mHeapAllocator.make<T>(*this, builder, std::forward<ARGS>(args)...);
if (UTILS_LIKELY(p)) {
@@ -895,7 +880,7 @@ FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material,
const FMaterialInstance* other, const char* name) noexcept {
FMaterialInstance* p = mHeapAllocator.make<FMaterialInstance>(*this, other, name);
if (UTILS_LIKELY(p)) {
auto const pos = mMaterialInstances.emplace(material, "MaterialInstance");
auto pos = mMaterialInstances.emplace(material, "MaterialInstance");
pos.first->second.insert(p);
}
return p;
@@ -973,7 +958,7 @@ FCamera* FEngine::createCamera(Entity const entity) noexcept {
}
FCamera* FEngine::getCameraComponent(Entity const entity) noexcept {
auto const ci = mCameraManager.getInstance(entity);
auto ci = mCameraManager.getInstance(entity);
return ci ? mCameraManager.getCamera(ci) : nullptr;
}
@@ -1026,16 +1011,16 @@ void FEngine::cleanupResourceListLocked(Lock& lock, ResourceList<T>&& list) {
template<typename T>
UTILS_ALWAYS_INLINE
bool FEngine::isValid(const T* ptr, ResourceList<T> const& list) const {
inline bool FEngine::isValid(const T* ptr, ResourceList<T> const& list) const {
auto& l = const_cast<ResourceList<T>&>(list);
return l.find(ptr) != l.end();
}
template<typename T>
UTILS_ALWAYS_INLINE
bool FEngine::terminateAndDestroy(const T* p, ResourceList<T>& list) {
if (p == nullptr) return true;
bool const success = list.remove(p);
inline bool FEngine::terminateAndDestroy(const T* ptr, ResourceList<T>& list) {
if (ptr == nullptr) return true;
bool const success = list.remove(ptr);
#if UTILS_HAS_RTTI
auto typeName = CallStack::typeName<T>();
@@ -1045,19 +1030,19 @@ bool FEngine::terminateAndDestroy(const T* p, ResourceList<T>& list) {
#endif
if (ASSERT_PRECONDITION_NON_FATAL(success,
"Object %s at %p doesn't exist (double free?)", typeNameCStr, p)) {
const_cast<T*>(p)->terminate(*this);
mHeapAllocator.destroy(const_cast<T*>(p));
"Object %s at %p doesn't exist (double free?)", typeNameCStr, ptr)) {
const_cast<T*>(ptr)->terminate(*this);
mHeapAllocator.destroy(const_cast<T*>(ptr));
}
return success;
}
template<typename T, typename Lock>
UTILS_ALWAYS_INLINE
bool FEngine::terminateAndDestroyLocked(Lock& lock, const T* p, ResourceList<T>& list) {
if (p == nullptr) return true;
inline bool FEngine::terminateAndDestroyLocked(Lock& lock, const T* ptr, ResourceList<T>& list) {
if (ptr == nullptr) return true;
lock.lock();
bool const success = list.remove(p);
bool const success = list.remove(ptr);
lock.unlock();
#if UTILS_HAS_RTTI
@@ -1068,9 +1053,9 @@ bool FEngine::terminateAndDestroyLocked(Lock& lock, const T* p, ResourceList<T>&
#endif
if (ASSERT_PRECONDITION_NON_FATAL(success,
"Object %s at %p doesn't exist (double free?)", typeNameCStr, p)) {
const_cast<T*>(p)->terminate(*this);
mHeapAllocator.destroy(const_cast<T*>(p));
"Object %s at %p doesn't exist (double free?)", typeNameCStr, ptr)) {
const_cast<T*>(ptr)->terminate(*this);
mHeapAllocator.destroy(const_cast<T*>(ptr));
}
return success;
}
@@ -1163,11 +1148,11 @@ bool FEngine::destroy(const FInstanceBuffer* p){
}
UTILS_NOINLINE
bool FEngine::destroy(const FMaterial* p) {
if (p == nullptr) return true;
bool const success = terminateAndDestroy(p, mMaterials);
bool FEngine::destroy(const FMaterial* ptr) {
if (ptr == nullptr) return true;
bool const success = terminateAndDestroy(ptr, mMaterials);
if (UTILS_LIKELY(success)) {
auto const pos = mMaterialInstances.find(p);
auto pos = mMaterialInstances.find(ptr);
if (UTILS_LIKELY(pos != mMaterialInstances.cend())) {
mMaterialInstances.erase(pos);
}
@@ -1176,8 +1161,8 @@ bool FEngine::destroy(const FMaterial* p) {
}
UTILS_NOINLINE
bool FEngine::destroy(const FMaterialInstance* p) {
if (p == nullptr) return true;
bool FEngine::destroy(const FMaterialInstance* ptr) {
if (ptr == nullptr) return true;
// Check that the material instance we're destroying is not in use in the RenderableManager
// To do this, we currently need to inspect all render primitives in the RenderableManager
@@ -1193,13 +1178,13 @@ bool FEngine::destroy(const FMaterialInstance* p) {
for (size_t j = 0; j < primitiveCount; j++) {
auto const* const mi = rcm.getMaterialInstanceAt(ri, 0, j);
if (features.engine.debug.assert_material_instance_in_use) {
FILAMENT_CHECK_PRECONDITION(mi != p)
FILAMENT_CHECK_PRECONDITION(mi != ptr)
<< "destroying MaterialInstance \""
<< mi->getName() << "\" which is still in use by Renderable (entity="
<< entity.getId() << ", instance="
<< ri.asValue() << ", index=" << j << ")";
} else {
if (UTILS_UNLIKELY(mi == p)) {
if (UTILS_UNLIKELY(mi == ptr)) {
slog.e << "destroying MaterialInstance \""
<< mi->getName() << "\" which is still in use by Renderable (entity="
<< entity.getId() << ", instance="
@@ -1211,11 +1196,11 @@ bool FEngine::destroy(const FMaterialInstance* p) {
}
}
if (p->isDefaultInstance()) return false;
auto const pos = mMaterialInstances.find(p->getMaterial());
if (ptr->isDefaultInstance()) return false;
auto pos = mMaterialInstances.find(ptr->getMaterial());
assert_invariant(pos != mMaterialInstances.cend());
if (pos != mMaterialInstances.cend()) {
return terminateAndDestroy(p, pos->second);
return terminateAndDestroy(ptr, pos->second);
}
// this shouldn't happen, this would be double-free
return false;
@@ -1268,7 +1253,7 @@ bool FEngine::isValid(const FMaterial* m, const FMaterialInstance* p) const {
}
// then find the material instance list for that material
auto const it = mMaterialInstances.find(m);
auto it = mMaterialInstances.find(m);
if (it == mMaterialInstances.end()) {
// this could happen if this material has no material instances at all
return false;
@@ -1357,7 +1342,7 @@ void* FEngine::streamAlloc(size_t const size, size_t const alignment) noexcept {
bool FEngine::execute() {
// wait until we get command buffers to be executed (or thread exit requested)
auto const buffers = mCommandBufferQueue.waitForCommands();
auto buffers = mCommandBufferQueue.waitForCommands();
if (UTILS_UNLIKELY(buffers.empty())) {
return false;
}
@@ -1396,7 +1381,7 @@ Engine::FeatureLevel FEngine::getSupportedFeatureLevel() const noexcept {
Engine::FeatureLevel FEngine::setActiveFeatureLevel(FeatureLevel featureLevel) {
FILAMENT_CHECK_PRECONDITION(featureLevel <= getSupportedFeatureLevel())
<< "Feature level " << unsigned(featureLevel) << " not supported";
<< "Feature level " << (unsigned)featureLevel << " not supported";
FILAMENT_CHECK_PRECONDITION(mActiveFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1)
<< "Cannot adjust feature level beyond 0 at runtime";
return (mActiveFeatureLevel = std::max(mActiveFeatureLevel, featureLevel));
@@ -1415,7 +1400,7 @@ void FEngine::unprotected() noexcept {
mUnprotectedDummySwapchain->makeCurrent(getDriverApi());
}
bool FEngine::setFeatureFlag(char const* name, bool const value) const noexcept {
bool FEngine::setFeatureFlag(char const* name, bool const value) noexcept {
auto* const p = getFeatureFlagPtr(name);
if (p) {
*p = value;
@@ -1486,7 +1471,7 @@ Engine::Builder& Engine::Builder::feature(char const* name, bool const value) no
}
Engine::Builder& Engine::Builder::features(std::initializer_list<char const *> const list) noexcept {
for (auto const name : list) {
for (auto name : list) {
if (name) {
feature(name, true);
}
@@ -1515,15 +1500,15 @@ Engine::Config Engine::BuilderDetails::validateConfig(Config config) noexcept {
// Use at least the defaults set by the build system
config.minCommandBufferSizeMB = std::max(
config.minCommandBufferSizeMB,
uint32_t(FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB)); // NOLINT(*-include-cleaner)
(uint32_t)FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB);
config.perFrameCommandsSizeMB = std::max(
config.perFrameCommandsSizeMB,
uint32_t(FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB)); // NOLINT(*-include-cleaner)
(uint32_t)FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB);
config.perRenderPassArenaSizeMB = std::max(
config.perRenderPassArenaSizeMB,
uint32_t(FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB)); // NOLINT(*-include-cleaner)
(uint32_t)FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB);
config.commandBufferSizeMB = std::max(
config.commandBufferSizeMB,
+8 -9
View File
@@ -61,8 +61,6 @@
#include <filament/Texture.h>
#include <filament/VertexBuffer.h>
#include <backend/DriverEnums.h>
#include <utils/Allocator.h>
#include <utils/compiler.h>
#include <utils/CountDownLatch.h>
@@ -124,11 +122,12 @@ class ResourceAllocator;
*/
class FEngine : public Engine {
public:
void* operator new(std::size_t const size) noexcept {
inline void* operator new(std::size_t const size) noexcept {
return utils::aligned_alloc(size, alignof(FEngine));
}
void operator delete(void* p) noexcept {
inline void operator delete(void* p) noexcept {
utils::aligned_free(p);
}
@@ -491,7 +490,7 @@ public:
backend::Handle<backend::HwTexture> getOneTextureArray() const { return mDummyOneTextureArray; }
backend::Handle<backend::HwTexture> getZeroTextureArray() const { return mDummyZeroTextureArray; }
static constexpr size_t MiB = 1024u * 1024u;
static constexpr const size_t MiB = 1024u * 1024u;
size_t getMinCommandBufferSize() const noexcept { return mConfig.minCommandBufferSizeMB * MiB; }
size_t getCommandBufferSize() const noexcept { return mConfig.commandBufferSizeMB * MiB; }
size_t getPerFrameCommandsSize() const noexcept { return mConfig.perFrameCommandsSizeMB * MiB; }
@@ -511,15 +510,15 @@ public:
void resetBackendState() noexcept;
#endif
backend::Driver& getDriver() const noexcept { return *mDriver; }
private:
explicit FEngine(Builder const& builder);
void init();
void shutdown();
int loop();
void flushCommandBuffer(backend::CommandBufferQueue& commandBufferQueue) const;
void flushCommandBuffer(backend::CommandBufferQueue& commandBufferQueue);
backend::Driver& getDriver() const noexcept { return *mDriver; }
template<typename T>
bool isValid(const T* ptr, ResourceList<T> const& list) const;
@@ -730,7 +729,7 @@ public:
return { mFeatures.data(), mFeatures.size() };
}
bool setFeatureFlag(char const* name, bool value) const noexcept;
bool setFeatureFlag(char const* name, bool value) noexcept;
std::optional<bool> getFeatureFlag(char const* name) const noexcept;
bool* getFeatureFlagPtr(std::string_view name, bool allowConstant = false) const noexcept;
};
+3 -3
View File
@@ -980,9 +980,9 @@ void FMaterial::processSpecializationConstants(FEngine& engine, Builder const& b
int const maxInstanceCount = (engine.getActiveFeatureLevel() == FeatureLevel::FEATURE_LEVEL_0)
? 1 : CONFIG_MAX_INSTANCES;
// The 16u below denotes the 16 bytes in a uvec4, which is how the froxel buffer is stored.
int const maxFroxelBufferHeight =
int(Froxelizer::getFroxelBufferByteCount(engine.getDriverApi()) / 16u);
int const maxFroxelBufferHeight = int(std::min(
FROXEL_BUFFER_MAX_ENTRY_COUNT / 4,
engine.getDriverApi().getMaxUniformBufferSize() / 16u));
bool const staticTextureWorkaround =
engine.getDriverApi().isWorkaroundNeeded(Workaround::METAL_STATIC_TEXTURE_TARGET_ERROR);
+7 -10
View File
@@ -61,7 +61,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
: mMaterial(material),
mDescriptorSet(material->getDescriptorSetLayout()),
mCulling(CullingMode::BACK),
mShadowCulling(CullingMode::BACK),
mDepthFunc(RasterState::DepthFunc::LE),
mColorWrite(false),
mDepthWrite(false),
@@ -93,7 +92,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
// We inherit the resolved culling mode rather than the builder-set culling mode.
// This preserves the property whereby double-sidedness automatically disables culling.
mCulling = rasterState.culling;
mShadowCulling = rasterState.culling;
mColorWrite = rasterState.colorWrite;
mDepthWrite = rasterState.depthWrite;
mDepthFunc = rasterState.depthFunc;
@@ -128,7 +126,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine,
mSpecularAntiAliasingVariance(other->mSpecularAntiAliasingVariance),
mSpecularAntiAliasingThreshold(other->mSpecularAntiAliasingThreshold),
mCulling(other->mCulling),
mShadowCulling(other->mShadowCulling),
mDepthFunc(other->mDepthFunc),
mColorWrite(other->mColorWrite),
mDepthWrite(other->mDepthWrite),
@@ -199,10 +196,10 @@ void FMaterialInstance::commit(DriverApi& driver) const {
for (auto const& [binding, p]: mTextureParameters) {
assert_invariant(p.texture);
// TODO: figure out a way to do this more efficiently (isValid() is a hashmap lookup)
FEngine const& engine = mMaterial->getEngine();
FEngine& engine = mMaterial->getEngine();
FILAMENT_CHECK_PRECONDITION(engine.isValid(p.texture))
<< "Invalid texture still bound to MaterialInstance: '" << getName() << "'\n";
Handle<HwTexture> const handle = p.texture->getHwHandleForSampling();
Handle<HwTexture> handle = p.texture->getHwHandleForSampling();
assert_invariant(handle);
mDescriptorSet.setSampler(binding, handle, p.params);
}
@@ -219,7 +216,7 @@ void FMaterialInstance::commit(DriverApi& driver) const {
void FMaterialInstance::setParameter(std::string_view const name,
Handle<HwTexture> texture, SamplerParams const params) {
auto const binding = mMaterial->getSamplerBinding(name);
auto binding = mMaterial->getSamplerBinding(name);
mDescriptorSet.setSampler(binding, texture, params);
}
@@ -246,7 +243,7 @@ void FMaterialInstance::setParameterImpl(std::string_view const name,
}
#endif
auto const binding = mMaterial->getSamplerBinding(name);
auto binding = mMaterial->getSamplerBinding(name);
if (texture && texture->textureHandleCanMutate()) {
mTextureParameters[binding] = { texture, sampler.getSamplerParams() };
} else {
@@ -331,13 +328,13 @@ const char* FMaterialInstance::getName() const noexcept {
void FMaterialInstance::use(FEngine::DriverApi& driver) const {
if (UTILS_UNLIKELY(mMissingSamplerDescriptors.any())) {
std::call_once(mMissingSamplersFlag, [this] {
std::call_once(mMissingSamplersFlag, [this]() {
auto const& list = mMaterial->getSamplerInterfaceBlock().getSamplerInfoList();
slog.w << "sampler parameters not set in MaterialInstance \""
<< mName.c_str_safe() << "\" or Material \""
<< mMaterial->getName().c_str_safe() << "\":\n";
mMissingSamplerDescriptors.forEachSetBit([&list](descriptor_binding_t binding) {
auto const pos = std::find_if(list.begin(), list.end(), [binding](const auto& item) {
auto pos = std::find_if(list.begin(), list.end(), [binding](const auto& item) {
return item.binding == binding;
});
// just safety-check, should never fail
@@ -371,7 +368,7 @@ void FMaterialInstance::fixMissingSamplers() const {
// here we need to set the samplers that are missing
auto const& list = mMaterial->getSamplerInterfaceBlock().getSamplerInfoList();
missingSamplerDescriptors.forEachSetBit([this, &list](descriptor_binding_t binding) {
auto const pos = std::find_if(list.begin(), list.end(), [binding](const auto& item) {
auto pos = std::find_if(list.begin(), list.end(), [binding](const auto& item) {
return item.binding == binding;
});
+3 -15
View File
@@ -94,8 +94,6 @@ public:
backend::CullingMode getCullingMode() const noexcept { return mCulling; }
backend::CullingMode getShadowCullingMode() const noexcept { return mShadowCulling; }
bool isColorWriteEnabled() const noexcept { return mColorWrite; }
bool isDepthWriteEnabled() const noexcept { return mDepthWrite; }
@@ -137,15 +135,7 @@ public:
void setTransparencyMode(TransparencyMode mode) noexcept;
void setCullingMode(CullingMode const culling) noexcept {
mCulling = culling;
mShadowCulling = culling;
}
void setCullingMode(CullingMode const color, CullingMode const shadow) noexcept {
mCulling = color;
mShadowCulling = shadow;
}
void setCullingMode(CullingMode const culling) noexcept { mCulling = culling; }
void setColorWrite(bool const enable) noexcept { mColorWrite = enable; }
@@ -281,9 +271,7 @@ private:
float mSpecularAntiAliasingThreshold = 0.0f;
backend::CullingMode mCulling : 2;
backend::CullingMode mShadowCulling : 2;
backend::RasterState::DepthFunc mDepthFunc : 3;
bool mColorWrite : 1;
bool mDepthWrite : 1;
bool mHasScissor : 1;
@@ -295,8 +283,8 @@ private:
// Scissor rectangle is specified as: Left Bottom Width Height.
backend::Viewport mScissorRect = { 0, 0,
uint32_t(std::numeric_limits<int32_t>::max()),
uint32_t(std::numeric_limits<int32_t>::max())
(uint32_t)std::numeric_limits<int32_t>::max(),
(uint32_t)std::numeric_limits<int32_t>::max()
};
utils::CString mName;
-5
View File
@@ -546,11 +546,6 @@ void FScene::removeEntities(const Entity* entities, size_t const count) {
}
}
UTILS_NOINLINE
void FScene::removeAllEntities() noexcept {
mEntities.clear();
}
UTILS_NOINLINE
size_t FScene::getRenderableCount() const noexcept {
FEngine& engine = mEngine;
-1
View File
@@ -199,7 +199,6 @@ private:
void addEntities(const utils::Entity* entities, size_t count);
void remove(utils::Entity entity);
void removeEntities(const utils::Entity* entities, size_t count);
void removeAllEntities() noexcept;
size_t getEntityCount() const noexcept { return mEntities.size(); }
size_t getRenderableCount() const noexcept;
size_t getLightCount() const noexcept;
-4
View File
@@ -658,10 +658,6 @@ bool FTexture::isTextureFormatSupported(FEngine& engine, InternalFormat const fo
return engine.getDriverApi().isTextureFormatSupported(format);
}
bool FTexture::isTextureFormatMipmappable(FEngine& engine, InternalFormat const format) noexcept {
return engine.getDriverApi().isTextureFormatMipmappable(format);
}
bool FTexture::isProtectedTexturesSupported(FEngine& engine) noexcept {
return engine.getDriverApi().isProtectedTexturesSupported();
}
-3
View File
@@ -89,9 +89,6 @@ public:
// Synchronous call to the backend. Returns whether a backend supports a particular format.
static bool isTextureFormatSupported(FEngine& engine, InternalFormat format) noexcept;
// Synchronous call to the backend. Returns whether a backend supports mipmapping of a particular format.
static bool isTextureFormatMipmappable(FEngine& engine, InternalFormat format) noexcept;
// Synchronous call to the backend. Returns whether a backend supports protected textures.
static bool isProtectedTexturesSupported(FEngine& engine) noexcept;
-1
View File
@@ -126,7 +126,6 @@ FView::FView(FEngine& engine)
mDefaultColorGrading = mColorGrading = engine.getDefaultColorGrading();
mColorPassDescriptorSet.init(
engine,
mLightUbh,
mFroxelizer.getRecordBuffer(),
mFroxelizer.getFroxelBuffer());
+3 -3
View File
@@ -126,17 +126,16 @@ ColorPassDescriptorSet::ColorPassDescriptorSet(FEngine& engine,
}
void ColorPassDescriptorSet::init(
FEngine& engine,
BufferObjectHandle lights,
BufferObjectHandle recordBuffer,
BufferObjectHandle froxelBuffer) noexcept {
for (auto&& descriptorSet: mDescriptorSet) {
descriptorSet.setBuffer(+PerViewBindingPoints::LIGHTS,
lights, 0, CONFIG_MAX_LIGHT_COUNT * sizeof(LightsUib));
lights, 0, sizeof(LightsUib));
descriptorSet.setBuffer(+PerViewBindingPoints::RECORD_BUFFER,
recordBuffer, 0, sizeof(FroxelRecordUib));
descriptorSet.setBuffer(+PerViewBindingPoints::FROXEL_BUFFER,
froxelBuffer, 0, Froxelizer::getFroxelBufferByteCount(engine.getDriverApi()));
froxelBuffer, 0, sizeof(FroxelsUib));
}
}
@@ -574,3 +573,4 @@ void ColorPassDescriptorSet::setBuffer(descriptor_binding_t const binding,
}
} // namespace filament
-1
View File
@@ -79,7 +79,6 @@ public:
TypedUniformBuffer<PerViewUib>& uniforms) noexcept;
void init(
FEngine& engine,
backend::BufferObjectHandle lights,
backend::BufferObjectHandle recordBuffer,
backend::BufferObjectHandle froxelBuffer) noexcept;
+8 -30
View File
@@ -32,7 +32,6 @@
#include <backend/Handle.h>
#include <utils/compiler.h>
#include <utils/CString.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
@@ -489,7 +488,7 @@ fgviewer::FrameGraphInfo FrameGraph::getFrameGraphInfo(const char *viewName) con
const auto activePassNodesEnd = mActivePassNodesEnd;
while (first != activePassNodesEnd) {
PassNode *const pass = *first;
++first;
first++;
assert_invariant(!pass->isCulled());
std::vector<fgviewer::ResourceId> reads;
@@ -497,12 +496,7 @@ fgviewer::FrameGraphInfo FrameGraph::getFrameGraphInfo(const char *viewName) con
for (auto const &edge: readEdges) {
// all incoming edges should be valid by construction
assert_invariant(mGraph.isEdgeValid(edge));
auto resourceNode = static_cast<const ResourceNode*>(mGraph.getNode(edge->from));
assert_invariant(resourceNode);
if (resourceNode->getRefCount() == 0)
continue;
reads.push_back(resourceNode->resourceHandle.index);
reads.push_back(edge->from);
}
std::vector<fgviewer::ResourceId> writes;
@@ -513,35 +507,19 @@ fgviewer::FrameGraphInfo FrameGraph::getFrameGraphInfo(const char *viewName) con
if (!mGraph.isEdgeValid(edge)) {
continue;
}
auto resourceNode = static_cast<const ResourceNode*>(mGraph.getNode(edge->to));
assert_invariant(resourceNode);
if (resourceNode->getRefCount() == 0)
continue;
writes.push_back(resourceNode->resourceHandle.index);
writes.push_back(edge->to);
}
passes.emplace_back(utils::CString(pass->getName()),
std::move(reads), std::move(writes));
}
std::unordered_map<fgviewer::ResourceId, fgviewer::FrameGraphInfo::Resource> resources;
for (const auto &resourceNode: mResourceNodes) {
const FrameGraphHandle resourceHandle = resourceNode->resourceHandle;
if (resources.find(resourceHandle.index) != resources.end())
continue;
for (const auto &resource: mResourceNodes) {
std::vector<fgviewer::FrameGraphInfo::Resource::Property> resourceProps;
if (resourceNode->getRefCount() == 0)
continue;
if (resourceNode->getParentNode() != nullptr) {
resourceProps.emplace_back(fgviewer::FrameGraphInfo::Resource::Property {
.name = "is_subresource",
.value = utils::CString(std::to_string(
resourceNode->getParentHandle().index).data())
});
}
resources.emplace(resourceHandle.index, fgviewer::FrameGraphInfo::Resource(
resourceHandle.index,
utils::CString(resourceNode->getName()),
// TODO: Fill in resource properties
fgviewer::ResourceId id = resource->getId();
resources.emplace(id, fgviewer::FrameGraphInfo::Resource(
id, utils::CString(resource->getName()),
std::move(resourceProps))
);
}
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.57.2"
spec.version = "1.56.7"
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.57.2/filament-v1.57.2-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.56.7/filament-v1.56.7-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
-1
View File
@@ -23,7 +23,6 @@ set(SRCS
src/ApiHandler.h
src/DebugServer.cpp
src/FrameGraphInfo.cpp
src/JsonWriter.cpp
)
# ==================================================================================================
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2024 The Android Open Source Project
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -36,25 +36,17 @@ public:
FrameGraphInfo(FrameGraphInfo const &) = delete;
bool operator==(const FrameGraphInfo& rhs) const;
struct Pass {
Pass(utils::CString name, std::vector<ResourceId> reads,
std::vector<ResourceId> writes);
bool operator==(const Pass& rhs) const;
utils::CString name;
std::vector<ResourceId> reads;
std::vector<ResourceId> writes;
};
struct Resource {
bool operator==(const Resource& rhs) const;
struct Property {
bool operator==(const Property& rhs) const;
utils::CString name;
utils::CString value;
};
@@ -78,12 +70,6 @@ public:
// The incoming passes should be sorted by the execution order.
void setPasses(std::vector<Pass> sortedPasses);
const char* getViewName() const;
const std::vector<Pass>& getPasses() const;
const std::unordered_map<ResourceId, Resource>& getResources() const;
private:
utils::CString viewName;
// The order of the passes in the vector indicates the execution
+19 -87
View File
@@ -18,7 +18,6 @@
#include "ApiHandler.h"
#include <fgviewer/DebugServer.h>
#include <fgviewer/JsonWriter.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Log.h>
@@ -41,96 +40,29 @@ auto const error = [](int line, std::string const& uri) {
} // anonymous
bool ApiHandler::handleGetApiFgInfo(struct mg_connection* conn,
struct mg_request_info const* request) {
auto const softError = [conn, request](char const* msg) {
utils::slog.e << "[fgviewer] DebugServer: " << msg << ": " << request->query_string << utils::io::endl;
mg_printf(conn, kErrorHeader.data(), "application/txt");
mg_write(conn, msg, strlen(msg));
return true;
};
// TODO: Implement the method
return true;
}
void ApiHandler::addFrameGraph(FrameGraphInfo const* framegraph) {
// TODO: Implement the method
}
bool ApiHandler::handleGet(CivetServer* server, struct mg_connection* conn) {
struct mg_request_info const* request = mg_get_request_info(conn);
std::string const& uri = request->local_uri;
if (uri.find("/api/status") == 0) {
return handleGetStatus(conn, request);
}
if (uri == "/api/framegraphs") {
std::unique_lock const lock(mServer->mViewsMutex);
mg_printf(conn, kSuccessHeader.data(), "application/json");
mg_printf(conn, "[");
int index = 0;
for (auto const& view: mServer->mViews) {
bool const last = (++index) == mServer->mViews.size();
JsonWriter writer;
if (!writer.writeFrameGraphInfo(view.second)) {
return error(__LINE__, uri);
}
mg_printf(conn, "{ \"fgid\": \"%8.8x\", %s } %s", view.first, writer.getJsonString(),
last ? "" : ",");
}
mg_printf(conn, "]");
return true;
}
if (uri == "/api/framegraph") {
const FrameGraphInfo* result = getFrameGraphInfo(request);
if (!result) {
return error(__LINE__, uri);
}
JsonWriter writer;
if (!writer.writeFrameGraphInfo(*result)) {
return error(__LINE__, uri);
}
mg_printf(conn, kSuccessHeader.data(), "application/json");
mg_printf(conn, "{ %s }", writer.getJsonString());
return true;
}
return error(__LINE__, uri);
}
void ApiHandler::updateFrameGraph(ViewHandle view_handle) {
std::unique_lock const lock(mStatusMutex);
snprintf(statusFrameGraphId, sizeof(statusFrameGraphId), "%8.8x", view_handle);
mCurrentStatus++;
mStatusCondition.notify_all();
}
const FrameGraphInfo* ApiHandler::getFrameGraphInfo(struct mg_request_info const* request) {
size_t const qlength = strlen(request->query_string);
char fgid[9] = {};
if (mg_get_var(request->query_string, qlength, "fgid", fgid, sizeof(fgid)) < 0) {
return nullptr;
}
uint32_t const id = strtoul(fgid, nullptr, 16);
std::unique_lock const lock(mServer->mViewsMutex);
const auto it = mServer->mViews.find(id);
return it == mServer->mViews.end()
? nullptr
: &(it->second);
}
bool ApiHandler::handleGetStatus(struct mg_connection* conn,
struct mg_request_info const* request) {
char const* qstr = request->query_string;
if (qstr && strcmp(qstr, "firstTime") == 0) {
mg_printf(conn, kSuccessHeader.data(), "application/txt");
mg_write(conn, "0", 1);
return true;
}
std::unique_lock<std::mutex> lock(mStatusMutex);
uint64_t const currentStatusCount = mCurrentStatus;
if (mStatusCondition.wait_for(lock, 10s,
[this, currentStatusCount] {
return currentStatusCount < mCurrentStatus;
})) {
mg_printf(conn, kSuccessHeader.data(), "application/txt");
mg_write(conn, statusFrameGraphId, 8);
} else {
mg_printf(conn, kSuccessHeader.data(), "application/txt");
// Use '1' to indicate a no-op. This ensures that we don't block forever if the client is
// gone.
mg_write(conn, "1", 1);
}
// TODO: Implement the method
return true;
}
+3 -19
View File
@@ -17,8 +17,6 @@
#ifndef FGVIEWER_APIHANDLER_H
#define FGVIEWER_APIHANDLER_H
#include <fgviewer/DebugServer.h>
#include <CivetServer.h>
namespace filament::fgviewer {
@@ -29,8 +27,7 @@ struct FrameGraphInfo;
// Handles the following REST requests, where {id} is an 8-digit hex string.
//
// GET /api/framegraphs
// GET /api/framegraph?fg={fgid}
// GET /api/status
// GET /api/framegraph?fg={viewname}
//
class ApiHandler : public CivetHandler {
public:
@@ -39,25 +36,12 @@ public:
~ApiHandler() = default;
bool handleGet(CivetServer* server, struct mg_connection* conn);
void updateFrameGraph(ViewHandle view_handle);
void addFrameGraph(FrameGraphInfo const* frameGraph);
private:
const FrameGraphInfo* getFrameGraphInfo(struct mg_request_info const* request);
bool handleGetStatus(struct mg_connection* conn,
struct mg_request_info const* request);
bool handleGetApiFgInfo(struct mg_connection* conn, struct mg_request_info const* request);
DebugServer* mServer;
std::mutex mStatusMutex;
std::condition_variable mStatusCondition;
char statusFrameGraphId[9] = {};
// This variable is to implement a *hanging* effect for /api/status. The call to /api/status
// will always block until statusMaterialId is updated again. The client is expected to keep
// calling /api/status (a constant "pull" to simulate a push).
uint64_t mCurrentStatus = 0;
};
} // filament::fgviewer
-12
View File
@@ -110,7 +110,6 @@ ViewHandle DebugServer::createView(utils::CString name) {
std::unique_lock<utils::Mutex> lock(mViewsMutex);
ViewHandle handle = mViewCounter++;
mViews.emplace(handle, FrameGraphInfo(std::move(name)));
mApiHandler->updateFrameGraph(handle);
return handle;
}
@@ -122,19 +121,8 @@ void DebugServer::destroyView(ViewHandle h) {
void DebugServer::update(ViewHandle h, FrameGraphInfo info) {
std::unique_lock<utils::Mutex> lock(mViewsMutex);
const auto it = mViews.find(h);
if (it == mViews.end()) {
slog.w << "[fgviewer] Received update for unknown handle " << h;
return;
}
bool has_changed = !(it->second == info);
if (!has_changed)
return;
mViews.erase(h);
mViews.emplace(h, std::move(info));
mApiHandler->updateFrameGraph(h);
}
} // namespace filament::fgviewer
+1 -32
View File
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2024 The Android Open Source Project
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,33 +25,15 @@ FrameGraphInfo::~FrameGraphInfo() = default;
FrameGraphInfo::FrameGraphInfo(FrameGraphInfo&& rhs) noexcept = default;
bool FrameGraphInfo::operator==(const FrameGraphInfo& rhs) const {
return viewName == rhs.viewName
&& passes == rhs.passes
&& resources == rhs.resources;
}
FrameGraphInfo::Pass::Pass(utils::CString name, std::vector<ResourceId> reads,
std::vector<ResourceId> writes): name(std::move(name)),
reads(std::move(reads)),
writes(std::move(writes)) {}
bool FrameGraphInfo::Pass::operator==(const Pass& rhs) const {
return name == rhs.name && reads == rhs.reads && writes == rhs.writes;
}
FrameGraphInfo::Resource::Resource(ResourceId id, utils::CString name,
std::vector<Property> properties): id(id),
name(std::move(name)), properties(std::move(properties)) {}
bool FrameGraphInfo::Resource::operator==(const Resource& rhs) const {
return id == rhs.id && name == rhs.name && properties == rhs.properties;
}
bool FrameGraphInfo::Resource::Property::operator==(const Property &rhs) const {
return name == rhs.name && value == rhs.value;
}
void FrameGraphInfo::setResources(
std::unordered_map<ResourceId, Resource> resources) {
this->resources = std::move(resources);
@@ -61,17 +43,4 @@ void FrameGraphInfo::setPasses(std::vector<Pass> sortedPasses) {
passes = std::move(sortedPasses);
}
const char* FrameGraphInfo::getViewName() const {
return viewName.c_str_safe();
}
const std::vector<FrameGraphInfo::Pass>& FrameGraphInfo::getPasses() const {
return passes;
}
const std::unordered_map<ResourceId, FrameGraphInfo::Resource>&
FrameGraphInfo::getResources() const {
return resources;
}
} // namespace filament::fgviewer
-135
View File
@@ -1,135 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <fgviewer/JsonWriter.h>
#include <fgviewer/FrameGraphInfo.h>
#include <iomanip>
#include <sstream>
namespace filament::fgviewer {
namespace {
void writeJSONString(std::ostream& os, const char* str) {
os << '"';
const char* p = str;
while (*p != '\0') {
switch (*p) {
case '"': os << "\\\""; break;
case '\\': os << "\\\\"; break;
case '\n': os << "\\n"; break;
case '\t': os << "\\t"; break;
default: os << *p; break;
}
++p;
}
os << '"';
}
void writeViewName(std::ostream& os, const FrameGraphInfo& frameGraph) {
os << " \"viewName\": ";
writeJSONString(os, frameGraph.getViewName());
os << ",\n";
}
void writeResourceIds(std::ostream& os, const std::vector<ResourceId>& resources) {
for (size_t j = 0; j < resources.size(); ++j) {
os << resources[j];
if (j + 1 < resources.size()) os << ", ";
}
}
void writePasses(std::ostream& os, const FrameGraphInfo& frameGraph) {
os << " \"passes\": [\n";
auto& passes = frameGraph.getPasses();
for (size_t i = 0; i < passes.size(); ++i) {
const FrameGraphInfo::Pass& pass = passes[i];
os << " {\n";
os << " \"name\": ";
writeJSONString(os, pass.name.c_str());
os << ",\n";
const std::vector<ResourceId>& reads = pass.reads;
os << " \"reads\": [";
writeResourceIds(os, reads);
os << "],\n";
const std::vector<ResourceId>& writes = pass.writes;
os << " \"writes\": [";
writeResourceIds(os, writes);
os << "]\n";
os << " }";
if (i + 1 < passes.size()) os << ",";
os << "\n";
}
os << " ],\n";
}
void writeResources(std::ostream& os, const FrameGraphInfo& frameGraph) {
os << " \"resources\": {\n";
size_t resourceCount = 0;
auto& resources = frameGraph.getResources();
for (const auto& [id, resource] : resources) {
os << " \"" << id << "\": {\n";
os << " \"id\": " << resource.id << ",\n";
os << " \"name\": ";
writeJSONString(os, resource.name.c_str());
os << ",\n";
os << " \"properties\": [\n";
for (size_t j = 0; j < resource.properties.size(); ++j) {
const auto& [key, value] = resource.properties[j];
os << " {\n";
os << " \"key\": ";
writeJSONString(os, key.c_str());
os << ",\n";
os << " \"value\": ";
writeJSONString(os, value.c_str());
os << "\n }";
if (j + 1 < resource.properties.size()) os << ",";
os << "\n";
}
os << " ]\n";
os << " }";
if (++resourceCount < resources.size()) os << ",";
os << "\n";
}
os << " }\n";
}
} // anonymous
const char* JsonWriter::getJsonString() const {
return mJsonString.c_str();
}
size_t JsonWriter::getJsonSize() const {
return mJsonString.size();
}
bool JsonWriter::writeFrameGraphInfo(const FrameGraphInfo& frameGraph) {
std::ostringstream os;
writeViewName(os, frameGraph);
writePasses(os, frameGraph);
writeResources(os, frameGraph);
mJsonString = utils::CString(os.str().c_str());
return true;
}
} // namespace filament::fgviewer
-67
View File
@@ -1,67 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// api.js encapsulates all the REST endpoints that the server provides
async function _fetchJson(uri) {
const response = await fetch(uri);
return await response.json();
}
async function _fetchText(uri) {
const response = await fetch(uri);
return await response.text();
}
async function fetchFrameGraphs() {
const fgJson = await _fetchJson("api/framegraphs")
const ret = {};
for (const fgInfo of fgJson) {
ret[fgInfo.fgid] = fgInfo;
}
return ret;
}
async function fetchFrameGraph(fgid) {
const fgInfo = await _fetchJson(`api/framegraph?fgid=${fgid}`);
fgInfo.fgid = fgid;
return fgInfo;
}
const STATUS_LOOP_TIMEOUT = 3000;
const STATUS_CONNECTED = 1;
const STATUS_DISCONNECTED = 2;
const STATUS_FRAMEGRAPH_UPDATED = 3;
// Status function should be of the form function(status, data)
async function statusLoop(isConnected, onStatus) {
// This is a hanging get except for when transition from disconnected to connected, which
// should return immediately.
try {
const fgid = await _fetchText("api/status" + (isConnected() ? '' : '?firstTime'));
// A first-time request returned successfully
if (fgid === '0') {
onStatus(STATUS_CONNECTED);
} else if (fgid !== '1') {
onStatus(STATUS_FRAMEGRAPH_UPDATED, fgid);
} // fgid == '1' is no-op, just loop again
statusLoop(isConnected, onStatus);
} catch {
onStatus(STATUS_DISCONNECTED);
setTimeout(() => statusLoop(isConnected, onStatus), STATUS_LOOP_TIMEOUT)
}
}
-529
View File
@@ -1,529 +0,0 @@
/*
* Copyright (C) 2025 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {LitElement, html, css, unsafeCSS, nothing} from "https://unpkg.com/lit@2.8.0?module";
const kUntitledPlaceholder = "untitled";
// CSS constants
const FOREGROUND_COLOR = '#fafafa';
const INACTIVE_COLOR = '#6f6f6f';
const UNSELECTED_COLOR = '#dfdfdf';
const BACKGROUND_COLOR = '#5362e5';
const REGULAR_FONT_SIZE = 12;
// Constants for color operations
const READ_COLOR = '#3ac224';
const WRITE_COLOR = '#d43232';
const NO_ACCESS_COLOR = '#d8dde754';
const READ_WRITE_COLOR = '#ffeb99';
const DEFAULT_COLOR = '#ffffff';
const SUBRESOURCE_COLOR = '#d3d3d3';
const RESOURCE_USAGE_TYPE_READ = 'read';
const RESOURCE_USAGE_TYPE_WRITE = 'write';
const RESOURCE_USAGE_TYPE_NO_ACCESS = 'no-access';
const RESOURCE_USAGE_TYPE_READ_WRITE = 'read-write';
const IS_SUBRESOURCE_KEY = 'is_subresource'
class MenuSection extends LitElement {
static get properties() {
return {
showing: {type: Boolean, state: true},
title: {type: String, attribute: 'title'},
};
}
static get styles() {
return css`
:host {
font-size: ${unsafeCSS(REGULAR_FONT_SIZE)}px;
color: ${unsafeCSS(UNSELECTED_COLOR)};
}
.section-title {
font-size: 16px;
color: ${unsafeCSS(UNSELECTED_COLOR)};
cursor: pointer;
}
.container {
margin-bottom: 20px;
}
hr {
display: block;
height: 1px;
border: 0px;
border-top: 1px solid ${unsafeCSS(UNSELECTED_COLOR)};
padding: 0;
width: 100%;
margin: 3px 0 8px 0;
}
.expander {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
`;
}
_showClick() {
this.showing = !this.showing;
}
constructor() {
super();
this.showing = true;
}
render() {
const expandedIcon = this.showing ? '-' : '+';
const slot = (() => html`
<slot></slot>`)();
return html`
<div class="container">
<div class="section-title expander" @click="${this._showClick}">
<span>${this.title}</span> <span>${expandedIcon}</span>
</div>
<hr/>
${this.showing ? slot : []}
</div>
`;
}
}
customElements.define('menu-section', MenuSection);
class FrameGraphSidePanel extends LitElement {
// Setting the style in render() has poor performance implications. We use it simply to avoid
// having another container descending from the root to host the background color.
dynamicStyle() {
return `
:host {
position: fixed;
background: ${this.connected ? BACKGROUND_COLOR : INACTIVE_COLOR};
width: 20%;
height: 100%;
max-width: 250px;
min-width: 250px;
padding: 10px 20px;
overflow-y: auto;
}
.title {
color: white;
width: 100%;
text-align: center;
margin: 0 0 10px 0;
font-size: 20px;
}
.framegraphs {
display: flex;
flex-direction: column;
margin-bottom: 20px;
font-size: ${REGULAR_FONT_SIZE}px;
color: ${UNSELECTED_COLOR};
}
.framegraph:hover {
text-decoration: underline;
}
.framegraph {
cursor: pointer;
}
.selected {
font-weight: bolder;
color: ${FOREGROUND_COLOR};
}
`;
}
static get properties() {
return {
connected: {type: Boolean, attribute: 'connected'},
currentFrameGraph: {type: String, attribute: 'current-framegraph'},
database: {type: Object, state: true},
framegraphs: {type: Array, state: true},
}
}
constructor() {
super();
this.connected = false;
this.framegraphs = [];
this.database = {};
}
updated(props) {
if (props.has('database')) {
const items = [];
// Names need not be unique, so we display a numeric suffix for non-unique names.
// To achieve stable ordering of anonymous framegraphs, we first sort by fgid.
const labels = new Set();
const fgids = Object.keys(this.database).sort();
const duplicatedLabels = {};
for (const fgid of fgids) {
const name = this.database[fgid].viewName || kUntitledPlaceholder;
if (labels.has(name)) {
duplicatedLabels[name] = 0;
} else {
labels.add(name);
}
}
this.framegraphs = fgids.map((fgid) => {
const framegraph = this.database[fgid];
let name = framegraph.viewName || kUntitledPlaceholder;
if (name in duplicatedLabels) {
const index = duplicatedLabels[name];
duplicatedLabels[name] = index + 1;
name = `${name} (${index})`;
}
return {
fgid: fgid,
name: name,
domain: "views"
};
});
}
}
_handleFrameGraphClick(ev) {
this.dispatchEvent(new CustomEvent('select-framegraph', {
detail: ev,
bubbles: true,
composed: true
}));
}
render() {
const sections = (title) => {
const fgs = this.framegraphs
.map((fg) => {
const framegraph = this.database[fg.fgid];
const onClick = this._handleFrameGraphClick.bind(this, fg.fgid);
const isFrameGraphSelected = fg.fgid === this.currentFrameGraph;
const fgName = (isFrameGraphSelected ? '● ' : '') + framegraph.viewName;
return html`
<div class="framegraph" @click="${onClick}" data-id="${fg.fgid}">
${fgName}
</div>
`;
});
if (fgs.length > 0) {
return html`
<menu-section title="${title}">${fgs}</menu-section>`;
}
return null;
};
return html`
<style>${this.dynamicStyle()}</style>
<div class="container">
<div class="title">fgviewer</div>
${sections("Views", "views") ?? nothing}
</div>
`;
}
}
customElements.define("framegraph-sidepanel", FrameGraphSidePanel);
class FrameGraphTable extends LitElement {
static get styles() {
return css`
:host {
display: block;
flex-grow: 1;
width: 80%;
}
.table-container {
max-height: 100%;
max-width: 100%;
overflow: auto;
border: 1px solid #ddd;
margin-left: 300px;
}
.scrollable-table {
width: 100%;
height: 100%;
border-collapse: collapse;
}
.collapsible {
background-color: #f9f9f9;
}
.toggle-icon {
cursor: pointer;
margin-right: 5px;
user-select: none;
}
.hidden {
display: none;
}
.scrollable-table td,
.scrollable-table th {
padding: 12px;
text-align: left;
border: 1px solid #ddd;
}
.scrollable-table tr {
position: sticky;
padding: 12px;
text-align: left;
border: 1px solid #ddd;
left: 0;
z-index: 1;
}
.sticky-col {
position: sticky;
left: 0;
z-index: 1;
}
th {
min-width: 100px;
background-color: #f2f2f2;
padding: 12px;
text-align: left;
border: 1px solid #ddd;
}
`;
}
static get properties() {
return {
frameGraphData: {type: Object, state: true}, // Expecting a JSON frame graph structure
};
}
constructor() {
super();
this.frameGraphData = null;
}
updated(props) {
if (props.has('frameGraphData')) {
this.requestUpdate();
}
}
_getCellColor(type, defaultColor) {
switch (type) {
case RESOURCE_USAGE_TYPE_READ:
return READ_COLOR;
case RESOURCE_USAGE_TYPE_WRITE:
return WRITE_COLOR;
case RESOURCE_USAGE_TYPE_NO_ACCESS:
return NO_ACCESS_COLOR;
case RESOURCE_USAGE_TYPE_READ_WRITE:
return READ_WRITE_COLOR;
default:
return defaultColor;
}
}
_toggleCollapse(resourceIndex) {
const subresourceRows = this.shadowRoot.querySelectorAll(`[id^="subresource-${resourceIndex}-"]`);
const icon = this.shadowRoot.querySelector(`#resource-${resourceIndex} .toggle-icon`);
const isHidden = subresourceRows[0]?.classList.contains('hidden');
subresourceRows.forEach(row => row.classList.toggle('hidden'));
icon.textContent = isHidden ? '▼' : '▶';
}
_getRowHtml(allPasses, resourceId, defaultColor) {
return allPasses.map((passData, index) => {
const isRead = passData?.reads.includes(resourceId);
const isWrite = passData?.writes.includes(resourceId);
let type = null;
const hasUsed = (passData) => {
return passData?.reads.includes(resourceId) || passData?.writes.includes(resourceId);
};
const hasBeenUsedBefore = allPasses.slice(0, index).some(hasUsed);
const willBeUsedLater = allPasses.slice(index + 1).some(hasUsed);
if (isRead && isWrite) type = RESOURCE_USAGE_TYPE_READ_WRITE;
else if (isRead) type = RESOURCE_USAGE_TYPE_READ;
else if (isWrite) type = RESOURCE_USAGE_TYPE_WRITE;
else if (hasBeenUsedBefore && willBeUsedLater) type = RESOURCE_USAGE_TYPE_NO_ACCESS;
return html`
<td style="background-color: ${unsafeCSS(this._getCellColor(type, defaultColor))};">
${type ?? nothing}
</td>`;
});
}
_isSubresourceOfParent(resource, parentResource){
return resource.properties?.some(prop =>
prop.key === IS_SUBRESOURCE_KEY &&
Number(prop.value) === parentResource.id)
}
render() {
if (!this.frameGraphData || !this.frameGraphData.passes || !this.frameGraphData.resources) return nothing;
const allPasses = this.frameGraphData.passes;
const resources = Object.values(this.frameGraphData.resources);
return html`
<div class="table-container">
<table class="scrollable-table">
<thead>
<tr>
<th class="sticky-col">Resources/Passes</th>
${allPasses.map(pass => html`
<th>${pass.name}</th>`)}
</tr>
</thead>
<tbody>
${resources.map((resource, resourceIndex) => {
const isSubresource = resource.properties?.some(prop => prop.key === IS_SUBRESOURCE_KEY);
if (isSubresource) return nothing;
const hasSubresources = resources.some(subresource => this._isSubresourceOfParent(subresource, resource));
return html`
<tr id="resource-${resourceIndex}">
<th class="sticky-col">
${hasSubresources ? html`
<span
class="toggle-icon"
@click="${() => this._toggleCollapse(resourceIndex)}"
>▶</span>` : nothing}
${resource.name}
</th>
${this._getRowHtml(allPasses, resource.id, DEFAULT_COLOR)}
</tr>
${resources.filter(subresource => this._isSubresourceOfParent(subresource, resource)
).map((subresource, subIndex) => html`
<tr id="subresource-${resourceIndex}-${subIndex}"
class="collapsible hidden">
<td class="sticky-col"
style="background-color: ${SUBRESOURCE_COLOR}">
${subresource.name}
</td>
${this._getRowHtml(allPasses, subresource.id, SUBRESOURCE_COLOR)}
</tr>
`)}
`;
})}
</tbody>
</table>
</div>
`;
}
}
customElements.define("framegraph-table", FrameGraphTable);
class FrameGraphViewer extends LitElement {
static get styles() {
return css`
:host {
height: 100%;
width: 100%;
display: flex;
}
`;
}
get _sidePanel() {
return this.renderRoot.querySelector('#sidepanel');
}
get _framegraphTable() {
return this.renderRoot.querySelector('#table');
}
async init() {
const isConnected = () => this.connected;
statusLoop(
isConnected,
async (status, fgid) => {
this.connected = status == STATUS_CONNECTED || status == STATUS_FRAMEGRAPH_UPDATED;
if (status == STATUS_FRAMEGRAPH_UPDATED) {
let fgInfo = await fetchFrameGraph(fgid);
this.database[fgInfo.fgid] = fgInfo;
this._framegraphTable.frameGraphData = fgInfo;
}
}
);
let framegraphs = await fetchFrameGraphs();
this.database = framegraphs;
}
_getFrameGraph() {
const framegraph = (this.database && this.currentFrameGraph) ?
this.database[this.currentFrameGraph] : null;
return framegraph;
}
constructor() {
super();
this.connected = false;
this.database = {};
this.currentFrameGraph = null;
this.init();
this.addEventListener('select-framegraph',
(ev) => {
this.currentFrameGraph = ev.detail;
}
);
}
static get properties() {
return {
connected: {type: Boolean, state: true},
database: {type: Object, state: true},
currentFrameGraph: {type: String, state: true},
}
}
updated(props) {
if (props.has('currentFrameGraph') || props.has('database')) {
const framegraph = this._getFrameGraph();
this._framegraphTable.frameGraphData = framegraph;
this._sidePanel.database = this.database;
}
}
render() {
return html`
<framegraph-sidepanel id="sidepanel"
?connected="${this.connected}"
current-framegraph="${this.currentFrameGraph}" >
</framegraph-sidepanel>
<framegraph-table id="table"
?connected="${this.connected}"
current-framegraph="${this.currentFrameGraph}"
</framegraph-table>
`;
}
}
customElements.define("framegraph-viewer", FrameGraphViewer);
-22
View File
@@ -1,22 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Filament Debugger</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1">
<link href="https://google.github.io/filament/favicon.png" rel="icon" type="image/x-icon" />
<link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,600,700" rel="stylesheet">
<style>
html, body {
height: 100%;
margin: 0;
font-family: "Open Sans";
}
</style>
<script src="api.js"></script>
</head>
<body>
<script src="app.js" type="module"></script>
<framegraph-viewer></framegraph-viewer>
</body>
</html>
@@ -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 = 57;
static constexpr size_t MATERIAL_VERSION = 56;
/**
* Supported shading models
+15 -13
View File
@@ -144,17 +144,19 @@ install(DIRECTORY ${PUBLIC_HDR_DIR}/filamat DESTINATION include)
# ==================================================================================================
# Tests
# ==================================================================================================
if (IS_HOST_PLATFORM)
project(test_filamat)
set(TARGET test_filamat)
set(SRCS
tests/test_filamat.cpp
tests/test_argBufferFixup.cpp
tests/test_clipDistanceFixup.cpp
tests/test_includes.cpp)
project(test_filamat)
set(TARGET test_filamat)
set(SRCS
tests/test_filamat.cpp
tests/test_argBufferFixup.cpp
tests/test_clipDistanceFixup.cpp
tests/test_includes.cpp)
add_executable(${TARGET} ${SRCS})
target_include_directories(${TARGET} PRIVATE src)
target_link_libraries(${TARGET} filamat gtest)
set_target_properties(${TARGET} PROPERTIES FOLDER Tests)
add_executable(${TARGET} ${SRCS})
target_include_directories(${TARGET} PRIVATE src)
target_link_libraries(${TARGET} filamat gtest)
set_target_properties(${TARGET} PROPERTIES FOLDER Tests)
endif()
+18 -28
View File
@@ -34,54 +34,44 @@
using namespace utils;
namespace {
constexpr uint8_t FAKE_DRY_RUNNER_START_ADDR = 0x1;
} // anonymous
namespace filamat {
class Flattener {
public:
explicit Flattener(uint8_t* dst) : mCursor(dst), mStart(dst){}
// DryRunner is used to compute the size of the flattened output but not actually carry out
// flattening. If we set mStart = nullptr and mEnd=nullptr, we would hit an error about
// offsetting on null when ubsan is enabled. Instead we point mStart to a fake address, and
// mCursor is offset from that.
static Flattener& getDryRunner() {
static Flattener dryRunner = Flattener(nullptr);
dryRunner.mStart = (uint8_t*) FAKE_DRY_RUNNER_START_ADDR;
dryRunner.mCursor = (uint8_t*) FAKE_DRY_RUNNER_START_ADDR;
dryRunner.mStart = nullptr;
dryRunner.mCursor = nullptr;
dryRunner.mOffsetPlaceholders.clear();
dryRunner.mSizePlaceholders.clear();
dryRunner.mValuePlaceholders.clear();
return dryRunner;
}
bool isDryRunner() {
return mStart == (uint8_t*) FAKE_DRY_RUNNER_START_ADDR;
}
bool isDryRunner() { return mStart == nullptr;}
size_t getBytesWritten() {
return mCursor - mStart;
}
void writeBool(bool b) {
if (!isDryRunner()) {
if (mStart != nullptr) {
mCursor[0] = static_cast<uint8_t>(b);
}
mCursor += 1;
}
void writeUint8(uint8_t i) {
if (!isDryRunner()) {
if (mStart != nullptr) {
mCursor[0] = i;
}
mCursor += 1;
}
void writeUint16(uint16_t i) {
if (!isDryRunner()) {
if (mStart != nullptr) {
mCursor[0] = static_cast<uint8_t>( i & 0xff);
mCursor[1] = static_cast<uint8_t>((i >> 8) & 0xff);
}
@@ -89,7 +79,7 @@ public:
}
void writeUint32(uint32_t i) {
if (!isDryRunner()) {
if (mStart != nullptr) {
mCursor[0] = static_cast<uint8_t>( i & 0xff);
mCursor[1] = static_cast<uint8_t>((i >> 8) & 0xff);
mCursor[2] = static_cast<uint8_t>((i >> 16) & 0xff);
@@ -99,7 +89,7 @@ public:
}
void writeUint64(uint64_t i) {
if (!isDryRunner()) {
if (mStart != nullptr) {
mCursor[0] = static_cast<uint8_t>( i & 0xff);
mCursor[1] = static_cast<uint8_t>((i >> 8) & 0xff);
mCursor[2] = static_cast<uint8_t>((i >> 16) & 0xff);
@@ -114,7 +104,7 @@ public:
void writeString(const char* str) {
size_t const len = strlen(str);
if (!isDryRunner()) {
if (mStart != nullptr) {
strcpy(reinterpret_cast<char*>(mCursor), str);
}
mCursor += len + 1;
@@ -122,7 +112,7 @@ public:
void writeString(std::string_view str) {
size_t const len = str.length();
if (!isDryRunner()) {
if (mStart != nullptr) {
memcpy(reinterpret_cast<char*>(mCursor), str.data(), len);
mCursor[len] = 0;
}
@@ -131,14 +121,14 @@ public:
void writeBlob(const char* blob, size_t nbytes) {
writeUint64(nbytes);
if (!isDryRunner()) {
if (mStart != nullptr) {
memcpy(reinterpret_cast<char*>(mCursor), blob, nbytes);
}
mCursor += nbytes;
}
void writeRaw(const char* raw, size_t nbytes) {
if (!isDryRunner()) {
if (mStart != nullptr) {
memcpy(reinterpret_cast<char*>(mCursor), raw, nbytes);
}
mCursor += nbytes;
@@ -146,7 +136,7 @@ public:
void writeSizePlaceholder() {
mSizePlaceholders.push_back(mCursor);
if (!isDryRunner()) {
if (mStart != nullptr) {
mCursor[0] = 0;
mCursor[1] = 0;
mCursor[2] = 0;
@@ -174,7 +164,7 @@ public:
mSizePlaceholders.pop_back();
// -4 to account for the 4 bytes we are about to write.
uint32_t const size = static_cast<uint32_t>(mCursor - dst - 4);
if (!isDryRunner()) {
if (mStart != nullptr) {
dst[0] = static_cast<uint8_t>( size & 0xff);
dst[1] = static_cast<uint8_t>((size >> 8) & 0xff);
dst[2] = static_cast<uint8_t>((size >> 16) & 0xff);
@@ -185,7 +175,7 @@ public:
void writeOffsetplaceholder(size_t index) {
mOffsetPlaceholders.insert(std::pair<size_t, uint8_t*>(index, mCursor));
if (!isDryRunner()) {
if (mStart != nullptr) {
mCursor[0] = 0x0;
mCursor[1] = 0x0;
mCursor[2] = 0x0;
@@ -195,7 +185,7 @@ public:
}
void writeOffsets(uint32_t forIndex) {
if (isDryRunner()) {
if (mStart == nullptr) {
return;
}
@@ -219,7 +209,7 @@ public:
void writeValuePlaceholder() {
mValuePlaceholders.push_back(mCursor);
if (!isDryRunner()) {
if (mStart != nullptr) {
mCursor[0] = 0;
mCursor[1] = 0;
mCursor[2] = 0;
@@ -238,7 +228,7 @@ public:
uint8_t* dst = mValuePlaceholders.back();
mValuePlaceholders.pop_back();
if (!isDryRunner()) {
if (mStart != nullptr) {
dst[0] = static_cast<uint8_t>( v & 0xff);
dst[1] = static_cast<uint8_t>((v >> 8) & 0xff);
dst[2] = static_cast<uint8_t>((v >> 16) & 0xff);
+28 -5
View File
@@ -295,12 +295,35 @@ utils::io::sstream& CodeGenerator::generateCommonProlog(utils::io::sstream& out,
generateSpecializationConstant(out, "BACKEND_FEATURE_LEVEL",
+ReservedSpecializationConstants::BACKEND_FEATURE_LEVEL, 1);
generateSpecializationConstant(out, "CONFIG_MAX_INSTANCES",
+ReservedSpecializationConstants::CONFIG_MAX_INSTANCES, (int)CONFIG_MAX_INSTANCES);
if (mTargetApi == TargetApi::VULKAN) {
// Note: This is a hack for a hack.
//
// Vulkan doesn't support sizing arrays within a block with specialization constants,
// as per this paragraph of the ARB_spir_v specification:
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_gl_spirv.txt
//
// Arrays inside a block may be sized with a specialization constant,
// but the block will have a static layout. Changing the specialized size will
// not re-layout the block. In the absence of explicit offsets, the layout will be
// based on the default size of the array.
//
// CONFIG_MAX_INSTANCES is only needed for WebGL, so we can replace it with a constant.
// CONFIG_FROXEL_BUFFER_HEIGHT can be hardcoded to 2048 because only 3% of Android devices
// only support 16KiB buffer or less (1024 lines).
//
// We *could* leave these as a specialization constant, but this triggers a crashing bug with
// some Adreno drivers on Android. see: https://github.com/google/filament/issues/6444
//
out << "const int CONFIG_MAX_INSTANCES = " << (int)CONFIG_MAX_INSTANCES << ";\n";
out << "const int CONFIG_FROXEL_BUFFER_HEIGHT = 2048;\n";
} else {
generateSpecializationConstant(out, "CONFIG_MAX_INSTANCES",
+ReservedSpecializationConstants::CONFIG_MAX_INSTANCES, (int)CONFIG_MAX_INSTANCES);
// the default of 1024 (16KiB) is needed for 32% of Android devices
generateSpecializationConstant(out, "CONFIG_FROXEL_BUFFER_HEIGHT",
+ReservedSpecializationConstants::CONFIG_FROXEL_BUFFER_HEIGHT, 1024);
// the default of 1024 (16KiB) is needed for 32% of Android devices
generateSpecializationConstant(out, "CONFIG_FROXEL_BUFFER_HEIGHT",
+ReservedSpecializationConstants::CONFIG_FROXEL_BUFFER_HEIGHT, 1024);
}
// directional shadowmap visualization
generateSpecializationConstant(out, "CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP",
+9 -2
View File
@@ -704,12 +704,19 @@ FilamentApp::Window::Window(FilamentApp* filamentApp,
::prepareNativeWindow(mWindow);
void* metalLayer = nullptr;
if (config.backend == filament::Engine::Backend::METAL || config.backend == filament::Engine::Backend::VULKAN) {
if (config.backend == filament::Engine::Backend::METAL) {
metalLayer = setUpMetalLayer(nativeWindow);
// The swap chain on both native Metal and MoltenVK is a CAMetalLayer.
// The swap chain on Metal is a CAMetalLayer.
nativeSwapChain = metalLayer;
}
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
if (config.backend == filament::Engine::Backend::VULKAN) {
// We request a Metal layer for rendering via MoltenVK.
setUpMetalLayer(nativeWindow);
}
#endif
#endif
// Write back the active feature level.
+2 -11
View File
@@ -988,7 +988,6 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive& inPrim, const char* na
vbb.enableBufferObjects();
bool hasUv0 = false, hasUv1 = false, hasVertexColor = false, hasNormals = false;
int8_t currentCustomIndex = -1;
uint32_t vertexCount = 0;
const size_t firstSlot = slots->size();
@@ -999,7 +998,6 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive& inPrim, const char* na
const int index = attribute.index;
const cgltf_attribute_type atype = attribute.type;
const cgltf_accessor* accessor = attribute.data;
int8_t customIndex = -1;
// The glTF tangent data is ignored here, but honored in ResourceLoader.
if (atype == cgltf_attribute_type_tangent) {
@@ -1017,19 +1015,12 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive& inPrim, const char* na
}
if (atype == cgltf_attribute_type_color) {
if (hasVertexColor) {
// We already had a vertex color before, we need to store this is as a custom
// attribute.
customIndex = ++currentCustomIndex;
} else {
hasVertexColor = true;
}
hasVertexColor = true;
}
// Translate the cgltf attribute enum into a Filament enum.
VertexAttribute semantic;
if (!getCustomVertexAttrType(customIndex, &semantic) &&
!getVertexAttrType(atype, &semantic)) {
if (!getVertexAttrType(atype, &semantic)) {
utils::slog.e << "Unrecognized vertex semantic in " << name << utils::io::endl;
return false;
}
-9
View File
@@ -98,15 +98,6 @@ inline bool getVertexAttrType(cgltf_attribute_type atype, filament::VertexAttrib
}
}
inline bool getCustomVertexAttrType(int8_t customIndex, filament::VertexAttribute* attrType) {
if (customIndex < 0) {
return false;
}
*attrType = static_cast<filament::VertexAttribute>(
customIndex + (uint8_t) filament::VertexAttribute::CUSTOM0);
return true;
}
inline bool getIndexType(cgltf_component_type ctype, filament::IndexBuffer::IndexType* itype) {
switch (ctype) {
case cgltf_component_type_r_8u:
+3
View File
@@ -449,6 +449,9 @@ bool ResourceLoader::loadResources(FFilamentAsset* asset, bool async) {
// that we need to generate the contents of a GPU buffer by processing one or more CPU
// buffer(s).
pImpl->computeTangents(asset);
std::get<FFilamentAsset::ResourceInfo>(asset->mResourceInfo).mBufferSlots.clear();
std::get<FFilamentAsset::ResourceInfo>(asset->mResourceInfo).mPrimitives.clear();
} else {
auto& slots = std::get<FFilamentAsset::ResourceInfoExtended>(asset->mResourceInfo).slots;
ResourceLoaderExtended::loadResources(slots, pImpl->mEngine, asset->mBufferObjects);
+3 -11
View File
@@ -222,13 +222,8 @@ bool ApiHandler::handleGetApiShader(struct mg_connection* conn,
}
void ApiHandler::addMaterial(MaterialRecord const* material) {
updateMaterial(material->key);
}
void ApiHandler::updateMaterial(uint32_t key) {
std::unique_lock const lock(mStatusMutex);
mCurrentStatus++;
snprintf(statusMaterialId, sizeof(statusMaterialId), "%8.8x", key);
snprintf(statusMaterialId, sizeof(statusMaterialId), "%8.8x", material->key);
mStatusCondition.notify_all();
}
@@ -293,11 +288,8 @@ bool ApiHandler::handlePost(CivetServer* server, struct mg_connection* conn) {
sstream >> std::hex >> matid >> std::dec >> api >> shaderIndex;
std::string const shader = sstream.str().substr(sstream.tellg());
if (!mServer->handleEditCommand(matid, backend::Backend(api), shaderIndex, shader.c_str(),
shader.size())) {
return error(__LINE__, uri);
}
updateMaterial(matid);
mServer->handleEditCommand(matid, backend::Backend(api), shaderIndex, shader.c_str(),
shader.size());
mg_printf(conn, "HTTP/1.1 200 OK\r\nConnection: close");
return true;
+1 -3
View File
@@ -55,8 +55,6 @@ private:
bool handleGetStatus(struct mg_connection* conn, struct mg_request_info const* request);
MaterialRecord const* getMaterialRecord(struct mg_request_info const* request);
void updateMaterial(uint32_t key);
DebugServer* mServer;
std::mutex mStatusMutex;
@@ -66,7 +64,7 @@ private:
// This variable is to implement a *hanging* effect for /api/status. The call to /api/status
// will always block until statusMaterialId is updated again. The client is expected to keep
// calling /api/status (a constant "pull" to simulate a push).
uint64_t mCurrentStatus = 0;
std::atomic<uint64_t> mCurrentStatus = 0;
SourceFormatter mFormatter;
};
+1 -25
View File
@@ -25,7 +25,6 @@
#include <utils/Log.h>
#include <algorithm>
#include <sstream>
#include <GlslangToSpv.h>
@@ -51,30 +50,10 @@ using namespace glslang;
using namespace utils;
using std::ostream;
using std::streampos;
using std::stringstream;
using std::streampos;
using std::vector;
namespace {
// This is to ensure that the edited material package have the same order of shaders as the output
// of the original material. We use the ordering in the front-end to simplify logic.
template<typename RecordType>
void sortRecords(std::vector<RecordType>& records) {
std::sort(records.begin(), records.end(), [](RecordType const& a, RecordType const& b) -> bool {
if (a.shaderModel != b.shaderModel) {
return a.shaderModel < b.shaderModel;
}
if (a.variant.key != b.variant.key) {
return a.variant.key < b.variant.key;
}
return a.stage < b.stage;
});
}
} // anonymous
// Tiny database of shader text that can import / export MaterialTextChunk and DictionaryTextChunk.
class ShaderIndex {
public:
@@ -365,7 +344,6 @@ void ShaderIndex::writeChunks(ostream& stream) {
for (const auto& record : mShaderRecords) {
lines.addText(record.shader);
}
sortRecords(mShaderRecords);
filamat::ChunkContainer cc;
const auto& dchunk = cc.push<DictionaryTextChunk>(std::move(lines), mDictTag);
@@ -428,8 +406,6 @@ void BlobIndex::writeChunks(ostream& stream) {
}
};
sortRecords(mShaderRecords);
// Apply SMOL-V compression and write out the results.
filamat::ChunkContainer cc;
cc.push<MaterialBinaryChunk>(std::move(mShaderRecords), ChunkType::MaterialSpirv);
+1 -1
View File
@@ -34,7 +34,7 @@ std::string SourceFormatter::format(char const* source) {
while (fgets(output, 1024, fp) != NULL) {}
int status = pclose(fp);
if (!fp || WEXITSTATUS(status)) {
if (!fp || !WEXITSTATUS(status)) {
std::call_once(mClangWarningFlag, []() {
utils::slog.w << "[matdbg] unable to run clang-format to format shader file. "
<< "Please make sure it's installed." << utils::io::endl;
+8 -2
View File
@@ -205,11 +205,17 @@ void setup_window(Window& w, Engine* engine) {
void* nativeSwapChain = nativeWindow;
#if defined(__APPLE__)
void* metalLayer = nullptr;
if (kBackend == filament::Engine::Backend::METAL || kBackend == filament::Engine::Backend::VULKAN) {
if (kBackend == filament::Engine::Backend::METAL) {
metalLayer = setUpMetalLayer(nativeWindow);
// The swap chain on both native Metal and MoltenVK is a CAMetalLayer.
// The swap chain on Metal is a CAMetalLayer.
nativeSwapChain = metalLayer;
}
#if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN)
if (kBackend == filament::Engine::Backend::VULKAN) {
// We request a Metal layer for rendering via MoltenVK.
setUpMetalLayer(nativeWindow);
}
#endif
#endif
w.swapChain = engine->createSwapChain(nativeSwapChain);
+14 -25
View File
@@ -1,27 +1,3 @@
//------------------------------------------------------------------------------
// Common Helpers
//------------------------------------------------------------------------------
/**
* Index of the eye being rendered, starting at 0.
* @public-api
*/
int getEyeIndex() {
#if defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_INSTANCED)
return instance_index % CONFIG_STEREO_EYE_COUNT;
#elif defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_MULTIVIEW)
# if defined(TARGET_VULKAN_ENVIRONMENT)
return int(gl_ViewIndex);
# else
// gl_ViewID_OVR is of uint type, which needs an explicit conversion.
return int(gl_ViewID_OVR);
# endif // TARGET_VULKAN_ENVIRONMENT
#endif
return 0;
}
//------------------------------------------------------------------------------
// Uniforms access
//------------------------------------------------------------------------------
@@ -48,7 +24,20 @@ highp mat4 getViewFromClipMatrix() {
/** @public-api */
highp mat4 getClipFromWorldMatrix() {
return frameUniforms.clipFromWorldMatrix[getEyeIndex()];
#if defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_INSTANCED)
int eye = instance_index % CONFIG_STEREO_EYE_COUNT;
return frameUniforms.clipFromWorldMatrix[eye];
#elif defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_MULTIVIEW)
#if defined(TARGET_VULKAN_ENVIRONMENT)
return frameUniforms.clipFromWorldMatrix[gl_ViewIndex];
#else
return frameUniforms.clipFromWorldMatrix[gl_ViewID_OVR];
#endif // TARGET_VULKAN_ENVIRONMENT
#else
return frameUniforms.clipFromWorldMatrix[0];
#endif
}
/** @public-api */
+2 -2
View File
@@ -65,7 +65,7 @@ float vmax(const vec3 v) {
}
float vmax(const vec4 v) {
return max(max(v.x, v.y), max(v.z, v.w));
return max(max(v.x, v.y), max(v.y, v.z));
}
/**
@@ -86,7 +86,7 @@ float vmin(const vec3 v) {
}
float vmin(const vec4 v) {
return min(min(v.x, v.y), min(v.z, v.w));
return min(min(v.x, v.y), min(v.y, v.z));
}
//------------------------------------------------------------------------------
+21 -1
View File
@@ -235,7 +235,7 @@ vec4 getCustom7() { return mesh_custom7; }
#endif
//------------------------------------------------------------------------------
// Surface Helpers
// Helpers
//------------------------------------------------------------------------------
/**
@@ -275,3 +275,23 @@ vec4 computeWorldPosition() {
#error Unknown Vertex Domain
#endif
}
/**
* Index of the eye being rendered, starting at 0.
* @public-api
*/
int getEyeIndex() {
#if defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_INSTANCED)
return instance_index % CONFIG_STEREO_EYE_COUNT;
#elif defined(VARIANT_HAS_STEREO) && defined(FILAMENT_STEREO_MULTIVIEW)
#if defined(TARGET_VULKAN_ENVIRONMENT)
return int(gl_ViewIndex);
#else
// gl_ViewID_OVR is of uint type, which needs an explicit conversion.
return int(gl_ViewID_OVR);
#endif // TARGET_VULKAN_ENVIRONMENT
#endif
return 0;
}
+1 -10
View File
@@ -661,7 +661,6 @@ class_<View>("View")
.function("getViewport", &View::getViewport)
.function("setVisibleLayers", &View::setVisibleLayers)
.function("setPostProcessingEnabled", &View::setPostProcessingEnabled)
.function("setDithering", &View::setDithering)
.function("_setAmbientOcclusionOptions", &View::setAmbientOcclusionOptions)
.function("_setDepthOfFieldOptions", &View::setDepthOfFieldOptions)
.function("_setMultiSampleAntiAliasingOptions", &View::setMultiSampleAntiAliasingOptions)
@@ -1392,14 +1391,8 @@ class_<MaterialInstance>("MaterialInstance")
.function("isDoubleSided", &MaterialInstance::isDoubleSided)
.function("setTransparencyMode", &MaterialInstance::setTransparencyMode)
.function("getTransparencyMode", &MaterialInstance::getTransparencyMode)
.function("setCullingMode", EMBIND_LAMBDA(void,
(MaterialInstance* self, MaterialInstance::CullingMode mode), {
self->setCullingMode(mode); }), allow_raw_pointers())
.function("setCullingModeSeparate", EMBIND_LAMBDA(void,
(MaterialInstance* self, MaterialInstance::CullingMode color, MaterialInstance::CullingMode shadows), {
self->setCullingMode(color, shadows); }), allow_raw_pointers())
.function("setCullingMode", &MaterialInstance::setCullingMode)
.function("getCullingMode", &MaterialInstance::getCullingMode)
.function("getShadowCullingMode", &MaterialInstance::getShadowCullingMode)
.function("setColorWrite", &MaterialInstance::setColorWrite)
.function("isColorWriteEnabled", &MaterialInstance::isColorWriteEnabled)
.function("setDepthWrite", &MaterialInstance::setDepthWrite)
@@ -1453,8 +1446,6 @@ class_<TextureSampler>("TextureSampler")
/// Texture ::core class:: 2D image or cubemap that can be sampled by the GPU, possibly mipmapped.
class_<Texture>("Texture")
.class_function("Builder", (TexBuilder (*)()) [] { return TexBuilder(); })
.class_function("isTextureFormatMipmappable", &Texture::isTextureFormatMipmappable)
.class_function("validatePixelFormatAndType", &Texture::validatePixelFormatAndType)
.function("generateMipmaps", &Texture::generateMipmaps)
.function("_setImage", EMBIND_LAMBDA(void, (Texture* self,
Engine* engine, uint8_t level, PixelBufferDescriptor pbd), {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.57.2",
"version": "1.56.7",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",