Compare commits

..
Author SHA1 Message Date
Mathias Agopian 0404be5127 new LinearAllocatorWithFallback
LinearAllocatorWithFallback is a linear allocator that can fall back
to the heap allocator. We use it for the high level command buffer to
avoid crashing when running out of memory.

FIXES=[277115740]
2024-02-21 14:52:04 -08:00
Mathias Agopian a784fb7d8b Rework RenderPass to improve allocations and API
RenderPass now is a fully immutable object that gets constructed with a
RenderPassBuilder. RenderPassBuilder can be passed around and doesn't
do any (major) allocations.

All RenderPass allocations and heavy lifting is done in 
RenderPassBuilder::Build().

Additionally, RenderPass cannot be copied anymore.

Where allocations happen is now much clearer.
2024-02-21 14:51:57 -08:00
Mathias Agopian 8459cbc91c Fix a couple threading vs. allocations
- prepareVisibleLights was run on a dedicated thread (via JobSystem), 
  but was using its own local ArenaScope. This is wrong because it
  could reset the root arena at any later point. This is fixed by
  just not using a local ArenaScope.

- related to the above, the root Arena (LinearAllocatorArena) didn't
  use a locked policy, which cause also cause problems since some
  allocations are done off the main thread. We now pre-allocate the one
  buffer we need.

