Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0404be5127 | ||
|
|
a784fb7d8b | ||
|
|
8459cbc91c | ||
|
|
344c04a2d8 | ||
|
|
55e2691f3f |
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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,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 @@
|
||||
26.1.10909125
|
||||
25.1.8937393
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.");
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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" };
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user