This PR also renames some variable and types to improve readability.
2024-02-21 14:51:51 -08:00
Mathias Agopian 344c04a2d8 clenaup CircularBuffer implementation and API
Also fix a bug in DEBUG mode that could corrupt the CircularBuffer, it
was due to a wrong debugging code attempting to clear the unused
area of the buffer (this was wrong because in "ashmem" mode, there are
no guaranteed unused areas).
2024-02-21 14:51:44 -08:00
Mathias Agopian 55e2691f3f Automatically flush CommandStream
When generating commands, we now automatically flush the CommandStream,
so that we're guaranteed to not overrun the circular buffer.
2024-02-21 14:51:36 -08:00
114 changed files with 1158 additions and 2620 deletions
+1 -1
View File
@@ -129,7 +129,7 @@ jobs:
- name: Sign sample-gltf-viewer
run: |
echo "${APK_KEYSTORE_BASE64}" > filament.jks.base64
base64 --decode -i filament.jks.base64 > filament.jks
base64 --decode filament.jks.base64 > filament.jks
BUILD_TOOLS_VERSION=$(ls ${ANDROID_HOME}/build-tools | sort -V | tail -n 1)
APKSIGNER=${ANDROID_HOME}/build-tools/${BUILD_TOOLS_VERSION}/apksigner
IN_FILE="out/sample-gltf-viewer-release.apk"
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.50.5'
implementation 'com.google.android.filament:filament-android:1.50.2'
}
```
@@ -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.50.5'
pod 'Filament', '~> 1.50.2'
```
### Snapshots
-10
View File
@@ -7,16 +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.50.5
- android: NDK 26.1.10909125 is used by default
- android: Minimum API level on Android is now API 21 instead of API 19. This allows the use of OpenGL ES 3.1
- rendering: New PBR Neutral tone mapper, designed to preserve materials color appearance
- android: Change default frameRateOptions.interval to 1.0
## v1.50.4
## v1.50.3
+1 -1
View File
@@ -80,7 +80,7 @@ buildscript {
ext.versions = [
'jdk': 17,
'minSdk': 21,
'minSdk': 19,
'targetSdk': 34,
'compileSdk': 34,
'kotlin': '1.9.21',
@@ -484,9 +484,7 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderConfig(JNIEnv*,
jclass, jlong nativeBuilder, jlong commandBufferSizeMB, jlong perRenderPassArenaSizeMB,
jlong driverHandleArenaSizeMB, jlong minCommandBufferSizeMB, jlong perFrameCommandsSizeMB,
jlong jobSystemThreadCount,
jlong textureUseAfterFreePoolSize, jboolean disableParallelShaderCompile,
jint stereoscopicType, jlong stereoscopicEyeCount,
jlong jobSystemThreadCount, jlong stereoscopicEyeCount,
jlong resourceAllocatorCacheSizeMB, jlong resourceAllocatorCacheMaxAge) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
Engine::Config config = {
@@ -496,9 +494,6 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
.minCommandBufferSizeMB = (uint32_t) minCommandBufferSizeMB,
.perFrameCommandsSizeMB = (uint32_t) perFrameCommandsSizeMB,
.jobSystemThreadCount = (uint32_t) jobSystemThreadCount,
.textureUseAfterFreePoolSize = (uint32_t) textureUseAfterFreePoolSize,
.disableParallelShaderCompile = (bool) disableParallelShaderCompile,
.stereoscopicType = (Engine::StereoscopicType) stereoscopicType,
.stereoscopicEyeCount = (uint8_t) stereoscopicEyeCount,
.resourceAllocatorCacheSizeMB = (uint32_t) resourceAllocatorCacheSizeMB,
.resourceAllocatorCacheMaxAge = (uint8_t) resourceAllocatorCacheMaxAge,
@@ -47,11 +47,6 @@ Java_com_google_android_filament_ToneMapper_nCreateFilmicToneMapper(JNIEnv*, jcl
return (jlong) new FilmicToneMapper();
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_ToneMapper_nCreatePBRNeutralToneMapper(JNIEnv*, jclass) {
return (jlong) new PBRNeutralToneMapper();
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_ToneMapper_nCreateAgxToneMapper(JNIEnv*, jclass, jint look) {
return (jlong) new AgxToneMapper(AgxToneMapper::AgxLook(look));
@@ -158,16 +158,6 @@ public class Engine {
FEATURE_LEVEL_3,
};
/**
* The type of technique for stereoscopic rendering
*/
public enum StereoscopicType {
/** Stereoscopic rendering is performed using instanced rendering technique. */
INSTANCED,
/** Stereoscopic rendering is performed using the multiview feature from the graphics backend. */
MULTIVIEW,
};
/**
* Constructs <code>Engine</code> objects using a builder pattern.
*/
@@ -221,9 +211,7 @@ public class Engine {
nSetBuilderConfig(mNativeBuilder, config.commandBufferSizeMB,
config.perRenderPassArenaSizeMB, config.driverHandleArenaSizeMB,
config.minCommandBufferSizeMB, config.perFrameCommandsSizeMB,
config.jobSystemThreadCount,
config.textureUseAfterFreePoolSize, config.disableParallelShaderCompile,
config.stereoscopicType.ordinal(), config.stereoscopicEyeCount,
config.jobSystemThreadCount, config.stereoscopicEyeCount,
config.resourceAllocatorCacheSizeMB, config.resourceAllocatorCacheMaxAge);
return this;
}
@@ -361,35 +349,6 @@ public class Engine {
*/
public long jobSystemThreadCount = 0;
/**
* Number of most-recently destroyed textures to track for use-after-free.
*
* This will cause the backend to throw an exception when a texture is freed but still bound
* to a SamplerGroup and used in a draw call. 0 disables completely.
*
* Currently only respected by the Metal backend.
*/
public long textureUseAfterFreePoolSize = 0;
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL backend.
*/
public boolean disableParallelShaderCompile = false;
/**
* The type of technique for stereoscopic rendering.
*
* This setting determines the algorithm used when stereoscopic rendering is enabled. This
* decision applies to the entire Engine for the lifetime of the Engine. E.g., multiple
* Views created from the Engine must use the same stereoscopic type.
*
* Each view can enable stereoscopic rendering via the StereoscopicOptions::enable flag.
*
* @see View#setStereoscopicOptions
*/
public StereoscopicType stereoscopicType = StereoscopicType.INSTANCED;
/**
* The number of eyes to render when stereoscopic rendering is enabled. Supported values are
* between 1 and Engine#getMaxStereoscopicEyes() (inclusive).
@@ -1281,8 +1240,7 @@ public class Engine {
private static native void nSetBuilderConfig(long nativeBuilder, long commandBufferSizeMB,
long perRenderPassArenaSizeMB, long driverHandleArenaSizeMB,
long minCommandBufferSizeMB, long perFrameCommandsSizeMB, long jobSystemThreadCount,
long textureUseAfterFreePoolSize, boolean disableParallelShaderCompile,
int stereoscopicType, long stereoscopicEyeCount,
long stereoscopicEyeCount,
long resourceAllocatorCacheSizeMB, long resourceAllocatorCacheMaxAge);
private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal);
private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext);
@@ -101,7 +101,7 @@ public class Renderer {
/**
* Desired frame interval in unit of 1 / DisplayInfo.refreshRate.
*/
public float interval = 1.0f;
public float interval = 1.0f / 60.0f;
/**
* Additional headroom for the GPU as a ratio of the targetFrameTime.
@@ -16,14 +16,12 @@ package com.google.android.filament;
* <li>Configurable tone mapping operators</li>
* <ul>
* <li>GenericToneMapper</li>
* <li>AgXToneMapper</li>
* </ul>
* <li>Fixed-aesthetic tone mapping operators</li>
* <ul>
* <li>ACESToneMapper</li>
* <li>ACESLegacyToneMapper</li>
* <li>FilmicToneMapper</li>
* <li>PBRNeutralToneMapper</li>
* </ul>
* <li>Debug/validation tone mapping operators</li>
* <ul>
@@ -102,21 +100,11 @@ public class ToneMapper {
}
}
/**
* Khronos PBR Neutral tone mapping operator. This tone mapper was designed
* to preserve the appearance of materials across lighting conditions while
* avoiding artifacts in the highlights in high dynamic range conditions.
*/
public static class PBRNeutralToneMapper extends ToneMapper {
public PBRNeutralToneMapper() {
super(nCreatePBRNeutralToneMapper());
}
}
/**
* AgX tone mapping operator.
*/
public static class Agx extends ToneMapper {
public enum AgxLook {
/**
* Base contrast with no look applied
@@ -245,7 +233,6 @@ public class ToneMapper {
private static native long nCreateACESToneMapper();
private static native long nCreateACESLegacyToneMapper();
private static native long nCreateFilmicToneMapper();
private static native long nCreatePBRNeutralToneMapper();
private static native long nCreateAgxToneMapper(int look);
private static native long nCreateGenericToneMapper(
float contrast, float midGrayIn, float midGrayOut, float hdrMax);
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.50.5
VERSION_NAME=1.50.2
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
@@ -30,7 +30,7 @@ android {
compileSdkVersion versions.compileSdk
defaultConfig {
applicationId "com.google.android.filament.gltf"
minSdkVersion versions.minSdk
minSdkVersion 19
targetSdkVersion versions.targetSdk
}
+1 -1
View File
@@ -1 +1 @@
26.1.10909125
25.1.8937393
+1 -1
View File
@@ -21,7 +21,7 @@ set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
# android
set(API_LEVEL 21)
set(API_LEVEL 19)
# architecture
set(ARCH armv7a-linux-androideabi)
+1 -1
View File
@@ -21,7 +21,7 @@ set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
# android
set(API_LEVEL 21)
set(API_LEVEL 19)
# architecture
set(ARCH i686-linux-android)
-4
View File
@@ -66,7 +66,6 @@ set(SRCS
src/Froxelizer.cpp
src/Frustum.cpp
src/HwRenderPrimitiveFactory.cpp
src/HwVertexBufferInfoFactory.cpp
src/IndexBuffer.cpp
src/IndirectLight.cpp
src/InstanceBuffer.cpp
@@ -138,7 +137,6 @@ set(SRCS
set(PRIVATE_HDRS
src/Allocators.h
src/Bimap.h
src/BufferPoolAllocator.h
src/ColorSpaceUtils.h
src/Culler.h
@@ -149,7 +147,6 @@ set(PRIVATE_HDRS
src/FrameSkipper.h
src/Froxelizer.h
src/HwRenderPrimitiveFactory.h
src/HwVertexBufferInfoFactory.h
src/Intersections.h
src/MaterialParser.h
src/PerViewUniforms.h
@@ -215,7 +212,6 @@ set(MATERIAL_SRCS
src/materials/antiAliasing/fxaa.mat
src/materials/antiAliasing/taa.mat
src/materials/blitLow.mat
src/materials/blitArray.mat
src/materials/bloom/bloomDownsample.mat
src/materials/bloom/bloomDownsample2x.mat
src/materials/bloom/bloomDownsample9.mat
@@ -1212,14 +1212,6 @@ enum class Workaround : uint16_t {
DISABLE_THREAD_AFFINITY
};
//! The type of technique for stereoscopic rendering
enum class StereoscopicType : uint8_t {
// Stereoscopic rendering is performed using instanced rendering technique.
INSTANCED,
// Stereoscopic rendering is performed using the multiview feature from the graphics backend.
MULTIVIEW,
};
} // namespace filament::backend
template<> struct utils::EnableBitMaskOperators<filament::backend::ShaderStageFlags>
+20 -22
View File
@@ -39,7 +39,6 @@ struct HwStream;
struct HwSwapChain;
struct HwTexture;
struct HwTimerQuery;
struct HwVertexBufferInfo;
struct HwVertexBuffer;
/*
@@ -63,6 +62,14 @@ public:
// clear the handle, this doesn't free associated resources
void clear() noexcept { object = nullid; }
// compare handles
bool operator==(const HandleBase& rhs) const noexcept { return object == rhs.object; }
bool operator!=(const HandleBase& rhs) const noexcept { return object != rhs.object; }
bool operator<(const HandleBase& rhs) const noexcept { return object < rhs.object; }
bool operator<=(const HandleBase& rhs) const noexcept { return object <= rhs.object; }
bool operator>(const HandleBase& rhs) const noexcept { return object > rhs.object; }
bool operator>=(const HandleBase& rhs) const noexcept { return object >= rhs.object; }
// get this handle's handleId
HandleId getId() const noexcept { return object; }
@@ -94,14 +101,6 @@ struct Handle : public HandleBase {
explicit Handle(HandleId id) noexcept : HandleBase(id) { }
// compare handles of the same type
bool operator==(const Handle& rhs) const noexcept { return getId() == rhs.getId(); }
bool operator!=(const Handle& rhs) const noexcept { return getId() != rhs.getId(); }
bool operator<(const Handle& rhs) const noexcept { return getId() < rhs.getId(); }
bool operator<=(const Handle& rhs) const noexcept { return getId() <= rhs.getId(); }
bool operator>(const Handle& rhs) const noexcept { return getId() > rhs.getId(); }
bool operator>=(const Handle& rhs) const noexcept { return getId() >= rhs.getId(); }
// type-safe Handle cast
template<typename B, typename = std::enable_if_t<std::is_base_of<T, B>::value> >
Handle(Handle<B> const& base) noexcept : HandleBase(base) { } // NOLINT(hicpp-explicit-conversions,google-explicit-constructor)
@@ -115,19 +114,18 @@ private:
// Types used by the command stream
// (we use this renaming because the macro-system doesn't deal well with "<" and ">")
using BufferObjectHandle = Handle<HwBufferObject>;
using FenceHandle = Handle<HwFence>;
using IndexBufferHandle = Handle<HwIndexBuffer>;
using ProgramHandle = Handle<HwProgram>;
using RenderPrimitiveHandle = Handle<HwRenderPrimitive>;
using RenderTargetHandle = Handle<HwRenderTarget>;
using SamplerGroupHandle = Handle<HwSamplerGroup>;
using StreamHandle = Handle<HwStream>;
using SwapChainHandle = Handle<HwSwapChain>;
using TextureHandle = Handle<HwTexture>;
using TimerQueryHandle = Handle<HwTimerQuery>;
using VertexBufferHandle = Handle<HwVertexBuffer>;
using VertexBufferInfoHandle = Handle<HwVertexBufferInfo>;
using BufferObjectHandle = Handle<HwBufferObject>;
using FenceHandle = Handle<HwFence>;
using IndexBufferHandle = Handle<HwIndexBuffer>;
using ProgramHandle = Handle<HwProgram>;
using RenderPrimitiveHandle = Handle<HwRenderPrimitive>;
using RenderTargetHandle = Handle<HwRenderTarget>;
using SamplerGroupHandle = Handle<HwSamplerGroup>;
using StreamHandle = Handle<HwStream>;
using SwapChainHandle = Handle<HwSwapChain>;
using TextureHandle = Handle<HwTexture>;
using TimerQueryHandle = Handle<HwTimerQuery>;
using VertexBufferHandle = Handle<HwVertexBuffer>;
} // namespace filament::backend
@@ -33,6 +33,7 @@ struct PipelineState {
RasterState rasterState;
StencilState stencilState;
PolygonOffset polygonOffset;
Viewport scissor{ 0, 0, (uint32_t)INT32_MAX, (uint32_t)INT32_MAX };
};
} // namespace filament::backend
@@ -151,13 +151,6 @@ protected:
} egl;
} ext;
struct SwapChainEGL : public Platform::SwapChain {
EGLSurface sur = EGL_NO_SURFACE;
Config attribs{};
EGLNativeWindowType nativeWindow{};
EGLConfig config{};
};
void initializeGlExtensions() noexcept;
protected:
@@ -167,14 +167,11 @@ DECL_DRIVER_API_0(resetState)
* -----------------------
*/
DECL_DRIVER_API_R_N(backend::VertexBufferInfoHandle, createVertexBufferInfo,
DECL_DRIVER_API_R_N(backend::VertexBufferHandle, createVertexBuffer,
uint8_t, bufferCount,
uint8_t, attributeCount,
backend::AttributeArray, attributes)
DECL_DRIVER_API_R_N(backend::VertexBufferHandle, createVertexBuffer,
uint32_t, vertexCount,
backend::VertexBufferInfoHandle, vbih)
backend::AttributeArray, attributes)
DECL_DRIVER_API_R_N(backend::IndexBufferHandle, createIndexBuffer,
backend::ElementType, elementType,
@@ -227,7 +224,11 @@ DECL_DRIVER_API_R_N(backend::SamplerGroupHandle, createSamplerGroup,
DECL_DRIVER_API_R_N(backend::RenderPrimitiveHandle, createRenderPrimitive,
backend::VertexBufferHandle, vbh,
backend::IndexBufferHandle, ibh,
backend::PrimitiveType, pt)
backend::PrimitiveType, pt,
uint32_t, offset,
uint32_t, minIndex,
uint32_t, maxIndex,
uint32_t, count)
DECL_DRIVER_API_R_N(backend::ProgramHandle, createProgram,
backend::Program&&, program)
@@ -263,7 +264,6 @@ DECL_DRIVER_API_R_0(backend::TimerQueryHandle, createTimerQuery)
*/
DECL_DRIVER_API_N(destroyVertexBuffer, backend::VertexBufferHandle, vbh)
DECL_DRIVER_API_N(destroyVertexBufferInfo,backend::VertexBufferInfoHandle, vbih)
DECL_DRIVER_API_N(destroyIndexBuffer, backend::IndexBufferHandle, ibh)
DECL_DRIVER_API_N(destroyBufferObject, backend::BufferObjectHandle, ibh)
DECL_DRIVER_API_N(destroyRenderPrimitive, backend::RenderPrimitiveHandle, rph)
@@ -298,7 +298,7 @@ DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameBufferFetchMultiSampleSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isFrameTimeSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isAutoDepthResolveSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isSRGBSwapChainSupported)
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isStereoSupported, backend::StereoscopicType, stereoscopicType)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isStereoSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthStencilResolveSupported)
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
@@ -494,17 +494,12 @@ DECL_DRIVER_API_N(blit,
DECL_DRIVER_API_N(draw,
backend::PipelineState, state,
backend::RenderPrimitiveHandle, rph,
uint32_t, indexOffset,
uint32_t, indexCount,
uint32_t, instanceCount)
DECL_DRIVER_API_N(dispatchCompute,
backend::ProgramHandle, program,
math::uint3, workGroupCount)
DECL_DRIVER_API_N(scissor,
Viewport, scissor)
#pragma clang diagnostic pop
@@ -24,31 +24,35 @@
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <tsl/robin_map.h>
#include <cstddef>
#include <exception>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <stddef.h>
#include <stdint.h>
#define HandleAllocatorGL HandleAllocator<24, 64, 136> // ~3640 / pool / MiB
#define HandleAllocatorVK HandleAllocator<80, 176, 320> // ~1820 / pool / MiB
#define HandleAllocatorMTL HandleAllocator<48, 160, 592> // ~1310 / pool / MiB
#if !defined(NDEBUG) && UTILS_HAS_RTTI
# define HANDLE_TYPE_SAFETY 1
#else
# define HANDLE_TYPE_SAFETY 0
#endif
#define HandleAllocatorGL HandleAllocator<16, 64, 208>
#define HandleAllocatorVK HandleAllocator<16, 64, 880>
#define HandleAllocatorMTL HandleAllocator<16, 64, 584>
namespace filament::backend {
/*
* A utility class to efficiently allocate and manage Handle<>
*/
template<size_t P0, size_t P1, size_t P2>
template <size_t P0, size_t P1, size_t P2>
class HandleAllocator {
public:
HandleAllocator(const char* name, size_t size) noexcept;
HandleAllocator(HandleAllocator const& rhs) = delete;
HandleAllocator& operator=(HandleAllocator const& rhs) = delete;
@@ -66,9 +70,14 @@ public:
*/
template<typename D, typename ... ARGS>
Handle<D> allocateAndConstruct(ARGS&& ... args) noexcept {
Handle<D> h{ allocateHandle<D>() };
Handle<D> h{ allocateHandle<sizeof(D)>() };
D* addr = handle_cast<D*>(h);
new(addr) D(std::forward<ARGS>(args)...);
#if HANDLE_TYPE_SAFETY
mLock.lock();
mHandleTypeId[addr] = typeid(D).name();
mLock.unlock();
#endif
return h;
}
@@ -84,7 +93,13 @@ public:
*/
template<typename D>
Handle<D> allocate() noexcept {
Handle<D> h{ allocateHandle<D>() };
Handle<D> h{ allocateHandle<sizeof(D)>() };
#if HANDLE_TYPE_SAFETY
D* addr = handle_cast<D*>(h);
mLock.lock();
mHandleTypeId[addr] = typeid(D).name();
mLock.unlock();
#endif
return h;
}
@@ -101,10 +116,17 @@ public:
assert_invariant(handle);
D* addr = handle_cast<D*>(const_cast<Handle<B>&>(handle));
assert_invariant(addr);
// currently we implement construct<> with dtor+ctor, we could use operator= also
// but all our dtors are trivial, ~D() is actually a noop.
addr->~D();
new(addr) D(std::forward<ARGS>(args)...);
#if HANDLE_TYPE_SAFETY
mLock.lock();
mHandleTypeId[addr] = typeid(D).name();
mLock.unlock();
#endif
return addr;
}
@@ -121,6 +143,12 @@ public:
D* addr = handle_cast<D*>(const_cast<Handle<B>&>(handle));
assert_invariant(addr);
new(addr) D(std::forward<ARGS>(args)...);
#if HANDLE_TYPE_SAFETY
mLock.lock();
mHandleTypeId[addr] = typeid(D).name();
mLock.unlock();
#endif
return addr;
}
@@ -136,8 +164,19 @@ public:
void deallocate(Handle<B>& handle, D const* p) noexcept {
// allow to destroy the nullptr, similarly to operator delete
if (p) {
#if HANDLE_TYPE_SAFETY
mLock.lock();
auto typeId = mHandleTypeId[p];
mHandleTypeId.erase(p);
mLock.unlock();
if (UTILS_UNLIKELY(typeId != typeid(D).name())) {
utils::slog.e << "Destroying handle " << handle.getId() << ", type " << typeid(D).name()
<< ", but handle's actual type is " << typeId << utils::io::endl;
std::terminate();
}
#endif
p->~D();
deallocateHandle<D>(handle.getId());
deallocateHandle<sizeof(D)>(handle.getId());
}
}
@@ -165,17 +204,7 @@ public:
std::is_base_of_v<B, typename std::remove_pointer_t<Dp>>, Dp>
handle_cast(Handle<B>& handle) noexcept {
assert_invariant(handle);
auto [p, tag] = handleToPointer(handle.getId());
if (isPoolHandle(handle.getId())) {
// check for use after free
uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
auto const pNode = static_cast<typename Allocator::Node*>(p);
uint8_t const expectedAge = pNode[-1].age;
ASSERT_POSTCONDITION(expectedAge == age,
"use-after-free of Handle with id=%d", handle.getId());
}
void* const p = handleToPointer(handle.getId());
return static_cast<Dp>(p);
}
@@ -190,57 +219,29 @@ public:
private:
template<typename D>
static constexpr size_t getBucketSize() noexcept {
if constexpr (sizeof(D) <= P0) { return P0; }
if constexpr (sizeof(D) <= P1) { return P1; }
static_assert(sizeof(D) <= P2);
return P2;
}
// template <int P0, int P1, int P2>
class Allocator {
friend class HandleAllocator;
static constexpr size_t MIN_ALIGNMENT = alignof(std::max_align_t);
struct Node { uint8_t age; };
// Note: using the `extra` parameter of PoolAllocator<>, even with a 1-byte structure,
// generally increases all pool allocations by 8-bytes because of alignment restrictions.
template<size_t SIZE>
using Pool = utils::PoolAllocator<SIZE, MIN_ALIGNMENT, sizeof(Node)>;
Pool<P0> mPool0;
Pool<P1> mPool1;
Pool<P2> mPool2;
utils::PoolAllocator<P0, 16> mPool0;
utils::PoolAllocator<P1, 16> mPool1;
utils::PoolAllocator<P2, 16> mPool2;
UTILS_UNUSED_IN_RELEASE const utils::AreaPolicy::HeapArea& mArea;
public:
static constexpr size_t MIN_ALIGNMENT_SHIFT = 4;
explicit Allocator(const utils::AreaPolicy::HeapArea& area);
static constexpr size_t getAlignment() noexcept { return MIN_ALIGNMENT; }
// this is in fact always called with a constexpr size argument
[[nodiscard]] inline void* alloc(size_t size, size_t, size_t, uint8_t* outAge) noexcept {
[[nodiscard]] inline void* alloc(size_t size, size_t, size_t extra = 0) noexcept {
void* p = nullptr;
if (size <= mPool0.getSize()) p = mPool0.alloc(size);
else if (size <= mPool1.getSize()) p = mPool1.alloc(size);
else if (size <= mPool2.getSize()) p = mPool2.alloc(size);
if (UTILS_LIKELY(p)) {
Node const* const pNode = static_cast<Node const*>(p);
// we are guaranteed to have at least sizeof<Node> bytes of extra storage before
// the allocation address.
*outAge = pNode[-1].age;
}
if (size <= mPool0.getSize()) p = mPool0.alloc(size, 16, extra);
else if (size <= mPool1.getSize()) p = mPool1.alloc(size, 16, extra);
else if (size <= mPool2.getSize()) p = mPool2.alloc(size, 16, extra);
return p;
}
// this is in fact always called with a constexpr size argument
inline void free(void* p, size_t size, uint8_t age) noexcept {
inline void free(void* p, size_t size) noexcept {
assert_invariant(p >= mArea.begin() && (char*)p + size <= (char*)mArea.end());
// check for double-free
Node* const pNode = static_cast<Node*>(p);
uint8_t& expectedAge = pNode[-1].age;
ASSERT_POSTCONDITION(expectedAge == age,
"double-free of Handle of size %d at %p", size, p);
expectedAge = (expectedAge + 1) & 0xF; // fixme
if (size <= mPool0.getSize()) { mPool0.free(p); return; }
if (size <= mPool1.getSize()) { mPool1.free(p); return; }
if (size <= mPool2.getSize()) { mPool2.free(p); return; }
@@ -262,16 +263,24 @@ private:
// allocateHandle()/deallocateHandle() selects the pool to use at compile-time based on the
// allocation size this is always inlined, because all these do is to call
// allocateHandleInPool()/deallocateHandleFromPool() with the right pool size.
template<typename D>
template<size_t SIZE>
HandleBase::HandleId allocateHandle() noexcept {
constexpr size_t BUCKET_SIZE = getBucketSize<D>();
return allocateHandleInPool<BUCKET_SIZE>();
if constexpr (SIZE <= P0) { return allocateHandleInPool<P0>(); }
if constexpr (SIZE <= P1) { return allocateHandleInPool<P1>(); }
static_assert(SIZE <= P2);
return allocateHandleInPool<P2>();
}
template<typename D>
template<size_t SIZE>
void deallocateHandle(HandleBase::HandleId id) noexcept {
constexpr size_t BUCKET_SIZE = getBucketSize<D>();
deallocateHandleFromPool<BUCKET_SIZE>(id);
if constexpr (SIZE <= P0) {
deallocateHandleFromPool<P0>(id);
} else if constexpr (SIZE <= P1) {
deallocateHandleFromPool<P1>(id);
} else {
static_assert(SIZE <= P2);
deallocateHandleFromPool<P2>(id);
}
}
// allocateHandleInPool()/deallocateHandleFromPool() is NOT inlined, which will cause three
@@ -280,11 +289,9 @@ private:
template<size_t SIZE>
UTILS_NOINLINE
HandleBase::HandleId allocateHandleInPool() noexcept {
uint8_t age;
void* p = mHandleArena.alloc(SIZE, alignof(std::max_align_t), 0, &age);
void* p = mHandleArena.alloc(SIZE);
if (UTILS_LIKELY(p)) {
uint32_t const tag = (uint32_t(age) << HANDLE_AGE_SHIFT) & HANDLE_AGE_MASK;
return arenaPointerToHandle(p, tag);
return pointerToHandle(p);
} else {
return allocateHandleSlow(SIZE);
}
@@ -294,51 +301,42 @@ private:
UTILS_NOINLINE
void deallocateHandleFromPool(HandleBase::HandleId id) noexcept {
if (UTILS_LIKELY(isPoolHandle(id))) {
auto [p, tag] = handleToPointer(id);
uint8_t const age = (tag & HANDLE_AGE_MASK) >> HANDLE_AGE_SHIFT;
mHandleArena.free(p, SIZE, age);
void* p = handleToPointer(id);
mHandleArena.free(p, SIZE);
} else {
deallocateHandleSlow(id, SIZE);
}
}
// we handle a 4 bits age per address
static constexpr uint32_t HANDLE_HEAP_FLAG = 0x80000000u; // pool vs heap handle
static constexpr uint32_t HANDLE_AGE_MASK = 0x78000000u; // handle's age
static constexpr uint32_t HANDLE_INDEX_MASK = 0x07FFFFFFu; // handle index
static constexpr uint32_t HANDLE_TAG_MASK = HANDLE_AGE_MASK;
static constexpr uint32_t HANDLE_AGE_SHIFT = 27;
static constexpr uint32_t HEAP_HANDLE_FLAG = 0x80000000u;
static bool isPoolHandle(HandleBase::HandleId id) noexcept {
return (id & HANDLE_HEAP_FLAG) == 0u;
return (id & HEAP_HANDLE_FLAG) == 0u;
}
HandleBase::HandleId allocateHandleSlow(size_t size) noexcept;
void deallocateHandleSlow(HandleBase::HandleId id, size_t size) noexcept;
// We inline this because it's just 4 instructions in the fast case
inline std::pair<void*, uint32_t> handleToPointer(HandleBase::HandleId id) const noexcept {
inline void* handleToPointer(HandleBase::HandleId id) const noexcept {
// note: the null handle will end-up returning nullptr b/c it'll be handled as
// a non-pool handle.
if (UTILS_LIKELY(isPoolHandle(id))) {
char* const base = (char*)mHandleArena.getArea().begin();
uint32_t const tag = id & HANDLE_TAG_MASK;
size_t const offset = (id & HANDLE_INDEX_MASK) * Allocator::getAlignment();
return { static_cast<void*>(base + offset), tag };
size_t offset = id << Allocator::MIN_ALIGNMENT_SHIFT;
return static_cast<void*>(base + offset);
}
return { handleToPointerSlow(id), 0 };
return handleToPointerSlow(id);
}
void* handleToPointerSlow(HandleBase::HandleId id) const noexcept;
// We inline this because it's just 3 instructions
inline HandleBase::HandleId arenaPointerToHandle(void* p, uint32_t tag) const noexcept {
inline HandleBase::HandleId pointerToHandle(void* p) const noexcept {
char* const base = (char*)mHandleArena.getArea().begin();
size_t const offset = (char*)p - base;
assert_invariant((offset % Allocator::getAlignment()) == 0);
auto id = HandleBase::HandleId(offset / Allocator::getAlignment());
id |= tag & HANDLE_TAG_MASK;
assert_invariant((id & HANDLE_HEAP_FLAG) == 0);
size_t offset = (char*)p - base;
auto id = HandleBase::HandleId(offset >> Allocator::MIN_ALIGNMENT_SHIFT);
assert_invariant((id & HEAP_HANDLE_FLAG) == 0);
return id;
}
@@ -348,6 +346,9 @@ private:
mutable utils::Mutex mLock;
tsl::robin_map<HandleBase::HandleId, void*> mOverflowMap;
HandleBase::HandleId mId = 0;
#if HANDLE_TYPE_SAFETY
mutable std::unordered_map<const void*, const char*> mHandleTypeId;
#endif
};
} // namespace filament::backend
+16 -15
View File
@@ -49,25 +49,21 @@ struct AcquiredImage;
struct HwBase {
};
struct HwVertexBufferInfo : public HwBase {
struct HwVertexBuffer : public HwBase {
AttributeArray attributes{}; // 8 * MAX_VERTEX_ATTRIBUTE_COUNT
uint32_t vertexCount{}; // 4
uint8_t bufferCount{}; // 1
uint8_t attributeCount{}; // 1
bool padding[2]{}; // 2
HwVertexBufferInfo() noexcept = default;
HwVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount) noexcept
: bufferCount(bufferCount),
attributeCount(attributeCount) {
}
};
bool padding{}; // 1
uint8_t bufferObjectsVersion{}; // 1 -> total struct is 136 bytes
struct HwVertexBuffer : public HwBase {
uint32_t vertexCount{}; // 4
uint8_t bufferObjectsVersion{0xff}; // 1
bool padding[3]{}; // 2
HwVertexBuffer() noexcept = default;
explicit HwVertexBuffer(uint32_t vertextCount) noexcept
: vertexCount(vertextCount) {
HwVertexBuffer(uint8_t bufferCount, uint8_t attributeCount, uint32_t elementCount,
AttributeArray const& attributes) noexcept
: attributes(attributes),
vertexCount(elementCount),
bufferCount(bufferCount),
attributeCount(attributeCount) {
}
};
@@ -92,6 +88,11 @@ struct HwIndexBuffer : public HwBase {
};
struct HwRenderPrimitive : public HwBase {
uint32_t offset{};
uint32_t minIndex{};
uint32_t maxIndex{};
uint32_t count{};
uint32_t maxVertexCount{};
PrimitiveType type = PrimitiveType::TRIANGLES;
};
+11 -50
View File
@@ -16,22 +16,9 @@
#include "private/backend/HandleAllocator.h"
#include <backend/Handle.h>
#include <utils/Allocator.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/ostream.h>
#include <algorithm>
#include <exception>
#include <limits>
#include <mutex>
#include <stdlib.h>
#include <string.h>
namespace filament::backend {
@@ -41,34 +28,14 @@ template <size_t P0, size_t P1, size_t P2>
UTILS_NOINLINE
HandleAllocator<P0, P1, P2>::Allocator::Allocator(AreaPolicy::HeapArea const& area)
: mArea(area) {
// The largest handle this allocator can generate currently depends on the architecture's
// min alignment, typically 8 or 16 bytes.
// e.g. On Android armv8, the alignment is 16 bytes, so for a 1 MiB heap, the largest handle
// index will be 65536. Note that this is not the same as the number of handles (which
// will always be less).
// Because our maximum representable handle currently is 0x07FFFFFF, the maximum no-nonsensical
// heap size is 2 GiB, which amounts to 7.6 millions handles per pool (in the GL case).
size_t const maxHeapSize = std::min(area.size(), HANDLE_INDEX_MASK * getAlignment());
if (UTILS_UNLIKELY(maxHeapSize != area.size())) {
slog.w << "HandleAllocator heap size reduced to "
<< maxHeapSize << " from " << area.size() << io::endl;
}
// make sure we start with a clean arena. This is needed to ensure that all blocks start
// with an age of 0.
memset(area.data(), 0, maxHeapSize);
// size the different pools so that they can all contain the same number of handles
size_t const count = maxHeapSize / (P0 + P1 + P2);
char* const p0 = static_cast<char*>(area.begin());
char* const p1 = p0 + count * P0;
char* const p2 = p1 + count * P1;
mPool0 = Pool<P0>(p0, count * P0);
mPool1 = Pool<P1>(p1, count * P1);
mPool2 = Pool<P2>(p2, count * P2);
// TODO: we probably need a better way to set the size of these pools
const size_t unit = area.size() / 32;
const size_t offsetPool1 = unit;
const size_t offsetPool2 = 16 * unit;
char* const p = (char*)area.begin();
mPool0 = PoolAllocator< P0, 16>(p, p + offsetPool1);
mPool1 = PoolAllocator< P1, 16>(p + offsetPool1, p + offsetPool2);
mPool2 = PoolAllocator< P2, 16>(p + offsetPool2, area.end());
}
// ------------------------------------------------------------------------------------------------
@@ -106,17 +73,11 @@ template <size_t P0, size_t P1, size_t P2>
HandleBase::HandleId HandleAllocator<P0, P1, P2>::allocateHandleSlow(size_t size) noexcept {
void* p = ::malloc(size);
std::unique_lock lock(mLock);
HandleBase::HandleId id = (++mId) | HANDLE_HEAP_FLAG;
ASSERT_POSTCONDITION(mId < HANDLE_HEAP_FLAG,
"No more Handle ids available! This can happen if HandleAllocator arena has been full"
" for a while. Please increase FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB");
HandleBase::HandleId id = (++mId) | HEAP_HANDLE_FLAG;
mOverflowMap.emplace(id, p);
lock.unlock();
if (UTILS_UNLIKELY(id == (HANDLE_HEAP_FLAG | 1u))) { // meaning id was zero
if (UTILS_UNLIKELY(id == (HEAP_HANDLE_FLAG|1u))) { // meaning id was zero
PANIC_LOG("HandleAllocator arena is full, using slower system heap. Please increase "
"the appropriate constant (e.g. FILAMENT_OPENGL_HANDLE_ARENA_SIZE_IN_MB).");
}
@@ -125,7 +86,7 @@ HandleBase::HandleId HandleAllocator<P0, P1, P2>::allocateHandleSlow(size_t size
template <size_t P0, size_t P1, size_t P2>
void HandleAllocator<P0, P1, P2>::deallocateHandleSlow(HandleBase::HandleId id, size_t) noexcept {
assert_invariant(id & HANDLE_HEAP_FLAG);
assert_invariant(id & HEAP_HANDLE_FLAG);
void* p = nullptr;
auto& overflowMap = mOverflowMap;
+4 -1
View File
@@ -133,9 +133,12 @@ private:
mHandleAllocator.deallocate(handle, p);
}
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh);
inline void setRenderPrimitiveRange(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
uint32_t offset, uint32_t minIndex, uint32_t maxIndex, uint32_t count);
void finalizeSamplerGroup(MetalSamplerGroup* sg);
void enumerateBoundBuffers(BufferObjectBinding bindingType,
const std::function<void(const BufferState&, MetalBuffer*, uint32_t)>& f);
+56 -104
View File
@@ -43,40 +43,6 @@ namespace filament {
namespace backend {
Driver* MetalDriverFactory::create(MetalPlatform* const platform, const Platform::DriverConfig& driverConfig) {
#if 0
// this is useful for development, but too verbose even for debug builds
// For reference on a 64-bits machine in Release mode:
// MetalTimerQuery : 16 few
// HwStream : 24 few
// MetalIndexBuffer : 40 moderate
// MetalFence : 48 few
// MetalBufferObject : 48 many
// -- less than or equal 48 bytes
// MetalSamplerGroup : 112 few
// MetalProgram : 144 moderate
// MetalTexture : 152 moderate
// MetalVertexBuffer : 152 moderate
// -- less than or equal 160 bytes
// MetalSwapChain : 184 few
// MetalRenderTarget : 272 few
// MetalRenderPrimitive : 584 many
// -- less than or equal to 592 bytes
utils::slog.d
<< "\nMetalSwapChain: " << sizeof(MetalSwapChain)
<< "\nMetalBufferObject: " << sizeof(MetalBufferObject)
<< "\nMetalVertexBuffer: " << sizeof(MetalVertexBuffer)
<< "\nMetalIndexBuffer: " << sizeof(MetalIndexBuffer)
<< "\nMetalSamplerGroup: " << sizeof(MetalSamplerGroup)
<< "\nMetalRenderPrimitive: " << sizeof(MetalRenderPrimitive)
<< "\nMetalTexture: " << sizeof(MetalTexture)
<< "\nMetalTimerQuery: " << sizeof(MetalTimerQuery)
<< "\nHwStream: " << sizeof(HwStream)
<< "\nMetalRenderTarget: " << sizeof(MetalRenderTarget)
<< "\nMetalFence: " << sizeof(MetalFence)
<< "\nMetalProgram: " << sizeof(MetalProgram)
<< utils::io::endl;
#endif
return MetalDriver::create(platform, driverConfig);
}
@@ -279,16 +245,10 @@ void MetalDriver::finish(int) {
[oneOffBuffer waitUntilCompleted];
}
void MetalDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes) {
construct_handle<MetalVertexBufferInfo>(vbih, *mContext,
bufferCount, attributeCount, attributes);
}
void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih) {
MetalVertexBufferInfo const* const vbi = handle_cast<const MetalVertexBufferInfo>(vbih);
construct_handle<MetalVertexBuffer>(vbh, *mContext, vertexCount, vbi->bufferCount, vbih);
void MetalDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint8_t bufferCount,
uint8_t attributeCount, uint32_t vertexCount, AttributeArray attributes) {
construct_handle<MetalVertexBuffer>(vbh, *mContext, bufferCount,
attributeCount, vertexCount, attributes);
}
void MetalDriver::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
@@ -356,9 +316,11 @@ void MetalDriver::createSamplerGroupR(
void MetalDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
PrimitiveType pt) {
PrimitiveType pt, uint32_t offset,
uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
construct_handle<MetalRenderPrimitive>(rph);
MetalDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh);
MetalDriver::setRenderPrimitiveBuffer(rph, vbh, ibh);
MetalDriver::setRenderPrimitiveRange(rph, pt, offset, minIndex, maxIndex, count);
}
void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program) {
@@ -444,10 +406,6 @@ void MetalDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
// nothing to do, timer query was constructed in createTimerQueryS
}
Handle<HwVertexBufferInfo> MetalDriver::createVertexBufferInfoS() noexcept {
return alloc_handle<MetalVertexBufferInfo>();
}
Handle<HwVertexBuffer> MetalDriver::createVertexBufferS() noexcept {
return alloc_handle<MetalVertexBuffer>();
}
@@ -512,12 +470,6 @@ Handle<HwTimerQuery> MetalDriver::createTimerQueryS() noexcept {
return alloc_and_construct_handle<MetalTimerQuery, HwTimerQuery>();
}
void MetalDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
if (vbih) {
destruct_handle<MetalVertexBufferInfo>(vbih);
}
}
void MetalDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
if (vbh) {
destruct_handle<MetalVertexBuffer>(vbh);
@@ -770,14 +722,8 @@ bool MetalDriver::isSRGBSwapChainSupported() {
return false;
}
bool MetalDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) {
switch (stereoscopicType) {
case backend::StereoscopicType::INSTANCED:
return true;
case backend::StereoscopicType::MULTIVIEW:
// TODO: implement multiview feature in Metal.
return false;
}
bool MetalDriver::isStereoSupported() {
return true;
}
bool MetalDriver::isParallelShaderCompileSupported() {
@@ -1083,14 +1029,23 @@ void MetalDriver::endRenderPass(int dummy) {
mContext->currentRenderPassEncoder = nil;
}
void MetalDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
void MetalDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh) {
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
auto vertexBuffer = handle_cast<MetalVertexBuffer>(vbh);
auto indexBuffer = handle_cast<MetalIndexBuffer>(ibh);
MetalVertexBufferInfo const* const vbi = handle_cast<MetalVertexBufferInfo>(vertexBuffer->vbih);
primitive->setBuffers(vbi, vertexBuffer, indexBuffer);
primitive->setBuffers(vertexBuffer, indexBuffer);
}
void MetalDriver::setRenderPrimitiveRange(Handle<HwRenderPrimitive> rph,
PrimitiveType pt, uint32_t offset, uint32_t minIndex, uint32_t maxIndex,
uint32_t count) {
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
primitive->type = pt;
primitive->offset = offset * primitive->indexBuffer->elementSize;
primitive->count = count;
primitive->minIndex = minIndex;
primitive->maxIndex = maxIndex > minIndex ? maxIndex : primitive->maxVertexCount - 1;
}
void MetalDriver::makeCurrent(Handle<HwSwapChain> schDraw, Handle<HwSwapChain> schRead) {
@@ -1592,8 +1547,7 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
}
}
void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t instanceCount) {
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
"Attempted to draw without a valid command encoder.");
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
@@ -1610,7 +1564,7 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
return;
}
functions.validate();
ASSERT_PRECONDITION(bool(functions), "Attempting to draw with an invalid Metal program.");
auto [fragment, vertex] = functions.getRasterFunctions();
@@ -1731,6 +1685,36 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
mContext->currentPolygonOffset = ps.polygonOffset;
}
// Set scissor-rectangle.
// In order to do this, we compute the intersection between:
// 1. the scissor rectangle
// 2. the render target attachment dimensions (important, as the scissor can't be set larger)
// fmax/min are used below to guard against NaN and because the MTLViewport/MTLRegion
// coordinates are doubles.
MTLRegion scissor = mContext->currentRenderTarget->getRegionFromClientRect(ps.scissor);
const float sleft = scissor.origin.x, sright = scissor.origin.x + scissor.size.width;
const float stop = scissor.origin.y, sbottom = scissor.origin.y + scissor.size.height;
// Attachment extent
const auto attachmentSize = mContext->currentRenderTarget->getAttachmentSize();
const float aleft = 0.0f, atop = 0.0f;
const float aright = static_cast<float>(attachmentSize.x);
const float abottom = static_cast<float>(attachmentSize.y);
const auto left = std::fmax(sleft, aleft);
const auto right = std::fmin(sright, aright);
const auto top = std::fmax(stop, atop);
const auto bottom = std::fmin(sbottom, abottom);
MTLScissorRect scissorRect = {
.x = static_cast<NSUInteger>(left),
.y = static_cast<NSUInteger>(top),
.width = static_cast<NSUInteger>(right - left),
.height = static_cast<NSUInteger>(bottom - top)
};
[mContext->currentRenderPassEncoder setScissorRect:scissorRect];
// Bind uniform buffers.
MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
@@ -1808,10 +1792,10 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(mContext);
id<MTLBuffer> metalIndexBuffer = indexBuffer->buffer.getGpuBufferForDraw(cmdBuffer);
[mContext->currentRenderPassEncoder drawIndexedPrimitives:getMetalPrimitiveType(primitive->type)
indexCount:indexCount
indexCount:primitive->count
indexType:getIndexType(indexBuffer->elementSize)
indexBuffer:metalIndexBuffer
indexBufferOffset:indexOffset * primitive->indexBuffer->elementSize
indexBufferOffset:primitive->offset
instanceCount:instanceCount];
}
@@ -1884,38 +1868,6 @@ void MetalDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGro
[computeEncoder endEncoding];
}
void MetalDriver::scissor(Viewport scissorBox) {
// Set scissor-rectangle.
// In order to do this, we compute the intersection between:
// 1. the scissor rectangle
// 2. the render target attachment dimensions (important, as the scissor can't be set larger)
// fmax/min are used below to guard against NaN and because the MTLViewport/MTLRegion
// coordinates are doubles.
MTLRegion scissor = mContext->currentRenderTarget->getRegionFromClientRect(scissorBox);
const float sleft = scissor.origin.x, sright = scissor.origin.x + scissor.size.width;
const float stop = scissor.origin.y, sbottom = scissor.origin.y + scissor.size.height;
// Attachment extent
const auto attachmentSize = mContext->currentRenderTarget->getAttachmentSize();
const float aleft = 0.0f, atop = 0.0f;
const float aright = static_cast<float>(attachmentSize.x);
const float abottom = static_cast<float>(attachmentSize.y);
const auto left = std::fmax(sleft, aleft);
const auto right = std::fmin(sright, aright);
const auto top = std::fmax(stop, atop);
const auto bottom = std::fmin(sbottom, abottom);
MTLScissorRect scissorRect = {
.x = static_cast<NSUInteger>(left),
.y = static_cast<NSUInteger>(top),
.width = static_cast<NSUInteger>(right - left),
.height = static_cast<NSUInteger>(bottom - top)
};
[mContext->currentRenderPassEncoder setScissorRect:scissorRect];
}
void MetalDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"beginTimerQuery must be called outside of a render pass.");
+3 -12
View File
@@ -145,18 +145,10 @@ private:
MetalBuffer buffer;
};
struct MetalVertexBufferInfo : public HwVertexBufferInfo {
MetalVertexBufferInfo(MetalContext& context,
uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes);
AttributeArray attributes;
};
struct MetalVertexBuffer : public HwVertexBuffer {
MetalVertexBuffer(MetalContext& context,
uint32_t vertexCount, uint32_t bufferCount, Handle<HwVertexBufferInfo> vbih);
MetalVertexBuffer(MetalContext& context, uint8_t bufferCount, uint8_t attributeCount,
uint32_t vertexCount, AttributeArray const& attributes);
Handle<HwVertexBufferInfo> vbih;
utils::FixedCapacityVector<MetalBuffer*> buffers;
};
@@ -169,8 +161,7 @@ struct MetalIndexBuffer : public HwIndexBuffer {
struct MetalRenderPrimitive : public HwRenderPrimitive {
MetalRenderPrimitive();
void setBuffers(MetalVertexBufferInfo const* const vbi,
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer);
void setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer);
// The pointers to MetalVertexBuffer and MetalIndexBuffer are "weak".
// The MetalVertexBuffer and MetalIndexBuffer must outlive the MetalRenderPrimitive.
+7 -14
View File
@@ -340,16 +340,9 @@ void MetalBufferObject::updateBufferUnsynchronized(void* data, size_t size, uint
buffer.copyIntoBufferUnsynchronized(data, size, byteOffset);
}
MetalVertexBufferInfo::MetalVertexBufferInfo(MetalContext& context, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
attributes(attributes) {
}
MetalVertexBuffer::MetalVertexBuffer(MetalContext& context,
uint32_t vertexCount, uint32_t bufferCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount), vbih(vbih), buffers(bufferCount, nullptr) {
}
MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, uint8_t bufferCount,
uint8_t attributeCount, uint32_t vertexCount, AttributeArray const& attributes)
: HwVertexBuffer(bufferCount, attributeCount, vertexCount, attributes), buffers(bufferCount, nullptr) {}
MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
@@ -358,12 +351,12 @@ MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uin
MetalRenderPrimitive::MetalRenderPrimitive()
: bufferMapping(utils::FixedCapacityVector<Entry>::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {}
void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) {
void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer*
indexBuffer) {
this->vertexBuffer = vertexBuffer;
this->indexBuffer = indexBuffer;
const size_t attributeCount = vbi->attributes.size();
const size_t attributeCount = vertexBuffer->attributes.size();
auto& mapping = bufferMapping;
mapping.clear();
@@ -404,7 +397,7 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
};
for (uint32_t attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) {
const auto& attribute = vbi->attributes[attributeIndex];
const auto& attribute = vertexBuffer->attributes[attributeIndex];
// If the attribute is unused, bind it to the zero buffer. It's a Metal error for a shader
// to read from missing vertex attributes.
@@ -30,8 +30,6 @@
#include <array>
#include <memory>
#include <tuple>
#include <variant>
namespace filament::backend {
@@ -43,69 +41,36 @@ class MetalShaderCompiler {
public:
class MetalFunctionBundle {
public:
using Raster = std::tuple<id<MTLFunction>, id<MTLFunction>>;
using Compute = id<MTLFunction>;
using Error = std::tuple<NSString*, NSString*>; // error message, Program name
struct None {};
MetalFunctionBundle() : mPrograms{None{}} {}
explicit operator bool() const { return isValid(); }
bool isValid() const noexcept {
return std::holds_alternative<Raster>(mPrograms) ||
std::holds_alternative<Compute>(mPrograms);
}
/**
* Throws an NSException if this MetalFunctionBundle either contains an error or is empty.
*
* If this MetalFunctionBundle contains an error, will throw a MetalCompilationFailure
* NSException with the error string and program name passed to
* MetalFunctionBundle::error(NSString*, NSString*).
*
* If this MetalFunctionBundle is empty, will throw a MetalEmptyFunctionBundle NSException.
*/
void validate() const;
Raster getRasterFunctions() const {
assert_invariant(std::holds_alternative<Raster>(mPrograms));
return std::get<Raster>(mPrograms);
}
Compute getComputeFunction() const {
assert_invariant(std::holds_alternative<Compute>(mPrograms));
return std::get<Compute>(mPrograms);
}
static MetalFunctionBundle none() {
return MetalFunctionBundle(None{});
}
static MetalFunctionBundle raster(id<MTLFunction> fragment, id<MTLFunction> vertex) {
MetalFunctionBundle() = default;
MetalFunctionBundle(id<MTLFunction> fragment, id<MTLFunction> vertex)
: functions{fragment, vertex} {
assert_invariant(fragment && vertex);
assert_invariant(fragment.functionType == MTLFunctionTypeFragment);
assert_invariant(vertex.functionType == MTLFunctionTypeVertex);
return MetalFunctionBundle(Raster{fragment, vertex});
}
static MetalFunctionBundle compute(id<MTLFunction> compute) {
explicit MetalFunctionBundle(id<MTLFunction> compute) : functions{compute, nil} {
assert_invariant(compute);
assert_invariant(compute.functionType == MTLFunctionTypeKernel);
return MetalFunctionBundle(Compute{compute});
}
static MetalFunctionBundle error(NSString* errorMessage, NSString* programName) {
return MetalFunctionBundle(Error{errorMessage, programName});
std::pair<id<MTLFunction>, id<MTLFunction>> getRasterFunctions() const noexcept {
assert_invariant(functions[0].functionType == MTLFunctionTypeFragment);
assert_invariant(functions[1].functionType == MTLFunctionTypeVertex);
return {functions[0], functions[1]};
}
id<MTLFunction> getComputeFunction() const noexcept {
assert_invariant(functions[0].functionType == MTLFunctionTypeKernel);
return functions[0];
}
explicit operator bool() const { return functions[0] != nil; }
private:
MetalFunctionBundle(None&& t) : mPrograms(std::move(t)) {}
MetalFunctionBundle(Raster&& t) : mPrograms(std::move(t)) {}
MetalFunctionBundle(Compute&& t) : mPrograms(std::move(t)) {}
MetalFunctionBundle(Error&& t) : mPrograms(std::move(t)) {}
std::variant<Raster, Compute, None, Error> mPrograms;
// Can hold two functions, either:
// - fragment and vertex (for rasterization pipelines)
// - compute (for compute pipelines)
id<MTLFunction> functions[2] = {nil, nil};
};
using program_token_t = std::shared_ptr<MetalProgramToken>;
@@ -118,16 +118,13 @@ void MetalShaderCompiler::terminate() noexcept {
options:options
error:&error];
if (library == nil) {
NSString* errorMessage = @"unknown error";
if (error) {
auto description =
[error.localizedDescription cStringUsingEncoding:NSUTF8StringEncoding];
utils::slog.w << description << utils::io::endl;
errorMessage = error.localizedDescription;
}
PANIC_LOG("Failed to compile Metal program.");
NSString* programName = [NSString stringWithFormat:@"%s", program.getName().c_str_safe()];
return MetalFunctionBundle::error(errorMessage, programName);
return {};
}
MTLFunctionConstantValues* constants = [MTLFunctionConstantValues new];
@@ -163,15 +160,14 @@ void MetalShaderCompiler::terminate() noexcept {
assert_invariant(isRasterizationProgram != isComputeProgram);
if (isRasterizationProgram) {
return MetalFunctionBundle::raster(fragmentFunction, vertexFunction);
return {fragmentFunction, vertexFunction};
}
if (isComputeProgram) {
return MetalFunctionBundle::compute(computeFunction);
return MetalFunctionBundle{computeFunction};
}
// Should never reach here.
return MetalFunctionBundle::none();
return {};
}
MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram(
@@ -229,24 +225,4 @@ void MetalShaderCompiler::notifyWhenAllProgramsAreReady(
mCallbackManager.setCallback(handler, callback, user);
}
UTILS_NOINLINE
void MetalShaderCompiler::MetalFunctionBundle::validate() const {
if (UTILS_UNLIKELY(std::holds_alternative<Error>(mPrograms))) {
auto [errorMessage, programName] = std::get<Error>(mPrograms);
NSString* reason =
[NSString stringWithFormat:
@"Attempting to draw with an id<MTLFunction> that failed to compile.\n"
@"Program: %@\n"
@"%@", programName, errorMessage];
[[NSException exceptionWithName:@"MetalCompilationFailure"
reason:reason
userInfo:nil] raise];
} else if (UTILS_UNLIKELY(std::holds_alternative<None>(mPrograms))) {
NSString* reason = @"Attempting to draw with an empty id<MTLFunction>.";
[[NSException exceptionWithName:@"MetalEmptyFunctionBundle"
reason:reason
userInfo:nil] raise];
}
}
} // namespace filament::backend
+2 -9
View File
@@ -77,9 +77,6 @@ void NoopDriver::finish(int) {
void NoopDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
}
void NoopDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
}
void NoopDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
}
@@ -177,7 +174,7 @@ bool NoopDriver::isSRGBSwapChainSupported() {
return false;
}
bool NoopDriver::isStereoSupported(backend::StereoscopicType) {
bool NoopDriver::isStereoSupported() {
return false;
}
@@ -343,16 +340,12 @@ void NoopDriver::blit(
}
void NoopDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
uint32_t instanceCount) {
}
void NoopDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
}
void NoopDriver::scissor(
Viewport scissor) {
}
void NoopDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
}
@@ -622,7 +622,6 @@ void OpenGLContext::initExtensionsGLES(Extensions* ext, GLint major, GLint minor
ext->OES_standard_derivatives = exts.has("GL_OES_standard_derivatives"sv);
ext->OES_texture_npot = exts.has("GL_OES_texture_npot"sv);
ext->OES_vertex_array_object = exts.has("GL_OES_vertex_array_object"sv);
ext->OVR_multiview2 = exts.has("GL_OVR_multiview2"sv);
ext->WEBGL_compressed_texture_etc = exts.has("WEBGL_compressed_texture_etc"sv);
ext->WEBGL_compressed_texture_s3tc = exts.has("WEBGL_compressed_texture_s3tc"sv);
ext->WEBGL_compressed_texture_s3tc_srgb = exts.has("WEBGL_compressed_texture_s3tc_srgb"sv);
@@ -687,7 +686,6 @@ void OpenGLContext::initExtensionsGL(Extensions* ext, GLint major, GLint minor)
ext->OES_standard_derivatives = true;
ext->OES_texture_npot = true;
ext->OES_vertex_array_object = true;
ext->OVR_multiview2 = exts.has("GL_OVR_multiview2"sv);
ext->WEBGL_compressed_texture_etc = false;
ext->WEBGL_compressed_texture_s3tc = false;
ext->WEBGL_compressed_texture_s3tc_srgb = false;
@@ -205,7 +205,6 @@ public:
bool OES_standard_derivatives;
bool OES_texture_npot;
bool OES_vertex_array_object;
bool OVR_multiview2;
bool WEBGL_compressed_texture_etc;
bool WEBGL_compressed_texture_s3tc;
bool WEBGL_compressed_texture_s3tc_srgb;
+60 -94
View File
@@ -90,28 +90,26 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
#if 0
// this is useful for development, but too verbose even for debug builds
// For reference on a 64-bits machine in Release mode:
// GLFence : 8 few
// GLIndexBuffer : 8 moderate
// GLSamplerGroup : 16 few
// GLSwapChain : 16 few
// GLTimerQuery : 16 few
// GLRenderPrimitive : 20 many
// GLFence : 24 few
// -- less than or equal 24 bytes
// GLBufferObject : 32 many
// OpenGLProgram : 56 moderate
// GLTexture : 64 moderate
// GLSamplerGroup : 8 few
// -- less than or equal 16 bytes
// GLBufferObject : 24 many
// GLSync : 24 few
// GLTimerQuery : 32 few
// OpenGLProgram : 32 moderate
// GLRenderPrimitive : 48 many
// -- less than or equal 64 bytes
// GLVertexBuffer : 76 moderate
// GLStream : 104 few
// GLTexture : 72 moderate
// GLRenderTarget : 112 few
// GLVertexBufferInfo : 132 moderate
// -- less than or equal to 136 bytes
// GLStream : 184 few
// GLVertexBuffer : 200 moderate
// -- less than or equal to 208 bytes
slog.d
<< "\nGLSwapChain: " << sizeof(GLSwapChain)
<< "HwFence: " << sizeof(HwFence)
<< "\nGLBufferObject: " << sizeof(GLBufferObject)
<< "\nGLVertexBuffer: " << sizeof(GLVertexBuffer)
<< "\nGLVertexBufferInfo: " << sizeof(GLVertexBufferInfo)
<< "\nGLIndexBuffer: " << sizeof(GLIndexBuffer)
<< "\nGLSamplerGroup: " << sizeof(GLSamplerGroup)
<< "\nGLRenderPrimitive: " << sizeof(GLRenderPrimitive)
@@ -119,7 +117,7 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
<< "\nGLTimerQuery: " << sizeof(GLTimerQuery)
<< "\nGLStream: " << sizeof(GLStream)
<< "\nGLRenderTarget: " << sizeof(GLRenderTarget)
<< "\nGLFence: " << sizeof(GLFence)
<< "\nGLSync: " << sizeof(GLSync)
<< "\nOpenGLProgram: " << sizeof(OpenGLProgram)
<< io::endl;
#endif
@@ -388,10 +386,6 @@ void OpenGLDriver::setStencilState(StencilState ss) noexcept {
// Creating driver objects
// ------------------------------------------------------------------------------------------------
Handle<HwVertexBufferInfo> OpenGLDriver::createVertexBufferInfoS() noexcept {
return initHandle<GLVertexBufferInfo>();
}
Handle<HwVertexBuffer> OpenGLDriver::createVertexBufferS() noexcept {
return initHandle<GLVertexBuffer>();
}
@@ -452,21 +446,14 @@ Handle<HwTimerQuery> OpenGLDriver::createTimerQueryS() noexcept {
return initHandle<GLTimerQuery>();
}
void OpenGLDriver::createVertexBufferInfoR(
Handle<HwVertexBufferInfo> vbih,
uint8_t bufferCount,
uint8_t attributeCount,
AttributeArray attributes) {
DEBUG_MARKER()
construct<GLVertexBufferInfo>(vbih, bufferCount, attributeCount, attributes);
}
void OpenGLDriver::createVertexBufferR(
Handle<HwVertexBuffer> vbh,
uint32_t vertexCount,
Handle<HwVertexBufferInfo> vbih) {
uint8_t bufferCount,
uint8_t attributeCount,
uint32_t elementCount,
AttributeArray attributes) {
DEBUG_MARKER()
construct<GLVertexBuffer>(vbh, vertexCount, vbih);
construct<GLVertexBuffer>(vbh, bufferCount, attributeCount, elementCount, attributes);
}
void OpenGLDriver::createIndexBufferR(
@@ -514,11 +501,13 @@ void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh,
void OpenGLDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
PrimitiveType pt) {
PrimitiveType pt, uint32_t offset,
uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
DEBUG_MARKER()
auto& gl = mContext;
GLVertexBuffer const* const eb = handle_cast<const GLVertexBuffer*>(vbh);
GLIndexBuffer const* const ib = handle_cast<const GLIndexBuffer*>(ibh);
assert_invariant(ib->elementSize == 2 || ib->elementSize == 4);
@@ -526,14 +515,17 @@ void OpenGLDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
rp->gl.indicesSize = (ib->elementSize == 4u) ? 4u : 2u;
rp->gl.vertexBufferWithObjects = vbh;
rp->type = pt;
rp->offset = offset * rp->gl.indicesSize;
rp->count = count;
rp->minIndex = minIndex;
rp->maxIndex = maxIndex > minIndex ? maxIndex : rp->maxVertexCount - 1; // sanitize max index
gl.procs.genVertexArrays(1, &rp->gl.vao);
gl.bindVertexArray(&rp->gl);
// Note: we don't update the vertex buffer bindings in the VAO just yet, we will do that
// later in draw() or bindRenderPrimitive(). At this point, the HwVertexBuffer might not
// have all its buffers set.
// update the VBO bindings in the VAO
updateVertexArrayObject(rp, eb);
// this records the index buffer into the currently bound VAO
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->gl.buffer);
@@ -856,6 +848,7 @@ void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer
// NOTE: this is called from draw() and must be as efficient as possible.
if (UTILS_LIKELY(gl.ext.OES_vertex_array_object)) {
// The VAO for the given render primitive must already be bound.
#ifndef NDEBUG
@@ -869,49 +862,42 @@ void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer
// that it's always reset in draw
}
GLVertexBufferInfo const* const vbi = handle_cast<const GLVertexBufferInfo*>(vb->vbih);
for (size_t i = 0, n = vbi->attributes.size(); i < n; i++) {
const auto& attribute = vbi->attributes[i];
rp->maxVertexCount = vb->vertexCount;
for (size_t i = 0, n = vb->attributes.size(); i < n; i++) {
const auto& attribute = vb->attributes[i];
const uint8_t bi = attribute.buffer;
if (bi != Attribute::BUFFER_UNUSED) {
// if a buffer is defined it must not be invalid.
assert_invariant(vb->gl.buffers[bi]);
// if w're on ES2, the user shouldn't use FLAG_INTEGER_TARGET
// Invoking glVertexAttribPointer without a bound VBO is an invalid operation, so we must
// take care to avoid it. This can occur when VertexBuffer is only partially populated with
// BufferObject items.
if (bi != Attribute::BUFFER_UNUSED && UTILS_LIKELY(vb->gl.buffers[bi] != 0)) {
assert_invariant(!(gl.isES2() && (attribute.flags & Attribute::FLAG_INTEGER_TARGET)));
gl.bindBuffer(GL_ARRAY_BUFFER, vb->gl.buffers[bi]);
GLuint const index = i;
GLint const size = getComponentCount(attribute.type);
GLenum const type = getComponentType(attribute.type);
GLboolean const normalized = getNormalization(attribute.flags & Attribute::FLAG_NORMALIZED);
GLsizei const stride = attribute.stride;
void const* pointer = reinterpret_cast<void const *>(attribute.offset);
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
if (UTILS_UNLIKELY(attribute.flags & Attribute::FLAG_INTEGER_TARGET)) {
// integer attributes can't be floats
assert_invariant(type == GL_BYTE || type == GL_UNSIGNED_BYTE || type == GL_SHORT ||
type == GL_UNSIGNED_SHORT || type == GL_INT || type == GL_UNSIGNED_INT);
glVertexAttribIPointer(index, size, type, stride, pointer);
glVertexAttribIPointer(GLuint(i),
(GLint)getComponentCount(attribute.type),
getComponentType(attribute.type),
attribute.stride,
(void*) uintptr_t(attribute.offset));
} else
#endif
{
glVertexAttribPointer(index, size, type, normalized, stride, pointer);
glVertexAttribPointer(GLuint(i),
(GLint)getComponentCount(attribute.type),
getComponentType(attribute.type),
getNormalization(attribute.flags & Attribute::FLAG_NORMALIZED),
attribute.stride,
(void*) uintptr_t(attribute.offset));
}
gl.enableVertexAttribArray(GLuint(i));
} else {
// In some OpenGL implementations, we must supply a properly-typed placeholder for
// every integer input that is declared in the vertex shader.
// Note that the corresponding doesn't have to be enabled and in fact won't be. If it
// was enabled, it would indicate a user-error (providing the wrong type of array).
// With a disabled array, the vertex shader gets the attribute from glVertexAttrib,
// and must have the proper intergerness.
// But at this point, we don't know what the shader requirements are, and so we must
// rely on the attribute.
// In some OpenGL implementations, we must supply a properly-typed placeholder for
// every integer input that is declared in the vertex shader, even if disabled.
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
if (UTILS_UNLIKELY(attribute.flags & Attribute::FLAG_INTEGER_TARGET)) {
if (!gl.isES2()) {
@@ -1424,14 +1410,6 @@ void OpenGLDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
// Destroying driver objects
// ------------------------------------------------------------------------------------------------
void OpenGLDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
DEBUG_MARKER()
if (vbih) {
GLVertexBufferInfo const* vbi = handle_cast<const GLVertexBufferInfo*>(vbih);
destruct(vbih, vbi);
}
}
void OpenGLDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
DEBUG_MARKER()
if (vbh) {
@@ -1921,20 +1899,12 @@ bool OpenGLDriver::isSRGBSwapChainSupported() {
return mPlatform.isSRGBSwapChainSupported();
}
bool OpenGLDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) {
// Instanced-stereo requires instancing and EXT_clip_cull_distance.
// Multiview-stereo requires ES 3.0 and OVR_multiview2.
bool OpenGLDriver::isStereoSupported() {
// Stereo requires instancing and EXT_clip_cull_distance.
if (UTILS_UNLIKELY(mContext.isES2())) {
return false;
}
switch (stereoscopicType) {
case backend::StereoscopicType::INSTANCED:
return mContext.ext.EXT_clip_cull_distance;
case backend::StereoscopicType::MULTIVIEW:
return mContext.ext.OVR_multiview2;
default:
return false;
}
return mContext.ext.EXT_clip_cull_distance;
}
bool OpenGLDriver::isParallelShaderCompileSupported() {
@@ -3728,8 +3698,7 @@ void OpenGLDriver::updateTextureLodRange(GLTexture* texture, int8_t targetLevel)
#endif
}
void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint32_t instanceCount) {
DEBUG_MARKER()
auto& gl = mContext;
@@ -3764,15 +3733,16 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant);
setScissor(state.scissor);
if (UTILS_LIKELY(instanceCount <= 1)) {
glDrawElements(GLenum(rp->type), (GLsizei)indexCount, rp->gl.getIndicesType(),
reinterpret_cast<const void*>(indexOffset * rp->gl.indicesSize));
glDrawElements(GLenum(rp->type), (GLsizei)rp->count, rp->gl.getIndicesType(),
reinterpret_cast<const void*>(rp->offset));
} else {
assert_invariant(!mContext.isES2());
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
glDrawElementsInstanced(GLenum(rp->type), (GLsizei)indexCount,
rp->gl.getIndicesType(),
reinterpret_cast<const void*>(indexOffset * rp->gl.indicesSize),
glDrawElementsInstanced(GLenum(rp->type), (GLsizei)rp->count,
rp->gl.getIndicesType(), reinterpret_cast<const void*>(rp->offset),
(GLsizei)instanceCount);
#endif
}
@@ -3784,10 +3754,6 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
#endif
}
void OpenGLDriver::scissor(Viewport scissor) {
setScissor(scissor);
}
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
getShaderCompilerService().tick();
+1 -15
View File
@@ -94,22 +94,8 @@ public:
uint16_t age = 0;
};
struct GLVertexBufferInfo : public HwVertexBufferInfo {
GLVertexBufferInfo() noexcept = default;
GLVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
attributes(attributes) {
}
AttributeArray attributes;
};
struct GLVertexBuffer : public HwVertexBuffer {
GLVertexBuffer() noexcept = default;
GLVertexBuffer(uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount), vbih(vbih) {
}
Handle<HwVertexBufferInfo> vbih;
using HwVertexBuffer::HwVertexBuffer;
struct {
// 4 * MAX_VERTEX_ATTRIBUTE_COUNT bytes
std::array<GLuint, MAX_VERTEX_ATTRIBUTE_COUNT> buffers{};
@@ -18,14 +18,6 @@
#include "OpenGLDriverFactory.h"
#include <backend/AcquiredImage.h>
#include <backend/DriverEnums.h>
#include <backend/Platform.h>
#include <utils/compiler.h>
#include <stdint.h>
namespace filament::backend {
Driver* OpenGLPlatform::createDefaultDriver(OpenGLPlatform* platform,
@@ -261,11 +261,12 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
[this, &gl, program = std::move(program), token]() mutable {
// compile the shaders
std::array<GLuint, Program::SHADER_TYPE_COUNT> shaders{};
std::array<utils::CString, Program::SHADER_TYPE_COUNT> shaderSourceCode;
compileShaders(gl,
std::move(program.getShadersSource()),
program.getSpecializationConstants(),
shaders,
token->shaderSourceCode);
shaderSourceCode);
// link the program
GLuint const glProgram = linkProgram(gl, shaders, token->attributes);
@@ -18,31 +18,16 @@
#include "opengl/GLUtils.h"
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/Platform.h>
#include <backend/DriverEnums.h>
#include <EGL/egl.h>
#include <EGL/eglext.h>
#if defined(__ANDROID__)
#if defined(__ANDROID__)
#include <sys/system_properties.h>
#endif
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <algorithm>
#include <new>
#include <initializer_list>
#include <utility>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <utils/Panic.h>
#ifndef EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE
# define EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE 0x3483
@@ -63,6 +48,7 @@ UTILS_PRIVATE PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = {};
}
using namespace glext;
// ---------------------------------------------------------------------------------------------
// Utilities
// ---------------------------------------------------------------------------------------------
@@ -151,7 +137,7 @@ Driver* PlatformEGL::createDriver(void* sharedContext, const Platform::DriverCon
ext.egl.KHR_gl_colorspace = extensions.has("EGL_KHR_gl_colorspace");
ext.egl.KHR_create_context = extensions.has("EGL_KHR_create_context");
ext.egl.KHR_no_config_context = extensions.has("EGL_KHR_no_config_context");
ext.egl.KHR_surfaceless_context = extensions.has("EGL_KHR_surfaceless_context");
ext.egl.KHR_surfaceless_context = extensions.has("KHR_surfaceless_context");
if (ext.egl.KHR_create_context) {
// KHR_create_context implies KHR_surfaceless_context for ES3.x contexts
ext.egl.KHR_surfaceless_context = true;
@@ -475,13 +461,7 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
// this is not fatal
eglSurfaceAttrib(mEGLDisplay, sur, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
SwapChainEGL* const sc = new(std::nothrow) SwapChainEGL({
.sur = sur,
.attribs = std::move(attribs),
.nativeWindow = (EGLNativeWindowType)nativeWindow,
.config = config
});
return sc;
return (SwapChain*)sur;
}
Platform::SwapChain* PlatformEGL::createSwapChain(
@@ -515,42 +495,30 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
logEglError("eglCreatePbufferSurface");
return nullptr;
}
SwapChainEGL* const sc = new(std::nothrow) SwapChainEGL({
.sur = sur,
.attribs = std::move(attribs),
.config = config
});
return sc;
return (SwapChain*)sur;
}
void PlatformEGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
if (swapChain) {
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
if (sc->sur != EGL_NO_SURFACE) {
makeCurrent(mEGLDummySurface, mEGLDummySurface);
eglDestroySurface(mEGLDisplay, sc->sur);
delete sc;
}
EGLSurface sur = (EGLSurface) swapChain;
if (sur != EGL_NO_SURFACE) {
makeCurrent(mEGLDummySurface, mEGLDummySurface);
eglDestroySurface(mEGLDisplay, sur);
}
}
void PlatformEGL::makeCurrent(Platform::SwapChain* drawSwapChain,
Platform::SwapChain* readSwapChain) noexcept {
SwapChainEGL const* const dsc = static_cast<SwapChainEGL const*>(drawSwapChain);
SwapChainEGL const* const rsc = static_cast<SwapChainEGL const*>(readSwapChain);
if (UTILS_UNLIKELY(dsc->sur == EGL_NO_SURFACE && rsc->sur == EGL_NO_SURFACE)) {
return;
EGLSurface drawSur = (EGLSurface) drawSwapChain;
EGLSurface readSur = (EGLSurface) readSwapChain;
if (drawSur != EGL_NO_SURFACE || readSur != EGL_NO_SURFACE) {
makeCurrent(drawSur, readSur);
}
makeCurrent(dsc->sur, rsc->sur);
}
void PlatformEGL::commit(Platform::SwapChain* swapChain) noexcept {
if (swapChain) {
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
if (sc->sur != EGL_NO_SURFACE) {
eglSwapBuffers(mEGLDisplay, sc->sur);
}
EGLSurface sur = (EGLSurface) swapChain;
if (sur != EGL_NO_SURFACE) {
eglSwapBuffers(mEGLDisplay, sur);
}
}
@@ -593,7 +561,7 @@ FenceStatus PlatformEGL::waitFence(
}
OpenGLPlatform::ExternalTexture* PlatformEGL::createExternalImageTexture() noexcept {
ExternalTexture* outTexture = new(std::nothrow) ExternalTexture{};
ExternalTexture* outTexture = new ExternalTexture{};
glGenTextures(1, &outTexture->id);
if (UTILS_LIKELY(ext.gl.OES_EGL_image_external_essl3)) {
outTexture->target = GL_TEXTURE_EXTERNAL_OES;
+1 -21
View File
@@ -39,12 +39,7 @@
// not required for validation.
// FVK is short for Filament Vulkan
// Enables Android systrace
#define FVK_DEBUG_SYSTRACE 0x00000001
// Group markers are used to denote collection of GPU commands. It is typically at the granualarity
// of a renderpass.
#define FVK_DEBUG_GROUP_MARKERS 0x00000002
#define FVK_DEBUG_TEXTURE 0x00000004
#define FVK_DEBUG_LAYOUT_TRANSITION 0x00000008
@@ -60,9 +55,6 @@
#define FVK_DEBUG_PIPELINE_CACHE 0x00002000
#define FVK_DEBUG_ALLOCATION 0x00004000
// Enable the debug utils extension if it is available.
#define FVK_DEBUG_DEBUG_UTILS 0x00008000
// Usefaul default combinations
#define FVK_DEBUG_EVERYTHING 0xFFFFFFFF
#define FVK_DEBUG_PERFORMANCE \
@@ -80,7 +72,7 @@
FVK_DEBUG_PRINT_GROUP_MARKERS
#ifndef NDEBUG
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_DEBUG_UTILS | FVK_DEBUG_VALIDATION)
#define FVK_DEBUG_FLAGS FVK_DEBUG_PERFORMANCE
#else
#define FVK_DEBUG_FLAGS 0
#endif
@@ -88,23 +80,11 @@
#define FVK_ENABLED(flags) ((FVK_DEBUG_FLAGS) & (flags))
#define FVK_ENABLED_BOOL(flags) ((bool) FVK_ENABLED(flags))
// Group marker only works only if validation or debug utils is enabled since it uses
// vkCmd(Begin/End)DebugUtilsLabelEXT or vkCmdDebugMarker(Begin/End)EXT
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
static_assert(FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) || FVK_ENABLED(FVK_DEBUG_VALIDATION));
#endif
// Ensure dependencies are met between debug options
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
static_assert(FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS));
#endif
// Only enable debug utils if validation is enabled.
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
#endif
// end dependcy checks
#if FVK_ENABLED(FVK_DEBUG_SYSTRACE)
+111 -209
View File
@@ -24,7 +24,6 @@
#include "VulkanHandles.h"
#include "VulkanImageUtility.h"
#include "VulkanMemory.h"
#include "VulkanTexture.h"
#include <backend/platforms/VulkanPlatform.h>
@@ -118,9 +117,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugReportCallback(VkDebugReportFlagsEXT flags,
utils::slog.e << utils::io::endl;
return VK_FALSE;
}
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION)
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
VkDebugUtilsMessageTypeFlagsEXT types, const VkDebugUtilsMessengerCallbackDataEXT* cbdata,
void* pUserData) {
@@ -139,63 +136,9 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFla
utils::slog.e << utils::io::endl;
return VK_FALSE;
}
#endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS)
}// anonymous namespace
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
using DebugUtils = VulkanDriver::DebugUtils;
DebugUtils* DebugUtils::mSingleton = nullptr;
DebugUtils::DebugUtils(VkInstance instance, VkDevice device, VulkanContext const* context)
: mInstance(instance), mDevice(device), mEnabled(context->isDebugUtilsSupported()) {
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
// Also initialize debug utils messenger here
if (mEnabled) {
VkDebugUtilsMessengerCreateInfoEXT const createInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
.pNext = nullptr,
.flags = 0,
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
.pfnUserCallback = debugUtilsCallback,
};
VkResult result = vkCreateDebugUtilsMessengerEXT(instance, &createInfo,
VKALLOC, &mDebugMessenger);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug messenger.");
}
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION)
}
DebugUtils* DebugUtils::get() {
assert_invariant(DebugUtils::mSingleton);
return DebugUtils::mSingleton;
}
DebugUtils::~DebugUtils() {
if (mDebugMessenger) {
vkDestroyDebugUtilsMessengerEXT(mInstance, mDebugMessenger, VKALLOC);
}
}
void DebugUtils::setName(VkObjectType type, uint64_t handle, char const* name) {
auto impl = DebugUtils::get();
if (!impl->mEnabled) {
return;
}
VkDebugUtilsObjectNameInfoEXT const info = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
.pNext = nullptr,
.objectType = type,
.objectHandle = handle,
.pObjectName = name,
};
vkSetDebugUtilsObjectNameEXT(impl->mDevice, &info);
}
#endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS)
} // anonymous namespace
using ImgUtil = VulkanImageUtility;
@@ -216,26 +159,35 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mReadPixels(mPlatform->getDevice()),
mIsSRGBSwapChainSupported(mPlatform->getCustomization().isSRGBSwapChainSupported) {
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
DebugUtils::mSingleton =
new DebugUtils(mPlatform->getInstance(), mPlatform->getDevice(), &context);
#endif
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
UTILS_UNUSED const PFN_vkCreateDebugReportCallbackEXT createDebugReportCallback
= vkCreateDebugReportCallbackEXT;
if (!context.isDebugUtilsSupported() && createDebugReportCallback) {
VkResult result;
if (mContext.isDebugUtilsSupported()) {
VkDebugUtilsMessengerCreateInfoEXT const createInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
.pNext = nullptr,
.flags = 0,
.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT,
.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT
| VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT,
.pfnUserCallback = debugUtilsCallback,
};
result = vkCreateDebugUtilsMessengerEXT(mPlatform->getInstance(), &createInfo, VKALLOC,
&mDebugMessenger);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug messenger.");
} else if (createDebugReportCallback) {
VkDebugReportCallbackCreateInfoEXT const cbinfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT,
.flags = VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_ERROR_BIT_EXT,
.pfnCallback = debugReportCallback,
};
VkResult result = createDebugReportCallback(mPlatform->getInstance(), &cbinfo, VKALLOC,
result = createDebugReportCallback(mPlatform->getInstance(), &cbinfo, VKALLOC,
&mDebugCallback);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create Vulkan debug callback.");
}
#endif
mTimestamps = std::make_unique<VulkanTimestamps>(mPlatform->getDevice());
mCommands = std::make_unique<VulkanCommands>(mPlatform->getDevice(),
mPlatform->getGraphicsQueue(), mPlatform->getGraphicsQueueFamilyIndex(), &mContext,
@@ -261,41 +213,6 @@ VulkanDriver::~VulkanDriver() noexcept = default;
UTILS_NOINLINE
Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& context,
Platform::DriverConfig const& driverConfig) noexcept {
#if 0
// this is useful for development, but too verbose even for debug builds
// For reference on a 64-bits machine in Release mode:
// VulkanSamplerGroup : 24 few
// HwStream : 24 few
// VulkanFence : 40 few
// VulkanProgram : 40 moderate
// VulkanIndexBuffer : 72 moderate
// VulkanBufferObject : 72 many
// -- less than or equal 80 bytes
// VulkanRenderPrimitive : 104 many
// VulkanSwapChain : 112 few
// VulkanTimerQuery : 168 few
// -- less than or equal 176 bytes
// VulkanTexture : 232 moderate
// VulkanVertexBuffer : 312 moderate
// VulkanRenderTarget : 320 few
// -- less than or equal to 320 bytes
utils::slog.d
<< "\nVulkanSwapChain: " << sizeof(VulkanSwapChain)
<< "\nVulkanBufferObject: " << sizeof(VulkanBufferObject)
<< "\nVulkanVertexBuffer: " << sizeof(VulkanVertexBuffer)
<< "\nVulkanIndexBuffer: " << sizeof(VulkanIndexBuffer)
<< "\nVulkanSamplerGroup: " << sizeof(VulkanSamplerGroup)
<< "\nVulkanRenderPrimitive: " << sizeof(VulkanRenderPrimitive)
<< "\nVulkanTexture: " << sizeof(VulkanTexture)
<< "\nVulkanTimerQuery: " << sizeof(VulkanTimerQuery)
<< "\nHwStream: " << sizeof(HwStream)
<< "\nVulkanRenderTarget: " << sizeof(VulkanRenderTarget)
<< "\nVulkanFence: " << sizeof(VulkanFence)
<< "\nVulkanProgram: " << sizeof(VulkanProgram)
<< utils::io::endl;
#endif
assert_invariant(platform);
size_t defaultSize = FVK_HANDLE_ARENA_SIZE_IN_MB * 1024U * 1024U;
Platform::DriverConfig validConfig {driverConfig};
@@ -334,12 +251,9 @@ void VulkanDriver::terminate() {
if (mDebugCallback) {
vkDestroyDebugReportCallbackEXT(mPlatform->getInstance(), mDebugCallback, VKALLOC);
}
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
assert_invariant(DebugUtils::mSingleton);
delete DebugUtils::mSingleton;
#endif
if (mDebugMessenger) {
vkDestroyDebugUtilsMessengerEXT(mPlatform->getInstance(), mDebugMessenger, VKALLOC);
}
mPlatform->terminate();
}
@@ -410,10 +324,12 @@ void VulkanDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, uint32_t coun
void VulkanDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
PrimitiveType pt) {
PrimitiveType pt, uint32_t offset,
uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
auto rp = mResourceAllocator.construct<VulkanRenderPrimitive>(rph, &mResourceAllocator);
mResourceManager.acquire(rp);
VulkanDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh);
VulkanDriver::setRenderPrimitiveBuffer(rph, vbh, ibh);
VulkanDriver::setRenderPrimitiveRange(rph, pt, offset, minIndex, maxIndex, count);
}
void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
@@ -424,26 +340,10 @@ void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
mResourceManager.release(rp);
}
void VulkanDriver::createVertexBufferInfoR(Handle<HwVertexBufferInfo> vbih, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray attributes) {
auto vbi = mResourceAllocator.construct<VulkanVertexBufferInfo>(vbih,
bufferCount, attributeCount, attributes);
mResourceManager.acquire(vbi);
}
void VulkanDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
if (!vbih) {
return;
}
auto vbi = mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(vbih);
mResourceManager.release(vbi);
}
void VulkanDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih) {
auto vertexBuffer = mResourceAllocator.construct<VulkanVertexBuffer>(vbh,
mContext, mStagePool, &mResourceAllocator, vertexCount, vbih);
void VulkanDriver::createVertexBufferR(Handle<HwVertexBuffer> vbh, uint8_t bufferCount,
uint8_t attributeCount, uint32_t elementCount, AttributeArray attributes) {
auto vertexBuffer = mResourceAllocator.construct<VulkanVertexBuffer>(vbh, mContext, mStagePool,
&mResourceAllocator, bufferCount, attributeCount, elementCount, attributes);
mResourceManager.acquire(vertexBuffer);
}
@@ -650,10 +550,6 @@ void VulkanDriver::createTimerQueryR(Handle<HwTimerQuery> tqh, int) {
// nothing to do, timer query was constructed in createTimerQueryS
}
Handle<HwVertexBufferInfo> VulkanDriver::createVertexBufferInfoS() noexcept {
return mResourceAllocator.allocHandle<VulkanVertexBufferInfo>();
}
Handle<HwVertexBuffer> VulkanDriver::createVertexBufferS() noexcept {
return mResourceAllocator.allocHandle<VulkanVertexBuffer>();
}
@@ -868,14 +764,8 @@ bool VulkanDriver::isSRGBSwapChainSupported() {
return mIsSRGBSwapChainSupported;
}
bool VulkanDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) {
switch (stereoscopicType) {
case backend::StereoscopicType::INSTANCED:
return true;
case backend::StereoscopicType::MULTIVIEW:
// TODO: implement multiview feature in Vulkan.
return false;
}
bool VulkanDriver::isStereoSupported() {
return true;
}
bool VulkanDriver::isParallelShaderCompileSupported() {
@@ -974,7 +864,7 @@ void VulkanDriver::setVertexBufferObject(Handle<HwVertexBuffer> vbh, uint32_t in
auto bo = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh);
assert_invariant(bo->bindingType == BufferObjectBinding::VERTEX);
vb->setBuffer(mResourceAllocator, bo, index);
vb->setBuffer(bo, index);
}
void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
@@ -1138,7 +1028,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
VulkanRenderTarget* const rt = mResourceAllocator.handle_cast<VulkanRenderTarget*>(rth);
const VkExtent2D extent = rt->getExtent();
assert_invariant(rt == mDefaultRenderTarget || extent.width > 0 && extent.height > 0);
assert_invariant(extent.width > 0 && extent.height > 0);
// Filament has the expectation that the contents of the swap chain are not preserved on the
// first render pass. Note however that its contents are often preserved on subsequent render
@@ -1290,14 +1180,22 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
}
VkFramebuffer vkfb = mFramebufferCache.getFramebuffer(fbkey);
// Assign a label to the framebuffer for debugging purposes.
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS | FVK_DEBUG_DEBUG_UTILS)
auto const topMarker = mCommands->getTopGroupMarker();
if (!topMarker.empty()) {
DebugUtils::setName(VK_OBJECT_TYPE_FRAMEBUFFER, reinterpret_cast<uint64_t>(vkfb),
topMarker.c_str());
// Assign a label to the framebuffer for debugging purposes.
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
if (UTILS_UNLIKELY(mContext.isDebugUtilsSupported())) {
auto const topMarker = mCommands->getTopGroupMarker();
if (!topMarker.empty()) {
const VkDebugUtilsObjectNameInfoEXT info = {
VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
nullptr,
VK_OBJECT_TYPE_FRAMEBUFFER,
reinterpret_cast<uint64_t>(vkfb),
topMarker.c_str(),
};
vkSetDebugUtilsObjectNameEXT(mPlatform->getDevice(), &info);
}
}
#endif
#endif
// The current command buffer now owns a reference to the render target and its attachments.
// Note that we must acquire parent textures, not sidecars.
@@ -1451,12 +1349,22 @@ void VulkanDriver::nextSubpass(int) {
}
}
void VulkanDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
void VulkanDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph,
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh) {
auto primitive = mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
primitive->setBuffers(mResourceAllocator.handle_cast<VulkanVertexBuffer*>(vbh),
mResourceAllocator.handle_cast<VulkanIndexBuffer*>(ibh));
primitive->setPrimitiveType(pt);
}
void VulkanDriver::setRenderPrimitiveRange(Handle<HwRenderPrimitive> rph,
PrimitiveType pt, uint32_t offset,
uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
auto& primitive = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
primitive.setPrimitiveType(pt);
primitive.offset = offset * primitive.indexBuffer->elementSize;
primitive.count = count;
primitive.minIndex = minIndex;
primitive.maxIndex = maxIndex > minIndex ? maxIndex : primitive.maxVertexCount - 1;
}
void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) {
@@ -1705,7 +1613,7 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
}
void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
const uint32_t instanceCount) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("draw");
@@ -1716,6 +1624,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
Handle<HwProgram> programHandle = pipelineState.program;
RasterState rasterState = pipelineState.rasterState;
PolygonOffset depthOffset = pipelineState.polygonOffset;
Viewport const& scissorBox = pipelineState.scissor;
auto* program = mResourceAllocator.handle_cast<VulkanProgram*>(programHandle);
commands->acquire(program);
@@ -1732,31 +1641,29 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
// Update the VK raster state.
const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
VulkanPipelineCache::RasterState const vulkanRasterState{
.cullMode = getCullMode(rasterState.culling),
.frontFace = getFrontFace(rasterState.inverseFrontFaces),
.depthBiasEnable = (depthOffset.constant || depthOffset.slope) ? true : false,
.blendEnable = rasterState.hasBlending(),
.depthWriteEnable = rasterState.depthWrite,
.alphaToCoverageEnable = rasterState.alphaToCoverage,
.srcColorBlendFactor = getBlendFactor(rasterState.blendFunctionSrcRGB),
.dstColorBlendFactor = getBlendFactor(rasterState.blendFunctionDstRGB),
.srcAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionSrcAlpha),
.dstAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionDstAlpha),
.colorWriteMask = (VkColorComponentFlags) (rasterState.colorWrite ? 0xf : 0x0),
.rasterizationSamples = rt->getSamples(),
.colorTargetCount = rt->getColorTargetCount(mCurrentRenderPass),
.colorBlendOp = rasterState.blendEquationRGB,
.alphaBlendOp = rasterState.blendEquationAlpha,
.depthCompareOp = rasterState.depthFunc,
.depthBiasConstantFactor = depthOffset.constant,
.depthBiasSlopeFactor = depthOffset.slope
};
auto vkraster = mPipelineCache.getCurrentRasterState();
vkraster.cullMode = getCullMode(rasterState.culling);
vkraster.frontFace = getFrontFace(rasterState.inverseFrontFaces);
vkraster.depthBiasEnable = (depthOffset.constant || depthOffset.slope) ? true : false;
vkraster.depthBiasConstantFactor = depthOffset.constant;
vkraster.depthBiasSlopeFactor = depthOffset.slope;
vkraster.blendEnable = rasterState.hasBlending();
vkraster.srcColorBlendFactor = getBlendFactor(rasterState.blendFunctionSrcRGB);
vkraster.dstColorBlendFactor = getBlendFactor(rasterState.blendFunctionDstRGB);
vkraster.colorBlendOp = rasterState.blendEquationRGB;
vkraster.srcAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionSrcAlpha);
vkraster.dstAlphaBlendFactor = getBlendFactor(rasterState.blendFunctionDstAlpha);
vkraster.alphaBlendOp = rasterState.blendEquationAlpha;
vkraster.colorWriteMask = (VkColorComponentFlags) (rasterState.colorWrite ? 0xf : 0x0);
vkraster.depthWriteEnable = rasterState.depthWrite;
vkraster.depthCompareOp = rasterState.depthFunc;
vkraster.rasterizationSamples = rt->getSamples();
vkraster.alphaToCoverageEnable = rasterState.alphaToCoverage;
vkraster.colorTargetCount = rt->getColorTargetCount(mCurrentRenderPass);
mPipelineCache.setCurrentRasterState(vkraster);
// Declare fixed-size arrays that get passed to the pipeCache and to vkCmdBindVertexBuffers.
VulkanVertexBufferInfo const* const vbi =
mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(prim.vertexBuffer->vbih);
uint32_t const bufferCount = vbi->attributes.size();
uint32_t const bufferCount = prim.vertexBuffer->attributes.size();
VkVertexInputAttributeDescription const* attribDesc = prim.vertexBuffer->getAttribDescriptions();
VkVertexInputBindingDescription const* bufferDesc = prim.vertexBuffer->getBufferDescriptions();
VkBuffer const* buffers = prim.vertexBuffer->getVkBuffers();
@@ -1764,7 +1671,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
// Push state changes to the VulkanPipelineCache instance. This is fast and does not make VK calls.
mPipelineCache.bindProgram(program);
mPipelineCache.bindRasterState(vulkanRasterState);
mPipelineCache.bindRasterState(mPipelineCache.getCurrentRasterState());
mPipelineCache.bindPrimitiveTopology(prim.primitiveTopology);
mPipelineCache.bindVertexArray(attribDesc, bufferDesc, bufferCount);
@@ -1849,37 +1756,6 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
return;
}
// Bind a new pipeline if the pipeline state changed.
// If allocation failed, skip the draw call and bail. We do not emit an error since the
// validation layer will already do so.
if (!mPipelineCache.bindPipeline(commands)) {
return;
}
// Next bind the vertex buffers and index buffer. One potential performance improvement is to
// avoid rebinding these if they are already bound, but since we do not (yet) support subranges
// it would be rare for a client to make consecutive draw calls with the same render primitive.
vkCmdBindVertexBuffers(cmdbuffer, 0, bufferCount, buffers, offsets);
vkCmdBindIndexBuffer(cmdbuffer, prim.indexBuffer->buffer.getGpuBuffer(), 0,
prim.indexBuffer->indexType);
// Finally, make the actual draw call. TODO: support subranges
const uint32_t firstIndex = indexOffset;
const int32_t vertexOffset = 0;
const uint32_t firstInstId = 0;
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
FVK_SYSTRACE_END();
}
void VulkanDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
// FIXME: implement me
}
void VulkanDriver::scissor(Viewport scissorBox) {
VulkanCommandBuffer* commands = &mCommands->get();
VkCommandBuffer cmdbuffer = commands->buffer();
// Set scissoring.
// clamp left-bottom to 0,0 and avoid overflows
constexpr int32_t maxvali = std::numeric_limits<int32_t>::max();
@@ -1898,9 +1774,35 @@ void VulkanDriver::scissor(Viewport scissorBox) {
.extent = { uint32_t(r - l), uint32_t(t - b) }
};
const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
rt->transformClientRectToPlatform(&scissor);
mPipelineCache.bindScissor(cmdbuffer, scissor);
// Bind a new pipeline if the pipeline state changed.
// If allocation failed, skip the draw call and bail. We do not emit an error since the
// validation layer will already do so.
if (!mPipelineCache.bindPipeline(commands)) {
return;
}
// Next bind the vertex buffers and index buffer. One potential performance improvement is to
// avoid rebinding these if they are already bound, but since we do not (yet) support subranges
// it would be rare for a client to make consecutive draw calls with the same render primitive.
vkCmdBindVertexBuffers(cmdbuffer, 0, bufferCount, buffers, offsets);
vkCmdBindIndexBuffer(cmdbuffer, prim.indexBuffer->buffer.getGpuBuffer(), 0,
prim.indexBuffer->indexType);
// Finally, make the actual draw call. TODO: support subranges
const uint32_t indexCount = prim.count;
const uint32_t firstIndex = prim.offset / prim.indexBuffer->elementSize;
const int32_t vertexOffset = 0;
const uint32_t firstInstId = 0;
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
FVK_SYSTRACE_END();
}
void VulkanDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
// FIXME: implement me
}
void VulkanDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
+5 -26
View File
@@ -45,30 +45,6 @@ public:
static Driver* create(VulkanPlatform* platform, VulkanContext const& context,
Platform::DriverConfig const& driverConfig) noexcept;
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
// Encapsulates the VK_EXT_debug_utils extension. In particular, we use
// vkSetDebugUtilsObjectNameEXT and vkCreateDebugUtilsMessengerEXT
class DebugUtils {
public:
static void setName(VkObjectType type, uint64_t handle, char const* name);
private:
static DebugUtils* get();
DebugUtils(VkInstance instance, VkDevice device, VulkanContext const* context);
~DebugUtils();
VkInstance const mInstance;
VkDevice const mDevice;
bool const mEnabled;
VkDebugUtilsMessengerEXT mDebugMessenger = VK_NULL_HANDLE;
static DebugUtils* mSingleton;
friend class VulkanDriver;
};
#endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
private:
void debugCommandBegin(CommandStream* cmds, bool synchronous,
const char* methodName) noexcept override;
@@ -101,10 +77,12 @@ private:
VulkanDriver& operator=(VulkanDriver const&) = delete;
private:
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
Handle<HwVertexBuffer> vbh,
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
Handle<HwIndexBuffer> ibh);
inline void setRenderPrimitiveRange(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
uint32_t offset, uint32_t minIndex, uint32_t maxIndex, uint32_t count);
void collectGarbage();
VulkanPlatform* mPlatform = nullptr;
@@ -117,6 +95,7 @@ private:
VulkanRenderPass mCurrentRenderPass = {};
VmaAllocator mAllocator = VK_NULL_HANDLE;
VkDebugReportCallbackEXT mDebugCallback = VK_NULL_HANDLE;
VkDebugUtilsMessengerEXT mDebugMessenger = VK_NULL_HANDLE;
VulkanContext mContext = {};
VulkanResourceAllocator mResourceAllocator;
+13 -46
View File
@@ -16,9 +16,7 @@
#include "VulkanHandles.h"
#include "VulkanDriver.h"
#include "VulkanConstants.h"
#include "VulkanDriver.h"
#include "VulkanMemory.h"
#include "spirv/VulkanSpirvUtils.h"
@@ -80,23 +78,6 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
};
VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, &module);
ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create shader module.");
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
std::string name{ builder.getName().c_str(), builder.getName().size() };
switch (static_cast<ShaderStage>(i)) {
case ShaderStage::VERTEX:
name += "_vs";
break;
case ShaderStage::FRAGMENT:
name += "_fs";
break;
default:
PANIC_POSTCONDITION("Unexpected stage");
break;
}
VulkanDriver::DebugUtils::setName(VK_OBJECT_TYPE_SHADER_MODULE,
reinterpret_cast<uint64_t>(module), name.c_str());
#endif
}
auto& groupInfo = builder.getSamplerGroupInfo();
@@ -112,10 +93,10 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
}
}
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << bundle.vertex
<< ", " << bundle.fragment << ")" << utils::io::endl;
#endif
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << bundle.vertex
<< ", " << bundle.fragment << ")" << utils::io::endl;
#endif
}
VulkanProgram::VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs,
@@ -281,23 +262,13 @@ uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPass& pass) co
return count;
}
VulkanVertexBufferInfo::VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
VulkanResource(VulkanResourceType::VERTEX_BUFFER_INFO),
attributes(attributes) {
}
VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
VulkanResourceAllocator* allocator, uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount),
VulkanResourceAllocator* allocator, uint8_t bufferCount, uint8_t attributeCount,
uint32_t elementCount, AttributeArray const& attribs)
: HwVertexBuffer(bufferCount, attributeCount, elementCount, attribs),
VulkanResource(VulkanResourceType::VERTEX_BUFFER),
vbih(vbih),
mInfo(new PipelineInfo(attribs.size())),
mResources(allocator) {
VulkanVertexBufferInfo const* const vbi = allocator->handle_cast<VulkanVertexBufferInfo*>(vbih);
mInfo = new PipelineInfo(vbi->attributes.size());
auto attribDesc = mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
auto bufferDesc = mInfo->mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
auto offsets = mInfo->mSoa.data<PipelineInfo::OFFSETS>();
@@ -305,8 +276,8 @@ VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool&
std::fill(mInfo->mSoa.begin<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>(),
mInfo->mSoa.end<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>(), -1);
for (uint32_t attribIndex = 0; attribIndex < vbi->attributes.size(); attribIndex++) {
Attribute attrib = vbi->attributes[attribIndex];
for (uint32_t attribIndex = 0; attribIndex < attribs.size(); attribIndex++) {
Attribute attrib = attribs[attribIndex];
bool const isInteger = attrib.flags & Attribute::FLAG_INTEGER_TARGET;
bool const isNormalized = attrib.flags & Attribute::FLAG_NORMALIZED;
VkFormat vkformat = getVkFormat(attrib.type, isNormalized, isInteger);
@@ -318,7 +289,7 @@ VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool&
// expects to receive floats or ints.
if (attrib.buffer == Attribute::BUFFER_UNUSED) {
vkformat = isInteger ? VK_FORMAT_R8G8B8A8_UINT : VK_FORMAT_R8G8B8A8_SNORM;
attrib = vbi->attributes[0];
attrib = attribs[0];
}
offsets[attribIndex] = attrib.offset;
attribDesc[attribIndex] = {
@@ -338,12 +309,8 @@ VulkanVertexBuffer::~VulkanVertexBuffer() {
delete mInfo;
}
void VulkanVertexBuffer::setBuffer(VulkanResourceAllocator const& allocator,
VulkanBufferObject* bufferObject, uint32_t index) {
VulkanVertexBufferInfo const* const vbi =
const_cast<VulkanResourceAllocator&>(allocator).handle_cast<VulkanVertexBufferInfo*>(vbih);
size_t count = vbi->attributes.size();
void VulkanVertexBuffer::setBuffer(VulkanBufferObject* bufferObject, uint32_t index) {
size_t count = attributes.size();
auto vkbuffers = mInfo->mSoa.data<PipelineInfo::VK_BUFFER>();
auto attribToBuffer = mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_TO_BUFFER_INDEX>();
for (uint8_t attribIndex = 0; attribIndex < count; attribIndex++) {
+4 -13
View File
@@ -129,21 +129,14 @@ private:
struct VulkanBufferObject;
struct VulkanVertexBufferInfo : public HwVertexBufferInfo, VulkanResource {
VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes);
AttributeArray attributes;
};
struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
VulkanResourceAllocator* allocator,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih);
VulkanResourceAllocator* allocator, uint8_t bufferCount, uint8_t attributeCount,
uint32_t elementCount, AttributeArray const& attributes);
~VulkanVertexBuffer();
void setBuffer(VulkanResourceAllocator const& allocator,
VulkanBufferObject* bufferObject, uint32_t index);
void setBuffer(VulkanBufferObject* bufferObject, uint32_t index);
inline VkVertexInputAttributeDescription const* getAttribDescriptions() {
return mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
@@ -161,8 +154,6 @@ struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
return mInfo->mSoa.data<PipelineInfo::OFFSETS>();
}
Handle<HwVertexBufferInfo> vbih;
private:
struct PipelineInfo {
PipelineInfo(size_t size)
@@ -170,7 +161,7 @@ private:
mSoa.resize(size);
}
// These correspond to the index of the element in the SoA
// These corresponds to the index of the element in the SoA
static constexpr uint8_t ATTRIBUTE_DESCRIPTION = 0;
static constexpr uint8_t BUFFER_DESCRIPTION = 1;
static constexpr uint8_t VK_BUFFER = 2;
@@ -34,6 +34,8 @@ using namespace bluevk;
namespace filament::backend {
static VulkanPipelineCache::RasterState createDefaultRasterState();
static VkShaderStageFlags getShaderStageFlags(VulkanPipelineCache::UsageFlags key, uint16_t binding) {
// NOTE: if you modify this function, you also need to modify getUsageFlags.
assert_invariant(binding < MAX_SAMPLER_COUNT);
@@ -71,7 +73,8 @@ VulkanPipelineCache::UsageFlags VulkanPipelineCache::disableUsageFlags(uint16_t
}
VulkanPipelineCache::VulkanPipelineCache(VulkanResourceAllocator* allocator)
: mResourceAllocator(allocator),
: mCurrentRasterState(createDefaultRasterState()),
mResourceAllocator(allocator),
mPipelineBoundResources(allocator) {
mDummyBufferWriteInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
mDummyBufferWriteInfo.pNext = nullptr;
@@ -566,7 +569,7 @@ void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
}
void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexcept {
mPipelineRequirements.rasterState = rasterState;
mPipelineRequirements.rasterState = mCurrentRasterState = rasterState;
}
void VulkanPipelineCache::bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept {
@@ -914,6 +917,23 @@ bool VulkanPipelineCache::DescEqual::operator()(const DescriptorKey& k1,
return true;
}
static VulkanPipelineCache::RasterState createDefaultRasterState() {
return VulkanPipelineCache::RasterState {
.cullMode = VK_CULL_MODE_NONE,
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
.depthBiasEnable = VK_FALSE,
.blendEnable = VK_FALSE,
.depthWriteEnable = VK_TRUE,
.alphaToCoverageEnable = true,
.colorWriteMask = 0xf,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
.colorTargetCount = 1,
.depthCompareOp = SamplerCompareFunc::LE,
.depthBiasConstantFactor = 0.0f,
.depthBiasSlopeFactor = 0.0f,
};
}
} // namespace filament::backend
#pragma clang diagnostic pop
@@ -199,6 +199,15 @@ public:
mPipelineBoundResources.acquire(resource);
}
inline RasterState getCurrentRasterState() const noexcept {
return mCurrentRasterState;
}
// We need to update this outside of bindRasterState due to VulkanDriver::draw.
inline void setCurrentRasterState(RasterState const& rasterState) noexcept {
mCurrentRasterState = rasterState;
}
private:
// PIPELINE LAYOUT CACHE KEY
// -------------------------
@@ -404,6 +413,7 @@ private:
VmaAllocator mAllocator = VK_NULL_HANDLE;
// Current requirements for the pipeline layout, pipeline, and descriptor sets.
RasterState mCurrentRasterState;
PipelineKey mPipelineRequirements = {};
DescriptorKey mDescriptorRequirements = {};
@@ -53,9 +53,6 @@ void deallocateResource(VulkanResourceAllocator* allocator, VulkanResourceType t
case VulkanResourceType::TIMER_QUERY:
allocator->destruct<VulkanTimerQuery>(Handle<HwTimerQuery>(id));
break;
case VulkanResourceType::VERTEX_BUFFER_INFO:
allocator->destruct<VulkanVertexBufferInfo>(Handle<HwVertexBufferInfo>(id));
break;
case VulkanResourceType::VERTEX_BUFFER:
allocator->destruct<VulkanVertexBuffer>(Handle<HwVertexBuffer>(id));
break;
+10 -16
View File
@@ -43,7 +43,6 @@ enum class VulkanResourceType : uint8_t {
TEXTURE,
TIMER_QUERY,
VERTEX_BUFFER,
VERTEX_BUFFER_INFO,
// Below are resources that are managed manually (i.e. not ref counted).
FENCE,
@@ -63,16 +62,15 @@ struct VulkanResourceBase {
protected:
explicit VulkanResourceBase(VulkanResourceType type)
: mRefCount(IS_HEAP_ALLOC_TYPE(type) ? 1 : 0),
mType(uint32_t(type)),
mHandleId(0) {
}
mType(type),
mHandleId(0) {}
private:
inline VulkanResourceType getType() const noexcept {
return VulkanResourceType(mType);
inline VulkanResourceType getType() {
return mType;
}
inline HandleBase::HandleId getId() const noexcept {
inline HandleBase::HandleId getId() {
return mHandleId;
}
@@ -81,27 +79,25 @@ private:
}
inline void ref() noexcept {
if (IS_HEAP_ALLOC_TYPE(getType())) {
if (IS_HEAP_ALLOC_TYPE(mType)) {
return;
}
assert_invariant(mRefCount < ((1<<24) - 1));
++mRefCount;
}
inline void deref() noexcept {
if (IS_HEAP_ALLOC_TYPE(getType())) {
if (IS_HEAP_ALLOC_TYPE(mType)) {
return;
}
assert_invariant(mRefCount > 0);
--mRefCount;
}
inline size_t refcount() const noexcept {
inline size_t refcount() noexcept {
return mRefCount;
}
uint32_t mRefCount : 24; // 16M is enough for the refcount
uint32_t mType : 8; // must be uint32_t or MSVC doesn't pack it. no codegen impact w/ clang.
size_t mRefCount = 0;
VulkanResourceType mType = VulkanResourceType::BUFFER_OBJECT;
HandleBase::HandleId mHandleId;
friend struct VulkanThreadSafeResource;
@@ -111,8 +107,6 @@ private:
friend class VulkanResourceManagerImpl;
};
static_assert(sizeof(VulkanResourceBase) == 8, "VulkanResourceBase should be 8 bytes");
struct VulkanThreadSafeResource {
protected:
explicit VulkanThreadSafeResource(VulkanResourceType type)
@@ -23,7 +23,6 @@
#include <bluevk/BlueVK.h>
#include <utils/PrivateImplementation-impl.h>
#include <utils/Panic.h>
#define SWAPCHAIN_RET_FUNC(func, handle, ...) \
if (mImpl->mSurfaceSwapChains.find(handle) != mImpl->mSurfaceSwapChains.end()) { \
@@ -159,9 +158,7 @@ ExtensionSet getInstanceExtensions() {
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
// Request these if available.
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
#endif
VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME,
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
@@ -184,9 +181,7 @@ ExtensionSet getInstanceExtensions() {
ExtensionSet getDeviceExtensions(VkPhysicalDevice device) {
std::string_view const TARGET_EXTS[] = {
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
VK_EXT_DEBUG_MARKER_EXTENSION_NAME,
#endif
VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME,
VK_KHR_MAINTENANCE1_EXTENSION_NAME,
VK_KHR_MAINTENANCE2_EXTENSION_NAME,
@@ -342,22 +337,36 @@ std::tuple<ExtensionSet, ExtensionSet> pruneExtensions(VkPhysicalDevice device,
ExtensionSet const& instExts, ExtensionSet const& deviceExts) {
ExtensionSet newInstExts = instExts;
ExtensionSet newDeviceExts = deviceExts;
if (vkGetPhysicalDeviceProperties2KHR) {
VkPhysicalDeviceDriverProperties driverProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES,
};
VkPhysicalDeviceProperties2 physicalDeviceProperties2 = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
.pNext = &driverProperties,
};
vkGetPhysicalDeviceProperties2KHR(device, &physicalDeviceProperties2);
char* driverInfo = driverProperties.driverInfo;
if (newInstExts.find(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) != newInstExts.end()) {
// Workaround for Mesa drivers. See issue #6192
if ((driverInfo && strstr(driverInfo, "Mesa"))) {
newInstExts.erase(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
}
}
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
// debugUtils and debugMarkers extensions are used mutually exclusively.
if (newInstExts.find(VK_EXT_DEBUG_UTILS_EXTENSION_NAME) != newInstExts.end()
&& newDeviceExts.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != newDeviceExts.end()) {
newDeviceExts.erase(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
}
#endif
#if FVK_ENABLED(FVK_DEBUG_VALIDATION)
// debugMarker must also request debugReport the instance extension. So check if that's present.
if (newDeviceExts.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != newDeviceExts.end()
&& newInstExts.find(VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == newInstExts.end()) {
newDeviceExts.erase(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
}
#endif
return std::tuple(newInstExts, newDeviceExts);
}
@@ -662,12 +671,6 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
context.mDebugMarkersSupported
= deviceExts.find(VK_EXT_DEBUG_MARKER_EXTENSION_NAME) != deviceExts.end();
#ifdef NDEBUG
// If we are in release build, we should not have turned on debug extensions
ASSERT_POSTCONDITION(!context.mDebugUtilsSupported && !context.mDebugMarkersSupported,
"Debug utils should not be enabled in release build.");
#endif
context.mDepthFormats = findAttachmentDepthFormats(mImpl->mPhysicalDevice);
assert_invariant(context.mDepthFormats.size() > 0);
+1 -1
View File
@@ -143,7 +143,7 @@ void BackendTest::renderTriangle(Handle<HwRenderTarget> renderTarget,
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.draw(state, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
}
+2 -5
View File
@@ -48,9 +48,7 @@ TrianglePrimitive::TrianglePrimitive(filament::backend::DriverApi& driverApi,
const size_t size = sizeof(math::float2) * 3;
mBufferObject = mDriverApi.createBufferObject(size, BufferObjectBinding::VERTEX, BufferUsage::STATIC);
mVertexBufferInfo = mDriverApi.createVertexBufferInfo(1, 1, attributes);
mVertexBuffer = mDriverApi.createVertexBuffer(mVertexCount, mVertexBufferInfo);
mVertexBuffer = mDriverApi.createVertexBuffer(1, 1, mVertexCount, attributes);
mDriverApi.setVertexBufferObject(mVertexBuffer, 0, mBufferObject);
BufferDescriptor vertexBufferDesc(gVertices, size);
mDriverApi.updateBufferObject(mBufferObject, std::move(vertexBufferDesc), 0);
@@ -62,7 +60,7 @@ TrianglePrimitive::TrianglePrimitive(filament::backend::DriverApi& driverApi,
mDriverApi.updateIndexBuffer(mIndexBuffer, std::move(indexBufferDesc), 0);
mRenderPrimitive = mDriverApi.createRenderPrimitive(
mVertexBuffer, mIndexBuffer, PrimitiveType::TRIANGLES);
mVertexBuffer, mIndexBuffer, PrimitiveType::TRIANGLES, 0, 0, 2, 3);
}
void TrianglePrimitive::updateVertices(const filament::math::float2 vertices[3]) noexcept {
@@ -104,7 +102,6 @@ void TrianglePrimitive::updateIndices(const index_type* indices, int count, int
TrianglePrimitive::~TrianglePrimitive() {
mDriverApi.destroyBufferObject(mBufferObject);
mDriverApi.destroyVertexBuffer(mVertexBuffer);
mDriverApi.destroyVertexBufferInfo(mVertexBufferInfo);
mDriverApi.destroyIndexBuffer(mIndexBuffer);
mDriverApi.destroyRenderPrimitive(mRenderPrimitive);
}
@@ -40,7 +40,6 @@ public:
using PrimitiveHandle = filament::backend::Handle<filament::backend::HwRenderPrimitive>;
using BufferObjectHandle = filament::backend::Handle<filament::backend::HwBufferObject>;
using VertexInfoHandle = filament::backend::Handle<filament::backend::HwVertexBufferInfo>;
using VertexHandle = filament::backend::Handle<filament::backend::HwVertexBuffer>;
using IndexHandle = filament::backend::Handle<filament::backend::HwIndexBuffer>;
@@ -64,7 +63,6 @@ private:
PrimitiveHandle mRenderPrimitive;
BufferObjectHandle mBufferObject;
VertexInfoHandle mVertexBufferInfo;
VertexHandle mVertexBuffer;
IndexHandle mIndexBuffer;
+1 -1
View File
@@ -405,7 +405,7 @@ TEST_F(BackendTest, ColorResolve) {
api.beginFrame(0, 0);
api.beginRenderPass(srcRenderTarget, params);
api.bindUniformBuffer(0, ubuffer);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.draw(state, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
api.endFrame(0);
+1 -1
View File
@@ -172,7 +172,7 @@ TEST_F(BackendTest, VertexBufferUpdate) {
}
getDriverApi().beginRenderPass(defaultRenderTarget, params);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 1);
getDriverApi().endRenderPass();
triangleIndex++;
+2 -2
View File
@@ -218,7 +218,7 @@ TEST_F(BackendTest, FeedbackLoops) {
.sourceLevel = float(sourceLevel),
});
api.beginRenderPass(renderTargets[targetLevel], params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.draw(state, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
}
@@ -237,7 +237,7 @@ TEST_F(BackendTest, FeedbackLoops) {
.sourceLevel = float(sourceLevel),
});
api.beginRenderPass(renderTargets[targetLevel], params);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.draw(state, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
}
+1 -1
View File
@@ -130,7 +130,7 @@ TEST_F(BackendTest, MRT) {
// Draw a triangle.
getDriverApi().beginRenderPass(renderTarget, params);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 1);
getDriverApi().endRenderPass();
getDriverApi().flush();
+4 -5
View File
@@ -168,7 +168,7 @@ TEST_F(BackendTest, SetMinMaxLevel) {
ps.rasterState.colorWrite = true;
ps.rasterState.depthWrite = false;
api.beginRenderPass(renderTarget, params);
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
api.draw(ps, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
}
@@ -182,6 +182,7 @@ TEST_F(BackendTest, SetMinMaxLevel) {
params.flags.discardEnd = TargetBufferFlags::NONE;
PipelineState state;
state.scissor = params.viewport;
state.program = textureProgram;
state.rasterState.colorWrite = true;
state.rasterState.depthWrite = false;
@@ -202,8 +203,7 @@ TEST_F(BackendTest, SetMinMaxLevel) {
// Because the min level is 1, the result color should be the white triangle drawn in the
// previous pass.
api.beginRenderPass(defaultRenderTarget, params);
api.scissor(params.viewport);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.draw(state, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
// Adjust the base mip to 2.
@@ -221,8 +221,7 @@ TEST_F(BackendTest, SetMinMaxLevel) {
params.flags.clear = TargetBufferFlags::NONE;
params.flags.discardStart = TargetBufferFlags::NONE;
api.beginRenderPass(defaultRenderTarget, params);
api.scissor(params.viewport);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.draw(state, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
api.commit(swapChain);
@@ -104,7 +104,7 @@ TEST_F(BackendTest, MissingRequiredAttributes) {
// Render a triangle.
getDriverApi().beginRenderPass(defaultRenderTarget, params);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 1);
getDriverApi().endRenderPass();
getDriverApi().flush();
+2 -2
View File
@@ -309,7 +309,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
state.rasterState.depthWrite = false;
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 1);
getDriverApi().endRenderPass();
@@ -438,7 +438,7 @@ TEST_F(ReadPixelsTest, ReadPixelsPerformance) {
// Render some content, just so we don't read back uninitialized data.
getDriverApi().beginRenderPass(renderTarget, params);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 1);
getDriverApi().endRenderPass();
PixelBufferDescriptor descriptor(buffer, renderTargetSize * renderTargetSize * 4,
@@ -120,7 +120,7 @@ TEST_F(BackendTest, RenderExternalImageWithoutSet) {
// Render a triangle.
getDriverApi().beginRenderPass(defaultRenderTarget, params);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 1);
getDriverApi().endRenderPass();
getDriverApi().flush();
@@ -242,7 +242,7 @@ TEST_F(BackendTest, RenderExternalImage) {
// Render a triangle.
getDriverApi().beginRenderPass(defaultRenderTarget, params);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 1);
getDriverApi().endRenderPass();
getDriverApi().flush();
+5 -6
View File
@@ -134,13 +134,13 @@ TEST_F(BackendTest, ScissorViewportRegion) {
ps.program = program;
ps.rasterState.colorWrite = true;
ps.rasterState.depthWrite = false;
ps.scissor = scissor;
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0);
api.beginRenderPass(srcRenderTarget, params);
api.scissor(scissor);
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
api.draw(ps, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
readPixelsAndAssertHash("scissor", kSrcTexWidth >> 1, kSrcTexHeight >> 1, fullRenderTarget,
@@ -225,21 +225,20 @@ TEST_F(BackendTest, ScissorViewportEdgeCases) {
ps.program = program;
ps.rasterState.colorWrite = true;
ps.rasterState.depthWrite = false;
ps.scissor = scissor;
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0);
api.beginRenderPass(renderTarget, params);
api.scissor(scissor);
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
api.draw(ps, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
params.viewport = topLeftViewport;
params.flags.clear = TargetBufferFlags::NONE;
params.flags.discardStart = TargetBufferFlags::NONE;
api.beginRenderPass(renderTarget, params);
api.scissor(scissor);
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
api.draw(ps, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
readPixelsAndAssertHash(
+4 -4
View File
@@ -113,7 +113,7 @@ public:
api.beginFrame(0, 0);
api.beginRenderPass(renderTarget, params);
api.draw(ps, smallTriangle.getRenderPrimitive(), 0, 3, 1);
api.draw(ps, smallTriangle.getRenderPrimitive(), 1);
api.endRenderPass();
// Step 2: Render a larger triangle with the stencil test enabled.
@@ -126,7 +126,7 @@ public:
ps.stencilState.front.ref = 0u;
api.beginRenderPass(renderTarget, params);
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
api.draw(ps, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
api.commit(swapChain);
@@ -240,7 +240,7 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
api.beginFrame(0, 0);
api.beginRenderPass(renderTarget0, params);
api.draw(ps, smallTriangle.getRenderPrimitive(), 0, 3, 1);
api.draw(ps, smallTriangle.getRenderPrimitive(), 1);
api.endRenderPass();
// Step 2: Render a larger triangle with the stencil test enabled.
@@ -255,7 +255,7 @@ TEST_F(BasicStencilBufferTest, StencilBufferMSAA) {
ps.stencilState.front.ref = 0u;
api.beginRenderPass(renderTarget1, params);
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
api.draw(ps, triangle.getRenderPrimitive(), 1);
api.endRenderPass();
api.commit(swapChain);
+3 -23
View File
@@ -178,7 +178,6 @@ public:
using Backend = backend::Backend;
using DriverConfig = backend::Platform::DriverConfig;
using FeatureLevel = backend::FeatureLevel;
using StereoscopicType = backend::StereoscopicType;
/**
* Config is used to define the memory footprint used by the engine, such as the
@@ -298,25 +297,6 @@ public:
*/
size_t textureUseAfterFreePoolSize = 0;
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL backend.
*/
bool disableParallelShaderCompile = false;
/*
* The type of technique for stereoscopic rendering.
*
* This setting determines the algorithm used when stereoscopic rendering is enabled. This
* decision applies to the entire Engine for the lifetime of the Engine. E.g., multiple
* Views created from the Engine must use the same stereoscopic type.
*
* Each view can enable stereoscopic rendering via the StereoscopicOptions::enable flag.
*
* @see View::setStereoscopicOptions
*/
StereoscopicType stereoscopicType = StereoscopicType::INSTANCED;
/*
* The number of eyes to render when stereoscopic rendering is enabled. Supported values are
* between 1 and Engine::getMaxStereoscopicEyes() (inclusive).
@@ -596,12 +576,12 @@ public:
size_t getMaxAutomaticInstances() const noexcept;
/**
* Queries the device and platform for support of the given stereoscopic type.
* Queries the device and platform for instanced stereo rendering support.
*
* @return true if the given stereo rendering is supported, false otherwise
* @return true if stereo rendering is supported, false otherwise
* @see View::setStereoscopicOptions
*/
bool isStereoSupported(StereoscopicType stereoscopicType) const noexcept;
bool isStereoSupported() const noexcept;
/**
* Retrieves the configuration settings of this Engine.
@@ -583,6 +583,8 @@ public:
VertexBuffer* UTILS_NULLABLE vertices = nullptr;
IndexBuffer* UTILS_NULLABLE indices = nullptr;
size_t offset = 0;
size_t minIndex = 0;
size_t maxIndex = 0;
size_t count = 0;
MaterialInstance const* UTILS_NULLABLE materialInstance = nullptr;
PrimitiveType type = PrimitiveType::TRIANGLES;
+5 -15
View File
@@ -21,7 +21,7 @@
#include <math/mathfwd.h>
#include <cstdint>
#include <stdint.h>
namespace filament {
@@ -39,12 +39,10 @@ namespace filament {
*
* - Configurable tone mapping operators
* - GenericToneMapper
* - AgXToneMapper
* - Fixed-aesthetic tone mapping operators
* - ACESToneMapper
* - ACESLegacyToneMapper
* - FilmicToneMapper
* - PBRNeutralToneMapper
* - Debug/validation tone mapping operators
* - LinearToneMapper
* - DisplayRangeToneMapper
@@ -119,22 +117,11 @@ struct UTILS_PUBLIC FilmicToneMapper final : public ToneMapper {
math::float3 operator()(math::float3 x) const noexcept override;
};
/**
* Khronos PBR Neutral tone mapping operator. This tone mapper was designed
* to preserve the appearance of materials across lighting conditions while
* avoiding artifacts in the highlights in high dynamic range conditions.
*/
struct UTILS_PUBLIC PBRNeutralToneMapper final : public ToneMapper {
PBRNeutralToneMapper() noexcept;
~PBRNeutralToneMapper() noexcept final;
math::float3 operator()(math::float3 x) const noexcept override;
};
/**
* AgX tone mapping operator.
*/
struct UTILS_PUBLIC AgxToneMapper final : public ToneMapper {
enum class AgxLook : uint8_t {
NONE = 0, //!< Base contrast with no look applied
PUNCHY, //!< A punchy and more chroma laden look for sRGB displays
@@ -198,6 +185,9 @@ struct UTILS_PUBLIC GenericToneMapper final : public ToneMapper {
/** Returns the contrast of the curve as a strictly positive value. */
float getContrast() const noexcept;
/** Returns how fast scene referred values map to output white as a value between 0.0 and 1.0. */
float getShoulder() const noexcept;
/** Returns the middle gray point for input values as a value between 0.0 and 1.0. */
float getMidGrayIn() const noexcept;
+1 -1
View File
@@ -719,7 +719,7 @@ public:
void setDebugCamera(Camera* UTILS_NULLABLE camera) noexcept;
//! debugging: returns a Camera from the point of view of *the* dominant directional light used for shadowing.
Camera const* UTILS_NULLABLE getDirectionalShadowCamera() const noexcept;
Camera const* UTILS_NULLABLE getDirectionalLightCamera() const noexcept;
/** Result of a picking query */
-128
View File
@@ -1,128 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BIMAP_H
#define TNT_FILAMENT_BIMAP_H
#include <utils/debug.h>
#include <tsl/robin_map.h>
#include <functional>
#include <memory>
#include <utility>
#include <stddef.h>
namespace filament {
/*
* A semi-generic custom bimap. This bimap stores a key/value pair and can retrieve
* the value from the key and the key from the value.
* It is optimized for large keys and small values. The keys are stored out-of-line and are
* never moved.
*/
template<typename Key, typename Value,
typename KeyHash = std::hash<Key>,
typename ValueHash = std::hash<Value>,
typename Allocator = std::allocator<Key>>
class Bimap {
struct KeyDelegate {
Key const* pKey = nullptr;
bool operator==(KeyDelegate const& rhs) const noexcept {
return *pKey == *rhs.pKey;
}
};
// KeyWrapperHash delegates the hash computation to KeyHash
struct KeyHasherDelegate {
size_t operator()(KeyDelegate const& p) const noexcept {
KeyHash const h;
return h(*p.pKey);
}
};
using ForwardMap = tsl::robin_map<KeyDelegate, Value, KeyHasherDelegate>;
using BackwardMap = tsl::robin_map<Value, KeyDelegate, ValueHash>;
Allocator mAllocator;
ForwardMap mForwardMap;
BackwardMap mBackwardMap;
public:
Bimap() = default;
explicit Bimap(Allocator&& allocator)
: mAllocator(std::forward<Allocator>(allocator)) {
}
void reserve(size_t capacity) {
mForwardMap.reserve(capacity);
mBackwardMap.reserve(capacity);
}
bool empty() const noexcept {
return mForwardMap.empty() && mBackwardMap.empty();
}
// insert a new key/value pair
void insert(Key const& key, Value const& value) noexcept {
Key* pKey = mAllocator.allocate(1); // allocate storage for the key
new((void*)pKey) Key{ key }; // copy-construct the key
mForwardMap.insert({{ pKey }, value });
mBackwardMap.insert({ value, { pKey }});
}
typename ForwardMap::iterator end() { return mForwardMap.end(); }
// Find the value iterator from the key in O(1)
typename ForwardMap::const_iterator find(Key const& key) const {
return mForwardMap.find(KeyDelegate{ .pKey = &key });
}
typename ForwardMap::iterator find(Key const& key) {
return mForwardMap.find(KeyDelegate{ .pKey = &key });
}
// Find the key iterator from the value in O(1). precondition, the value must exist.
typename BackwardMap::const_iterator find(Value const& value) const {
auto pos = mBackwardMap.find(value);
assert_invariant( pos != mBackwardMap.end() );
return pos;
}
typename BackwardMap::iterator find(Value& value) const {
return mBackwardMap.find(value);
}
// erase a key/value pair using an iterator to the value
void erase(typename BackwardMap::const_iterator it) {
// find the key
Key const& key = *(it->second.pKey);
// and its iterator
auto pos = find(key);
// destroy the key
it->second.pKey->~Key();
// free its memory
mAllocator.deallocate(const_cast<Key *>(it->second.pKey), 1);
// remove the entries from both maps
mForwardMap.erase(pos);
mBackwardMap.erase(it);
}
};
} // namespace filament
#endif // TNT_FILAMENT_BIMAP_H
+2 -2
View File
@@ -331,8 +331,8 @@ const Engine::Config& Engine::getConfig() const noexcept {
return downcast(this)->getConfig();
}
bool Engine::isStereoSupported(StereoscopicType stereoscopicType) const noexcept {
return downcast(this)->isStereoSupported(stereoscopicType);
bool Engine::isStereoSupported() const noexcept {
return downcast(this)->isStereoSupported();
}
size_t Engine::getMaxStereoscopicEyes() noexcept {
+66 -40
View File
@@ -16,16 +16,6 @@
#include "HwRenderPrimitiveFactory.h"
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <private/backend/DriverApi.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Hash.h>
#include <stdlib.h>
namespace filament {
@@ -33,60 +23,96 @@ namespace filament {
using namespace utils;
using namespace backend;
size_t HwRenderPrimitiveFactory::KeyHash::operator()(
HwRenderPrimitiveFactory::Key const& p) const noexcept {
return utils::hash::combine(
p.params.vbh.getId(),
utils::hash::combine(p.params.ibh.getId(), (size_t)p.params.type));
bool operator<(HwRenderPrimitiveFactory::Key const& lhs,
HwRenderPrimitiveFactory::Key const& rhs) noexcept {
if (lhs.vbh == rhs.vbh) {
if (lhs.ibh == rhs.ibh) {
if (lhs.offset == rhs.offset) {
if (lhs.count == rhs.count) {
return lhs.type < rhs.type;
} else {
return lhs.count < rhs.count;
}
} else {
return lhs.offset < rhs.offset;
}
} else {
return lhs.ibh < rhs.ibh;
}
} else {
return lhs.vbh < rhs.vbh;
}
}
bool operator==(HwRenderPrimitiveFactory::Key const& lhs,
inline bool operator<(HwRenderPrimitiveFactory::Entry const& lhs,
HwRenderPrimitiveFactory::Entry const& rhs) noexcept {
return lhs.key < rhs.key;
}
inline bool operator<(HwRenderPrimitiveFactory::Key const& lhs,
HwRenderPrimitiveFactory::Entry const& rhs) noexcept {
return lhs < rhs.key;
}
inline bool operator<(HwRenderPrimitiveFactory::Entry const& lhs,
HwRenderPrimitiveFactory::Key const& rhs) noexcept {
return lhs.params.vbh == rhs.params.vbh &&
lhs.params.ibh == rhs.params.ibh &&
lhs.params.type == rhs.params.type;
return lhs.key < rhs;
}
// ------------------------------------------------------------------------------------------------
HwRenderPrimitiveFactory::HwRenderPrimitiveFactory()
: mArena("HwRenderPrimitiveFactory::mArena", SET_ARENA_SIZE),
mBimap(mArena) {
mBimap.reserve(256);
mSet(mArena) {
mMap.reserve(256);
}
HwRenderPrimitiveFactory::~HwRenderPrimitiveFactory() noexcept = default;
void HwRenderPrimitiveFactory::terminate(DriverApi&) noexcept {
assert_invariant(mBimap.empty());
void HwRenderPrimitiveFactory::terminate(DriverApi& driver) noexcept {
assert_invariant(mMap.empty());
assert_invariant(mSet.empty());
}
auto HwRenderPrimitiveFactory::create(DriverApi& driver,
backend::VertexBufferHandle vbh,
backend::IndexBufferHandle ibh,
backend::PrimitiveType type) noexcept -> Handle {
RenderPrimitiveHandle HwRenderPrimitiveFactory::create(DriverApi& driver,
VertexBufferHandle vbh, IndexBufferHandle ibh,
PrimitiveType type, uint32_t offset, uint32_t minIndex, uint32_t maxIndex,
uint32_t count) noexcept {
const Key key = { vbh, ibh, offset, count, type };
// see if we already have seen this RenderPrimitive
Key const key{{ vbh, ibh, type }, 1 };
auto pos = mBimap.find(key);
auto pos = mSet.find(key);
// the common case is that we've never seen it (i.e.: no reuse)
if (UTILS_LIKELY(pos == mBimap.end())) {
auto handle = driver.createRenderPrimitive(vbh, ibh, type);
mBimap.insert(key, { handle });
if (UTILS_LIKELY(pos == mSet.end())) {
// create the backend object
auto handle = driver.createRenderPrimitive(vbh, ibh,
type, offset, minIndex, maxIndex, count);
// insert key/handle in our set with a refcount of 1
// IMPORTANT: std::set<> doesn't invalidate iterators in insert/erase
auto [ipos, _] = mSet.insert({ key, handle, 1 });
// map the handle back to the key/payload
mMap.insert({ handle.getId(), ipos });
return handle;
}
pos->first.pKey->refs++;
return pos->second.handle;
pos->refs++;
return pos->handle;
}
void HwRenderPrimitiveFactory::destroy(DriverApi& driver, Handle handle) noexcept {
void HwRenderPrimitiveFactory::destroy(DriverApi& driver, RenderPrimitiveHandle rph) noexcept {
// look for this handle in our map
auto pos = mBimap.find(Value{ handle });
if (--pos->second.pKey->refs == 0) {
mBimap.erase(pos);
driver.destroyRenderPrimitive(handle);
auto pos = mMap.find(rph.getId());
// it must be there
assert_invariant(pos != mMap.end());
// check the refcount and destroy if needed
auto ipos = pos->second;
if (--ipos->refs == 0) {
mSet.erase(ipos);
mMap.erase(pos);
driver.destroyRenderPrimitive(rph);
}
}
+50 -45
View File
@@ -17,17 +17,17 @@
#ifndef TNT_FILAMENT_HWRENDERPRIMITIVEFACTORY_H
#define TNT_FILAMENT_HWRENDERPRIMITIVEFACTORY_H
#include "Bimap.h"
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <private/backend/DriverApi.h>
#include <utils/Allocator.h>
#include <functional>
#include <tsl/robin_map.h>
#include <set>
#include <stddef.h>
#include <stdint.h>
namespace filament {
@@ -36,7 +36,6 @@ class FEngine;
class HwRenderPrimitiveFactory {
public:
using Handle = backend::RenderPrimitiveHandle;
HwRenderPrimitiveFactory();
~HwRenderPrimitiveFactory() noexcept;
@@ -48,64 +47,70 @@ public:
void terminate(backend::DriverApi& driver) noexcept;
struct Parameters { // 20 bytes
backend::RenderPrimitiveHandle create(backend::DriverApi& driver,
backend::VertexBufferHandle vbh,
backend::IndexBufferHandle ibh,
backend::PrimitiveType type,
uint32_t offset,
uint32_t minIndex,
uint32_t maxIndex,
uint32_t count) noexcept;
void destroy(backend::DriverApi& driver,
backend::RenderPrimitiveHandle rph) noexcept;
private:
struct Key { // 20 bytes
backend::VertexBufferHandle vbh; // 4
backend::IndexBufferHandle ibh; // 4
uint32_t offset; // 4
uint32_t count; // 4
backend::PrimitiveType type; // 4
};
Handle create(backend::DriverApi& driver,
backend::VertexBufferHandle vbh,
backend::IndexBufferHandle ibh,
backend::PrimitiveType type) noexcept;
void destroy(backend::DriverApi& driver, Handle handle) noexcept;
private:
struct Key {
Parameters params;
mutable uint32_t refs; // 4 bytes
struct Entry { // 28 bytes
Key key; // 20
backend::RenderPrimitiveHandle handle; // 4
mutable uint32_t refs; // 4
};
struct KeyHash {
size_t operator()(Key const& p) const noexcept;
};
friend bool operator==(Key const& lhs, Key const& rhs) noexcept;
struct Value { // 4 bytes
Handle handle;
};
struct ValueHash {
size_t operator()(Value const& p) const noexcept {
std::hash<Handle::HandleId> const h;
return h(p.handle.getId());
}
};
friend bool operator==(Value const& lhs, Value const& rhs) noexcept {
return lhs.handle == rhs.handle;
}
// Size of the arena used for the "set" part of the bimap
static constexpr size_t SET_ARENA_SIZE = 4 * 1024 * 1024;
// Arena for the set<>, using a pool allocator inside a heap area.
using PoolAllocatorArena = utils::Arena<
utils::PoolAllocatorWithFallback<sizeof(Key)>,
using Arena = utils::Arena<
utils::PoolAllocator<64>, // this seems to work with clang and msvc
utils::LockingPolicy::NoLock,
#ifndef NDEBUG
utils::TrackingPolicy::HighWatermark,
#else
utils::TrackingPolicy::Untracked,
#endif
utils::AreaPolicy::HeapArea>;
using Set = std::set<
Entry,
std::less<>,
utils::STLAllocator<Entry, Arena>>;
using Map = tsl::robin_map<
backend::RenderPrimitiveHandle::HandleId,
Set::const_iterator>;
// Arena where the set memory is allocated
PoolAllocatorArena mArena;
Arena mArena;
// The special Bimap
Bimap<Key, Value, KeyHash, ValueHash,
utils::STLAllocator<Key, PoolAllocatorArena>> mBimap;
// set of HwRenderPrimitive data
Set mSet;
// map of RenderPrimitiveHandle to Set Entry
Map mMap;
friend bool operator<(Key const& lhs, Key const& rhs) noexcept;
friend bool operator<(Key const& lhs, Entry const& rhs) noexcept;
friend bool operator<(Entry const& lhs, Key const& rhs) noexcept;
friend bool operator<(Entry const& lhs, Entry const& rhs) noexcept;
};
} // namespace filament
@@ -1,94 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "HwVertexBufferInfoFactory.h"
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <private/backend/DriverApi.h>
#include <utils/Log.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Hash.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
namespace filament {
using namespace utils;
using namespace backend;
size_t HwVertexBufferInfoFactory::Parameters::hash() const noexcept {
static_assert((sizeof(*this) % sizeof(uint32_t)) == 0);
return utils::hash::murmur3(
reinterpret_cast<uint32_t const*>(this), sizeof(Parameters) / sizeof(uint32_t), 0);
}
bool operator==(HwVertexBufferInfoFactory::Parameters const& lhs,
HwVertexBufferInfoFactory::Parameters const& rhs) noexcept {
return !memcmp(&lhs, &rhs, sizeof(HwVertexBufferInfoFactory::Parameters));
}
// ------------------------------------------------------------------------------------------------
HwVertexBufferInfoFactory::HwVertexBufferInfoFactory()
: mArena("HwVertexBufferInfoFactory::mArena", SET_ARENA_SIZE),
mBimap(mArena) {
mBimap.reserve(256);
}
HwVertexBufferInfoFactory::~HwVertexBufferInfoFactory() noexcept = default;
void HwVertexBufferInfoFactory::terminate(DriverApi&) noexcept {
assert_invariant(mBimap.empty());
}
auto HwVertexBufferInfoFactory::create(DriverApi& driver,
uint8_t bufferCount,
uint8_t attributeCount,
backend::AttributeArray attributes) noexcept -> Handle {
Key const key{{ bufferCount, attributeCount, {}, attributes }, 1 };
auto pos = mBimap.find(key);
// the common case is that we've never seen it (i.e.: no reuse)
if (UTILS_LIKELY(pos == mBimap.end())) {
auto handle = driver.createVertexBufferInfo(
bufferCount, attributeCount,
attributes);
mBimap.insert(key, { handle });
return handle;
}
++(pos->first.pKey->refs);
return pos->second.handle;
}
void HwVertexBufferInfoFactory::destroy(DriverApi& driver, Handle handle) noexcept {
// look for this handle in our map
auto pos = mBimap.find(Value{ handle });
if (--(pos->second.pKey->refs) == 0) {
mBimap.erase(pos);
driver.destroyVertexBufferInfo(handle);
}
}
} // namespace filament
-119
View File
@@ -1,119 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_HWVERTEXBUFFERINFOFACTORY_H
#define TNT_FILAMENT_HWVERTEXBUFFERINFOFACTORY_H
#include "Bimap.h"
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <utils/Allocator.h>
#include <functional>
#include <stddef.h>
#include <stdint.h>
namespace filament {
class FEngine;
class HwVertexBufferInfoFactory {
public:
using Handle = backend::VertexBufferInfoHandle;
HwVertexBufferInfoFactory();
~HwVertexBufferInfoFactory() noexcept;
HwVertexBufferInfoFactory(HwVertexBufferInfoFactory const& rhs) = delete;
HwVertexBufferInfoFactory(HwVertexBufferInfoFactory&& rhs) noexcept = delete;
HwVertexBufferInfoFactory& operator=(HwVertexBufferInfoFactory const& rhs) = delete;
HwVertexBufferInfoFactory& operator=(HwVertexBufferInfoFactory&& rhs) noexcept = delete;
void terminate(backend::DriverApi& driver) noexcept;
struct Parameters { // 136 bytes
uint8_t bufferCount;
uint8_t attributeCount;
uint8_t padding[2] = {};
backend::AttributeArray attributes;
size_t hash() const noexcept;
};
friend bool operator==(Parameters const& lhs, Parameters const& rhs) noexcept;
Handle create(backend::DriverApi& driver,
uint8_t bufferCount,
uint8_t attributeCount,
backend::AttributeArray attributes) noexcept;
void destroy(backend::DriverApi& driver, Handle handle) noexcept;
private:
struct Key { // 140 bytes
Parameters params;
mutable uint32_t refs; // 4 bytes
bool operator==(Key const& rhs) const noexcept {
return params == rhs.params;
}
};
struct KeyHasher {
size_t operator()(Key const& p) const noexcept {
return p.params.hash();
}
};
struct Value {
Handle handle;
};
struct ValueHasher {
size_t operator()(Value v) const noexcept {
std::hash<Handle::HandleId> const hasher;
return hasher(v.handle.getId());
}
};
friend bool operator==(Value const& lhs, Value const& rhs) noexcept {
return lhs.handle == rhs.handle;
}
// Size of the arena used for the "set" part of the bimap
static constexpr size_t SET_ARENA_SIZE = 4 * 1024 * 1024;
// Arena for the set<>, using a pool allocator inside a heap area.
using PoolAllocatorArena = utils::Arena<
utils::PoolAllocatorWithFallback<sizeof(Key)>,
utils::LockingPolicy::NoLock,
utils::TrackingPolicy::Untracked,
utils::AreaPolicy::HeapArea>;
// Arena where the set memory is allocated
PoolAllocatorArena mArena;
// The special Bimap
Bimap<Key, Value, KeyHasher, ValueHasher,
utils::STLAllocator<Key, PoolAllocatorArena>> mBimap;
};
} // namespace filament
#endif // TNT_FILAMENT_HWVERTEXBUFFERINFOFACTORY_H
+43 -129
View File
@@ -184,15 +184,15 @@ FMaterial* PostProcessManager::PostProcessMaterial::getMaterial(FEngine& engine)
}
UTILS_NOINLINE
std::pair<backend::PipelineState, backend::Viewport>
PostProcessManager::PostProcessMaterial::getPipelineState(
PipelineState PostProcessManager::PostProcessMaterial::getPipelineState(
FEngine& engine, Variant::type_t variantKey) const noexcept {
FMaterial* const material = getMaterial(engine);
material->prepareProgram(Variant{ variantKey });
return {{
.program = material->getProgram(Variant{ variantKey }),
.rasterState = material->getRasterState() },
material->getDefaultInstance()->getScissor() };
return {
.program = material->getProgram(Variant{ variantKey }),
.rasterState = material->getRasterState(),
.scissor = material->getDefaultInstance()->getScissor()
};
}
UTILS_NOINLINE
@@ -262,7 +262,6 @@ static const PostProcessManager::MaterialInfo sMaterialListFeatureLevel0[] = {
static const PostProcessManager::MaterialInfo sMaterialList[] = {
{ "bilateralBlur", MATERIAL(BILATERALBLUR) },
{ "bilateralBlurBentNormals", MATERIAL(BILATERALBLURBENTNORMALS) },
{ "blitArray", MATERIAL(BLITARRAY) },
{ "bloomDownsample", MATERIAL(BLOOMDOWNSAMPLE) },
{ "bloomDownsample2x", MATERIAL(BLOOMDOWNSAMPLE2X) },
{ "bloomDownsample9", MATERIAL(BLOOMDOWNSAMPLE9) },
@@ -382,7 +381,7 @@ backend::Handle<backend::HwTexture> PostProcessManager::getZeroTextureArray() co
UTILS_NOINLINE
void PostProcessManager::render(FrameGraphResources::RenderPassInfo const& out,
backend::PipelineState const& pipeline, backend::Viewport const& scissor,
backend::PipelineState const& pipeline,
DriverApi& driver) const noexcept {
assert_invariant(
@@ -393,8 +392,7 @@ void PostProcessManager::render(FrameGraphResources::RenderPassInfo const& out,
FEngine const& engine = mEngine;
Handle<HwRenderPrimitive> const fullScreenQuad = engine.getFullScreenRenderPrimitive();
driver.beginRenderPass(out.target, out.params);
driver.scissor(scissor);
driver.draw(pipeline, fullScreenQuad, 0, 3, 1);
driver.draw(pipeline, fullScreenQuad, 1);
driver.endRenderPass();
}
@@ -880,8 +878,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
mi->commit(driver);
mi->use(driver);
auto pipeline = material.getPipelineState(mEngine);
pipeline.first.rasterState.depthFunc = RasterState::DepthFunc::L;
PipelineState pipeline(material.getPipelineState(mEngine));
pipeline.rasterState.depthFunc = RasterState::DepthFunc::L;
assert_invariant(ssao.params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH);
render(ssao, pipeline, driver);
});
@@ -995,8 +993,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::bilateralBlurPass(FrameGraph
mi->commit(driver);
mi->use(driver);
auto pipeline = material.getPipelineState(mEngine);
pipeline.first.rasterState.depthFunc = RasterState::DepthFunc::L;
PipelineState pipeline(material.getPipelineState(mEngine));
pipeline.rasterState.depthFunc = RasterState::DepthFunc::L;
render(blurred, pipeline, driver);
});
@@ -1655,7 +1653,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
mi->setParameter("coc", inOutCoc, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
mi->use(driver);
auto const pipeline = material.getPipelineState(mEngine, variant);
const PipelineState pipeline(material.getPipelineState(mEngine, variant));
for (size_t level = 0 ; level < mipmapCount - 1u ; level++) {
const float w = FTexture::valueForLevel(level, desc.width);
@@ -2102,7 +2100,7 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
mi13->commit(driver);
// PipelineState for both materials should be the same
auto const pipeline = material9.getPipelineState(mEngine);
const PipelineState pipeline(material9.getPipelineState(mEngine));
for (size_t i = 1; i < inoutBloomOptions.levels; i++) {
auto hwDstRT = resources.getRenderPassInfo(data.outRT[i]);
@@ -2146,9 +2144,9 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST});
mi->use(driver);
auto pipeline = material.getPipelineState(mEngine);
pipeline.first.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
pipeline.first.rasterState.blendFunctionDstRGB = BlendFunction::ONE;
PipelineState pipeline(material.getPipelineState(mEngine));
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
pipeline.rasterState.blendFunctionDstRGB = BlendFunction::ONE;
for (size_t j = inoutBloomOptions.levels, i = j - 1; i >= 1; i--, j++) {
auto hwDstRT = resources.getRenderPassInfo(data.outRT[i - 1]);
@@ -2301,11 +2299,8 @@ void PostProcessManager::colorGradingSubpass(DriverApi& driver,
const Variant::type_t variant = Variant::type_t(colorGradingConfig.translucent ?
PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE);
auto const pipeline = material.getPipelineState(mEngine, variant);
driver.nextSubpass();
driver.scissor(pipeline.second);
driver.draw(pipeline.first, fullScreenRenderPrimitive, 0, 3, 1);
driver.draw(material.getPipelineState(mEngine, variant), fullScreenRenderPrimitive, 1);
}
void PostProcessManager::customResolvePrepareSubpass(DriverApi& driver, CustomResolveOp op) noexcept {
@@ -2321,12 +2316,10 @@ void PostProcessManager::customResolveSubpass(DriverApi& driver) noexcept {
Handle<HwRenderPrimitive> const& fullScreenRenderPrimitive = engine.getFullScreenRenderPrimitive();
auto const& material = getPostProcessMaterial("customResolveAsSubpass");
// the UBO has been set and committed in colorGradingPrepareSubpass()
auto const pipeline = material.getPipelineState(mEngine);
FMaterialInstance* mi = material.getMaterialInstance(mEngine);
mi->use(driver);
driver.nextSubpass();
driver.scissor(pipeline.second);
driver.draw(pipeline.first, fullScreenRenderPrimitive, 0, 3, 1);
driver.draw(material.getPipelineState(mEngine), fullScreenRenderPrimitive, 1);
}
FrameGraphId<FrameGraphTexture> PostProcessManager::customResolveUncompressPass(FrameGraph& fg,
@@ -2769,10 +2762,9 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
if (colorGradingConfig.asSubpass) {
out.params.subpassMask = 1;
}
auto const pipeline = material.getPipelineState(mEngine, variant);
PipelineState const pipeline(material.getPipelineState(mEngine, variant));
driver.beginRenderPass(out.target, out.params);
driver.scissor(pipeline.second);
driver.draw(pipeline.first, mEngine.getFullScreenRenderPrimitive(), 0, 3, 1);
driver.draw(pipeline, mEngine.getFullScreenRenderPrimitive(), 1);
if (colorGradingConfig.asSubpass) {
colorGradingSubpass(driver, colorGradingConfig);
}
@@ -2846,7 +2838,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::rcas(
const uint8_t variant = uint8_t(
translucent ? PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE);
auto const pipeline = material.getPipelineState(mEngine, variant);
PipelineState const pipeline(material.getPipelineState(mEngine, variant));
render(out, pipeline, driver);
});
@@ -2988,25 +2980,25 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscale(FrameGraph& fg, bool
auto out = resources.getRenderPassInfo();
if (UTILS_UNLIKELY(twoPassesEASU)) {
auto pipeline0 = splitEasuMaterial->getPipelineState(mEngine);
auto pipeline1 = easuMaterial->getPipelineState(mEngine);
pipeline1.first.rasterState.depthFunc = backend::SamplerCompareFunc::NE;
PipelineState pipeline0(splitEasuMaterial->getPipelineState(mEngine));
PipelineState pipeline1(easuMaterial->getPipelineState(mEngine));
pipeline1.rasterState.depthFunc = backend::SamplerCompareFunc::NE;
if (translucent) {
enableTranslucentBlending(pipeline0.first);
enableTranslucentBlending(pipeline1.first);
enableTranslucentBlending(pipeline0);
enableTranslucentBlending(pipeline1);
}
driver.beginRenderPass(out.target, out.params);
driver.scissor(pipeline0.second);
driver.draw(pipeline0.first, fullScreenRenderPrimitive, 0, 3, 1);
driver.scissor(pipeline1.second);
driver.draw(pipeline1.first, fullScreenRenderPrimitive, 0, 3, 1);
driver.draw(pipeline0, fullScreenRenderPrimitive, 1);
driver.draw(pipeline1, fullScreenRenderPrimitive, 1);
driver.endRenderPass();
} else {
auto pipeline = easuMaterial->getPipelineState(mEngine);
PipelineState pipeline(easuMaterial->getPipelineState(mEngine));
if (translucent) {
enableTranslucentBlending(pipeline.first);
enableTranslucentBlending(pipeline);
}
render(out, pipeline, driver);
driver.beginRenderPass(out.target, out.params);
driver.draw(pipeline, fullScreenRenderPrimitive, 1);
driver.endRenderPass();
}
});
@@ -3075,12 +3067,12 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
mi->commit(driver);
mi->use(driver);
auto pipeline = material.getPipelineState(mEngine);
PipelineState pipeline(material.getPipelineState(mEngine));
if (translucent) {
pipeline.first.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
pipeline.first.rasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
pipeline.first.rasterState.blendFunctionDstRGB = BlendFunction::ONE_MINUS_SRC_ALPHA;
pipeline.first.rasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_ALPHA;
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
pipeline.rasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
pipeline.rasterState.blendFunctionDstRGB = BlendFunction::ONE_MINUS_SRC_ALPHA;
pipeline.rasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_ALPHA;
}
render(out, pipeline, driver);
});
@@ -3226,8 +3218,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::vsmMipmapPass(FrameGraph& fg
// When generating shadow map mip levels, we want to preserve the 1 texel border.
// (note clearing never respects the scissor in Filament)
auto const [pipeline, _] = material.getPipelineState(mEngine);
backend::Viewport const scissor = { 1u, 1u, dim - 2u, dim - 2u };
PipelineState pipeline(material.getPipelineState(mEngine));
pipeline.scissor = { 1u, 1u, dim - 2u, dim - 2u };
FMaterialInstance* const mi = material.getMaterialInstance(mEngine);
mi->setParameter("color", in, {
@@ -3238,7 +3230,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::vsmMipmapPass(FrameGraph& fg
mi->setParameter("uvscale", 1.0f / float(dim));
mi->commit(driver);
mi->use(driver);
render(out, pipeline, scissor, driver);
render(out, pipeline, driver);
if (finalize) {
driver.setMinMaxLevels(in, 0, level);
@@ -3271,7 +3263,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugShadowCascades(FrameGra
auto depth = resources.getTexture(data.depth);
auto out = resources.getRenderPassInfo();
auto& material = getPostProcessMaterial("debugShadowCascades");
auto const pipeline = material.getPipelineState(mEngine);
PipelineState const pipeline(material.getPipelineState(mEngine));
FMaterialInstance* mi = material.getMaterialInstance(mEngine);
mi->setParameter("color", color, {}); // nearest
mi->setParameter("depth", depth, {}); // nearest
@@ -3284,84 +3276,6 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugShadowCascades(FrameGra
return debugShadowCascadePass->output;
}
FrameGraphId<FrameGraphTexture> PostProcessManager::debugCombineArrayTexture(FrameGraph& fg,
bool translucent, FrameGraphId<FrameGraphTexture> input,
filament::Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc,
SamplerMagFilter filterMag,
SamplerMinFilter filterMin) noexcept {
assert_invariant(fg.getDescriptor(input).depth > 1);
assert_invariant(fg.getDescriptor(input).type == SamplerType::SAMPLER_2D_ARRAY);
// TODO: add support for sub-resources
assert_invariant(fg.getSubResourceDescriptor(input).layer == 0);
assert_invariant(fg.getSubResourceDescriptor(input).level == 0);
struct QuadBlitData {
FrameGraphId<FrameGraphTexture> input;
FrameGraphId<FrameGraphTexture> output;
};
auto& ppQuadBlit = fg.addPass<QuadBlitData>("combining array tex",
[&](FrameGraph::Builder& builder, auto& data) {
data.input = builder.sample(input);
data.output = builder.createTexture("upscaled output", outDesc);
data.output = builder.write(data.output,
FrameGraphTexture::Usage::COLOR_ATTACHMENT);
builder.declareRenderPass(builder.getName(data.output), {
.attachments = {.color = { data.output }},
.clearFlags = TargetBufferFlags::DEPTH });
},
[=](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) {
auto color = resources.getTexture(data.input);
auto const& inputDesc = resources.getDescriptor(data.input);
auto out = resources.getRenderPassInfo();
// --------------------------------------------------------------------------------
// set uniforms
PostProcessMaterial const& material = getPostProcessMaterial("blitArray");
auto* mi = material.getMaterialInstance(mEngine);
mi->setParameter("color", color, {
.filterMag = filterMag,
.filterMin = filterMin
});
mi->setParameter("layerIndex", 0);
mi->setParameter("viewport", float4{
float(vp.left) / inputDesc.width,
float(vp.bottom) / inputDesc.height,
float(vp.width) / inputDesc.width,
float(vp.height) / inputDesc.height
});
mi->commit(driver);
mi->use(driver);
auto pipeline = material.getPipelineState(mEngine);
if (translucent) {
pipeline.first.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
pipeline.first.rasterState.blendFunctionSrcAlpha = BlendFunction::ONE;
pipeline.first.rasterState.blendFunctionDstRGB = BlendFunction::ONE_MINUS_SRC_ALPHA;
pipeline.first.rasterState.blendFunctionDstAlpha = BlendFunction::ONE_MINUS_SRC_ALPHA;
}
// Blit the scene rendered to the first layer to the left half of the screen.
out.params.viewport.width /= 2;
render(out, pipeline, driver);
// Blit the scene rendered to the second layer to the right half of the screen.
// Don't clear or discard the target to keep the previously rendered left half image.
mi->setParameter("layerIndex", 1);
mi->commit(driver);
out.params.flags.clear = filament::backend::TargetBufferFlags::NONE;
out.params.flags.discardStart = filament::backend::TargetBufferFlags::NONE;
out.params.viewport.left += out.params.viewport.width;
render(out, pipeline, driver);
});
return ppQuadBlit->output;
}
FrameGraphId<FrameGraphTexture> PostProcessManager::debugDisplayShadowTexture(
FrameGraph& fg,
FrameGraphId<FrameGraphTexture> input,
+2 -17
View File
@@ -288,15 +288,6 @@ public:
FrameGraphId<FrameGraphTexture> shadowmap, float scale,
uint8_t layer, uint8_t level, uint8_t channel, float power) noexcept;
// Combine an array texture pointed to by `input` into a single image, then return it.
// This is only useful to check the multiview rendered scene as a debugging purpose, thus this
// is not expected to be used in normal cases.
FrameGraphId<FrameGraphTexture> debugCombineArrayTexture(FrameGraph& fg, bool translucent,
FrameGraphId<FrameGraphTexture> input,
filament::Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc,
backend::SamplerMagFilter filterMag,
backend::SamplerMinFilter filterMin) noexcept;
backend::Handle<backend::HwTexture> getOneTexture() const;
backend::Handle<backend::HwTexture> getZeroTexture() const;
backend::Handle<backend::HwTexture> getOneTextureArray() const;
@@ -320,7 +311,7 @@ public:
FMaterial* getMaterial(FEngine& engine) const noexcept;
FMaterialInstance* getMaterialInstance(FEngine& engine) const noexcept;
std::pair<backend::PipelineState, backend::Viewport> getPipelineState(FEngine& engine,
backend::PipelineState getPipelineState(FEngine& engine,
Variant::type_t variantKey = 0u) const noexcept;
private:
@@ -348,15 +339,9 @@ public:
backend::DriverApi& driver) const noexcept;
void render(FrameGraphResources::RenderPassInfo const& out,
backend::PipelineState const& pipeline, backend::Viewport const& scissor,
backend::PipelineState const& pipeline,
backend::DriverApi& driver) const noexcept;
void render(FrameGraphResources::RenderPassInfo const& out,
std::pair<backend::PipelineState, backend::Viewport> const& combo,
backend::DriverApi& driver) const noexcept {
render(out, combo.first, combo.second, driver);
}
private:
FEngine& mEngine;
+73 -90
View File
@@ -164,11 +164,6 @@ void RenderPass::appendCommands(FEngine& engine,
// trace the number of visible renderables
SYSTRACE_VALUE32("visibleRenderables", vr.size());
if (UTILS_UNLIKELY(vr.empty())) {
// no renderables, we still need the sentinel and the command buffer size should be
// exactly 1.
assert_invariant(commands.size() == 1);
Command* curr = commands.data();
curr->key = uint64_t(Pass::SENTINEL);
return;
}
@@ -213,7 +208,7 @@ void RenderPass::appendCommands(FEngine& engine,
// This must be done from the main thread.
for (Command const* first = curr, *last = curr + commandCount ; first != last ; ++first) {
if (UTILS_LIKELY((first->key & CUSTOM_MASK) == uint64_t(CustomCommand::PASS))) {
auto ma = first->primitive.primitive->getMaterialInstance()->getMaterial();
auto ma = first->primitive.mi->getMaterial();
ma->prepareProgram(first->primitive.materialVariant);
}
}
@@ -295,7 +290,8 @@ void RenderPass::instanceify(FEngine& engine, Arena& arena) noexcept {
[lhs = *curr](Command const& rhs) {
// primitives must be identical to be instanced. Currently, instancing doesn't support
// skinning/morphing.
return lhs.primitive.primitive == rhs.primitive.primitive &&
return lhs.primitive.mi == rhs.primitive.mi &&
lhs.primitive.primitiveHandle == rhs.primitive.primitiveHandle &&
lhs.primitive.rasterState == rhs.primitive.rasterState &&
lhs.primitive.skinningHandle == rhs.primitive.skinningHandle &&
lhs.primitive.skinningOffset == rhs.primitive.skinningOffset &&
@@ -424,6 +420,7 @@ void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant,
cmdDraw.primitive.rasterState.colorWrite = mi->isColorWriteEnabled();
cmdDraw.primitive.rasterState.depthWrite = mi->isDepthWriteEnabled();
cmdDraw.primitive.rasterState.depthFunc = mi->getDepthFunc();
cmdDraw.primitive.mi = mi;
cmdDraw.primitive.materialVariant = variant;
// we keep "RasterState::colorWrite" to the value set by material (could be disabled)
}
@@ -434,7 +431,7 @@ void RenderPass::generateCommands(CommandTypeFlags commandTypeFlags, Command* co
FScene::RenderableSoa const& soa, Range<uint32_t> range,
Variant variant, RenderFlags renderFlags,
FScene::VisibleMaskType visibilityMask, float3 cameraPosition, float3 cameraForward,
uint8_t stereoEyeCount) noexcept {
uint8_t instancedStereoEyeCount) noexcept {
SYSTRACE_CALL();
@@ -466,12 +463,12 @@ void RenderPass::generateCommands(CommandTypeFlags commandTypeFlags, Command* co
case CommandTypeFlags::COLOR:
curr = generateCommandsImpl<CommandTypeFlags::COLOR>(commandTypeFlags, curr,
soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward,
stereoEyeCount);
instancedStereoEyeCount);
break;
case CommandTypeFlags::DEPTH:
curr = generateCommandsImpl<CommandTypeFlags::DEPTH>(commandTypeFlags, curr,
soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward,
stereoEyeCount);
instancedStereoEyeCount);
break;
default:
// we should never end-up here
@@ -494,7 +491,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
Command* UTILS_RESTRICT curr,
FScene::RenderableSoa const& UTILS_RESTRICT soa, Range<uint32_t> range,
Variant const variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask,
float3 cameraPosition, float3 cameraForward, uint8_t stereoEyeCount) noexcept {
float3 cameraPosition, float3 cameraForward, uint8_t instancedStereoEyeCount) noexcept {
// generateCommands() writes both the draw and depth commands simultaneously such that
// we go throw the list of renderables just once.
@@ -520,7 +517,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
const bool hasShadowing = renderFlags & HAS_SHADOWING;
const bool viewInverseFrontFaces = renderFlags & HAS_INVERSE_FRONT_FACES;
const bool hasStereo = renderFlags & IS_STEREOSCOPIC;
const bool hasInstancedStereo = renderFlags & IS_STEREOSCOPIC;
Command cmdColor;
@@ -576,12 +573,11 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
// calculate the per-primitive face winding order inversion
const bool inverseFrontFaces = viewInverseFrontFaces ^ soaVisibility[i].reversedWindingOrder;
const bool hasMorphing = soaVisibility[i].morphing;
const bool hasSkinning = soaVisibility[i].skinning;
const bool hasSkinningOrMorphing = hasSkinning || hasMorphing;
const bool hasSkinningOrMorphing = soaVisibility[i].skinning || hasMorphing;
cmdColor.key = makeField(soaVisibility[i].priority, PRIORITY_MASK, PRIORITY_SHIFT);
cmdColor.key |= makeField(soaVisibility[i].channel, CHANNEL_MASK, CHANNEL_SHIFT);
cmdColor.primitive.index = i;
cmdColor.primitive.index = (uint16_t)i;
cmdColor.primitive.instanceCount =
soaInstanceInfo[i].count | PrimitiveInfo::USER_INSTANCE_MASK;
cmdColor.primitive.instanceBufferHandle = soaInstanceInfo[i].handle;
@@ -589,9 +585,9 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
// soaInstanceInfo[i].count is the number of instances the user has requested, either for
// manual or hybrid instancing. Instanced stereo multiplies the number of instances by the
// eye count.
if (UTILS_UNLIKELY(hasStereo)) {
if (UTILS_UNLIKELY(hasInstancedStereo)) {
cmdColor.primitive.instanceCount =
(soaInstanceInfo[i].count * stereoEyeCount) |
(soaInstanceInfo[i].count * instancedStereoEyeCount) |
PrimitiveInfo::USER_INSTANCE_MASK;
}
@@ -602,46 +598,36 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
Variant::isSSRVariant(variant) || (soaVisibility[i].receiveShadows & hasShadowing));
renderableVariant.setSkinning(hasSkinningOrMorphing);
const FRenderableManager::SkinningBindingInfo& skinning = soaSkinning[i];
const FRenderableManager::MorphingBindingInfo& morphing = soaMorphing[i];
if constexpr (isDepthPass) {
cmdDepth.key = uint64_t(Pass::DEPTH);
cmdDepth.key |= uint64_t(CustomCommand::PASS);
cmdDepth.key |= makeField(soaVisibility[i].priority, PRIORITY_MASK, PRIORITY_SHIFT);
cmdDepth.key |= makeField(soaVisibility[i].channel, CHANNEL_MASK, CHANNEL_SHIFT);
cmdDepth.key |= makeField(distanceBits >> 22u, Z_BUCKET_MASK, Z_BUCKET_SHIFT);
cmdDepth.primitive.index = i;
cmdDepth.primitive.index = (uint16_t)i;
cmdDepth.primitive.instanceCount =
soaInstanceInfo[i].count | PrimitiveInfo::USER_INSTANCE_MASK;
cmdDepth.primitive.instanceBufferHandle = soaInstanceInfo[i].handle;
cmdDepth.primitive.materialVariant.setSkinning(hasSkinningOrMorphing);
cmdDepth.primitive.rasterState.inverseFrontFaces = inverseFrontFaces;
cmdDepth.primitive.skinningHandle = skinning.handle;
cmdDepth.primitive.skinningOffset = skinning.offset;
cmdDepth.primitive.skinningTexture = skinning.handleSampler;
cmdDepth.primitive.morphWeightBuffer = morphing.handle;
if (UTILS_UNLIKELY(hasStereo)) {
if (UTILS_UNLIKELY(hasInstancedStereo)) {
cmdColor.primitive.instanceCount =
(soaInstanceInfo[i].count * stereoEyeCount) |
(soaInstanceInfo[i].count * instancedStereoEyeCount) |
PrimitiveInfo::USER_INSTANCE_MASK;
}
}
if constexpr (isColorPass) {
renderableVariant.setFog(soaVisibility[i].fog && Variant::isFogVariant(variant));
cmdColor.primitive.skinningHandle = skinning.handle;
cmdColor.primitive.skinningOffset = skinning.offset;
cmdColor.primitive.skinningTexture = skinning.handleSampler;
cmdColor.primitive.morphWeightBuffer = morphing.handle;
}
const bool shadowCaster = soaVisibility[i].castShadows & hasShadowing;
const bool writeDepthForShadowCasters = depthContainsShadowCasters & shadowCaster;
const Slice<FRenderPrimitive>& primitives = soaPrimitives[i];
const FRenderableManager::SkinningBindingInfo& skinning = soaSkinning[i];
const FRenderableManager::MorphingBindingInfo& morphing = soaMorphing[i];
/*
* This is our hot loop. It's written to avoid branches.
* When modifying this code, always ensure it stays efficient.
@@ -652,13 +638,15 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
FMaterialInstance const* const mi = primitive.getMaterialInstance();
FMaterial const* const ma = mi->getMaterial();
// TODO: we should disable the SKN variant if this primitive doesn't have either
// skinning or morphing.
if constexpr (isColorPass) {
cmdColor.primitive.primitive = &primitive;
cmdColor.primitive.primitiveHandle = primitive.getHwHandle();
RenderPass::setupColorCommand(cmdColor, renderableVariant, mi, inverseFrontFaces);
cmdColor.primitive.skinningHandle = skinning.handle;
cmdColor.primitive.skinningOffset = skinning.offset;
cmdColor.primitive.skinningTexture = skinning.handleSampler;
cmdColor.primitive.morphWeightBuffer = morphing.handle;
cmdColor.primitive.morphTargetBuffer = morphTargets.buffer->getHwHandle();
const bool blendPass = Pass(cmdColor.key & PASS_MASK) == Pass::BLENDED;
@@ -753,14 +741,18 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
const bool translucent = (blendingMode != BlendingMode::OPAQUE
&& blendingMode != BlendingMode::MASKED);
// TODO: we should disable the SKN variant if this primitive doesn't have either
// skinning or morphing.
cmdDepth.key |= mi->getSortingKey(); // already all set-up for direct or'ing
// unconditionally write the command
cmdDepth.primitive.primitive = &primitive;
cmdDepth.primitive.primitiveHandle = primitive.getHwHandle();
cmdDepth.primitive.mi = mi;
cmdDepth.primitive.rasterState.culling = mi->getCullingMode();
cmdDepth.primitive.skinningHandle = skinning.handle;
cmdDepth.primitive.skinningOffset = skinning.offset;
cmdDepth.primitive.skinningTexture = skinning.handleSampler;
cmdDepth.primitive.morphWeightBuffer = morphing.handle;
cmdDepth.primitive.morphTargetBuffer = morphTargets.buffer->getHwHandle();
// FIXME: should writeDepthForShadowCasters take precedence over mi->getDepthWrite()?
@@ -818,28 +810,6 @@ void RenderPass::Executor::execute(FEngine& engine, const char*) const noexcept
execute(engine, mCommands.begin(), mCommands.end());
}
UTILS_NOINLINE // no need to be inlined
backend::Viewport RenderPass::Executor::applyScissorViewport(
backend::Viewport const& scissorViewport, backend::Viewport const& scissor) noexcept {
// scissor is set, we need to apply the offset/clip
// clang vectorizes this!
constexpr int32_t maxvali = std::numeric_limits<int32_t>::max();
// compute new left/bottom, assume no overflow
int32_t l = scissor.left + scissorViewport.left;
int32_t b = scissor.bottom + scissorViewport.bottom;
// compute right/top without overflowing, scissor.width/height guaranteed
// to convert to int32
int32_t r = (l > maxvali - int32_t(scissor.width)) ? maxvali : l + int32_t(scissor.width);
int32_t t = (b > maxvali - int32_t(scissor.height)) ? maxvali : b + int32_t(scissor.height);
// clip to the viewport
l = std::max(l, scissorViewport.left);
b = std::max(b, scissorViewport.bottom);
r = std::min(r, scissorViewport.left + int32_t(scissorViewport.width));
t = std::min(t, scissorViewport.bottom + int32_t(scissorViewport.height));
assert_invariant(r >= l && t >= b);
return { l, b, uint32_t(r - l), uint32_t(t - b) };
}
UTILS_NOINLINE // no need to be inlined
void RenderPass::Executor::execute(FEngine& engine,
const Command* first, const Command* last) const noexcept {
@@ -854,17 +824,16 @@ void RenderPass::Executor::execute(FEngine& engine,
if (first != last) {
SYSTRACE_VALUE32("commandCount", last - first);
bool const scissorOverride = mScissorOverride;
if (UTILS_UNLIKELY(scissorOverride)) {
// initialize with scissor overide
driver.scissor(mScissor);
}
bool const polygonOffsetOverride = mPolygonOffsetOverride;
PipelineState pipeline{
// initialize with polygon offset override
.polygonOffset = mPolygonOffset,
};
.scissor = mScissor
}, dummyPipeline;
auto* const pPipelinePolygonOffset =
mPolygonOffsetOverride ? &dummyPipeline.polygonOffset : &pipeline.polygonOffset;
auto* const pScissor =
mScissorOverride ? &dummyPipeline.scissor : &pipeline.scissor;
FMaterialInstance const* UTILS_RESTRICT mi = nullptr;
FMaterial const* UTILS_RESTRICT ma = nullptr;
@@ -911,33 +880,49 @@ void RenderPass::Executor::execute(FEngine& engine,
}
// primitiveHandle may be invalid if no geometry was set on the renderable.
if (UTILS_UNLIKELY(!first->primitive.primitive->getHwHandle())) {
if (UTILS_UNLIKELY(!first->primitive.primitiveHandle)) {
continue;
}
// per-renderable uniform
PrimitiveInfo const info = first->primitive;
const PrimitiveInfo info = first->primitive;
pipeline.rasterState = info.rasterState;
//pipeline.vertexBufferInfo = info.vertexBufferInfo;
if (UTILS_UNLIKELY(mi != info.primitive->getMaterialInstance())) {
if (UTILS_UNLIKELY(mi != info.mi)) {
// this is always taken the first time
mi = info.primitive->getMaterialInstance();
mi = info.mi;
assert_invariant(mi);
ma = mi->getMaterial();
if (UTILS_LIKELY(!scissorOverride)) {
backend::Viewport scissor = mi->getScissor();
if (UTILS_UNLIKELY(mi->hasScissor())) {
scissor = applyScissorViewport(mScissorViewport, scissor);
}
driver.scissor(scissor);
}
auto const& scissor = mi->getScissor();
if (UTILS_UNLIKELY(mi->hasScissor())) {
// scissor is set, we need to apply the offset/clip
// clang vectorizes this!
constexpr int32_t maxvali = std::numeric_limits<int32_t>::max();
const backend::Viewport scissorViewport = mScissorViewport;
// compute new left/bottom, assume no overflow
int32_t l = scissor.left + scissorViewport.left;
int32_t b = scissor.bottom + scissorViewport.bottom;
// compute right/top without overflowing, scissor.width/height guaranteed
// to convert to int32
int32_t r = (l > maxvali - int32_t(scissor.width)) ?
maxvali : l + int32_t(scissor.width);
int32_t t = (b > maxvali - int32_t(scissor.height)) ?
maxvali : b + int32_t(scissor.height);
// clip to the viewport
l = std::max(l, scissorViewport.left);
b = std::max(b, scissorViewport.bottom);
r = std::min(r, scissorViewport.left + int32_t(scissorViewport.width));
t = std::min(t, scissorViewport.bottom + int32_t(scissorViewport.height));
assert_invariant(r >= l && t >= b);
*pScissor = { l, b, uint32_t(r - l), uint32_t(t - b) };
} else {
// no scissor set (common case), 'scissor' has its default value, use that.
*pScissor = scissor;
}
if (UTILS_LIKELY(!polygonOffsetOverride)) {
pipeline.polygonOffset = mi->getPolygonOffset();
}
*pPipelinePolygonOffset = mi->getPolygonOffset();
pipeline.stencilState = mi->getStencilState();
mi->use(driver);
}
@@ -1003,9 +988,7 @@ void RenderPass::Executor::execute(FEngine& engine,
info.skinningTexture);
}
driver.draw(pipeline, info.primitive->getHwHandle(),
info.primitive->getIndexOffset(), info.primitive->getIndexCount(),
instanceCount);
driver.draw(pipeline, info.primitiveHandle, instanceCount);
}
}
+8 -13
View File
@@ -53,7 +53,6 @@ class CommandBufferQueue;
}
class FMaterialInstance;
class FRenderPrimitive;
class RenderPassBuilder;
class RenderPass {
@@ -236,13 +235,13 @@ public:
return boolish ? value : uint64_t(0);
}
struct PrimitiveInfo { // 56 bytes
struct PrimitiveInfo { // 48 bytes
union {
FRenderPrimitive const* primitive; // 8 bytes;
uint64_t padding = {}; // ensures primitive is 8 bytes on all archs
FMaterialInstance const* mi;
uint64_t padding = {}; // ensures mi is 8 bytes on all archs
}; // 8 bytes
uint64_t rfu0; // 8 bytes
backend::RasterState rasterState; // 4 bytes
backend::Handle<backend::HwRenderPrimitive> primitiveHandle; // 4 bytes
backend::Handle<backend::HwBufferObject> skinningHandle; // 4 bytes
backend::Handle<backend::HwSamplerGroup> skinningTexture; // 4 bytes
backend::Handle<backend::HwBufferObject> morphWeightBuffer; // 4 bytes
@@ -252,17 +251,16 @@ public:
uint32_t skinningOffset = 0; // 4 bytes
uint16_t instanceCount; // 2 bytes [MSb: user]
Variant materialVariant; // 1 byte
uint8_t rfu1; // 1 byte
uint32_t rfu2; // 4 byte
static const uint16_t USER_INSTANCE_MASK = 0x8000u;
static const uint16_t INSTANCE_COUNT_MASK = 0x7fffu;
};
static_assert(sizeof(PrimitiveInfo) == 56);
static_assert(sizeof(PrimitiveInfo) == 48);
struct alignas(8) Command { // 64 bytes
CommandKey key = 0; // 8 bytes
PrimitiveInfo primitive; // 56 bytes
PrimitiveInfo primitive; // 48 bytes
uint64_t reserved[1] = {}; // 8 bytes
bool operator < (Command const& rhs) const noexcept { return key < rhs.key; }
// placement new declared as "throw" to avoid the compiler's null-check
inline void* operator new (size_t, void* ptr) {
@@ -331,10 +329,6 @@ public:
void execute(FEngine& engine, const Command* first, const Command* last) const noexcept;
static backend::Viewport applyScissorViewport(
backend::Viewport const& scissorViewport,
backend::Viewport const& scissor) noexcept;
public:
Executor() = default;
Executor(Executor const& rhs);
@@ -413,6 +407,7 @@ private:
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> vr) noexcept;
// Arena where all Commands are allocated. The Arena owns the commands.
FScene::RenderableSoa const& mRenderableSoa;
utils::Range<uint32_t> const mVisibleRenderables;
backend::Handle<backend::HwBufferObject> const mUboHandle;
+12 -15
View File
@@ -23,9 +23,6 @@
#include <utils/debug.h>
#include <stdint.h>
#include <stddef.h>
namespace filament {
void FRenderPrimitive::init(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver,
@@ -40,16 +37,15 @@ void FRenderPrimitive::init(HwRenderPrimitiveFactory& factory, backend::DriverAp
FVertexBuffer* vertexBuffer = downcast(entry.vertices);
FIndexBuffer* indexBuffer = downcast(entry.indices);
AttributeBitset const enabledAttributes = vertexBuffer->getDeclaredAttributes();
AttributeBitset enabledAttributes = vertexBuffer->getDeclaredAttributes();
auto const& ebh = vertexBuffer->getHwHandle();
auto const& ibh = indexBuffer->getHwHandle();
mHandle = factory.create(driver, ebh, ibh, entry.type);
mHandle = factory.create(driver, ebh, ibh, entry.type, (uint32_t)entry.offset,
(uint32_t)entry.minIndex, (uint32_t)entry.maxIndex, (uint32_t)entry.count);
mPrimitiveType = entry.type;
mIndexOffset = entry.offset;
mIndexCount = entry.count;
mEnabledAttributes = enabledAttributes;
}
}
@@ -62,21 +58,22 @@ void FRenderPrimitive::terminate(HwRenderPrimitiveFactory& factory, backend::Dri
void FRenderPrimitive::set(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver,
RenderableManager::PrimitiveType type,
FVertexBuffer* vertices, FIndexBuffer* indices, size_t offset, size_t count) noexcept {
if (mHandle) {
factory.destroy(driver, mHandle);
}
FVertexBuffer* vertices, FIndexBuffer* indices, size_t offset,
size_t minIndex, size_t maxIndex, size_t count) noexcept {
AttributeBitset const enabledAttributes = vertices->getDeclaredAttributes();
AttributeBitset enabledAttributes = vertices->getDeclaredAttributes();
auto const& ebh = vertices->getHwHandle();
auto const& ibh = indices->getHwHandle();
mHandle = factory.create(driver, ebh, ibh, type);
if (mHandle) {
factory.destroy(driver, mHandle);
}
mHandle = factory.create(driver, ebh, ibh, type,
(uint32_t)offset, (uint32_t)minIndex, (uint32_t)maxIndex, (uint32_t)count);
mPrimitiveType = type;
mIndexOffset = offset;
mIndexCount = count;
mEnabledAttributes = enabledAttributes;
}
+5 -17
View File
@@ -17,16 +17,13 @@
#ifndef TNT_FILAMENT_DETAILS_RENDERPRIMITIVE_H
#define TNT_FILAMENT_DETAILS_RENDERPRIMITIVE_H
#include <filament/RenderableManager.h>
#include "components/RenderableManager.h"
#include "details/MaterialInstance.h"
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <stdint.h>
#include <utils/compiler.h>
namespace filament {
@@ -46,16 +43,13 @@ public:
void set(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver,
RenderableManager::PrimitiveType type,
FVertexBuffer* vertices, FIndexBuffer* indices, size_t offset,
size_t count) noexcept;
size_t minIndex, size_t maxIndex, size_t count) noexcept;
// frees driver resources, object becomes invalid
void terminate(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver);
const FMaterialInstance* getMaterialInstance() const noexcept { return mMaterialInstance; }
backend::Handle<backend::HwRenderPrimitive> getHwHandle() const noexcept { return mHandle; }
uint32_t getIndexOffset() const noexcept { return mIndexOffset; }
uint32_t getIndexCount() const noexcept { return mIndexCount; }
backend::PrimitiveType getPrimitiveType() const noexcept { return mPrimitiveType; }
AttributeBitset getEnabledAttributes() const noexcept { return mEnabledAttributes; }
uint16_t getBlendOrder() const noexcept { return mBlendOrder; }
@@ -72,19 +66,13 @@ public:
}
private:
// These first fields are dereferences from PrimitiveInfo, keep them together
struct {
FMaterialInstance const* mMaterialInstance = nullptr;
backend::Handle<backend::HwRenderPrimitive> mHandle = {};
UTILS_UNUSED uint8_t padding[4]= {};
uint32_t mIndexOffset = 0;
uint32_t mIndexCount = 0;
};
FMaterialInstance const* mMaterialInstance = nullptr;
backend::Handle<backend::HwRenderPrimitive> mHandle = {};
AttributeBitset mEnabledAttributes = {};
uint16_t mBlendOrder = 0;
bool mGlobalBlendOrderEnabled = false;
backend::PrimitiveType mPrimitiveType = backend::PrimitiveType::TRIANGLES;
UTILS_UNUSED uint8_t reserved[4] = {};
};
} // namespace filament
+1 -1
View File
@@ -109,7 +109,7 @@ size_t ResourceAllocator::TextureKey::getSize() const noexcept {
}
ResourceAllocator::ResourceAllocator(Engine::Config const& config, DriverApi& driverApi) noexcept
: mCacheCapacity(config.resourceAllocatorCacheSizeMB << 20),
: mCacheCapacity(config.resourceAllocatorCacheSizeMB),
mCacheMaxAge(config.resourceAllocatorCacheMaxAge),
mBackend(driverApi) {
}
+2 -23
View File
@@ -16,39 +16,19 @@
#include "ShadowMap.h"
#include <filament/Box.h>
#include <filament/Frustum.h>
#include <filament/LightManager.h>
#include "RenderPass.h"
#include "components/LightManager.h"
#include "details/DebugRegistry.h"
#include "details/Engine.h"
#include "details/Scene.h"
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Entity.h>
#include <utils/Slice.h>
#include <utils/Systrace.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/mat3.h>
#include <math/mat4.h>
#include <math/scalar.h>
#include <algorithm>
#include <array>
#include <cmath>
#include <limits>
#include <type_traits>
#include <stddef.h>
#include <stdint.h>
using namespace utils;
@@ -259,8 +239,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine,
ShadowMap::ShaderParameters ShadowMap::updatePunctual(
mat4f const& Mv, float outerConeAngle, float nearPlane, float farPlane,
const ShadowMapInfo& shadowMapInfo, const FLightManager::ShadowParams& params) noexcept {
const mat4f Mp = mat4f::perspective(
outerConeAngle * f::RAD_TO_DEG * 2.0f, 1.0f, nearPlane, farPlane);
const mat4f Mp = mat4f::perspective(outerConeAngle * f::RAD_TO_DEG * 2.0f, 1.0f, nearPlane, farPlane);
assert_invariant(shadowMapInfo.textureDimension == mOptions->mapSize);
+4 -14
View File
@@ -17,30 +17,20 @@
#ifndef TNT_FILAMENT_DETAILS_SHADOWMAP_H
#define TNT_FILAMENT_DETAILS_SHADOWMAP_H
#include <filament/Box.h>
#include "components/LightManager.h"
#include "Culler.h"
#include "PerShadowMapUniforms.h"
#include "details/Camera.h"
#include "details/Scene.h"
#include "components/LightManager.h"
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <utils/compiler.h>
#include <filament/Box.h>
#include <filament/Viewport.h>
#include <math/mathfwd.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <math/mat4.h>
#include <array>
#include <stddef.h>
#include <stdint.h>
#include <math/vec4.h>
namespace filament {
+44 -116
View File
@@ -15,49 +15,25 @@
*/
#include "ShadowMapManager.h"
#include "RenderPass.h"
#include "ShadowMap.h"
#include <filament/Frustum.h>
#include <filament/LightManager.h>
#include <filament/Options.h>
#include <private/filament/EngineEnums.h>
#include "components/RenderableManager.h"
#include "details/Camera.h"
#include "details/DebugRegistry.h"
#include "details/Texture.h"
#include "details/View.h"
#include "fg/FrameGraph.h"
#include "fg/FrameGraphId.h"
#include "fg/FrameGraphRenderPass.h"
#include "fg/FrameGraphTexture.h"
#include <fg/FrameGraph.h>
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Range.h>
#include <utils/Slice.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <math/half.h>
#include <math/mat4.h>
#include <math/vec4.h>
#include <math/scalar.h>
#include <cmath>
#include <limits>
#include <new>
#include <memory>
#include <stdint.h>
#include <stddef.h>
namespace filament {
using namespace backend;
@@ -65,6 +41,15 @@ using namespace math;
ShadowMapManager::ShadowMapManager(FEngine& engine)
: mEngine(engine) {
// initialize our ShadowMap array in-place
UTILS_NOUNROLL
for (auto& entry : mShadowMapCache) {
new (&entry) ShadowMap(engine);
}
mShadowUbh = engine.getDriverApi().createBufferObject(mShadowUb.getSize(),
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
debugRegistry.registerProperty("d.shadowmap.visualize_cascades",
&engine.debug.shadowmap.visualize_cascades);
@@ -74,75 +59,25 @@ ShadowMapManager::ShadowMapManager(FEngine& engine)
ShadowMapManager::~ShadowMapManager() {
// destroy the ShadowMap array in-place
if (UTILS_UNLIKELY(mInitialized)) {
UTILS_NOUNROLL
for (auto& entry: mShadowMapCache) {
std::destroy_at(std::launder(reinterpret_cast<ShadowMap*>(&entry)));
}
}
}
void ShadowMapManager::createIfNeeded(FEngine& engine,
std::unique_ptr<ShadowMapManager>& inOutShadowMapManager) {
if (UTILS_UNLIKELY(!inOutShadowMapManager)) {
inOutShadowMapManager.reset(new ShadowMapManager(engine));
}
}
void ShadowMapManager::terminate(FEngine& engine,
std::unique_ptr<ShadowMapManager>& shadowMapManager) {
if (shadowMapManager) {
shadowMapManager->terminate(engine);
UTILS_NOUNROLL
for (auto& entry : mShadowMapCache) {
std::destroy_at(std::launder(reinterpret_cast<ShadowMap*>(&entry)));
}
}
void ShadowMapManager::terminate(FEngine& engine) {
if (UTILS_UNLIKELY(mInitialized)) {
DriverApi& driver = engine.getDriverApi();
driver.destroyBufferObject(mShadowUbh);
UTILS_NOUNROLL
for (auto& entry: mShadowMapCache) {
std::launder(reinterpret_cast<ShadowMap*>(&entry))->terminate(engine);
}
DriverApi& driver = engine.getDriverApi();
driver.destroyBufferObject(mShadowUbh);
UTILS_NOUNROLL
for (auto& entry : mShadowMapCache) {
std::launder(reinterpret_cast<ShadowMap*>(&entry))->terminate(engine);
}
}
ShadowMapManager::ShadowTechnique ShadowMapManager::update(
Builder const& builder,
FEngine& engine, FView& view,
ShadowMapManager::ShadowTechnique ShadowMapManager::update(FEngine& engine, FView& view,
CameraInfo const& cameraInfo,
FScene::RenderableSoa& renderableData, FScene::LightSoa const& lightData) noexcept {
if (!builder.mDirectionalShadowMapCount && !builder.mSpotShadowMapCount) {
// no shadows were recorder
return ShadowTechnique::NONE;
}
// initialize the shadowmap array the first time
if (UTILS_UNLIKELY(!mInitialized)) {
mInitialized = true;
// initialize our ShadowMap array in-place
mShadowUbh = engine.getDriverApi().createBufferObject(mShadowUb.getSize(),
BufferObjectBinding::UNIFORM, BufferUsage::DYNAMIC);
UTILS_NOUNROLL
for (auto& entry: mShadowMapCache) {
new(&entry) ShadowMap(engine);
}
}
mDirectionalShadowMapCount = builder.mDirectionalShadowMapCount;
mSpotShadowMapCount = builder.mSpotShadowMapCount;
for (auto const& entry : builder.mShadowMaps) {
auto& shadowMap = getShadowMap(entry.shadowIndex);
shadowMap.initialize(
entry.lightIndex,
entry.shadowType,
entry.shadowIndex,
entry.face,
entry.options);
}
ShadowTechnique shadowTechnique = {};
calculateTextureRequirements(engine, view, lightData);
@@ -161,49 +96,42 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::update(
return shadowTechnique;
}
ShadowMapManager::Builder& ShadowMapManager::Builder::directionalShadowMap(size_t lightIndex,
LightManager::ShadowOptions const* options) noexcept {
assert_invariant(options->shadowCascades <= CONFIG_MAX_SHADOW_CASCADES);
// this updates getCascadedShadowMap()
mDirectionalShadowMapCount = options->shadowCascades;
for (size_t c = 0; c < options->shadowCascades; c++) {
mShadowMaps.push_back({
.lightIndex = lightIndex,
.shadowType = ShadowType::DIRECTIONAL,
.shadowIndex = uint8_t(c),
.face = 0,
.options = options });
}
return *this;
void ShadowMapManager::reset() noexcept {
mDirectionalShadowMapCount = 0;
mSpotShadowMapCount = 0;
}
ShadowMapManager::Builder& ShadowMapManager::Builder::shadowMap(size_t lightIndex, bool spotlight,
void ShadowMapManager::setDirectionalShadowMap(size_t lightIndex,
LightManager::ShadowOptions const* options) noexcept {
assert_invariant(options->shadowCascades <= CONFIG_MAX_SHADOW_CASCADES);
// this updates getCascadedShadowMap()
mDirectionalShadowMapCount = options->shadowCascades;
utils::Slice<ShadowMap> cascadedShadowMap = getCascadedShadowMap();
for (size_t c = 0; c < options->shadowCascades; c++) {
ShadowMap& shadowMap = cascadedShadowMap[c];
shadowMap.initialize(lightIndex, ShadowType::DIRECTIONAL, c, 0, options);
}
}
void ShadowMapManager::addShadowMap(size_t lightIndex, bool spotlight,
LightManager::ShadowOptions const* options) noexcept {
if (spotlight) {
const size_t c = mSpotShadowMapCount++;
const size_t i = c + CONFIG_MAX_SHADOW_CASCADES;
assert_invariant(i < CONFIG_MAX_SHADOWMAPS);
mShadowMaps.push_back({
.lightIndex = lightIndex,
.shadowType = ShadowType::SPOT,
.shadowIndex = uint8_t(i),
.face = 0,
.options = options });
auto& shadowMap = getShadowMap(i);
shadowMap.initialize(lightIndex, ShadowType::SPOT, i, 0, options);
} else {
// point-light, generate 6 independent shadowmaps
for (size_t face = 0; face < 6; face++) {
const size_t c = mSpotShadowMapCount++;
const size_t i = c + CONFIG_MAX_SHADOW_CASCADES;
assert_invariant(i < CONFIG_MAX_SHADOWMAPS);
mShadowMaps.push_back({
.lightIndex = lightIndex,
.shadowType = ShadowType::POINT,
.shadowIndex = uint8_t(i),
.face = uint8_t(face),
.options = options });
auto& shadowMap = getShadowMap(i);
shadowMap.initialize(lightIndex, ShadowType::POINT, i, face, options);
}
}
return *this;
}
FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameGraph& fg,
@@ -845,7 +773,7 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
FEngine& engine, FView& view, CameraInfo const& mainCameraInfo,
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
FScene::LightSoa& lightData,
ShadowMap::SceneInfo const&) noexcept {
ShadowMap::SceneInfo const& sceneInfo) noexcept {
const uint8_t face = shadowMap.getFace();
const size_t lightIndex = shadowMap.getLightIndex();
+18 -61
View File
@@ -17,48 +17,29 @@
#ifndef TNT_FILAMENT_DETAILS_SHADOWMAPMANAGER_H
#define TNT_FILAMENT_DETAILS_SHADOWMAPMANAGER_H
#include "Culler.h"
#include <filament/Viewport.h>
#include "ShadowMap.h"
#include "TypedUniformBuffer.h"
#include <filament/LightManager.h>
#include <filament/Options.h>
#include <private/filament/EngineEnums.h>
#include <private/filament/UibStructs.h>
#include "components/RenderableManager.h"
#include "details/Engine.h"
#include "details/Scene.h"
#include "fg/FrameGraphId.h"
#include "fg/FrameGraphTexture.h"
#include <private/filament/EngineEnums.h>
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <utils/BitmaskEnum.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Range.h>
#include <utils/Slice.h>
#include <math/mat4.h>
#include <math/vec4.h>
#include <math/vec3.h>
#include <array>
#include <memory>
#include <new>
#include <type_traits>
#include <vector>
#include <stdint.h>
#include <stddef.h>
namespace filament {
class FCamera;
class FView;
class FrameGraph;
class RenderPass;
@@ -74,7 +55,7 @@ struct ShadowMappingUniforms {
class ShadowMapManager {
public:
using ShadowMappingUniforms = filament::ShadowMappingUniforms;
using ShadowMappingUniforms = ShadowMappingUniforms;
using ShadowType = ShadowMap::ShadowType;
@@ -84,42 +65,24 @@ public:
SCREEN_SPACE = 0x2u,
};
class Builder {
friend class ShadowMapManager;
uint32_t mDirectionalShadowMapCount = 0;
uint32_t mSpotShadowMapCount = 0;
struct ShadowMap {
size_t lightIndex;
ShadowType shadowType;
uint16_t shadowIndex;
uint8_t face;
LightManager::ShadowOptions const* options;
};
std::vector<ShadowMap> mShadowMaps;
public:
Builder& directionalShadowMap(size_t lightIndex,
LightManager::ShadowOptions const* options) noexcept;
Builder& shadowMap(size_t lightIndex, bool spotlight,
LightManager::ShadowOptions const* options) noexcept;
bool hasShadowMaps() const noexcept {
return mDirectionalShadowMapCount || mSpotShadowMapCount;
}
};
explicit ShadowMapManager(FEngine& engine);
~ShadowMapManager();
static void createIfNeeded(FEngine& engine,
std::unique_ptr<ShadowMapManager>& inOutShadowMapManager);
void terminate(FEngine& engine);
static void terminate(FEngine& engine,
std::unique_ptr<ShadowMapManager>& shadowMapManager);
// Reset shadow map layout.
void reset() noexcept;
void setDirectionalShadowMap(size_t lightIndex,
LightManager::ShadowOptions const* options) noexcept;
void addShadowMap(size_t lightIndex, bool spotlight,
LightManager::ShadowOptions const* options) noexcept;
// Updates all the shadow maps and performs culling.
// Returns true if any of the shadow maps have visible shadows.
ShadowMapManager::ShadowTechnique update(Builder const& builder,
FEngine& engine, FView& view,
ShadowMapManager::ShadowTechnique update(FEngine& engine, FView& view,
CameraInfo const& cameraInfo,
FScene::RenderableSoa& renderableData, FScene::LightSoa const& lightData) noexcept;
@@ -138,16 +101,11 @@ public:
bool hasSpotShadows() const { return !mSpotShadowMapCount; }
// for debugging only
FCamera const* getDirectionalShadowCamera() const noexcept {
if (!mInitialized) return nullptr;
FCamera const* getDirectionalLightCamera() const noexcept {
return getShadowMap(0).getDebugCamera();
}
private:
explicit ShadowMapManager(FEngine& engine);
void terminate(FEngine& engine);
ShadowMapManager::ShadowTechnique updateCascadeShadowMaps(FEngine& engine,
FView& view, CameraInfo cameraInfo, FScene::RenderableSoa& renderableData,
FScene::LightSoa const& lightData, ShadowMap::SceneInfo sceneInfo) noexcept;
@@ -232,7 +190,6 @@ private:
ShadowMapCacheContainer mShadowMapCache;
uint32_t mDirectionalShadowMapCount = 0;
uint32_t mSpotShadowMapCount = 0;
bool mInitialized = false;
ShadowMap& getShadowMap(size_t index) noexcept {
assert_invariant(index < CONFIG_MAX_SHADOWMAPS);
+21 -47
View File
@@ -230,32 +230,6 @@ float3 FilmicToneMapper::operator()(math::float3 x) const noexcept {
return (x * (a * x + b)) / (x * (c * x + d) + e);
}
//------------------------------------------------------------------------------
// PBR Neutral tone mapper
//------------------------------------------------------------------------------
DEFAULT_CONSTRUCTORS(PBRNeutralToneMapper)
float3 PBRNeutralToneMapper::operator()(math::float3 color) const noexcept {
// PBR Tone Mapping, https://modelviewer.dev/examples/tone-mapping.html
constexpr float startCompression = 0.8f - 0.04f;
constexpr float desaturation = 0.15f;
float x = min(color.r, min(color.g, color.b));
float offset = x < 0.08f ? x - 6.25f * x * x : 0.04f;
color -= offset;
float peak = max(color.r, max(color.g, color.b));
if (peak < startCompression) return color;
float d = 1.0f - startCompression;
float newPeak = 1.0f - d * d / (peak + d - startCompression);
color *= newPeak / peak;
float g = 1.0f - 1.0f / (desaturation * (peak - newPeak) + 1.0f);
return mix(color, float3(1.0f), g);
}
//------------------------------------------------------------------------------
// AgX tone mapper
//------------------------------------------------------------------------------
@@ -288,14 +262,14 @@ float3 agxDefaultContrastApprox(float3 x) {
float3 x2 = x * x;
float3 x4 = x2 * x2;
float3 x6 = x4 * x2;
return - 17.86f * x6 * x
+ 78.01f * x6
- 126.7f * x4 * x
+ 92.06f * x4
- 28.72f * x2 * x
+ 4.361f * x2
- 0.1718f * x
+ 0.002857f;
return - 17.86 * x6 * x
+ 78.01 * x6
- 126.7 * x4 * x
+ 92.06 * x4
- 28.72 * x2 * x
+ 4.361 * x2
- 0.1718 * x
+ 0.002857;
}
// Adapted from https://iolite-engine.com/blog_posts/minimal_agx_implementation
@@ -304,23 +278,23 @@ float3 agxLook(float3 val, AgxToneMapper::AgxLook look) {
return val;
}
const float3 lw = float3(0.2126f, 0.7152f, 0.0722f);
const float3 lw = float3(0.2126, 0.7152, 0.0722);
float luma = dot(val, lw);
// Default
float3 offset = float3(0.0f);
float3 slope = float3(1.0f);
float3 power = float3(1.0f);
float sat = 1.0f;
float3 offset = float3(0.0);
float3 slope = float3(1.0);
float3 power = float3(1.0);
float sat = 1.0;
if (look == AgxToneMapper::AgxLook::GOLDEN) {
slope = float3(1.0f, 0.9f, 0.5f);
power = float3(0.8f);
slope = float3(1.0, 0.9, 0.5);
power = float3(0.8);
sat = 1.3;
}
if (look == AgxToneMapper::AgxLook::PUNCHY) {
slope = float3(1.0f);
power = float3(1.35f, 1.35f, 1.35f);
slope = float3(1.0);
power = float3(1.35, 1.35, 1.35);
sat = 1.4;
}
@@ -331,16 +305,16 @@ float3 agxLook(float3 val, AgxToneMapper::AgxLook look) {
float3 AgxToneMapper::operator()(float3 v) const noexcept {
// Ensure no negative values
v = max(float3(0.0f), v);
v = max(float3(0.0), v);
v = AgXInsetMatrix * v;
// Log2 encoding
v = max(v, 1E-10f); // avoid 0 or negative numbers for log2
v = max(v, 1E-10); // avoid 0 or negative numbers for log2
v = log2(v);
v = (v - AgxMinEv) / (AgxMaxEv - AgxMinEv);
v = clamp(v, 0.0f, 1.0f);
v = clamp(v, 0, 1);
// Apply sigmoid
v = agxDefaultContrastApprox(v);
@@ -351,7 +325,7 @@ float3 AgxToneMapper::operator()(float3 v) const noexcept {
v = AgXOutsetMatrix * v;
// Linearize
v = pow(max(float3(0.0f), v), 2.2f);
v = pow(max(float3(0.0), v), 2.2);
return v;
}
+2 -2
View File
@@ -67,8 +67,8 @@ const char* View::getName() const noexcept {
return downcast(this)->getName();
}
Camera const* View::getDirectionalShadowCamera() const noexcept {
return downcast(this)->getDirectionalShadowCamera();
Camera const* View::getDirectionalLightCamera() const noexcept {
return downcast(this)->getDirectionalLightCamera();
}
void View::setShadowingEnabled(bool enabled) noexcept {
@@ -109,12 +109,14 @@ RenderableManager::Builder& RenderableManager::Builder::geometry(size_t index,
RenderableManager::Builder& RenderableManager::Builder::geometry(size_t index,
PrimitiveType type, VertexBuffer* vertices, IndexBuffer* indices,
size_t offset, UTILS_UNUSED size_t minIndex, UTILS_UNUSED size_t maxIndex, size_t count) noexcept {
size_t offset, size_t minIndex, size_t maxIndex, size_t count) noexcept {
std::vector<Entry>& entries = mImpl->mEntries;
if (index < entries.size()) {
entries[index].vertices = vertices;
entries[index].indices = indices;
entries[index].offset = offset;
entries[index].minIndex = minIndex;
entries[index].maxIndex = maxIndex;
entries[index].count = count;
entries[index].type = type;
}
@@ -434,6 +436,11 @@ RenderableManager::Builder::Result RenderableManager::Builder::build(Engine& eng
entity.getId(), i,
entry.offset, entry.count, entry.indices->getIndexCount());
ASSERT_PRECONDITION(entry.minIndex <= entry.maxIndex,
"[entity=%u, primitive @ %u] minIndex (%u) > maxIndex (%u)",
entity.getId(), i,
entry.minIndex, entry.maxIndex);
// this can't be an error because (1) those values are not immutable, so the caller
// could fix later, and (2) the material's shader will work (i.e. compile), and
// use the default values for this attribute, which maybe be acceptable.
@@ -809,7 +816,7 @@ void FRenderableManager::setGeometryAt(Instance instance, uint8_t level, size_t
Slice<FRenderPrimitive>& primitives = getRenderPrimitives(instance, level);
if (primitiveIndex < primitives.size()) {
primitives[primitiveIndex].set(mHwRenderPrimitiveFactory, mEngine.getDriverApi(),
type, vertices, indices, offset, count);
type, vertices, indices, offset, 0, vertices->getVertexCount() - 1, count);
}
}
}
+2 -21
View File
@@ -16,18 +16,12 @@
#include "details/DebugRegistry.h"
#include <utils/compiler.h>
#include <utils/Invocable.h>
#include <utils/Panic.h>
#include <math/vec2.h>
#include <math/vec3.h>
#include <math/vec4.h>
#include <functional>
#include <string_view>
#include <utility>
#ifndef NDEBUG
# define DEBUG_PROPERTIES_WRITABLE true
#else
@@ -126,25 +120,12 @@ void FDebugRegistry::registerDataSource(std::string_view name,
}
}
void FDebugRegistry::registerDataSource(std::string_view name,
utils::Invocable<DataSource()>&& creator) noexcept {
mDataSourceCreatorMap[name] = std::move(creator);
}
DebugRegistry::DataSource FDebugRegistry::getDataSource(const char* name) const noexcept {
std::string_view const key{ name };
auto& dataSourceMap = mDataSourceMap;
auto const& it = dataSourceMap.find(key);
if (UTILS_UNLIKELY(it == dataSourceMap.end())) {
auto& dataSourceCreatorMap = mDataSourceCreatorMap;
auto const& pos = dataSourceCreatorMap.find(key);
if (pos == dataSourceCreatorMap.end()) {
return { nullptr, 0u };
}
DataSource dataSource{ pos->second() };
dataSourceMap[key] = dataSource;
dataSourceCreatorMap.erase(pos);
return dataSource;
if (it == dataSourceMap.end()) {
return { nullptr, 0u };
}
return it->second;
}
+1 -12
View File
@@ -22,17 +22,12 @@
#include <filament/DebugRegistry.h>
#include <utils/compiler.h>
#include <utils/Invocable.h>
#include <math/mathfwd.h>
#include <functional>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <stddef.h>
namespace filament {
class FEngine;
@@ -100,13 +95,8 @@ public:
registerProperty(name, p, FLOAT4, std::move(fn));
}
// registers a DataSource directly
void registerDataSource(std::string_view name, void const* data, size_t count) noexcept;
// registers a DataSource lazily
void registerDataSource(std::string_view name,
utils::Invocable<DataSource()>&& creator) noexcept;
#if !defined(_MSC_VER)
private:
#endif
@@ -123,8 +113,7 @@ private:
void const* getPropertyAddress(const char* name) const noexcept;
DataSource getDataSource(const char* name) const noexcept;
std::unordered_map<std::string_view, PropertyInfo> mPropertyMap;
mutable std::unordered_map<std::string_view, DataSource> mDataSourceMap;
mutable std::unordered_map<std::string_view, utils::Invocable<DataSource()>> mDataSourceCreatorMap;
std::unordered_map<std::string_view, DataSource> mDataSourceMap;
};
FILAMENT_DOWNCAST(DebugRegistry)
+3 -7
View File
@@ -98,10 +98,7 @@ Engine* FEngine::create(Engine::Builder const& builder) {
return nullptr;
}
DriverConfig const driverConfig{
.handleArenaSize = instance->getRequestedDriverHandleArenaSize(),
.textureUseAfterFreePoolSize = instance->getConfig().textureUseAfterFreePoolSize,
.disableParallelShaderCompile = instance->getConfig().disableParallelShaderCompile
};
.handleArenaSize = instance->getRequestedDriverHandleArenaSize() };
instance->mDriver = platform->createDriver(sharedContext, driverConfig);
} else {
@@ -276,7 +273,7 @@ void FEngine::init() {
mFullScreenTriangleRph = driverApi.createRenderPrimitive(
mFullScreenTriangleVb->getHwHandle(), mFullScreenTriangleIb->getHwHandle(),
PrimitiveType::TRIANGLES);
PrimitiveType::TRIANGLES, 0, 0, 2, (uint32_t)mFullScreenTriangleIb->getIndexCount());
// Compute a clip-space [-1 to 1] to texture space [0 to 1] matrix, taking into account
// backend differences.
@@ -654,8 +651,7 @@ int FEngine::loop() {
DriverConfig const driverConfig {
.handleArenaSize = getRequestedDriverHandleArenaSize(),
.textureUseAfterFreePoolSize = mConfig.textureUseAfterFreePoolSize,
.disableParallelShaderCompile = mConfig.disableParallelShaderCompile
.textureUseAfterFreePoolSize = mConfig.textureUseAfterFreePoolSize
};
mDriver = mPlatform->createDriver(mSharedGLContext, driverConfig);
+1 -9
View File
@@ -23,7 +23,6 @@
#include "DFG.h"
#include "PostProcessManager.h"
#include "ResourceList.h"
#include "HwVertexBufferInfoFactory.h"
#include "components/CameraManager.h"
#include "components/LightManager.h"
@@ -183,9 +182,7 @@ public:
return CONFIG_MAX_INSTANCES;
}
bool isStereoSupported(StereoscopicType stereoscopicType) const noexcept {
return getDriver().isStereoSupported(stereoscopicType);
}
bool isStereoSupported() const noexcept { return getDriver().isStereoSupported(); }
static size_t getMaxStereoscopicEyes() noexcept {
return CONFIG_MAX_STEREOSCOPIC_EYES;
@@ -406,10 +403,6 @@ public:
return mAutomaticInstancingEnabled;
}
HwVertexBufferInfoFactory& getVertexBufferInfoFactory() noexcept {
return mHwVertexBufferInfoFactory;
}
backend::Handle<backend::HwTexture> getOneTexture() const { return mDummyOneTexture; }
backend::Handle<backend::HwTexture> getZeroTexture() const { return mDummyZeroTexture; }
backend::Handle<backend::HwTexture> getOneTextureArray() const { return mDummyOneTextureArray; }
@@ -478,7 +471,6 @@ private:
FLightManager mLightManager;
FCameraManager mCameraManager;
ResourceAllocator* mResourceAllocator = nullptr;
HwVertexBufferInfoFactory mHwVertexBufferInfoFactory;
ResourceList<FBufferObject> mBufferObjects{ "BufferObject" };
ResourceList<FRenderer> mRenderers{ "Renderer" };
+1 -2
View File
@@ -532,8 +532,7 @@ void FMaterial::compile(CompilerPriorityQueue priority,
utils::Invocable<void(Material*)>&& callback) noexcept {
// Turn off the STE variant if stereo is not supported.
const StereoscopicType stereoscopicType = mEngine.getConfig().stereoscopicType;
if (!mEngine.getDriverApi().isStereoSupported(stereoscopicType)) {
if (!mEngine.getDriverApi().isStereoSupported()) {
variantSpec &= ~UserVariantFilterMask(UserVariantFilterBit::STE);
}
+2 -2
View File
@@ -679,7 +679,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
RenderPass::RenderFlags renderFlags = 0;
if (view.hasShadowing()) renderFlags |= RenderPass::HAS_SHADOWING;
if (view.isFrontFaceWindingInverted()) renderFlags |= RenderPass::HAS_INVERSE_FRONT_FACES;
if (view.hasStereo()) renderFlags |= RenderPass::IS_STEREOSCOPIC;
if (view.hasInstancedStereo()) renderFlags |= RenderPass::IS_STEREOSCOPIC;
RenderPassBuilder passBuilder(commandArena);
passBuilder.renderFlags(renderFlags);
@@ -689,7 +689,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
variant.setDynamicLighting(view.hasDynamicLighting());
variant.setFog(view.hasFog());
variant.setVsm(view.hasShadowing() && view.getShadowType() != ShadowType::PCF);
variant.setStereo(view.hasStereo());
variant.setStereo(view.hasInstancedStereo());
/*
* Frame graph
+1 -1
View File
@@ -148,7 +148,7 @@ void FScene::prepare(utils::JobSystem& js,
// TODO: the resize below could happen in a job
if (!sceneData.capacity() || sceneData.size() != renderableInstances.size()) {
if (sceneData.size() != renderableInstances.size()) {
sceneData.clear();
if (sceneData.capacity() < renderableDataCapacity) {
sceneData.setCapacity(renderableDataCapacity);
+94 -162
View File
@@ -21,41 +21,18 @@
#include "FilamentAPI-impl.h"
#include <filament/MaterialEnums.h>
#include <filament/VertexBuffer.h>
#include <math/quat.h>
#include <math/vec2.h>
#include <backend/DriverEnums.h>
#include <backend/BufferDescriptor.h>
#include <utils/bitset.h>
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Log.h>
#include <utils/ostream.h>
#include <utils/Panic.h>
#include <algorithm>
#include <array>
#include <iterator>
#include <memory>
#include <utility>
#include <stddef.h>
#include <stdint.h>
namespace filament {
using namespace backend;
using namespace filament::math;
struct VertexBuffer::BuilderDetails {
struct AttributeData : backend::Attribute {
AttributeData() : backend::Attribute{ .type = backend::ElementType::FLOAT4 } {
static_assert(sizeof(Attribute) == sizeof(AttributeData),
"Attribute and Builder::Attribute must match");
}
};
std::array<AttributeData, MAX_VERTEX_ATTRIBUTE_COUNT> mAttributes{};
FVertexBuffer::AttributeData mAttributes[MAX_VERTEX_ATTRIBUTE_COUNT];
AttributeBitset mDeclaredAttributes;
uint32_t mVertexCount = 0;
uint8_t mBufferCount = 0;
@@ -111,16 +88,11 @@ VertexBuffer::Builder& VertexBuffer::Builder::attribute(VertexAttribute attribut
}
#endif
auto& entry = mImpl->mAttributes[attribute];
FVertexBuffer::AttributeData& entry = mImpl->mAttributes[attribute];
entry.buffer = bufferIndex;
entry.offset = byteOffset;
entry.stride = byteStride;
entry.type = attributeType;
if (attribute == VertexAttribute::BONE_INDICES) {
// BONE_INDICES must always be an integer type
entry.flags |= Attribute::FLAG_INTEGER_TARGET;
}
mImpl->mDeclaredAttributes.set(attribute);
} else {
utils::slog.w << "Ignoring VertexBuffer attribute, the limit of " <<
@@ -132,7 +104,7 @@ VertexBuffer::Builder& VertexBuffer::Builder::attribute(VertexAttribute attribut
VertexBuffer::Builder& VertexBuffer::Builder::normalized(VertexAttribute attribute,
bool normalized) noexcept {
if (size_t(attribute) < MAX_VERTEX_ATTRIBUTE_COUNT) {
auto& entry = mImpl->mAttributes[attribute];
FVertexBuffer::AttributeData& entry = mImpl->mAttributes[attribute];
if (normalized) {
entry.flags |= Attribute::FLAG_NORMALIZED;
} else {
@@ -158,47 +130,15 @@ VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
auto const& declaredAttributes = mImpl->mDeclaredAttributes;
auto const& attributes = mImpl->mAttributes;
utils::bitset32 attributedBuffers;
declaredAttributes.forEachSetBit([&](size_t j){
// update set of used buffers
attributedBuffers.set(attributes[j].buffer);
if (engine.getActiveFeatureLevel() == backend::FeatureLevel::FEATURE_LEVEL_0) {
ASSERT_PRECONDITION(!(attributes[j].flags & Attribute::FLAG_INTEGER_TARGET),
"Attribute::FLAG_INTEGER_TARGET not supported at FEATURE_LEVEL_0");
for (size_t j = 0; j < MAX_VERTEX_ATTRIBUTE_COUNT; ++j) {
if (declaredAttributes[j]) {
attributedBuffers.set(attributes[j].buffer);
}
// also checks that we don't use an invalid type with integer attributes
if (attributes[j].flags & Attribute::FLAG_INTEGER_TARGET) {
using ET = ElementType;
constexpr uint32_t const invalidIntegerTypes =
(1 << (int)ET::FLOAT) |
(1 << (int)ET::FLOAT2) |
(1 << (int)ET::FLOAT3) |
(1 << (int)ET::FLOAT4) |
(1 << (int)ET::HALF) |
(1 << (int)ET::HALF2) |
(1 << (int)ET::HALF3) |
(1 << (int)ET::HALF4);
ASSERT_PRECONDITION(!(invalidIntegerTypes & (1 << (int)attributes[j].type)),
"invalid integer vertex attribute type %d", attributes[j].type);
}
});
}
ASSERT_PRECONDITION(attributedBuffers.count() == mImpl->mBufferCount,
"At least one buffer slot was never assigned to an attribute.");
if (mImpl->mAdvancedSkinningEnabled) {
ASSERT_PRECONDITION(!mImpl->mDeclaredAttributes[VertexAttribute::BONE_INDICES],
"Vertex buffer attribute BONE_INDICES is already defined, "
"no advanced skinning is allowed");
ASSERT_PRECONDITION(!mImpl->mDeclaredAttributes[VertexAttribute::BONE_WEIGHTS],
"Vertex buffer attribute BONE_WEIGHTS is already defined, "
"no advanced skinning is allowed");
ASSERT_PRECONDITION(mImpl->mBufferCount < (MAX_VERTEX_BUFFER_COUNT - 2),
"Vertex buffer uses to many buffers (%u)", mImpl->mBufferCount);
}
return downcast(engine).createVertexBuffer(*this);
}
@@ -209,106 +149,96 @@ FVertexBuffer::FVertexBuffer(FEngine& engine, const VertexBuffer::Builder& build
mBufferObjectsEnabled(builder->mBufferObjectsEnabled),
mAdvancedSkinningEnabled(builder->mAdvancedSkinningEnabled){
std::copy(std::begin(builder->mAttributes), std::end(builder->mAttributes), mAttributes.begin());
mDeclaredAttributes = builder->mDeclaredAttributes;
uint8_t const attributeCount = (uint8_t)mDeclaredAttributes.count();
AttributeArray attributeArray;
static_assert(attributeArray.size() == MAX_VERTEX_ATTRIBUTE_COUNT,
"Attribute and Builder::Attribute arrays must match");
static_assert(sizeof(Attribute) == sizeof(AttributeData),
"Attribute and Builder::Attribute must match");
if (mAdvancedSkinningEnabled) {
mAttributes[VertexAttribute::BONE_INDICES] = {
.offset = 0,
.stride = 8,
.buffer = mBufferCount,
.type = VertexBuffer::AttributeType::USHORT4,
.flags = Attribute::FLAG_INTEGER_TARGET,
};
ASSERT_PRECONDITION(!mDeclaredAttributes[VertexAttribute::BONE_INDICES],
"Vertex buffer attribute BONE_INDICES is already defined, no advanced skinning is allowed");
ASSERT_PRECONDITION(!mDeclaredAttributes[VertexAttribute::BONE_WEIGHTS],
"Vertex buffer attribute BONE_WEIGHTS is already defined, no advanced skinning is allowed");
ASSERT_PRECONDITION(mBufferCount < (MAX_VERTEX_BUFFER_COUNT - 2),
"Vertex buffer uses to many buffers (%u)", mBufferCount);
mDeclaredAttributes.set(VertexAttribute::BONE_INDICES);
mAttributes[VertexAttribute::BONE_INDICES].offset = 0;
mAttributes[VertexAttribute::BONE_INDICES].stride = 8;
mAttributes[VertexAttribute::BONE_INDICES].buffer = mBufferCount;
mAttributes[VertexAttribute::BONE_INDICES].type = VertexBuffer::AttributeType::USHORT4;
mAttributes[VertexAttribute::BONE_INDICES].flags = Attribute::FLAG_INTEGER_TARGET;
mBufferCount++;
mAttributes[VertexAttribute::BONE_WEIGHTS] = {
.offset = 0,
.stride = 16,
.buffer = mBufferCount,
.type = VertexBuffer::AttributeType::FLOAT4,
.flags = 0,
};
mDeclaredAttributes.set(VertexAttribute::BONE_WEIGHTS);
mAttributes[VertexAttribute::BONE_WEIGHTS].offset = 0;
mAttributes[VertexAttribute::BONE_WEIGHTS].stride = 16;
mAttributes[VertexAttribute::BONE_WEIGHTS].buffer = mBufferCount;
mAttributes[VertexAttribute::BONE_WEIGHTS].type = VertexBuffer::AttributeType::FLOAT4;
mAttributes[VertexAttribute::BONE_WEIGHTS].flags = 0;
mBufferCount++;
} else {
// Because the Material's SKN variant supports both skinning and morphing, it expects
// all attributes related to *both* to be present. In turn, this means that a VertexBuffer
// used for skinning and/or morphing, needs to provide all related attributes.
// Currently, the backend must handle disabled arrays in the VertexBuffer that are declared
// in the shader. In GL this happens automatically, in vulkan/metal, the backends have to
// use dummy buffers.
// - A complication is that backends need to know if an attribute is declared as float or
// integer in the shader, regardless of if the attribute is enabled or not in the
// VertexBuffer (e.g. the morphing attributes could be disabled because we're only using
// skinning).
// - Another complication is that the SKN variant is selected by the renderable
// (as opposed to the RenderPrimitive), so it's possible and valid for a primitive
// that isn't skinned nor morphed to be rendered with the SKN variant (morphing/skinning
// will then be disabled dynamically).
//
// Because of that we need to set FLAG_INTEGER_TARGET on all attributes that we know are
// integer in the shader and the bottom line is that BONE_INDICES always needs to be set to
// FLAG_INTEGER_TARGET.
mAttributes[BONE_INDICES].flags |= Attribute::FLAG_INTEGER_TARGET;
}
size_t bufferSizes[MAX_VERTEX_BUFFER_COUNT] = {};
auto const& declaredAttributes = mDeclaredAttributes;
auto const& attributes = mAttributes;
#pragma nounroll
for (size_t i = 0, n = attributeArray.size(); i < n; ++i) {
if (declaredAttributes[i]) {
const uint32_t offset = attributes[i].offset;
const uint8_t stride = attributes[i].stride;
const uint8_t slot = attributes[i].buffer;
attributeArray[i].offset = offset;
attributeArray[i].stride = stride;
attributeArray[i].buffer = slot;
attributeArray[i].type = attributes[i].type;
attributeArray[i].flags = attributes[i].flags;
const size_t end = offset + mVertexCount * stride;
bufferSizes[slot] = math::max(bufferSizes[slot], end);
}
}
// Backends do not (and should not) know the semantics of each vertex attribute, but they
// need to know whether the vertex shader consumes them as integers or as floats.
// NOTE: This flag needs to be set regardless of whether the attribute is actually declared.
attributeArray[BONE_INDICES].flags |= Attribute::FLAG_INTEGER_TARGET;
FEngine::DriverApi& driver = engine.getDriverApi();
mVertexBufferInfoHandle = engine.getVertexBufferInfoFactory().create(driver,
mBufferCount, mDeclaredAttributes.count(), mAttributes);
mHandle = driver.createVertexBuffer(mVertexCount, mVertexBufferInfoHandle);
// calculate buffer sizes
size_t bufferSizes[MAX_VERTEX_BUFFER_COUNT] = {};
#pragma nounroll
for (size_t i = 0, n = mAttributes.size(); i < n; ++i) {
if (mDeclaredAttributes[i]) {
const uint32_t offset = mAttributes[i].offset;
const uint8_t stride = mAttributes[i].stride;
const uint8_t slot = mAttributes[i].buffer;
const size_t end = offset + mVertexCount * stride;
if (slot != Attribute::BUFFER_UNUSED) {
assert_invariant(slot < MAX_VERTEX_BUFFER_COUNT);
bufferSizes[slot] = std::max(bufferSizes[slot], end);
}
}
}
mHandle = driver.createVertexBuffer(
mBufferCount, attributeCount, mVertexCount, attributeArray);
// If buffer objects are not enabled at the API level, then we create them internally.
if (!mBufferObjectsEnabled) {
// If buffer objects are not enabled at the API level, then we create them internally.
#pragma nounroll
for (size_t index = 0; index < MAX_VERTEX_BUFFER_COUNT; ++index) {
size_t const i = mAttributes[index].buffer;
if (i != Attribute::BUFFER_UNUSED) {
assert_invariant(bufferSizes[i] > 0);
if (!mBufferObjects[i]) {
BufferObjectHandle bo = driver.createBufferObject(bufferSizes[i],
backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC);
driver.setVertexBufferObject(mHandle, i, bo);
mBufferObjects[i] = bo;
}
for (size_t i = 0; i < MAX_VERTEX_BUFFER_COUNT; ++i) {
if (bufferSizes[i] > 0) {
BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i],
backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC);
driver.setVertexBufferObject(mHandle, i, bo);
mBufferObjects[i] = bo;
}
}
} else {
// in advanced skinning mode, we manage the BONE_INDICES and BONE_WEIGHTS arrays ourselves,
// so we have to set the corresponding buffer objects.
// add buffer objects for indices and weights
if (mAdvancedSkinningEnabled) {
for (auto index : { VertexAttribute::BONE_INDICES, VertexAttribute::BONE_WEIGHTS }) {
size_t const i = mAttributes[index].buffer;
assert_invariant(i != Attribute::BUFFER_UNUSED);
assert_invariant(bufferSizes[i] > 0);
if (!mBufferObjects[i]) {
BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i],
backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC);
driver.setVertexBufferObject(mHandle, i, bo);
mBufferObjects[i] = bo;
}
for (size_t i = mBufferCount - 2; i < mBufferCount; ++i) {
BufferObjectHandle const bo = driver.createBufferObject(bufferSizes[i],
backend::BufferObjectBinding::VERTEX, backend::BufferUsage::STATIC);
driver.setVertexBufferObject(mHandle, i, bo);
mBufferObjects[i] = bo;
}
}
}
}
void FVertexBuffer::terminate(FEngine& engine) {
@@ -319,7 +249,6 @@ void FVertexBuffer::terminate(FEngine& engine) {
}
}
driver.destroyVertexBuffer(mHandle);
engine.getVertexBufferInfoFactory().destroy(driver, mVertexBufferInfoHandle);
}
size_t FVertexBuffer::getVertexCount() const noexcept {
@@ -355,22 +284,25 @@ void FVertexBuffer::setBufferObjectAt(FEngine& engine, uint8_t bufferIndex,
}
void FVertexBuffer::updateBoneIndicesAndWeights(FEngine& engine,
std::unique_ptr<uint16_t[]> skinJoints,
std::unique_ptr<float[]> skinWeights) {
std::unique_ptr<uint16_t[]> skinJoints,
std::unique_ptr<float[]> skinWeights) {
ASSERT_PRECONDITION(mAdvancedSkinningEnabled, "No advanced skinning enabled");
auto jointsData = skinJoints.release();
uint8_t const indicesIndex = mAttributes[VertexAttribute::BONE_INDICES].buffer;
engine.getDriverApi().updateBufferObject(mBufferObjects[indicesIndex],
{jointsData, mVertexCount * 8,
[](void* buffer, size_t, void*) { delete[] static_cast<uint16_t*>(buffer); }},
0);
auto bdJoints = BufferDescriptor(
jointsData, mVertexCount * 8,
[](void *buffer, size_t size, void *user) {
delete[] static_cast<uint16_t *>(buffer); });
engine.getDriverApi().updateBufferObject(mBufferObjects[mBufferCount - 2],
std::move(bdJoints), 0);
auto weightsData = skinWeights.release();
uint8_t const weightsIndex = mAttributes[VertexAttribute::BONE_WEIGHTS].buffer;
engine.getDriverApi().updateBufferObject(mBufferObjects[weightsIndex],
{weightsData, mVertexCount * 16,
[](void* buffer, size_t, void*) { delete[] static_cast<float*>(buffer); }},
0);
}
auto bdWeights = BufferDescriptor(
weightsData, mVertexCount * 16,
[](void *buffer, size_t size, void *user) {
delete[] static_cast<float *>(buffer); });
engine.getDriverApi().updateBufferObject(mBufferObjects[mBufferCount - 1],
std::move(bdWeights), 0);
}
} // namespace filament
+6 -3
View File
@@ -40,7 +40,6 @@ class FEngine;
class FVertexBuffer : public VertexBuffer {
public:
using VertexBufferInfoHandle = backend::VertexBufferInfoHandle;
using VertexBufferHandle = backend::VertexBufferHandle;
using BufferObjectHandle = backend::BufferObjectHandle;
@@ -70,9 +69,13 @@ public:
private:
friend class VertexBuffer;
VertexBufferInfoHandle mVertexBufferInfoHandle;
struct AttributeData : backend::Attribute {
AttributeData() : backend::Attribute{ .type = backend::ElementType::FLOAT4 } {}
};
VertexBufferHandle mHandle;
backend::AttributeArray mAttributes;
std::array<AttributeData, backend::MAX_VERTEX_ATTRIBUTE_COUNT> mAttributes;
std::array<BufferObjectHandle, backend::MAX_VERTEX_BUFFER_COUNT> mBufferObjects;
AttributeBitset mDeclaredAttributes;
uint32_t mVertexCount = 0;
+33 -55
View File
@@ -20,7 +20,6 @@
#include "Froxelizer.h"
#include "RenderPrimitive.h"
#include "ResourceAllocator.h"
#include "ShadowMapManager.h"
#include "details/Engine.h"
#include "details/IndirectLight.h"
@@ -44,7 +43,6 @@
#include <math/scalar.h>
#include <math/fast.h>
#include <array>
#include <memory>
using namespace utils;
@@ -60,9 +58,9 @@ static constexpr float PID_CONTROLLER_Kd = 0.0f;
FView::FView(FEngine& engine)
: mFroxelizer(engine),
mFogEntity(engine.getEntityManager().create()),
mIsStereoSupported(engine.getDriverApi().isStereoSupported(engine.getConfig().stereoscopicType)),
mPerViewUniforms(engine) {
mIsStereoSupported(engine.getDriverApi().isStereoSupported()),
mPerViewUniforms(engine),
mShadowMapManager(engine) {
DriverApi& driver = engine.getDriverApi();
FDebugRegistry& debugRegistry = engine.getDebugRegistry();
@@ -78,11 +76,7 @@ FView::FView(FEngine& engine)
#ifndef NDEBUG
debugRegistry.registerDataSource("d.view.frame_info",
[this]() -> DebugRegistry::DataSource {
assert_invariant(!mDebugFrameHistory);
mDebugFrameHistory = std::make_unique<std::array<DebugRegistry::FrameHistory, 5*60>>();
return { mDebugFrameHistory->data(), mDebugFrameHistory->size() };
});
mDebugFrameHistory.data(), mDebugFrameHistory.size());
debugRegistry.registerProperty("d.view.pid.kp", &engine.debug.view.pid.kp);
debugRegistry.registerProperty("d.view.pid.ki", &engine.debug.view.pid.ki);
debugRegistry.registerProperty("d.view.pid.kd", &engine.debug.view.pid.kd);
@@ -119,8 +113,7 @@ void FView::terminate(FEngine& engine) {
driver.destroyBufferObject(mLightUbh);
driver.destroyBufferObject(mRenderableUbh);
drainFrameHistory(engine);
ShadowMapManager::terminate(engine, mShadowMapManager);
mShadowMapManager.terminate(engine);
mPerViewUniforms.terminate(driver);
mFroxelizer.terminate(driver);
@@ -249,24 +242,21 @@ float2 FView::updateScale(FEngine& engine,
#ifndef NDEBUG
// only for debugging...
if (mDebugFrameHistory) {
using namespace std::chrono;
using duration_ms = duration<float, std::milli>;
const float target = (1000.0f * float(frameRateOptions.interval)) / displayInfo.refreshRate;
const float targetWithHeadroom = target * (1.0f - frameRateOptions.headRoomRatio);
std::move(mDebugFrameHistory->begin() + 1,
mDebugFrameHistory->end(), mDebugFrameHistory->begin());
mDebugFrameHistory->back() = {
.target = target,
.targetWithHeadroom = targetWithHeadroom,
.frameTime = duration_cast<duration_ms>(info.frameTime).count(),
.frameTimeDenoised = duration_cast<duration_ms>(info.denoisedFrameTime).count(),
.scale = mScale.x * mScale.y,
.pid_e = mPidController.getError(),
.pid_i = mPidController.getIntegral(),
.pid_d = mPidController.getDerivative()
};
}
using duration_ms = std::chrono::duration<float, std::milli>;
const float target = (1000.0f * float(frameRateOptions.interval)) / displayInfo.refreshRate;
const float targetWithHeadroom = target * (1.0f - frameRateOptions.headRoomRatio);
std::move(mDebugFrameHistory.begin() + 1,
mDebugFrameHistory.end(), mDebugFrameHistory.begin());
mDebugFrameHistory.back() = {
.target = target,
.targetWithHeadroom = targetWithHeadroom,
.frameTime = std::chrono::duration_cast<duration_ms>(info.frameTime).count(),
.frameTimeDenoised = std::chrono::duration_cast<duration_ms>(info.denoisedFrameTime).count(),
.scale = mScale.x * mScale.y,
.pid_e = mPidController.getError(),
.pid_i = mPidController.getIntegral(),
.pid_d = mPidController.getDerivative()
};
#endif
return mScale;
@@ -291,9 +281,9 @@ void FView::prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableD
return;
}
auto& lcm = engine.getLightManager();
mShadowMapManager.reset();
ShadowMapManager::Builder builder;
auto& lcm = engine.getLightManager();
// dominant directional light is always as index 0
FLightManager::Instance const directionalLight = lightData.elementAt<FScene::LIGHT_INSTANCE>(0);
@@ -302,7 +292,7 @@ void FView::prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableD
const auto& shadowOptions = lcm.getShadowOptions(directionalLight);
assert_invariant(shadowOptions.shadowCascades >= 1 &&
shadowOptions.shadowCascades <= CONFIG_MAX_SHADOW_CASCADES);
builder.directionalShadowMap(0, &shadowOptions);
mShadowMapManager.setDirectionalShadowMap(0, &shadowOptions);
}
// Find all shadow-casting spotlights.
@@ -336,7 +326,7 @@ void FView::prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableD
if (shadowMapCount + shadowMapCountNeeded <= CONFIG_MAX_SHADOWMAPS) {
shadowMapCount += shadowMapCountNeeded;
const auto& shadowOptions = lcm.getShadowOptions(li);
builder.shadowMap(l, spotLight, &shadowOptions);
mShadowMapManager.addShadowMap(l, spotLight, &shadowOptions);
}
if (shadowMapCount >= CONFIG_MAX_SHADOWMAPS) {
@@ -344,14 +334,11 @@ void FView::prepareShadowing(FEngine& engine, FScene::RenderableSoa& renderableD
}
}
if (builder.hasShadowMaps()) {
ShadowMapManager::createIfNeeded(engine, mShadowMapManager);
auto shadowTechnique = mShadowMapManager->update(builder, engine, *this,
cameraInfo, renderableData, lightData);
auto shadowTechnique = mShadowMapManager.update(engine, *this, cameraInfo,
renderableData, lightData);
mHasShadowing = any(shadowTechnique);
mNeedsShadowMap = any(shadowTechnique & ShadowMapManager::ShadowTechnique::SHADOW_MAP);
}
mHasShadowing = any(shadowTechnique);
mNeedsShadowMap = any(shadowTechnique & ShadowMapManager::ShadowTechnique::SHADOW_MAP);
}
void FView::prepareLighting(FEngine& engine, CameraInfo const& cameraInfo) noexcept {
@@ -626,7 +613,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, RootArenaScope& rootAren
uint32_t(endDirCastersOnly - beginRenderables)};
merged = { 0, uint32_t(endPotentialSpotCastersOnly - beginRenderables) };
if (!needsShadowMap() || !mShadowMapManager->hasSpotShadows()) {
if (!mShadowMapManager.hasSpotShadows()) {
// we know we don't have spot shadows, we can reduce the range to not even include
// the potential spot casters
merged = { 0, uint32_t(endDirCastersOnly - beginRenderables) };
@@ -692,11 +679,8 @@ void FView::bindPerViewUniformsAndSamplers(FEngine::DriverApi& driver) const noe
driver.bindUniformBuffer(+UniformBindingPoints::LIGHTS,
mLightUbh);
if (needsShadowMap()) {
assert_invariant(mShadowMapManager->getShadowUniformsHandle());
driver.bindUniformBuffer(+UniformBindingPoints::SHADOW,
mShadowMapManager->getShadowUniformsHandle());
}
driver.bindUniformBuffer(+UniformBindingPoints::SHADOW,
mShadowMapManager.getShadowUniformsHandle());
driver.bindUniformBuffer(+UniformBindingPoints::FROXEL_RECORDS,
mFroxelizer.getRecordBuffer());
@@ -797,12 +781,7 @@ void FView::prepareStructure(Handle<HwTexture> structure) const noexcept {
}
void FView::prepareShadow(Handle<HwTexture> texture) const noexcept {
// when needsShadowMap() is not set, this method only just sets a dummy texture
// in the needed samplers (in that case `texture` is actually a dummy texture).
ShadowMapManager::ShadowMappingUniforms uniforms;
if (needsShadowMap()) {
uniforms = mShadowMapManager->getShadowMappingUniforms();
}
const auto& uniforms = mShadowMapManager.getShadowMappingUniforms();
switch (mShadowType) {
case filament::ShadowType::PCF:
mPerViewUniforms.prepareShadowPCF(texture, uniforms);
@@ -1000,8 +979,7 @@ void FView::updatePrimitivesLod(FEngine& engine, const CameraInfo&,
FrameGraphId<FrameGraphTexture> FView::renderShadowMaps(FEngine& engine, FrameGraph& fg,
CameraInfo const& cameraInfo, float4 const& userTime,
RenderPassBuilder const& passBuilder) noexcept {
assert_invariant(needsShadowMap());
return mShadowMapManager->render(engine, fg, passBuilder, *this, cameraInfo, userTime);
return mShadowMapManager.render(engine, fg, passBuilder, *this, cameraInfo, userTime);
}
void FView::commitFrameHistory(FEngine& engine) noexcept {
+5 -9
View File
@@ -53,9 +53,6 @@
#include <math/scalar.h>
#include <math/mat4.h>
#include <array>
#include <memory>
namespace utils {
class JobSystem;
} // namespace utils;
@@ -173,7 +170,7 @@ public:
bool hasDPCF() const noexcept { return mShadowType == ShadowType::DPCF; }
bool hasPCSS() const noexcept { return mShadowType == ShadowType::PCSS; }
bool hasPicking() const noexcept { return mActivePickingQueriesList != nullptr; }
bool hasStereo() const noexcept {
bool hasInstancedStereo() const noexcept {
return mIsStereoSupported && mStereoscopicOptions.enabled;
}
@@ -201,9 +198,8 @@ public:
void setStereoscopicOptions(StereoscopicOptions const& options) noexcept;
FCamera const* getDirectionalShadowCamera() const noexcept {
if (!mShadowMapManager) return nullptr;
return mShadowMapManager->getDirectionalShadowCamera();
FCamera const* getDirectionalLightCamera() const noexcept {
return mShadowMapManager.getDirectionalLightCamera();
}
void setRenderTarget(FRenderTarget* renderTarget) noexcept {
@@ -559,7 +555,7 @@ private:
mutable bool mHasShadowing = false;
mutable bool mNeedsShadowMap = false;
std::unique_ptr<ShadowMapManager> mShadowMapManager;
ShadowMapManager mShadowMapManager;
std::array<math::float4, 4> mMaterialGlobals = {{
{ 0, 0, 0, 1 },
@@ -569,7 +565,7 @@ private:
}};
#ifndef NDEBUG
std::unique_ptr<std::array<DebugRegistry::FrameHistory, 5*60>> mDebugFrameHistory;
std::array<DebugRegistry::FrameHistory, 5*60> mDebugFrameHistory;
#endif
};
-38
View File
@@ -1,38 +0,0 @@
material {
name : blitArray,
parameters : [
{
type : sampler2dArray,
name : color,
precision: medium
},
{
type : int,
name : layerIndex
},
{
type : float4,
name : viewport,
precision: high
}
],
variables : [
vertex
],
depthWrite : false,
depthCulling : false,
domain: postprocess
}
vertex {
void postProcessVertex(inout PostProcessVertexInputs postProcess) {
postProcess.vertex.xy = materialParams.viewport.xy + postProcess.normalizedUV * materialParams.viewport.zw;
postProcess.vertex.xy = uvToRenderTargetUV(postProcess.vertex.xy);
}
}
fragment {
void postProcess(inout PostProcessInputs postProcess) {
postProcess.color = textureLod(materialParams_color, vec3(variable_vertex.xy, materialParams.layerIndex), 0.0);
}
}
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.50.5"
spec.version = "1.50.2"
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.50.5/filament-v1.50.5-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.50.2/filament-v1.50.2-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
+36 -77
View File
@@ -24,7 +24,6 @@
#include <math/mat4.h>
#include <math/quat.h>
#include <algorithm>
#include <cmath>
namespace filament {
@@ -122,90 +121,50 @@ public:
}
void update(FLOAT deltaTime) override {
vec3 forceLocal { 0.0, 0.0, 0.0 };
auto getLocalDirection = [this]() -> vec3 {
vec3 directionLocal{ 0.0, 0.0, 0.0 };
if (mKeyDown[(int)Base::Key::FORWARD]) {
directionLocal += vec3{ 0.0, 0.0, -1.0 };
}
if (mKeyDown[(int)Base::Key::LEFT]) {
directionLocal += vec3{ -1.0, 0.0, 0.0 };
}
if (mKeyDown[(int)Base::Key::BACKWARD]) {
directionLocal += vec3{ 0.0, 0.0, 1.0 };
}
if (mKeyDown[(int)Base::Key::RIGHT]) {
directionLocal += vec3{ 1.0, 0.0, 0.0 };
}
return directionLocal;
};
if (mKeyDown[(int) Base::Key::FORWARD]) {
forceLocal += vec3{ 0.0, 0.0, -1.0 };
}
if (mKeyDown[(int) Base::Key::LEFT]) {
forceLocal += vec3{ -1.0, 0.0, 0.0 };
}
if (mKeyDown[(int) Base::Key::BACKWARD]) {
forceLocal += vec3{ 0.0, 0.0, 1.0 };
}
if (mKeyDown[(int) Base::Key::RIGHT]) {
forceLocal += vec3{ 1.0, 0.0, 0.0 };
}
auto getWorldDirection = [this](vec3 directionLocal) -> vec3 {
const mat4 orientation = mat4::lookAt(Base::mEye, Base::mTarget, Base::mProps.upVector);
vec3 directionWorld = (orientation * vec4{ directionLocal, 0.0f }).xyz;
if (mKeyDown[(int)Base::Key::UP]) {
directionWorld += vec3{ 0.0, 1.0, 0.0 };
}
if (mKeyDown[(int)Base::Key::DOWN]) {
directionWorld += vec3{ 0.0, -1.0, 0.0 };
}
return directionWorld;
};
const mat4 orientation = mat4::lookAt(Base::mEye, Base::mTarget, Base::mProps.upVector);
vec3 forceWorld = (orientation * vec4{ forceLocal, 0.0f }).xyz;
vec3 const localDirection = getLocalDirection();
vec3 const worldDirection = getWorldDirection(localDirection);
if (mKeyDown[(int) Base::Key::UP]) {
forceWorld += vec3{ 0.0, 1.0, 0.0 };
}
if (mKeyDown[(int) Base::Key::DOWN]) {
forceWorld += vec3{ 0.0, -1.0, 0.0 };
}
// unit of dampingFactor is [1/s]
FLOAT const dampingFactor = Base::mProps.flightMoveDamping;
forceWorld *= mMoveSpeed;
const auto dampingFactor = Base::mProps.flightMoveDamping;
if (dampingFactor == 0.0) {
// Without damping, we simply treat the force as our velocity.
vec3 const speed = worldDirection * mMoveSpeed;
mEyeVelocity = speed;
vec3 const positionDelta = mEyeVelocity * deltaTime;
Base::mEye += positionDelta;
Base::mTarget += positionDelta;
mEyeVelocity = forceWorld;
} else {
auto dt = deltaTime / 16.0;
for (size_t i = 0; i < 16; i++) {
// Note: the algorithm below doesn't work well for large time steps because
// we're not using a closed form for updating the position, so we need
// to loop a few times. We could make this better by having a dynamic
// loop count. What we're really doing is evaluation the solution to
// a differential equation numerically.
// Kinetic friction is a force opposing velocity and proportional to it.:
// F = -kv
// F = ma
// ==> ma = -kv
// a = -vk/m [m.s^-2] = [m/s] * [Kg/s] / [Kg]
// ==> dampingFactor = k/m [1/s] = [Kg/s] / [Kg]
//
// The velocity update for dt due to friction is then:
// v = v + a.dt
// = v - v * dampingFactor * dt
// = v * (1.0 - dampingFactor * dt)
mEyeVelocity = mEyeVelocity * saturate(1.0 - dampingFactor * dt);
// We also undergo an acceleration proportional to the distance to the target speed
// (the closer we are the less we accelerate, similar to a car).
// F = k * (target_v - v)
// F = ma
// ==> ma = k * (target_v - v)
// a = k/m * (target_v - v) [m.s^-2] = [Kg/s] / [Kg] * [m/s]
//
// The velocity update for dt due to the acceleration (the gas basically) is then:
// v = v + a.dt
// = v + k/m * (target_v - v).dt
// We're using the same dampingFactor here, but we don't have to.
auto const accelerationFactor = dampingFactor;
vec3 const acceleration = worldDirection *
(accelerationFactor * std::max(mMoveSpeed - length(mEyeVelocity), FLOAT(0)));
mEyeVelocity += acceleration * dt;
vec3 const positionDelta = mEyeVelocity * dt;
Base::mEye += positionDelta;
Base::mTarget += positionDelta;
}
// The dampingFactor acts as "friction", which acts upon the camera in the direction
// opposite its velocity.
// Force is also multiplied by the dampingFactor, to "make up" for the friction.
// This ensures that the max velocity still approaches mMoveSpeed;
vec3 velocityDelta = (forceWorld - mEyeVelocity) * dampingFactor;
mEyeVelocity += velocityDelta * deltaTime;
}
const vec3 positionDelta = mEyeVelocity * deltaTime;
Base::mEye += positionDelta;
Base::mTarget += positionDelta;
}
Bookmark getCurrentBookmark() const override {
@@ -115,7 +115,7 @@ struct Variant {
inline bool hasDirectionalLighting() const noexcept { return key & DIR; }
inline bool hasDynamicLighting() const noexcept { return key & DYN; }
inline bool hasSkinningOrMorphing() const noexcept { return key & SKN; }
inline bool hasStereo() const noexcept { return key & STE; }
inline bool hasInstancedStereo() const noexcept { return key & STE; }
inline void setDirectionalLighting(bool v) noexcept { set(v, DIR); }
inline void setDynamicLighting(bool v) noexcept { set(v, DYN); }
+1 -1
View File
@@ -894,7 +894,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
.domain = mMaterialDomain,
.materialInfo = &info,
.hasFramebufferFetch = mEnableFramebufferFetch,
.usesClipDistance = v.variant.hasStereo(),
.usesClipDistance = v.variant.hasInstancedStereo(),
.glsl = {},
};

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