Merge branch 'rc/1.50.5' into release
This commit is contained in:
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.50.4'
|
||||
implementation 'com.google.android.filament:filament-android:1.50.5'
|
||||
}
|
||||
```
|
||||
|
||||
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
|
||||
iOS projects can use CocoaPods to install the latest release:
|
||||
|
||||
```shell
|
||||
pod 'Filament', '~> 1.50.4'
|
||||
pod 'Filament', '~> 1.50.5'
|
||||
```
|
||||
|
||||
### Snapshots
|
||||
|
||||
@@ -7,6 +7,13 @@ 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
|
||||
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ buildscript {
|
||||
|
||||
ext.versions = [
|
||||
'jdk': 17,
|
||||
'minSdk': 19,
|
||||
'minSdk': 21,
|
||||
'targetSdk': 34,
|
||||
'compileSdk': 34,
|
||||
'kotlin': '1.9.21',
|
||||
|
||||
@@ -47,6 +47,11 @@ 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));
|
||||
|
||||
@@ -101,7 +101,7 @@ public class Renderer {
|
||||
/**
|
||||
* Desired frame interval in unit of 1 / DisplayInfo.refreshRate.
|
||||
*/
|
||||
public float interval = 1.0f / 60.0f;
|
||||
public float interval = 1.0f;
|
||||
|
||||
/**
|
||||
* Additional headroom for the GPU as a ratio of the targetFrameTime.
|
||||
|
||||
@@ -16,12 +16,14 @@ 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>
|
||||
@@ -100,11 +102,21 @@ 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
|
||||
@@ -233,6 +245,7 @@ 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.4
|
||||
VERSION_NAME=1.50.5
|
||||
|
||||
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 19
|
||||
minSdkVersion versions.minSdk
|
||||
targetSdkVersion versions.targetSdk
|
||||
}
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
25.1.8937393
|
||||
26.1.10909125
|
||||
@@ -21,7 +21,7 @@ set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_VERSION 1)
|
||||
|
||||
# android
|
||||
set(API_LEVEL 19)
|
||||
set(API_LEVEL 21)
|
||||
|
||||
# architecture
|
||||
set(ARCH armv7a-linux-androideabi)
|
||||
|
||||
@@ -21,7 +21,7 @@ set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_VERSION 1)
|
||||
|
||||
# android
|
||||
set(API_LEVEL 19)
|
||||
set(API_LEVEL 21)
|
||||
|
||||
# architecture
|
||||
set(ARCH i686-linux-android)
|
||||
|
||||
@@ -66,6 +66,7 @@ set(SRCS
|
||||
src/Froxelizer.cpp
|
||||
src/Frustum.cpp
|
||||
src/HwRenderPrimitiveFactory.cpp
|
||||
src/HwVertexBufferInfoFactory.cpp
|
||||
src/IndexBuffer.cpp
|
||||
src/IndirectLight.cpp
|
||||
src/InstanceBuffer.cpp
|
||||
@@ -137,6 +138,7 @@ set(SRCS
|
||||
|
||||
set(PRIVATE_HDRS
|
||||
src/Allocators.h
|
||||
src/Bimap.h
|
||||
src/BufferPoolAllocator.h
|
||||
src/ColorSpaceUtils.h
|
||||
src/Culler.h
|
||||
@@ -147,6 +149,7 @@ set(PRIVATE_HDRS
|
||||
src/FrameSkipper.h
|
||||
src/Froxelizer.h
|
||||
src/HwRenderPrimitiveFactory.h
|
||||
src/HwVertexBufferInfoFactory.h
|
||||
src/Intersections.h
|
||||
src/MaterialParser.h
|
||||
src/PerViewUniforms.h
|
||||
@@ -212,6 +215,7 @@ 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
|
||||
|
||||
@@ -39,6 +39,7 @@ struct HwStream;
|
||||
struct HwSwapChain;
|
||||
struct HwTexture;
|
||||
struct HwTimerQuery;
|
||||
struct HwVertexBufferInfo;
|
||||
struct HwVertexBuffer;
|
||||
|
||||
/*
|
||||
@@ -114,18 +115,19 @@ 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 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>;
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ struct PipelineState {
|
||||
RasterState rasterState;
|
||||
StencilState stencilState;
|
||||
PolygonOffset polygonOffset;
|
||||
Viewport scissor{ 0, 0, (uint32_t)INT32_MAX, (uint32_t)INT32_MAX };
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
@@ -151,6 +151,13 @@ protected:
|
||||
} egl;
|
||||
} ext;
|
||||
|
||||
struct SwapChainEGL : public Platform::SwapChain {
|
||||
EGLSurface sur = EGL_NO_SURFACE;
|
||||
Config attribs{};
|
||||
EGLNativeWindowType nativeWindow{};
|
||||
EGLConfig config{};
|
||||
};
|
||||
|
||||
void initializeGlExtensions() noexcept;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -167,12 +167,15 @@ DECL_DRIVER_API_0(resetState)
|
||||
* -----------------------
|
||||
*/
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::VertexBufferHandle, createVertexBuffer,
|
||||
DECL_DRIVER_API_R_N(backend::VertexBufferInfoHandle, createVertexBufferInfo,
|
||||
uint8_t, bufferCount,
|
||||
uint8_t, attributeCount,
|
||||
uint32_t, vertexCount,
|
||||
backend::AttributeArray, attributes)
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::VertexBufferHandle, createVertexBuffer,
|
||||
uint32_t, vertexCount,
|
||||
backend::VertexBufferInfoHandle, vbih)
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::IndexBufferHandle, createIndexBuffer,
|
||||
backend::ElementType, elementType,
|
||||
uint32_t, indexCount,
|
||||
@@ -224,11 +227,7 @@ 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,
|
||||
uint32_t, offset,
|
||||
uint32_t, minIndex,
|
||||
uint32_t, maxIndex,
|
||||
uint32_t, count)
|
||||
backend::PrimitiveType, pt)
|
||||
|
||||
DECL_DRIVER_API_R_N(backend::ProgramHandle, createProgram,
|
||||
backend::Program&&, program)
|
||||
@@ -264,6 +263,7 @@ 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_0(bool, isStereoSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_N(bool, isStereoSupported, backend::StereoscopicType, stereoscopicType)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isParallelShaderCompileSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(bool, isDepthStencilResolveSupported)
|
||||
DECL_DRIVER_API_SYNCHRONOUS_0(uint8_t, getMaxDrawBuffers)
|
||||
@@ -494,12 +494,17 @@ 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
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define HandleAllocatorGL HandleAllocator<16, 64, 208> // ~3640 / pool / MiB
|
||||
#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
|
||||
|
||||
|
||||
@@ -49,24 +49,28 @@ struct AcquiredImage;
|
||||
struct HwBase {
|
||||
};
|
||||
|
||||
struct HwVertexBuffer : public HwBase {
|
||||
AttributeArray attributes{}; // 8 * MAX_VERTEX_ATTRIBUTE_COUNT
|
||||
uint32_t vertexCount{}; // 4
|
||||
|
||||
struct HwVertexBufferInfo : public HwBase {
|
||||
uint8_t bufferCount{}; // 1
|
||||
uint8_t attributeCount{}; // 1
|
||||
bool padding{}; // 1
|
||||
uint8_t bufferObjectsVersion{}; // 1 -> total struct is 136 bytes
|
||||
|
||||
HwVertexBuffer() noexcept = default;
|
||||
HwVertexBuffer(uint8_t bufferCount, uint8_t attributeCount, uint32_t elementCount,
|
||||
AttributeArray const& attributes) noexcept
|
||||
: attributes(attributes),
|
||||
vertexCount(elementCount),
|
||||
bufferCount(bufferCount),
|
||||
bool padding[2]{}; // 2
|
||||
HwVertexBufferInfo() noexcept = default;
|
||||
HwVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount) noexcept
|
||||
: bufferCount(bufferCount),
|
||||
attributeCount(attributeCount) {
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
}
|
||||
};
|
||||
|
||||
struct HwBufferObject : public HwBase {
|
||||
uint32_t byteCount{};
|
||||
|
||||
@@ -88,11 +92,6 @@ 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;
|
||||
};
|
||||
|
||||
|
||||
@@ -133,12 +133,9 @@ private:
|
||||
mHandleAllocator.deallocate(handle, p);
|
||||
}
|
||||
|
||||
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph,
|
||||
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
|
||||
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);
|
||||
|
||||
@@ -279,10 +279,16 @@ void MetalDriver::finish(int) {
|
||||
[oneOffBuffer waitUntilCompleted];
|
||||
}
|
||||
|
||||
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::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::createIndexBufferR(Handle<HwIndexBuffer> ibh, ElementType elementType,
|
||||
@@ -350,11 +356,9 @@ void MetalDriver::createSamplerGroupR(
|
||||
|
||||
void MetalDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
|
||||
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
|
||||
PrimitiveType pt, uint32_t offset,
|
||||
uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
|
||||
PrimitiveType pt) {
|
||||
construct_handle<MetalRenderPrimitive>(rph);
|
||||
MetalDriver::setRenderPrimitiveBuffer(rph, vbh, ibh);
|
||||
MetalDriver::setRenderPrimitiveRange(rph, pt, offset, minIndex, maxIndex, count);
|
||||
MetalDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh);
|
||||
}
|
||||
|
||||
void MetalDriver::createProgramR(Handle<HwProgram> rph, Program&& program) {
|
||||
@@ -440,6 +444,10 @@ 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>();
|
||||
}
|
||||
@@ -504,6 +512,12 @@ 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);
|
||||
@@ -756,8 +770,14 @@ bool MetalDriver::isSRGBSwapChainSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MetalDriver::isStereoSupported() {
|
||||
return true;
|
||||
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::isParallelShaderCompileSupported() {
|
||||
@@ -1063,23 +1083,14 @@ void MetalDriver::endRenderPass(int dummy) {
|
||||
mContext->currentRenderPassEncoder = nil;
|
||||
}
|
||||
|
||||
void MetalDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph,
|
||||
void MetalDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
|
||||
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh) {
|
||||
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
|
||||
auto vertexBuffer = handle_cast<MetalVertexBuffer>(vbh);
|
||||
auto indexBuffer = handle_cast<MetalIndexBuffer>(ibh);
|
||||
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);
|
||||
MetalVertexBufferInfo const* const vbi = handle_cast<MetalVertexBufferInfo>(vertexBuffer->vbih);
|
||||
primitive->setBuffers(vbi, vertexBuffer, indexBuffer);
|
||||
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) {
|
||||
@@ -1581,7 +1592,8 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
|
||||
}
|
||||
}
|
||||
|
||||
void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t instanceCount) {
|
||||
void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph,
|
||||
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
|
||||
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
|
||||
"Attempted to draw without a valid command encoder.");
|
||||
auto primitive = handle_cast<MetalRenderPrimitive>(rph);
|
||||
@@ -1598,7 +1610,7 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
return;
|
||||
}
|
||||
|
||||
ASSERT_PRECONDITION(bool(functions), "Attempting to draw with an invalid Metal program.");
|
||||
functions.validate();
|
||||
|
||||
auto [fragment, vertex] = functions.getRasterFunctions();
|
||||
|
||||
@@ -1719,36 +1731,6 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
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 };
|
||||
@@ -1826,10 +1808,10 @@ void MetalDriver::draw(PipelineState ps, Handle<HwRenderPrimitive> rph, uint32_t
|
||||
id<MTLCommandBuffer> cmdBuffer = getPendingCommandBuffer(mContext);
|
||||
id<MTLBuffer> metalIndexBuffer = indexBuffer->buffer.getGpuBufferForDraw(cmdBuffer);
|
||||
[mContext->currentRenderPassEncoder drawIndexedPrimitives:getMetalPrimitiveType(primitive->type)
|
||||
indexCount:primitive->count
|
||||
indexCount:indexCount
|
||||
indexType:getIndexType(indexBuffer->elementSize)
|
||||
indexBuffer:metalIndexBuffer
|
||||
indexBufferOffset:primitive->offset
|
||||
indexBufferOffset:indexOffset * primitive->indexBuffer->elementSize
|
||||
instanceCount:instanceCount];
|
||||
}
|
||||
|
||||
@@ -1902,6 +1884,38 @@ 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,10 +145,18 @@ private:
|
||||
MetalBuffer buffer;
|
||||
};
|
||||
|
||||
struct MetalVertexBuffer : public HwVertexBuffer {
|
||||
MetalVertexBuffer(MetalContext& context, uint8_t bufferCount, uint8_t attributeCount,
|
||||
uint32_t vertexCount, AttributeArray const& attributes);
|
||||
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);
|
||||
|
||||
Handle<HwVertexBufferInfo> vbih;
|
||||
utils::FixedCapacityVector<MetalBuffer*> buffers;
|
||||
};
|
||||
|
||||
@@ -161,7 +169,8 @@ struct MetalIndexBuffer : public HwIndexBuffer {
|
||||
|
||||
struct MetalRenderPrimitive : public HwRenderPrimitive {
|
||||
MetalRenderPrimitive();
|
||||
void setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer);
|
||||
void setBuffers(MetalVertexBufferInfo const* const vbi,
|
||||
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer);
|
||||
// The pointers to MetalVertexBuffer and MetalIndexBuffer are "weak".
|
||||
// The MetalVertexBuffer and MetalIndexBuffer must outlive the MetalRenderPrimitive.
|
||||
|
||||
|
||||
@@ -340,9 +340,16 @@ void MetalBufferObject::updateBufferUnsynchronized(void* data, size_t size, uint
|
||||
buffer.copyIntoBufferUnsynchronized(data, size, byteOffset);
|
||||
}
|
||||
|
||||
MetalVertexBuffer::MetalVertexBuffer(MetalContext& context, uint8_t bufferCount,
|
||||
uint8_t attributeCount, uint32_t vertexCount, AttributeArray const& attributes)
|
||||
: HwVertexBuffer(bufferCount, attributeCount, vertexCount, attributes), buffers(bufferCount, nullptr) {}
|
||||
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) {
|
||||
}
|
||||
|
||||
MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uint8_t elementSize,
|
||||
uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount),
|
||||
@@ -351,12 +358,12 @@ MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uin
|
||||
MetalRenderPrimitive::MetalRenderPrimitive()
|
||||
: bufferMapping(utils::FixedCapacityVector<Entry>::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {}
|
||||
|
||||
void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer*
|
||||
indexBuffer) {
|
||||
void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
|
||||
MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) {
|
||||
this->vertexBuffer = vertexBuffer;
|
||||
this->indexBuffer = indexBuffer;
|
||||
|
||||
const size_t attributeCount = vertexBuffer->attributes.size();
|
||||
const size_t attributeCount = vbi->attributes.size();
|
||||
|
||||
auto& mapping = bufferMapping;
|
||||
mapping.clear();
|
||||
@@ -397,7 +404,7 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalInde
|
||||
};
|
||||
|
||||
for (uint32_t attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) {
|
||||
const auto& attribute = vertexBuffer->attributes[attributeIndex];
|
||||
const auto& attribute = vbi->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,6 +30,8 @@
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <variant>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
@@ -41,36 +43,69 @@ class MetalShaderCompiler {
|
||||
public:
|
||||
class MetalFunctionBundle {
|
||||
public:
|
||||
MetalFunctionBundle() = default;
|
||||
MetalFunctionBundle(id<MTLFunction> fragment, id<MTLFunction> vertex)
|
||||
: functions{fragment, vertex} {
|
||||
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) {
|
||||
assert_invariant(fragment && vertex);
|
||||
assert_invariant(fragment.functionType == MTLFunctionTypeFragment);
|
||||
assert_invariant(vertex.functionType == MTLFunctionTypeVertex);
|
||||
return MetalFunctionBundle(Raster{fragment, vertex});
|
||||
}
|
||||
explicit MetalFunctionBundle(id<MTLFunction> compute) : functions{compute, nil} {
|
||||
|
||||
static MetalFunctionBundle compute(id<MTLFunction> compute) {
|
||||
assert_invariant(compute);
|
||||
assert_invariant(compute.functionType == MTLFunctionTypeKernel);
|
||||
return MetalFunctionBundle(Compute{compute});
|
||||
}
|
||||
|
||||
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]};
|
||||
static MetalFunctionBundle error(NSString* errorMessage, NSString* programName) {
|
||||
return MetalFunctionBundle(Error{errorMessage, programName});
|
||||
}
|
||||
|
||||
id<MTLFunction> getComputeFunction() const noexcept {
|
||||
assert_invariant(functions[0].functionType == MTLFunctionTypeKernel);
|
||||
return functions[0];
|
||||
}
|
||||
|
||||
explicit operator bool() const { return functions[0] != nil; }
|
||||
|
||||
private:
|
||||
// Can hold two functions, either:
|
||||
// - fragment and vertex (for rasterization pipelines)
|
||||
// - compute (for compute pipelines)
|
||||
id<MTLFunction> functions[2] = {nil, nil};
|
||||
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;
|
||||
};
|
||||
|
||||
using program_token_t = std::shared_ptr<MetalProgramToken>;
|
||||
|
||||
@@ -118,13 +118,16 @@ 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.");
|
||||
return {};
|
||||
NSString* programName = [NSString stringWithFormat:@"%s", program.getName().c_str_safe()];
|
||||
return MetalFunctionBundle::error(errorMessage, programName);
|
||||
}
|
||||
|
||||
MTLFunctionConstantValues* constants = [MTLFunctionConstantValues new];
|
||||
@@ -160,14 +163,15 @@ void MetalShaderCompiler::terminate() noexcept {
|
||||
assert_invariant(isRasterizationProgram != isComputeProgram);
|
||||
|
||||
if (isRasterizationProgram) {
|
||||
return {fragmentFunction, vertexFunction};
|
||||
return MetalFunctionBundle::raster(fragmentFunction, vertexFunction);
|
||||
}
|
||||
|
||||
if (isComputeProgram) {
|
||||
return MetalFunctionBundle{computeFunction};
|
||||
return MetalFunctionBundle::compute(computeFunction);
|
||||
}
|
||||
|
||||
return {};
|
||||
// Should never reach here.
|
||||
return MetalFunctionBundle::none();
|
||||
}
|
||||
|
||||
MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram(
|
||||
@@ -225,4 +229,24 @@ 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,6 +77,9 @@ void NoopDriver::finish(int) {
|
||||
void NoopDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
}
|
||||
|
||||
void NoopDriver::destroyVertexBufferInfo(Handle<HwVertexBufferInfo> vbih) {
|
||||
}
|
||||
|
||||
void NoopDriver::destroyVertexBuffer(Handle<HwVertexBuffer> vbh) {
|
||||
}
|
||||
|
||||
@@ -174,7 +177,7 @@ bool NoopDriver::isSRGBSwapChainSupported() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NoopDriver::isStereoSupported() {
|
||||
bool NoopDriver::isStereoSupported(backend::StereoscopicType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -340,12 +343,16 @@ void NoopDriver::blit(
|
||||
}
|
||||
|
||||
void NoopDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
|
||||
uint32_t instanceCount) {
|
||||
uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
}
|
||||
|
||||
void NoopDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
}
|
||||
|
||||
void NoopDriver::scissor(
|
||||
Viewport scissor) {
|
||||
}
|
||||
|
||||
void NoopDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
|
||||
}
|
||||
|
||||
|
||||
@@ -622,6 +622,7 @@ 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);
|
||||
@@ -686,6 +687,7 @@ 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,6 +205,7 @@ 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;
|
||||
|
||||
@@ -94,22 +94,24 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
|
||||
// GLSamplerGroup : 16 few
|
||||
// GLSwapChain : 16 few
|
||||
// GLTimerQuery : 16 few
|
||||
// -- less than or equal 16 bytes
|
||||
// GLRenderPrimitive : 20 many
|
||||
// GLFence : 24 few
|
||||
// -- less than or equal 24 bytes
|
||||
// GLBufferObject : 32 many
|
||||
// GLRenderPrimitive : 40 many
|
||||
// OpenGLProgram : 56 moderate
|
||||
// GLTexture : 64 moderate
|
||||
// -- less than or equal 64 bytes
|
||||
// GLVertexBuffer : 76 moderate
|
||||
// GLStream : 104 few
|
||||
// GLRenderTarget : 112 few
|
||||
// GLVertexBuffer : 200 moderate
|
||||
// -- less than or equal to 208 bytes
|
||||
// GLVertexBufferInfo : 132 moderate
|
||||
// -- less than or equal to 136 bytes
|
||||
|
||||
slog.d
|
||||
<< "\nGLSwapChain: " << sizeof(GLSwapChain)
|
||||
<< "\nGLBufferObject: " << sizeof(GLBufferObject)
|
||||
<< "\nGLVertexBuffer: " << sizeof(GLVertexBuffer)
|
||||
<< "\nGLVertexBufferInfo: " << sizeof(GLVertexBufferInfo)
|
||||
<< "\nGLIndexBuffer: " << sizeof(GLIndexBuffer)
|
||||
<< "\nGLSamplerGroup: " << sizeof(GLSamplerGroup)
|
||||
<< "\nGLRenderPrimitive: " << sizeof(GLRenderPrimitive)
|
||||
@@ -386,6 +388,10 @@ 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>();
|
||||
}
|
||||
@@ -446,14 +452,21 @@ Handle<HwTimerQuery> OpenGLDriver::createTimerQueryS() noexcept {
|
||||
return initHandle<GLTimerQuery>();
|
||||
}
|
||||
|
||||
void OpenGLDriver::createVertexBufferR(
|
||||
Handle<HwVertexBuffer> vbh,
|
||||
void OpenGLDriver::createVertexBufferInfoR(
|
||||
Handle<HwVertexBufferInfo> vbih,
|
||||
uint8_t bufferCount,
|
||||
uint8_t attributeCount,
|
||||
uint32_t elementCount,
|
||||
AttributeArray attributes) {
|
||||
DEBUG_MARKER()
|
||||
construct<GLVertexBuffer>(vbh, bufferCount, attributeCount, elementCount, attributes);
|
||||
construct<GLVertexBufferInfo>(vbih, bufferCount, attributeCount, attributes);
|
||||
}
|
||||
|
||||
void OpenGLDriver::createVertexBufferR(
|
||||
Handle<HwVertexBuffer> vbh,
|
||||
uint32_t vertexCount,
|
||||
Handle<HwVertexBufferInfo> vbih) {
|
||||
DEBUG_MARKER()
|
||||
construct<GLVertexBuffer>(vbh, vertexCount, vbih);
|
||||
}
|
||||
|
||||
void OpenGLDriver::createIndexBufferR(
|
||||
@@ -501,13 +514,11 @@ void OpenGLDriver::createBufferObjectR(Handle<HwBufferObject> boh,
|
||||
|
||||
void OpenGLDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
|
||||
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
|
||||
PrimitiveType pt, uint32_t offset,
|
||||
uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
|
||||
PrimitiveType pt) {
|
||||
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);
|
||||
|
||||
@@ -515,17 +526,14 @@ 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);
|
||||
|
||||
// update the VBO bindings in the VAO
|
||||
updateVertexArrayObject(rp, eb);
|
||||
// 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.
|
||||
|
||||
// this records the index buffer into the currently bound VAO
|
||||
gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, ib->gl.buffer);
|
||||
@@ -848,7 +856,6 @@ 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
|
||||
@@ -862,42 +869,49 @@ void OpenGLDriver::updateVertexArrayObject(GLRenderPrimitive* rp, GLVertexBuffer
|
||||
// that it's always reset in draw
|
||||
}
|
||||
|
||||
rp->maxVertexCount = vb->vertexCount;
|
||||
for (size_t i = 0, n = vb->attributes.size(); i < n; i++) {
|
||||
const auto& attribute = vb->attributes[i];
|
||||
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];
|
||||
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]);
|
||||
|
||||
// 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)) {
|
||||
|
||||
// if w're on ES2, the user shouldn't use FLAG_INTEGER_TARGET
|
||||
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)) {
|
||||
glVertexAttribIPointer(GLuint(i),
|
||||
(GLint)getComponentCount(attribute.type),
|
||||
getComponentType(attribute.type),
|
||||
attribute.stride,
|
||||
(void*) uintptr_t(attribute.offset));
|
||||
// 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);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
glVertexAttribPointer(GLuint(i),
|
||||
(GLint)getComponentCount(attribute.type),
|
||||
getComponentType(attribute.type),
|
||||
getNormalization(attribute.flags & Attribute::FLAG_NORMALIZED),
|
||||
attribute.stride,
|
||||
(void*) uintptr_t(attribute.offset));
|
||||
glVertexAttribPointer(index, size, type, normalized, stride, pointer);
|
||||
}
|
||||
|
||||
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, even if disabled.
|
||||
// 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.
|
||||
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
if (UTILS_UNLIKELY(attribute.flags & Attribute::FLAG_INTEGER_TARGET)) {
|
||||
if (!gl.isES2()) {
|
||||
@@ -1410,6 +1424,14 @@ 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) {
|
||||
@@ -1899,12 +1921,20 @@ bool OpenGLDriver::isSRGBSwapChainSupported() {
|
||||
return mPlatform.isSRGBSwapChainSupported();
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isStereoSupported() {
|
||||
// Stereo requires instancing and EXT_clip_cull_distance.
|
||||
bool OpenGLDriver::isStereoSupported(backend::StereoscopicType stereoscopicType) {
|
||||
// Instanced-stereo requires instancing and EXT_clip_cull_distance.
|
||||
// Multiview-stereo requires ES 3.0 and OVR_multiview2.
|
||||
if (UTILS_UNLIKELY(mContext.isES2())) {
|
||||
return false;
|
||||
}
|
||||
return mContext.ext.EXT_clip_cull_distance;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
bool OpenGLDriver::isParallelShaderCompileSupported() {
|
||||
@@ -3698,7 +3728,8 @@ void OpenGLDriver::updateTextureLodRange(GLTexture* texture, int8_t targetLevel)
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint32_t instanceCount) {
|
||||
void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
|
||||
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
|
||||
DEBUG_MARKER()
|
||||
auto& gl = mContext;
|
||||
|
||||
@@ -3733,16 +3764,15 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint
|
||||
|
||||
gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant);
|
||||
|
||||
setScissor(state.scissor);
|
||||
|
||||
if (UTILS_LIKELY(instanceCount <= 1)) {
|
||||
glDrawElements(GLenum(rp->type), (GLsizei)rp->count, rp->gl.getIndicesType(),
|
||||
reinterpret_cast<const void*>(rp->offset));
|
||||
glDrawElements(GLenum(rp->type), (GLsizei)indexCount, rp->gl.getIndicesType(),
|
||||
reinterpret_cast<const void*>(indexOffset * rp->gl.indicesSize));
|
||||
} else {
|
||||
assert_invariant(!mContext.isES2());
|
||||
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
|
||||
glDrawElementsInstanced(GLenum(rp->type), (GLsizei)rp->count,
|
||||
rp->gl.getIndicesType(), reinterpret_cast<const void*>(rp->offset),
|
||||
glDrawElementsInstanced(GLenum(rp->type), (GLsizei)indexCount,
|
||||
rp->gl.getIndicesType(),
|
||||
reinterpret_cast<const void*>(indexOffset * rp->gl.indicesSize),
|
||||
(GLsizei)instanceCount);
|
||||
#endif
|
||||
}
|
||||
@@ -3754,6 +3784,10 @@ void OpenGLDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph, uint
|
||||
#endif
|
||||
}
|
||||
|
||||
void OpenGLDriver::scissor(Viewport scissor) {
|
||||
setScissor(scissor);
|
||||
}
|
||||
|
||||
void OpenGLDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGroupCount) {
|
||||
getShaderCompilerService().tick();
|
||||
|
||||
|
||||
@@ -94,8 +94,22 @@ 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 {
|
||||
using HwVertexBuffer::HwVertexBuffer;
|
||||
GLVertexBuffer() noexcept = default;
|
||||
GLVertexBuffer(uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih)
|
||||
: HwVertexBuffer(vertexCount), vbih(vbih) {
|
||||
}
|
||||
Handle<HwVertexBufferInfo> vbih;
|
||||
struct {
|
||||
// 4 * MAX_VERTEX_ATTRIBUTE_COUNT bytes
|
||||
std::array<GLuint, MAX_VERTEX_ATTRIBUTE_COUNT> buffers{};
|
||||
|
||||
@@ -18,6 +18,14 @@
|
||||
|
||||
#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,12 +261,11 @@ 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,
|
||||
shaderSourceCode);
|
||||
token->shaderSourceCode);
|
||||
|
||||
// link the program
|
||||
GLuint const glProgram = linkProgram(gl, shaders, token->attributes);
|
||||
|
||||
@@ -18,16 +18,31 @@
|
||||
|
||||
#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__)
|
||||
|
||||
#include <sys/system_properties.h>
|
||||
#endif
|
||||
|
||||
#include <utils/compiler.h>
|
||||
|
||||
#include <utils/debug.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/Panic.h>
|
||||
#include <utils/ostream.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <new>
|
||||
#include <initializer_list>
|
||||
#include <utility>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifndef EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE
|
||||
# define EGL_CONTEXT_OPENGL_BACKWARDS_COMPATIBLE_ANGLE 0x3483
|
||||
@@ -48,7 +63,6 @@ UTILS_PRIVATE PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = {};
|
||||
}
|
||||
using namespace glext;
|
||||
|
||||
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
// Utilities
|
||||
// ---------------------------------------------------------------------------------------------
|
||||
@@ -137,7 +151,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("KHR_surfaceless_context");
|
||||
ext.egl.KHR_surfaceless_context = extensions.has("EGL_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;
|
||||
@@ -461,7 +475,13 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
|
||||
// this is not fatal
|
||||
eglSurfaceAttrib(mEGLDisplay, sur, EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
|
||||
|
||||
return (SwapChain*)sur;
|
||||
SwapChainEGL* const sc = new(std::nothrow) SwapChainEGL({
|
||||
.sur = sur,
|
||||
.attribs = std::move(attribs),
|
||||
.nativeWindow = (EGLNativeWindowType)nativeWindow,
|
||||
.config = config
|
||||
});
|
||||
return sc;
|
||||
}
|
||||
|
||||
Platform::SwapChain* PlatformEGL::createSwapChain(
|
||||
@@ -495,30 +515,42 @@ Platform::SwapChain* PlatformEGL::createSwapChain(
|
||||
logEglError("eglCreatePbufferSurface");
|
||||
return nullptr;
|
||||
}
|
||||
return (SwapChain*)sur;
|
||||
|
||||
SwapChainEGL* const sc = new(std::nothrow) SwapChainEGL({
|
||||
.sur = sur,
|
||||
.attribs = std::move(attribs),
|
||||
.config = config
|
||||
});
|
||||
return sc;
|
||||
}
|
||||
|
||||
void PlatformEGL::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
|
||||
EGLSurface sur = (EGLSurface) swapChain;
|
||||
if (sur != EGL_NO_SURFACE) {
|
||||
makeCurrent(mEGLDummySurface, mEGLDummySurface);
|
||||
eglDestroySurface(mEGLDisplay, sur);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformEGL::makeCurrent(Platform::SwapChain* drawSwapChain,
|
||||
Platform::SwapChain* readSwapChain) noexcept {
|
||||
EGLSurface drawSur = (EGLSurface) drawSwapChain;
|
||||
EGLSurface readSur = (EGLSurface) readSwapChain;
|
||||
if (drawSur != EGL_NO_SURFACE || readSur != EGL_NO_SURFACE) {
|
||||
makeCurrent(drawSur, readSur);
|
||||
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;
|
||||
}
|
||||
makeCurrent(dsc->sur, rsc->sur);
|
||||
}
|
||||
|
||||
void PlatformEGL::commit(Platform::SwapChain* swapChain) noexcept {
|
||||
EGLSurface sur = (EGLSurface) swapChain;
|
||||
if (sur != EGL_NO_SURFACE) {
|
||||
eglSwapBuffers(mEGLDisplay, sur);
|
||||
if (swapChain) {
|
||||
SwapChainEGL const* const sc = static_cast<SwapChainEGL const*>(swapChain);
|
||||
if (sc->sur != EGL_NO_SURFACE) {
|
||||
eglSwapBuffers(mEGLDisplay, sc->sur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -561,7 +593,7 @@ FenceStatus PlatformEGL::waitFence(
|
||||
}
|
||||
|
||||
OpenGLPlatform::ExternalTexture* PlatformEGL::createExternalImageTexture() noexcept {
|
||||
ExternalTexture* outTexture = new ExternalTexture{};
|
||||
ExternalTexture* outTexture = new(std::nothrow) ExternalTexture{};
|
||||
glGenTextures(1, &outTexture->id);
|
||||
if (UTILS_LIKELY(ext.gl.OES_EGL_image_external_essl3)) {
|
||||
outTexture->target = GL_TEXTURE_EXTERNAL_OES;
|
||||
|
||||
@@ -39,7 +39,12 @@
|
||||
// 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
|
||||
@@ -55,6 +60,9 @@
|
||||
#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 \
|
||||
@@ -72,7 +80,7 @@
|
||||
FVK_DEBUG_PRINT_GROUP_MARKERS
|
||||
|
||||
#ifndef NDEBUG
|
||||
#define FVK_DEBUG_FLAGS FVK_DEBUG_PERFORMANCE
|
||||
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_DEBUG_UTILS | FVK_DEBUG_VALIDATION)
|
||||
#else
|
||||
#define FVK_DEBUG_FLAGS 0
|
||||
#endif
|
||||
@@ -80,11 +88,23 @@
|
||||
#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,6 +24,7 @@
|
||||
#include "VulkanHandles.h"
|
||||
#include "VulkanImageUtility.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "VulkanTexture.h"
|
||||
|
||||
#include <backend/platforms/VulkanPlatform.h>
|
||||
|
||||
@@ -117,7 +118,9 @@ 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) {
|
||||
@@ -136,9 +139,63 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFla
|
||||
utils::slog.e << utils::io::endl;
|
||||
return VK_FALSE;
|
||||
}
|
||||
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION)
|
||||
#endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
|
||||
} // anonymous namespace
|
||||
}// 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)
|
||||
|
||||
using ImgUtil = VulkanImageUtility;
|
||||
|
||||
@@ -159,35 +216,26 @@ 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;
|
||||
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) {
|
||||
if (!context.isDebugUtilsSupported() && 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,
|
||||
};
|
||||
result = createDebugReportCallback(mPlatform->getInstance(), &cbinfo, VKALLOC,
|
||||
VkResult 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,
|
||||
@@ -286,9 +334,12 @@ void VulkanDriver::terminate() {
|
||||
if (mDebugCallback) {
|
||||
vkDestroyDebugReportCallbackEXT(mPlatform->getInstance(), mDebugCallback, VKALLOC);
|
||||
}
|
||||
if (mDebugMessenger) {
|
||||
vkDestroyDebugUtilsMessengerEXT(mPlatform->getInstance(), mDebugMessenger, VKALLOC);
|
||||
}
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
assert_invariant(DebugUtils::mSingleton);
|
||||
delete DebugUtils::mSingleton;
|
||||
#endif
|
||||
|
||||
mPlatform->terminate();
|
||||
}
|
||||
|
||||
@@ -359,12 +410,10 @@ void VulkanDriver::createSamplerGroupR(Handle<HwSamplerGroup> sbh, uint32_t coun
|
||||
|
||||
void VulkanDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
|
||||
Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh,
|
||||
PrimitiveType pt, uint32_t offset,
|
||||
uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
|
||||
PrimitiveType pt) {
|
||||
auto rp = mResourceAllocator.construct<VulkanRenderPrimitive>(rph, &mResourceAllocator);
|
||||
mResourceManager.acquire(rp);
|
||||
VulkanDriver::setRenderPrimitiveBuffer(rph, vbh, ibh);
|
||||
VulkanDriver::setRenderPrimitiveRange(rph, pt, offset, minIndex, maxIndex, count);
|
||||
VulkanDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh);
|
||||
}
|
||||
|
||||
void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
@@ -375,10 +424,26 @@ void VulkanDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
mResourceManager.release(rp);
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
mResourceManager.acquire(vertexBuffer);
|
||||
}
|
||||
|
||||
@@ -585,6 +650,10 @@ 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>();
|
||||
}
|
||||
@@ -799,8 +868,14 @@ bool VulkanDriver::isSRGBSwapChainSupported() {
|
||||
return mIsSRGBSwapChainSupported;
|
||||
}
|
||||
|
||||
bool VulkanDriver::isStereoSupported() {
|
||||
return true;
|
||||
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::isParallelShaderCompileSupported() {
|
||||
@@ -899,7 +974,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(bo, index);
|
||||
vb->setBuffer(mResourceAllocator, bo, index);
|
||||
}
|
||||
|
||||
void VulkanDriver::updateIndexBuffer(Handle<HwIndexBuffer> ibh, BufferDescriptor&& p,
|
||||
@@ -1063,7 +1138,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
|
||||
VulkanRenderTarget* const rt = mResourceAllocator.handle_cast<VulkanRenderTarget*>(rth);
|
||||
const VkExtent2D extent = rt->getExtent();
|
||||
assert_invariant(extent.width > 0 && extent.height > 0);
|
||||
assert_invariant(rt == mDefaultRenderTarget || 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
|
||||
@@ -1215,22 +1290,14 @@ 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)
|
||||
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);
|
||||
}
|
||||
// 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());
|
||||
}
|
||||
#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.
|
||||
@@ -1384,22 +1451,12 @@ void VulkanDriver::nextSubpass(int) {
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph,
|
||||
void VulkanDriver::setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
|
||||
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));
|
||||
}
|
||||
|
||||
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;
|
||||
primitive->setPrimitiveType(pt);
|
||||
}
|
||||
|
||||
void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) {
|
||||
@@ -1648,7 +1705,7 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
}
|
||||
|
||||
void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> rph,
|
||||
const uint32_t instanceCount) {
|
||||
uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("draw");
|
||||
|
||||
@@ -1659,7 +1716,6 @@ 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);
|
||||
@@ -1698,7 +1754,9 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
|
||||
};
|
||||
|
||||
// Declare fixed-size arrays that get passed to the pipeCache and to vkCmdBindVertexBuffers.
|
||||
uint32_t const bufferCount = prim.vertexBuffer->attributes.size();
|
||||
VulkanVertexBufferInfo const* const vbi =
|
||||
mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(prim.vertexBuffer->vbih);
|
||||
uint32_t const bufferCount = vbi->attributes.size();
|
||||
VkVertexInputAttributeDescription const* attribDesc = prim.vertexBuffer->getAttribDescriptions();
|
||||
VkVertexInputBindingDescription const* bufferDesc = prim.vertexBuffer->getBufferDescriptions();
|
||||
VkBuffer const* buffers = prim.vertexBuffer->getVkBuffers();
|
||||
@@ -1791,6 +1849,37 @@ 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();
|
||||
@@ -1809,35 +1898,9 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle<HwRenderPrimitive> r
|
||||
.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,6 +45,30 @@ 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;
|
||||
@@ -77,12 +101,10 @@ private:
|
||||
VulkanDriver& operator=(VulkanDriver const&) = delete;
|
||||
|
||||
private:
|
||||
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, Handle<HwVertexBuffer> vbh,
|
||||
inline void setRenderPrimitiveBuffer(Handle<HwRenderPrimitive> rph, PrimitiveType pt,
|
||||
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;
|
||||
@@ -95,7 +117,6 @@ private:
|
||||
VulkanRenderPass mCurrentRenderPass = {};
|
||||
VmaAllocator mAllocator = VK_NULL_HANDLE;
|
||||
VkDebugReportCallbackEXT mDebugCallback = VK_NULL_HANDLE;
|
||||
VkDebugUtilsMessengerEXT mDebugMessenger = VK_NULL_HANDLE;
|
||||
|
||||
VulkanContext mContext = {};
|
||||
VulkanResourceAllocator mResourceAllocator;
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
|
||||
#include "VulkanHandles.h"
|
||||
|
||||
#include "VulkanDriver.h"
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanDriver.h"
|
||||
#include "VulkanMemory.h"
|
||||
#include "spirv/VulkanSpirvUtils.h"
|
||||
|
||||
@@ -78,6 +80,23 @@ 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();
|
||||
@@ -93,10 +112,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,
|
||||
@@ -262,13 +281,23 @@ 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, uint8_t bufferCount, uint8_t attributeCount,
|
||||
uint32_t elementCount, AttributeArray const& attribs)
|
||||
: HwVertexBuffer(bufferCount, attributeCount, elementCount, attribs),
|
||||
VulkanResourceAllocator* allocator, uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih)
|
||||
: HwVertexBuffer(vertexCount),
|
||||
VulkanResource(VulkanResourceType::VERTEX_BUFFER),
|
||||
mInfo(new PipelineInfo(attribs.size())),
|
||||
vbih(vbih),
|
||||
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>();
|
||||
@@ -276,8 +305,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 < attribs.size(); attribIndex++) {
|
||||
Attribute attrib = attribs[attribIndex];
|
||||
for (uint32_t attribIndex = 0; attribIndex < vbi->attributes.size(); attribIndex++) {
|
||||
Attribute attrib = vbi->attributes[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);
|
||||
@@ -289,7 +318,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 = attribs[0];
|
||||
attrib = vbi->attributes[0];
|
||||
}
|
||||
offsets[attribIndex] = attrib.offset;
|
||||
attribDesc[attribIndex] = {
|
||||
@@ -309,8 +338,12 @@ VulkanVertexBuffer::~VulkanVertexBuffer() {
|
||||
delete mInfo;
|
||||
}
|
||||
|
||||
void VulkanVertexBuffer::setBuffer(VulkanBufferObject* bufferObject, uint32_t index) {
|
||||
size_t count = attributes.size();
|
||||
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();
|
||||
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,14 +129,21 @@ 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, uint8_t bufferCount, uint8_t attributeCount,
|
||||
uint32_t elementCount, AttributeArray const& attributes);
|
||||
VulkanResourceAllocator* allocator,
|
||||
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih);
|
||||
|
||||
~VulkanVertexBuffer();
|
||||
|
||||
void setBuffer(VulkanBufferObject* bufferObject, uint32_t index);
|
||||
void setBuffer(VulkanResourceAllocator const& allocator,
|
||||
VulkanBufferObject* bufferObject, uint32_t index);
|
||||
|
||||
inline VkVertexInputAttributeDescription const* getAttribDescriptions() {
|
||||
return mInfo->mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
|
||||
@@ -154,6 +161,8 @@ struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
|
||||
return mInfo->mSoa.data<PipelineInfo::OFFSETS>();
|
||||
}
|
||||
|
||||
Handle<HwVertexBufferInfo> vbih;
|
||||
|
||||
private:
|
||||
struct PipelineInfo {
|
||||
PipelineInfo(size_t size)
|
||||
@@ -161,7 +170,7 @@ private:
|
||||
mSoa.resize(size);
|
||||
}
|
||||
|
||||
// These corresponds to the index of the element in the SoA
|
||||
// These correspond 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;
|
||||
|
||||
@@ -53,6 +53,9 @@ 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,6 +43,7 @@ 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,
|
||||
@@ -62,16 +63,16 @@ struct VulkanResourceBase {
|
||||
protected:
|
||||
explicit VulkanResourceBase(VulkanResourceType type)
|
||||
: mRefCount(IS_HEAP_ALLOC_TYPE(type) ? 1 : 0),
|
||||
mType(type),
|
||||
mType(uint32_t(type)),
|
||||
mHandleId(0) {
|
||||
}
|
||||
|
||||
private:
|
||||
inline VulkanResourceType getType() {
|
||||
return mType;
|
||||
inline VulkanResourceType getType() const noexcept {
|
||||
return VulkanResourceType(mType);
|
||||
}
|
||||
|
||||
inline HandleBase::HandleId getId() {
|
||||
inline HandleBase::HandleId getId() const noexcept {
|
||||
return mHandleId;
|
||||
}
|
||||
|
||||
@@ -80,7 +81,7 @@ private:
|
||||
}
|
||||
|
||||
inline void ref() noexcept {
|
||||
if (IS_HEAP_ALLOC_TYPE(mType)) {
|
||||
if (IS_HEAP_ALLOC_TYPE(getType())) {
|
||||
return;
|
||||
}
|
||||
assert_invariant(mRefCount < ((1<<24) - 1));
|
||||
@@ -88,19 +89,19 @@ private:
|
||||
}
|
||||
|
||||
inline void deref() noexcept {
|
||||
if (IS_HEAP_ALLOC_TYPE(mType)) {
|
||||
if (IS_HEAP_ALLOC_TYPE(getType())) {
|
||||
return;
|
||||
}
|
||||
assert_invariant(mRefCount > 0);
|
||||
--mRefCount;
|
||||
}
|
||||
|
||||
inline size_t refcount() noexcept {
|
||||
inline size_t refcount() const noexcept {
|
||||
return mRefCount;
|
||||
}
|
||||
|
||||
uint32_t mRefCount : 24; // 16M is enough for the refcount
|
||||
VulkanResourceType mType : 8;
|
||||
uint32_t mType : 8; // must be uint32_t or MSVC doesn't pack it. no codegen impact w/ clang.
|
||||
HandleBase::HandleId mHandleId;
|
||||
|
||||
friend struct VulkanThreadSafeResource;
|
||||
@@ -110,6 +111,8 @@ private:
|
||||
friend class VulkanResourceManagerImpl;
|
||||
};
|
||||
|
||||
static_assert(sizeof(VulkanResourceBase) == 8, "VulkanResourceBase should be 8 bytes");
|
||||
|
||||
struct VulkanThreadSafeResource {
|
||||
protected:
|
||||
explicit VulkanThreadSafeResource(VulkanResourceType type)
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#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()) { \
|
||||
@@ -158,7 +159,9 @@ 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)
|
||||
@@ -181,7 +184,9 @@ 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,
|
||||
@@ -337,36 +342,22 @@ 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);
|
||||
}
|
||||
@@ -671,6 +662,12 @@ 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(), 1);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
|
||||
api.endRenderPass();
|
||||
}
|
||||
|
||||
@@ -48,7 +48,9 @@ TrianglePrimitive::TrianglePrimitive(filament::backend::DriverApi& driverApi,
|
||||
|
||||
const size_t size = sizeof(math::float2) * 3;
|
||||
mBufferObject = mDriverApi.createBufferObject(size, BufferObjectBinding::VERTEX, BufferUsage::STATIC);
|
||||
mVertexBuffer = mDriverApi.createVertexBuffer(1, 1, mVertexCount, attributes);
|
||||
mVertexBufferInfo = mDriverApi.createVertexBufferInfo(1, 1, attributes);
|
||||
mVertexBuffer = mDriverApi.createVertexBuffer(mVertexCount, mVertexBufferInfo);
|
||||
|
||||
mDriverApi.setVertexBufferObject(mVertexBuffer, 0, mBufferObject);
|
||||
BufferDescriptor vertexBufferDesc(gVertices, size);
|
||||
mDriverApi.updateBufferObject(mBufferObject, std::move(vertexBufferDesc), 0);
|
||||
@@ -60,7 +62,7 @@ TrianglePrimitive::TrianglePrimitive(filament::backend::DriverApi& driverApi,
|
||||
mDriverApi.updateIndexBuffer(mIndexBuffer, std::move(indexBufferDesc), 0);
|
||||
|
||||
mRenderPrimitive = mDriverApi.createRenderPrimitive(
|
||||
mVertexBuffer, mIndexBuffer, PrimitiveType::TRIANGLES, 0, 0, 2, 3);
|
||||
mVertexBuffer, mIndexBuffer, PrimitiveType::TRIANGLES);
|
||||
}
|
||||
|
||||
void TrianglePrimitive::updateVertices(const filament::math::float2 vertices[3]) noexcept {
|
||||
@@ -102,6 +104,7 @@ 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,6 +40,7 @@ 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>;
|
||||
|
||||
@@ -63,6 +64,7 @@ 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(), 1);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
api.endFrame(0);
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ TEST_F(BackendTest, VertexBufferUpdate) {
|
||||
}
|
||||
|
||||
getDriverApi().beginRenderPass(defaultRenderTarget, params);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 1);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 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(), 1);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
}
|
||||
|
||||
@@ -237,7 +237,7 @@ TEST_F(BackendTest, FeedbackLoops) {
|
||||
.sourceLevel = float(sourceLevel),
|
||||
});
|
||||
api.beginRenderPass(renderTargets[targetLevel], params);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 1);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
}
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ TEST_F(BackendTest, MRT) {
|
||||
|
||||
// Draw a triangle.
|
||||
getDriverApi().beginRenderPass(renderTarget, params);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 1);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 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(), 1);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
}
|
||||
|
||||
@@ -182,7 +182,6 @@ 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;
|
||||
@@ -203,7 +202,8 @@ 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.draw(state, triangle.getRenderPrimitive(), 1);
|
||||
api.scissor(params.viewport);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
// Adjust the base mip to 2.
|
||||
@@ -221,7 +221,8 @@ TEST_F(BackendTest, SetMinMaxLevel) {
|
||||
params.flags.clear = TargetBufferFlags::NONE;
|
||||
params.flags.discardStart = TargetBufferFlags::NONE;
|
||||
api.beginRenderPass(defaultRenderTarget, params);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 1);
|
||||
api.scissor(params.viewport);
|
||||
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 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(), 1);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 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(), 1);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 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(), 1);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 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(), 1);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 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(), 1);
|
||||
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 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.draw(ps, triangle.getRenderPrimitive(), 1);
|
||||
api.scissor(scissor);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
readPixelsAndAssertHash("scissor", kSrcTexWidth >> 1, kSrcTexHeight >> 1, fullRenderTarget,
|
||||
@@ -225,20 +225,21 @@ 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.draw(ps, triangle.getRenderPrimitive(), 1);
|
||||
api.scissor(scissor);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
params.viewport = topLeftViewport;
|
||||
params.flags.clear = TargetBufferFlags::NONE;
|
||||
params.flags.discardStart = TargetBufferFlags::NONE;
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 1);
|
||||
api.scissor(scissor);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
readPixelsAndAssertHash(
|
||||
|
||||
@@ -113,7 +113,7 @@ public:
|
||||
api.beginFrame(0, 0);
|
||||
|
||||
api.beginRenderPass(renderTarget, params);
|
||||
api.draw(ps, smallTriangle.getRenderPrimitive(), 1);
|
||||
api.draw(ps, smallTriangle.getRenderPrimitive(), 0, 3, 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(), 1);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 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(), 1);
|
||||
api.draw(ps, smallTriangle.getRenderPrimitive(), 0, 3, 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(), 1);
|
||||
api.draw(ps, triangle.getRenderPrimitive(), 0, 3, 1);
|
||||
api.endRenderPass();
|
||||
|
||||
api.commit(swapChain);
|
||||
|
||||
@@ -596,12 +596,12 @@ public:
|
||||
size_t getMaxAutomaticInstances() const noexcept;
|
||||
|
||||
/**
|
||||
* Queries the device and platform for instanced stereo rendering support.
|
||||
* Queries the device and platform for support of the given stereoscopic type.
|
||||
*
|
||||
* @return true if stereo rendering is supported, false otherwise
|
||||
* @return true if the given stereo rendering is supported, false otherwise
|
||||
* @see View::setStereoscopicOptions
|
||||
*/
|
||||
bool isStereoSupported() const noexcept;
|
||||
bool isStereoSupported(StereoscopicType stereoscopicType) const noexcept;
|
||||
|
||||
/**
|
||||
* Retrieves the configuration settings of this Engine.
|
||||
|
||||
@@ -583,8 +583,6 @@ 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 <stdint.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -39,10 +39,12 @@ namespace filament {
|
||||
*
|
||||
* - Configurable tone mapping operators
|
||||
* - GenericToneMapper
|
||||
* - AgXToneMapper
|
||||
* - Fixed-aesthetic tone mapping operators
|
||||
* - ACESToneMapper
|
||||
* - ACESLegacyToneMapper
|
||||
* - FilmicToneMapper
|
||||
* - PBRNeutralToneMapper
|
||||
* - Debug/validation tone mapping operators
|
||||
* - LinearToneMapper
|
||||
* - DisplayRangeToneMapper
|
||||
@@ -117,11 +119,22 @@ 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
|
||||
@@ -185,9 +198,6 @@ 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;
|
||||
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* 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() const noexcept {
|
||||
return downcast(this)->isStereoSupported();
|
||||
bool Engine::isStereoSupported(StereoscopicType stereoscopicType) const noexcept {
|
||||
return downcast(this)->isStereoSupported(stereoscopicType);
|
||||
}
|
||||
|
||||
size_t Engine::getMaxStereoscopicEyes() noexcept {
|
||||
|
||||
@@ -16,6 +16,16 @@
|
||||
|
||||
#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 {
|
||||
@@ -23,96 +33,60 @@ namespace filament {
|
||||
using namespace utils;
|
||||
using namespace backend;
|
||||
|
||||
bool operator<(HwRenderPrimitiveFactory::Key const& lhs,
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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.key < rhs;
|
||||
return lhs.params.vbh == rhs.params.vbh &&
|
||||
lhs.params.ibh == rhs.params.ibh &&
|
||||
lhs.params.type == rhs.params.type;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
HwRenderPrimitiveFactory::HwRenderPrimitiveFactory()
|
||||
: mArena("HwRenderPrimitiveFactory::mArena", SET_ARENA_SIZE),
|
||||
mSet(mArena) {
|
||||
mMap.reserve(256);
|
||||
mBimap(mArena) {
|
||||
mBimap.reserve(256);
|
||||
}
|
||||
|
||||
HwRenderPrimitiveFactory::~HwRenderPrimitiveFactory() noexcept = default;
|
||||
|
||||
void HwRenderPrimitiveFactory::terminate(DriverApi& driver) noexcept {
|
||||
assert_invariant(mMap.empty());
|
||||
assert_invariant(mSet.empty());
|
||||
void HwRenderPrimitiveFactory::terminate(DriverApi&) noexcept {
|
||||
assert_invariant(mBimap.empty());
|
||||
}
|
||||
|
||||
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 };
|
||||
auto HwRenderPrimitiveFactory::create(DriverApi& driver,
|
||||
backend::VertexBufferHandle vbh,
|
||||
backend::IndexBufferHandle ibh,
|
||||
backend::PrimitiveType type) noexcept -> Handle {
|
||||
|
||||
// see if we already have seen this RenderPrimitive
|
||||
auto pos = mSet.find(key);
|
||||
Key const key{{ vbh, ibh, type }, 1 };
|
||||
auto pos = mBimap.find(key);
|
||||
|
||||
// the common case is that we've never seen it (i.e.: no reuse)
|
||||
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 });
|
||||
if (UTILS_LIKELY(pos == mBimap.end())) {
|
||||
auto handle = driver.createRenderPrimitive(vbh, ibh, type);
|
||||
mBimap.insert(key, { handle });
|
||||
return handle;
|
||||
}
|
||||
pos->refs++;
|
||||
return pos->handle;
|
||||
|
||||
pos->first.pKey->refs++;
|
||||
return pos->second.handle;
|
||||
}
|
||||
|
||||
void HwRenderPrimitiveFactory::destroy(DriverApi& driver, RenderPrimitiveHandle rph) noexcept {
|
||||
void HwRenderPrimitiveFactory::destroy(DriverApi& driver, Handle handle) noexcept {
|
||||
// look for this handle in our map
|
||||
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);
|
||||
auto pos = mBimap.find(Value{ handle });
|
||||
if (--pos->second.pKey->refs == 0) {
|
||||
mBimap.erase(pos);
|
||||
driver.destroyRenderPrimitive(handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 <tsl/robin_map.h>
|
||||
|
||||
#include <set>
|
||||
#include <functional>
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
@@ -36,6 +36,7 @@ class FEngine;
|
||||
|
||||
class HwRenderPrimitiveFactory {
|
||||
public:
|
||||
using Handle = backend::RenderPrimitiveHandle;
|
||||
|
||||
HwRenderPrimitiveFactory();
|
||||
~HwRenderPrimitiveFactory() noexcept;
|
||||
@@ -47,70 +48,64 @@ public:
|
||||
|
||||
void terminate(backend::DriverApi& driver) noexcept;
|
||||
|
||||
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
|
||||
struct Parameters { // 20 bytes
|
||||
backend::VertexBufferHandle vbh; // 4
|
||||
backend::IndexBufferHandle ibh; // 4
|
||||
uint32_t offset; // 4
|
||||
uint32_t count; // 4
|
||||
backend::PrimitiveType type; // 4
|
||||
};
|
||||
|
||||
struct Entry { // 28 bytes
|
||||
Key key; // 20
|
||||
backend::RenderPrimitiveHandle handle; // 4
|
||||
mutable uint32_t refs; // 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 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 Arena = utils::Arena<
|
||||
utils::PoolAllocator<64>, // this seems to work with clang and msvc
|
||||
using PoolAllocatorArena = utils::Arena<
|
||||
utils::PoolAllocatorWithFallback<sizeof(Key)>,
|
||||
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
|
||||
Arena mArena;
|
||||
PoolAllocatorArena mArena;
|
||||
|
||||
// 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;
|
||||
// The special Bimap
|
||||
Bimap<Key, Value, KeyHash, ValueHash,
|
||||
utils::STLAllocator<Key, PoolAllocatorArena>> mBimap;
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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
|
||||
PipelineState PostProcessManager::PostProcessMaterial::getPipelineState(
|
||||
std::pair<backend::PipelineState, backend::Viewport>
|
||||
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(),
|
||||
.scissor = material->getDefaultInstance()->getScissor()
|
||||
};
|
||||
return {{
|
||||
.program = material->getProgram(Variant{ variantKey }),
|
||||
.rasterState = material->getRasterState() },
|
||||
material->getDefaultInstance()->getScissor() };
|
||||
}
|
||||
|
||||
UTILS_NOINLINE
|
||||
@@ -262,6 +262,7 @@ 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) },
|
||||
@@ -381,7 +382,7 @@ backend::Handle<backend::HwTexture> PostProcessManager::getZeroTextureArray() co
|
||||
|
||||
UTILS_NOINLINE
|
||||
void PostProcessManager::render(FrameGraphResources::RenderPassInfo const& out,
|
||||
backend::PipelineState const& pipeline,
|
||||
backend::PipelineState const& pipeline, backend::Viewport const& scissor,
|
||||
DriverApi& driver) const noexcept {
|
||||
|
||||
assert_invariant(
|
||||
@@ -392,7 +393,8 @@ void PostProcessManager::render(FrameGraphResources::RenderPassInfo const& out,
|
||||
FEngine const& engine = mEngine;
|
||||
Handle<HwRenderPrimitive> const fullScreenQuad = engine.getFullScreenRenderPrimitive();
|
||||
driver.beginRenderPass(out.target, out.params);
|
||||
driver.draw(pipeline, fullScreenQuad, 1);
|
||||
driver.scissor(scissor);
|
||||
driver.draw(pipeline, fullScreenQuad, 0, 3, 1);
|
||||
driver.endRenderPass();
|
||||
}
|
||||
|
||||
@@ -878,8 +880,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::screenSpaceAmbientOcclusion(
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
|
||||
PipelineState pipeline(material.getPipelineState(mEngine));
|
||||
pipeline.rasterState.depthFunc = RasterState::DepthFunc::L;
|
||||
auto pipeline = material.getPipelineState(mEngine);
|
||||
pipeline.first.rasterState.depthFunc = RasterState::DepthFunc::L;
|
||||
assert_invariant(ssao.params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH);
|
||||
render(ssao, pipeline, driver);
|
||||
});
|
||||
@@ -993,8 +995,8 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::bilateralBlurPass(FrameGraph
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
|
||||
PipelineState pipeline(material.getPipelineState(mEngine));
|
||||
pipeline.rasterState.depthFunc = RasterState::DepthFunc::L;
|
||||
auto pipeline = material.getPipelineState(mEngine);
|
||||
pipeline.first.rasterState.depthFunc = RasterState::DepthFunc::L;
|
||||
render(blurred, pipeline, driver);
|
||||
});
|
||||
|
||||
@@ -1653,7 +1655,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::dof(FrameGraph& fg,
|
||||
mi->setParameter("coc", inOutCoc, { .filterMin = SamplerMinFilter::NEAREST_MIPMAP_NEAREST });
|
||||
mi->use(driver);
|
||||
|
||||
const PipelineState pipeline(material.getPipelineState(mEngine, variant));
|
||||
auto const pipeline = material.getPipelineState(mEngine, variant);
|
||||
|
||||
for (size_t level = 0 ; level < mipmapCount - 1u ; level++) {
|
||||
const float w = FTexture::valueForLevel(level, desc.width);
|
||||
@@ -2100,7 +2102,7 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
|
||||
mi13->commit(driver);
|
||||
|
||||
// PipelineState for both materials should be the same
|
||||
const PipelineState pipeline(material9.getPipelineState(mEngine));
|
||||
auto const pipeline = material9.getPipelineState(mEngine);
|
||||
|
||||
for (size_t i = 1; i < inoutBloomOptions.levels; i++) {
|
||||
auto hwDstRT = resources.getRenderPassInfo(data.outRT[i]);
|
||||
@@ -2144,9 +2146,9 @@ PostProcessManager::BloomPassOutput PostProcessManager::bloom(FrameGraph& fg,
|
||||
.filterMin = SamplerMinFilter::LINEAR_MIPMAP_NEAREST});
|
||||
mi->use(driver);
|
||||
|
||||
PipelineState pipeline(material.getPipelineState(mEngine));
|
||||
pipeline.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
pipeline.rasterState.blendFunctionDstRGB = BlendFunction::ONE;
|
||||
auto pipeline = material.getPipelineState(mEngine);
|
||||
pipeline.first.rasterState.blendFunctionSrcRGB = BlendFunction::ONE;
|
||||
pipeline.first.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]);
|
||||
@@ -2299,8 +2301,11 @@ 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.draw(material.getPipelineState(mEngine, variant), fullScreenRenderPrimitive, 1);
|
||||
driver.scissor(pipeline.second);
|
||||
driver.draw(pipeline.first, fullScreenRenderPrimitive, 0, 3, 1);
|
||||
}
|
||||
|
||||
void PostProcessManager::customResolvePrepareSubpass(DriverApi& driver, CustomResolveOp op) noexcept {
|
||||
@@ -2316,10 +2321,12 @@ 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.draw(material.getPipelineState(mEngine), fullScreenRenderPrimitive, 1);
|
||||
driver.scissor(pipeline.second);
|
||||
driver.draw(pipeline.first, fullScreenRenderPrimitive, 0, 3, 1);
|
||||
}
|
||||
|
||||
FrameGraphId<FrameGraphTexture> PostProcessManager::customResolveUncompressPass(FrameGraph& fg,
|
||||
@@ -2762,9 +2769,10 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::taa(FrameGraph& fg,
|
||||
if (colorGradingConfig.asSubpass) {
|
||||
out.params.subpassMask = 1;
|
||||
}
|
||||
PipelineState const pipeline(material.getPipelineState(mEngine, variant));
|
||||
auto const pipeline = material.getPipelineState(mEngine, variant);
|
||||
driver.beginRenderPass(out.target, out.params);
|
||||
driver.draw(pipeline, mEngine.getFullScreenRenderPrimitive(), 1);
|
||||
driver.scissor(pipeline.second);
|
||||
driver.draw(pipeline.first, mEngine.getFullScreenRenderPrimitive(), 0, 3, 1);
|
||||
if (colorGradingConfig.asSubpass) {
|
||||
colorGradingSubpass(driver, colorGradingConfig);
|
||||
}
|
||||
@@ -2838,7 +2846,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::rcas(
|
||||
const uint8_t variant = uint8_t(
|
||||
translucent ? PostProcessVariant::TRANSLUCENT : PostProcessVariant::OPAQUE);
|
||||
|
||||
PipelineState const pipeline(material.getPipelineState(mEngine, variant));
|
||||
auto const pipeline = material.getPipelineState(mEngine, variant);
|
||||
render(out, pipeline, driver);
|
||||
});
|
||||
|
||||
@@ -2980,25 +2988,25 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::upscale(FrameGraph& fg, bool
|
||||
auto out = resources.getRenderPassInfo();
|
||||
|
||||
if (UTILS_UNLIKELY(twoPassesEASU)) {
|
||||
PipelineState pipeline0(splitEasuMaterial->getPipelineState(mEngine));
|
||||
PipelineState pipeline1(easuMaterial->getPipelineState(mEngine));
|
||||
pipeline1.rasterState.depthFunc = backend::SamplerCompareFunc::NE;
|
||||
auto pipeline0 = splitEasuMaterial->getPipelineState(mEngine);
|
||||
auto pipeline1 = easuMaterial->getPipelineState(mEngine);
|
||||
pipeline1.first.rasterState.depthFunc = backend::SamplerCompareFunc::NE;
|
||||
if (translucent) {
|
||||
enableTranslucentBlending(pipeline0);
|
||||
enableTranslucentBlending(pipeline1);
|
||||
enableTranslucentBlending(pipeline0.first);
|
||||
enableTranslucentBlending(pipeline1.first);
|
||||
}
|
||||
driver.beginRenderPass(out.target, out.params);
|
||||
driver.draw(pipeline0, fullScreenRenderPrimitive, 1);
|
||||
driver.draw(pipeline1, fullScreenRenderPrimitive, 1);
|
||||
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.endRenderPass();
|
||||
} else {
|
||||
PipelineState pipeline(easuMaterial->getPipelineState(mEngine));
|
||||
auto pipeline = easuMaterial->getPipelineState(mEngine);
|
||||
if (translucent) {
|
||||
enableTranslucentBlending(pipeline);
|
||||
enableTranslucentBlending(pipeline.first);
|
||||
}
|
||||
driver.beginRenderPass(out.target, out.params);
|
||||
driver.draw(pipeline, fullScreenRenderPrimitive, 1);
|
||||
driver.endRenderPass();
|
||||
render(out, pipeline, driver);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -3067,12 +3075,12 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::blit(FrameGraph& fg, bool tr
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
|
||||
PipelineState pipeline(material.getPipelineState(mEngine));
|
||||
auto pipeline = material.getPipelineState(mEngine);
|
||||
if (translucent) {
|
||||
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;
|
||||
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;
|
||||
}
|
||||
render(out, pipeline, driver);
|
||||
});
|
||||
@@ -3218,8 +3226,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)
|
||||
PipelineState pipeline(material.getPipelineState(mEngine));
|
||||
pipeline.scissor = { 1u, 1u, dim - 2u, dim - 2u };
|
||||
auto const [pipeline, _] = material.getPipelineState(mEngine);
|
||||
backend::Viewport const scissor = { 1u, 1u, dim - 2u, dim - 2u };
|
||||
|
||||
FMaterialInstance* const mi = material.getMaterialInstance(mEngine);
|
||||
mi->setParameter("color", in, {
|
||||
@@ -3230,7 +3238,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::vsmMipmapPass(FrameGraph& fg
|
||||
mi->setParameter("uvscale", 1.0f / float(dim));
|
||||
mi->commit(driver);
|
||||
mi->use(driver);
|
||||
render(out, pipeline, driver);
|
||||
render(out, pipeline, scissor, driver);
|
||||
|
||||
if (finalize) {
|
||||
driver.setMinMaxLevels(in, 0, level);
|
||||
@@ -3263,7 +3271,7 @@ FrameGraphId<FrameGraphTexture> PostProcessManager::debugShadowCascades(FrameGra
|
||||
auto depth = resources.getTexture(data.depth);
|
||||
auto out = resources.getRenderPassInfo();
|
||||
auto& material = getPostProcessMaterial("debugShadowCascades");
|
||||
PipelineState const pipeline(material.getPipelineState(mEngine));
|
||||
auto const pipeline = material.getPipelineState(mEngine);
|
||||
FMaterialInstance* mi = material.getMaterialInstance(mEngine);
|
||||
mi->setParameter("color", color, {}); // nearest
|
||||
mi->setParameter("depth", depth, {}); // nearest
|
||||
@@ -3276,6 +3284,84 @@ 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,6 +288,15 @@ 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;
|
||||
@@ -311,7 +320,7 @@ public:
|
||||
FMaterial* getMaterial(FEngine& engine) const noexcept;
|
||||
FMaterialInstance* getMaterialInstance(FEngine& engine) const noexcept;
|
||||
|
||||
backend::PipelineState getPipelineState(FEngine& engine,
|
||||
std::pair<backend::PipelineState, backend::Viewport> getPipelineState(FEngine& engine,
|
||||
Variant::type_t variantKey = 0u) const noexcept;
|
||||
|
||||
private:
|
||||
@@ -339,9 +348,15 @@ public:
|
||||
backend::DriverApi& driver) const noexcept;
|
||||
|
||||
void render(FrameGraphResources::RenderPassInfo const& out,
|
||||
backend::PipelineState const& pipeline,
|
||||
backend::PipelineState const& pipeline, backend::Viewport const& scissor,
|
||||
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;
|
||||
|
||||
|
||||
+85
-73
@@ -213,7 +213,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.mi->getMaterial();
|
||||
auto ma = first->primitive.primitive->getMaterialInstance()->getMaterial();
|
||||
ma->prepareProgram(first->primitive.materialVariant);
|
||||
}
|
||||
}
|
||||
@@ -295,8 +295,7 @@ 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.mi == rhs.primitive.mi &&
|
||||
lhs.primitive.primitiveHandle == rhs.primitive.primitiveHandle &&
|
||||
return lhs.primitive.primitive == rhs.primitive.primitive &&
|
||||
lhs.primitive.rasterState == rhs.primitive.rasterState &&
|
||||
lhs.primitive.skinningHandle == rhs.primitive.skinningHandle &&
|
||||
lhs.primitive.skinningOffset == rhs.primitive.skinningOffset &&
|
||||
@@ -425,7 +424,6 @@ 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)
|
||||
}
|
||||
@@ -436,7 +434,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 instancedStereoEyeCount) noexcept {
|
||||
uint8_t stereoEyeCount) noexcept {
|
||||
|
||||
SYSTRACE_CALL();
|
||||
|
||||
@@ -468,12 +466,12 @@ void RenderPass::generateCommands(CommandTypeFlags commandTypeFlags, Command* co
|
||||
case CommandTypeFlags::COLOR:
|
||||
curr = generateCommandsImpl<CommandTypeFlags::COLOR>(commandTypeFlags, curr,
|
||||
soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward,
|
||||
instancedStereoEyeCount);
|
||||
stereoEyeCount);
|
||||
break;
|
||||
case CommandTypeFlags::DEPTH:
|
||||
curr = generateCommandsImpl<CommandTypeFlags::DEPTH>(commandTypeFlags, curr,
|
||||
soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward,
|
||||
instancedStereoEyeCount);
|
||||
stereoEyeCount);
|
||||
break;
|
||||
default:
|
||||
// we should never end-up here
|
||||
@@ -496,7 +494,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 instancedStereoEyeCount) noexcept {
|
||||
float3 cameraPosition, float3 cameraForward, uint8_t stereoEyeCount) noexcept {
|
||||
|
||||
// generateCommands() writes both the draw and depth commands simultaneously such that
|
||||
// we go throw the list of renderables just once.
|
||||
@@ -522,7 +520,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
|
||||
|
||||
const bool hasShadowing = renderFlags & HAS_SHADOWING;
|
||||
const bool viewInverseFrontFaces = renderFlags & HAS_INVERSE_FRONT_FACES;
|
||||
const bool hasInstancedStereo = renderFlags & IS_STEREOSCOPIC;
|
||||
const bool hasStereo = renderFlags & IS_STEREOSCOPIC;
|
||||
|
||||
Command cmdColor;
|
||||
|
||||
@@ -578,11 +576,12 @@ 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 hasSkinningOrMorphing = soaVisibility[i].skinning || hasMorphing;
|
||||
const bool hasSkinning = soaVisibility[i].skinning;
|
||||
const bool hasSkinningOrMorphing = hasSkinning || hasMorphing;
|
||||
|
||||
cmdColor.key = makeField(soaVisibility[i].priority, PRIORITY_MASK, PRIORITY_SHIFT);
|
||||
cmdColor.key |= makeField(soaVisibility[i].channel, CHANNEL_MASK, CHANNEL_SHIFT);
|
||||
cmdColor.primitive.index = (uint16_t)i;
|
||||
cmdColor.primitive.index = i;
|
||||
cmdColor.primitive.instanceCount =
|
||||
soaInstanceInfo[i].count | PrimitiveInfo::USER_INSTANCE_MASK;
|
||||
cmdColor.primitive.instanceBufferHandle = soaInstanceInfo[i].handle;
|
||||
@@ -590,9 +589,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(hasInstancedStereo)) {
|
||||
if (UTILS_UNLIKELY(hasStereo)) {
|
||||
cmdColor.primitive.instanceCount =
|
||||
(soaInstanceInfo[i].count * instancedStereoEyeCount) |
|
||||
(soaInstanceInfo[i].count * stereoEyeCount) |
|
||||
PrimitiveInfo::USER_INSTANCE_MASK;
|
||||
}
|
||||
|
||||
@@ -603,36 +602,46 @@ 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 = (uint16_t)i;
|
||||
cmdDepth.primitive.index = 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;
|
||||
|
||||
if (UTILS_UNLIKELY(hasInstancedStereo)) {
|
||||
cmdDepth.primitive.skinningHandle = skinning.handle;
|
||||
cmdDepth.primitive.skinningOffset = skinning.offset;
|
||||
cmdDepth.primitive.skinningTexture = skinning.handleSampler;
|
||||
cmdDepth.primitive.morphWeightBuffer = morphing.handle;
|
||||
|
||||
if (UTILS_UNLIKELY(hasStereo)) {
|
||||
cmdColor.primitive.instanceCount =
|
||||
(soaInstanceInfo[i].count * instancedStereoEyeCount) |
|
||||
(soaInstanceInfo[i].count * stereoEyeCount) |
|
||||
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.
|
||||
@@ -643,15 +652,13 @@ 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.primitiveHandle = primitive.getHwHandle();
|
||||
cmdColor.primitive.primitive = &primitive;
|
||||
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;
|
||||
@@ -746,18 +753,14 @@ 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.primitiveHandle = primitive.getHwHandle();
|
||||
cmdDepth.primitive.mi = mi;
|
||||
cmdDepth.primitive.primitive = &primitive;
|
||||
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()?
|
||||
@@ -815,6 +818,28 @@ 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 {
|
||||
@@ -829,16 +854,17 @@ 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;
|
||||
@@ -885,49 +911,33 @@ void RenderPass::Executor::execute(FEngine& engine,
|
||||
}
|
||||
|
||||
// primitiveHandle may be invalid if no geometry was set on the renderable.
|
||||
if (UTILS_UNLIKELY(!first->primitive.primitiveHandle)) {
|
||||
if (UTILS_UNLIKELY(!first->primitive.primitive->getHwHandle())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// per-renderable uniform
|
||||
const PrimitiveInfo info = first->primitive;
|
||||
PrimitiveInfo const info = first->primitive;
|
||||
pipeline.rasterState = info.rasterState;
|
||||
//pipeline.vertexBufferInfo = info.vertexBufferInfo;
|
||||
|
||||
if (UTILS_UNLIKELY(mi != info.mi)) {
|
||||
if (UTILS_UNLIKELY(mi != info.primitive->getMaterialInstance())) {
|
||||
// this is always taken the first time
|
||||
mi = info.mi;
|
||||
mi = info.primitive->getMaterialInstance();
|
||||
assert_invariant(mi);
|
||||
|
||||
ma = mi->getMaterial();
|
||||
|
||||
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(!scissorOverride)) {
|
||||
backend::Viewport scissor = mi->getScissor();
|
||||
if (UTILS_UNLIKELY(mi->hasScissor())) {
|
||||
scissor = applyScissorViewport(mScissorViewport, scissor);
|
||||
}
|
||||
driver.scissor(scissor);
|
||||
}
|
||||
|
||||
*pPipelinePolygonOffset = mi->getPolygonOffset();
|
||||
if (UTILS_LIKELY(!polygonOffsetOverride)) {
|
||||
pipeline.polygonOffset = mi->getPolygonOffset();
|
||||
}
|
||||
pipeline.stencilState = mi->getStencilState();
|
||||
mi->use(driver);
|
||||
}
|
||||
@@ -993,7 +1003,9 @@ void RenderPass::Executor::execute(FEngine& engine,
|
||||
info.skinningTexture);
|
||||
}
|
||||
|
||||
driver.draw(pipeline, info.primitiveHandle, instanceCount);
|
||||
driver.draw(pipeline, info.primitive->getHwHandle(),
|
||||
info.primitive->getIndexOffset(), info.primitive->getIndexCount(),
|
||||
instanceCount);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ class CommandBufferQueue;
|
||||
}
|
||||
|
||||
class FMaterialInstance;
|
||||
class FRenderPrimitive;
|
||||
class RenderPassBuilder;
|
||||
|
||||
class RenderPass {
|
||||
@@ -235,13 +236,13 @@ public:
|
||||
return boolish ? value : uint64_t(0);
|
||||
}
|
||||
|
||||
struct PrimitiveInfo { // 48 bytes
|
||||
struct PrimitiveInfo { // 56 bytes
|
||||
union {
|
||||
FMaterialInstance const* mi;
|
||||
uint64_t padding = {}; // ensures mi is 8 bytes on all archs
|
||||
FRenderPrimitive const* primitive; // 8 bytes;
|
||||
uint64_t padding = {}; // ensures primitive 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
|
||||
@@ -251,16 +252,17 @@ 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) == 48);
|
||||
static_assert(sizeof(PrimitiveInfo) == 56);
|
||||
|
||||
struct alignas(8) Command { // 64 bytes
|
||||
CommandKey key = 0; // 8 bytes
|
||||
PrimitiveInfo primitive; // 48 bytes
|
||||
uint64_t reserved[1] = {}; // 8 bytes
|
||||
PrimitiveInfo primitive; // 56 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) {
|
||||
@@ -329,6 +331,10 @@ 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);
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
|
||||
#include <utils/debug.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
void FRenderPrimitive::init(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver,
|
||||
@@ -37,15 +40,16 @@ void FRenderPrimitive::init(HwRenderPrimitiveFactory& factory, backend::DriverAp
|
||||
FVertexBuffer* vertexBuffer = downcast(entry.vertices);
|
||||
FIndexBuffer* indexBuffer = downcast(entry.indices);
|
||||
|
||||
AttributeBitset enabledAttributes = vertexBuffer->getDeclaredAttributes();
|
||||
AttributeBitset const enabledAttributes = vertexBuffer->getDeclaredAttributes();
|
||||
|
||||
auto const& ebh = vertexBuffer->getHwHandle();
|
||||
auto const& ibh = indexBuffer->getHwHandle();
|
||||
|
||||
mHandle = factory.create(driver, ebh, ibh, entry.type, (uint32_t)entry.offset,
|
||||
(uint32_t)entry.minIndex, (uint32_t)entry.maxIndex, (uint32_t)entry.count);
|
||||
mHandle = factory.create(driver, ebh, ibh, entry.type);
|
||||
|
||||
mPrimitiveType = entry.type;
|
||||
mIndexOffset = entry.offset;
|
||||
mIndexCount = entry.count;
|
||||
mEnabledAttributes = enabledAttributes;
|
||||
}
|
||||
}
|
||||
@@ -58,22 +62,21 @@ 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 minIndex, size_t maxIndex, size_t count) noexcept {
|
||||
|
||||
AttributeBitset enabledAttributes = vertices->getDeclaredAttributes();
|
||||
|
||||
auto const& ebh = vertices->getHwHandle();
|
||||
auto const& ibh = indices->getHwHandle();
|
||||
|
||||
FVertexBuffer* vertices, FIndexBuffer* indices, size_t offset, size_t count) noexcept {
|
||||
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);
|
||||
AttributeBitset const enabledAttributes = vertices->getDeclaredAttributes();
|
||||
|
||||
auto const& ebh = vertices->getHwHandle();
|
||||
auto const& ibh = indices->getHwHandle();
|
||||
|
||||
mHandle = factory.create(driver, ebh, ibh, type);
|
||||
|
||||
mPrimitiveType = type;
|
||||
mIndexOffset = offset;
|
||||
mIndexCount = count;
|
||||
mEnabledAttributes = enabledAttributes;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,13 +17,16 @@
|
||||
#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 <utils/compiler.h>
|
||||
#include <stdint.h>
|
||||
|
||||
namespace filament {
|
||||
|
||||
@@ -43,13 +46,16 @@ public:
|
||||
void set(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver,
|
||||
RenderableManager::PrimitiveType type,
|
||||
FVertexBuffer* vertices, FIndexBuffer* indices, size_t offset,
|
||||
size_t minIndex, size_t maxIndex, size_t count) noexcept;
|
||||
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; }
|
||||
@@ -66,13 +72,19 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
FMaterialInstance const* mMaterialInstance = nullptr;
|
||||
backend::Handle<backend::HwRenderPrimitive> mHandle = {};
|
||||
// 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;
|
||||
};
|
||||
|
||||
AttributeBitset mEnabledAttributes = {};
|
||||
uint16_t mBlendOrder = 0;
|
||||
bool mGlobalBlendOrderEnabled = false;
|
||||
backend::PrimitiveType mPrimitiveType = backend::PrimitiveType::TRIANGLES;
|
||||
UTILS_UNUSED uint8_t reserved[4] = {};
|
||||
};
|
||||
|
||||
} // namespace filament
|
||||
|
||||
+47
-21
@@ -230,6 +230,32 @@ 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
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -262,14 +288,14 @@ float3 agxDefaultContrastApprox(float3 x) {
|
||||
float3 x2 = x * x;
|
||||
float3 x4 = x2 * x2;
|
||||
float3 x6 = x4 * x2;
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
// Adapted from https://iolite-engine.com/blog_posts/minimal_agx_implementation
|
||||
@@ -278,23 +304,23 @@ float3 agxLook(float3 val, AgxToneMapper::AgxLook look) {
|
||||
return val;
|
||||
}
|
||||
|
||||
const float3 lw = float3(0.2126, 0.7152, 0.0722);
|
||||
const float3 lw = float3(0.2126f, 0.7152f, 0.0722f);
|
||||
float luma = dot(val, lw);
|
||||
|
||||
// Default
|
||||
float3 offset = float3(0.0);
|
||||
float3 slope = float3(1.0);
|
||||
float3 power = float3(1.0);
|
||||
float sat = 1.0;
|
||||
float3 offset = float3(0.0f);
|
||||
float3 slope = float3(1.0f);
|
||||
float3 power = float3(1.0f);
|
||||
float sat = 1.0f;
|
||||
|
||||
if (look == AgxToneMapper::AgxLook::GOLDEN) {
|
||||
slope = float3(1.0, 0.9, 0.5);
|
||||
power = float3(0.8);
|
||||
slope = float3(1.0f, 0.9f, 0.5f);
|
||||
power = float3(0.8f);
|
||||
sat = 1.3;
|
||||
}
|
||||
if (look == AgxToneMapper::AgxLook::PUNCHY) {
|
||||
slope = float3(1.0);
|
||||
power = float3(1.35, 1.35, 1.35);
|
||||
slope = float3(1.0f);
|
||||
power = float3(1.35f, 1.35f, 1.35f);
|
||||
sat = 1.4;
|
||||
}
|
||||
|
||||
@@ -305,16 +331,16 @@ float3 agxLook(float3 val, AgxToneMapper::AgxLook look) {
|
||||
|
||||
float3 AgxToneMapper::operator()(float3 v) const noexcept {
|
||||
// Ensure no negative values
|
||||
v = max(float3(0.0), v);
|
||||
v = max(float3(0.0f), v);
|
||||
|
||||
v = AgXInsetMatrix * v;
|
||||
|
||||
// Log2 encoding
|
||||
v = max(v, 1E-10); // avoid 0 or negative numbers for log2
|
||||
v = max(v, 1E-10f); // avoid 0 or negative numbers for log2
|
||||
v = log2(v);
|
||||
v = (v - AgxMinEv) / (AgxMaxEv - AgxMinEv);
|
||||
|
||||
v = clamp(v, 0, 1);
|
||||
v = clamp(v, 0.0f, 1.0f);
|
||||
|
||||
// Apply sigmoid
|
||||
v = agxDefaultContrastApprox(v);
|
||||
@@ -325,7 +351,7 @@ float3 AgxToneMapper::operator()(float3 v) const noexcept {
|
||||
v = AgXOutsetMatrix * v;
|
||||
|
||||
// Linearize
|
||||
v = pow(max(float3(0.0), v), 2.2);
|
||||
v = pow(max(float3(0.0f), v), 2.2f);
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -109,14 +109,12 @@ 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, size_t minIndex, size_t maxIndex, size_t count) noexcept {
|
||||
size_t offset, UTILS_UNUSED size_t minIndex, UTILS_UNUSED 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;
|
||||
}
|
||||
@@ -436,11 +434,6 @@ 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.
|
||||
@@ -816,7 +809,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, 0, vertices->getVertexCount() - 1, count);
|
||||
type, vertices, indices, offset, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +276,7 @@ void FEngine::init() {
|
||||
|
||||
mFullScreenTriangleRph = driverApi.createRenderPrimitive(
|
||||
mFullScreenTriangleVb->getHwHandle(), mFullScreenTriangleIb->getHwHandle(),
|
||||
PrimitiveType::TRIANGLES, 0, 0, 2, (uint32_t)mFullScreenTriangleIb->getIndexCount());
|
||||
PrimitiveType::TRIANGLES);
|
||||
|
||||
// Compute a clip-space [-1 to 1] to texture space [0 to 1] matrix, taking into account
|
||||
// backend differences.
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "DFG.h"
|
||||
#include "PostProcessManager.h"
|
||||
#include "ResourceList.h"
|
||||
#include "HwVertexBufferInfoFactory.h"
|
||||
|
||||
#include "components/CameraManager.h"
|
||||
#include "components/LightManager.h"
|
||||
@@ -182,7 +183,9 @@ public:
|
||||
return CONFIG_MAX_INSTANCES;
|
||||
}
|
||||
|
||||
bool isStereoSupported() const noexcept { return getDriver().isStereoSupported(); }
|
||||
bool isStereoSupported(StereoscopicType stereoscopicType) const noexcept {
|
||||
return getDriver().isStereoSupported(stereoscopicType);
|
||||
}
|
||||
|
||||
static size_t getMaxStereoscopicEyes() noexcept {
|
||||
return CONFIG_MAX_STEREOSCOPIC_EYES;
|
||||
@@ -403,6 +406,10 @@ 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; }
|
||||
@@ -471,6 +478,7 @@ private:
|
||||
FLightManager mLightManager;
|
||||
FCameraManager mCameraManager;
|
||||
ResourceAllocator* mResourceAllocator = nullptr;
|
||||
HwVertexBufferInfoFactory mHwVertexBufferInfoFactory;
|
||||
|
||||
ResourceList<FBufferObject> mBufferObjects{ "BufferObject" };
|
||||
ResourceList<FRenderer> mRenderers{ "Renderer" };
|
||||
|
||||
@@ -532,7 +532,8 @@ void FMaterial::compile(CompilerPriorityQueue priority,
|
||||
utils::Invocable<void(Material*)>&& callback) noexcept {
|
||||
|
||||
// Turn off the STE variant if stereo is not supported.
|
||||
if (!mEngine.getDriverApi().isStereoSupported()) {
|
||||
const StereoscopicType stereoscopicType = mEngine.getConfig().stereoscopicType;
|
||||
if (!mEngine.getDriverApi().isStereoSupported(stereoscopicType)) {
|
||||
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.hasInstancedStereo()) renderFlags |= RenderPass::IS_STEREOSCOPIC;
|
||||
if (view.hasStereo()) 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.hasInstancedStereo());
|
||||
variant.setStereo(view.hasStereo());
|
||||
|
||||
/*
|
||||
* Frame graph
|
||||
|
||||
@@ -21,18 +21,41 @@
|
||||
|
||||
#include "FilamentAPI-impl.h"
|
||||
|
||||
#include <math/quat.h>
|
||||
#include <math/vec2.h>
|
||||
#include <filament/MaterialEnums.h>
|
||||
#include <filament/VertexBuffer.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 {
|
||||
FVertexBuffer::AttributeData mAttributes[MAX_VERTEX_ATTRIBUTE_COUNT];
|
||||
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{};
|
||||
AttributeBitset mDeclaredAttributes;
|
||||
uint32_t mVertexCount = 0;
|
||||
uint8_t mBufferCount = 0;
|
||||
@@ -88,11 +111,16 @@ VertexBuffer::Builder& VertexBuffer::Builder::attribute(VertexAttribute attribut
|
||||
}
|
||||
#endif
|
||||
|
||||
FVertexBuffer::AttributeData& entry = mImpl->mAttributes[attribute];
|
||||
auto& 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 " <<
|
||||
@@ -104,7 +132,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) {
|
||||
FVertexBuffer::AttributeData& entry = mImpl->mAttributes[attribute];
|
||||
auto& entry = mImpl->mAttributes[attribute];
|
||||
if (normalized) {
|
||||
entry.flags |= Attribute::FLAG_NORMALIZED;
|
||||
} else {
|
||||
@@ -130,15 +158,47 @@ VertexBuffer* VertexBuffer::Builder::build(Engine& engine) {
|
||||
auto const& declaredAttributes = mImpl->mDeclaredAttributes;
|
||||
auto const& attributes = mImpl->mAttributes;
|
||||
utils::bitset32 attributedBuffers;
|
||||
for (size_t j = 0; j < MAX_VERTEX_ATTRIBUTE_COUNT; ++j) {
|
||||
if (declaredAttributes[j]) {
|
||||
attributedBuffers.set(attributes[j].buffer);
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
@@ -149,96 +209,106 @@ 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) {
|
||||
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);
|
||||
mAttributes[VertexAttribute::BONE_INDICES] = {
|
||||
.offset = 0,
|
||||
.stride = 8,
|
||||
.buffer = mBufferCount,
|
||||
.type = VertexBuffer::AttributeType::USHORT4,
|
||||
.flags = Attribute::FLAG_INTEGER_TARGET,
|
||||
};
|
||||
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();
|
||||
|
||||
mHandle = driver.createVertexBuffer(
|
||||
mBufferCount, attributeCount, mVertexCount, attributeArray);
|
||||
mVertexBufferInfoHandle = engine.getVertexBufferInfoFactory().create(driver,
|
||||
mBufferCount, mDeclaredAttributes.count(), mAttributes);
|
||||
|
||||
// If buffer objects are not enabled at the API level, then we create them internally.
|
||||
if (!mBufferObjectsEnabled) {
|
||||
#pragma nounroll
|
||||
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 {
|
||||
// add buffer objects for indices and weights
|
||||
if (mAdvancedSkinningEnabled) {
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// in advanced skinning mode, we manage the BONE_INDICES and BONE_WEIGHTS arrays ourselves,
|
||||
// so we have to set the corresponding buffer objects.
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FVertexBuffer::terminate(FEngine& engine) {
|
||||
@@ -249,6 +319,7 @@ void FVertexBuffer::terminate(FEngine& engine) {
|
||||
}
|
||||
}
|
||||
driver.destroyVertexBuffer(mHandle);
|
||||
engine.getVertexBufferInfoFactory().destroy(driver, mVertexBufferInfoHandle);
|
||||
}
|
||||
|
||||
size_t FVertexBuffer::getVertexCount() const noexcept {
|
||||
@@ -284,25 +355,22 @@ 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();
|
||||
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);
|
||||
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 weightsData = skinWeights.release();
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace filament
|
||||
|
||||
@@ -40,6 +40,7 @@ class FEngine;
|
||||
|
||||
class FVertexBuffer : public VertexBuffer {
|
||||
public:
|
||||
using VertexBufferInfoHandle = backend::VertexBufferInfoHandle;
|
||||
using VertexBufferHandle = backend::VertexBufferHandle;
|
||||
using BufferObjectHandle = backend::BufferObjectHandle;
|
||||
|
||||
@@ -69,13 +70,9 @@ public:
|
||||
|
||||
private:
|
||||
friend class VertexBuffer;
|
||||
|
||||
struct AttributeData : backend::Attribute {
|
||||
AttributeData() : backend::Attribute{ .type = backend::ElementType::FLOAT4 } {}
|
||||
};
|
||||
|
||||
VertexBufferInfoHandle mVertexBufferInfoHandle;
|
||||
VertexBufferHandle mHandle;
|
||||
std::array<AttributeData, backend::MAX_VERTEX_ATTRIBUTE_COUNT> mAttributes;
|
||||
backend::AttributeArray mAttributes;
|
||||
std::array<BufferObjectHandle, backend::MAX_VERTEX_BUFFER_COUNT> mBufferObjects;
|
||||
AttributeBitset mDeclaredAttributes;
|
||||
uint32_t mVertexCount = 0;
|
||||
|
||||
@@ -60,7 +60,7 @@ static constexpr float PID_CONTROLLER_Kd = 0.0f;
|
||||
FView::FView(FEngine& engine)
|
||||
: mFroxelizer(engine),
|
||||
mFogEntity(engine.getEntityManager().create()),
|
||||
mIsStereoSupported(engine.getDriverApi().isStereoSupported()),
|
||||
mIsStereoSupported(engine.getDriverApi().isStereoSupported(engine.getConfig().stereoscopicType)),
|
||||
mPerViewUniforms(engine) {
|
||||
|
||||
DriverApi& driver = engine.getDriverApi();
|
||||
|
||||
@@ -173,7 +173,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 hasInstancedStereo() const noexcept {
|
||||
bool hasStereo() const noexcept {
|
||||
return mIsStereoSupported && mStereoscopicOptions.enabled;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
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.4"
|
||||
spec.version = "1.50.5"
|
||||
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
|
||||
spec.homepage = "https://google.github.io/filament"
|
||||
spec.authors = "Google LLC."
|
||||
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
|
||||
spec.platform = :ios, "11.0"
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.50.4/filament-v1.50.4-ios.tgz" }
|
||||
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.50.5/filament-v1.50.5-ios.tgz" }
|
||||
|
||||
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
|
||||
spec.pod_target_xcconfig = {
|
||||
|
||||
@@ -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 hasInstancedStereo() const noexcept { return key & STE; }
|
||||
inline bool hasStereo() 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.hasInstancedStereo(),
|
||||
.usesClipDistance = v.variant.hasStereo(),
|
||||
.glsl = {},
|
||||
};
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade
|
||||
out << "#extension GL_OES_EGL_image_external : require\n\n";
|
||||
}
|
||||
}
|
||||
if (v.hasInstancedStereo() && stage == ShaderStage::VERTEX) {
|
||||
if (v.hasStereo() && stage == ShaderStage::VERTEX) {
|
||||
// If we're not processing the shader through glslang (in the case of unoptimized
|
||||
// OpenGL shaders), then we need to add the #extension string ourselves.
|
||||
// If we ARE running the shader through glslang, then we must not include it,
|
||||
|
||||
@@ -51,8 +51,8 @@ void ShaderGenerator::generateSurfaceMaterialVariantDefines(utils::io::sstream&
|
||||
litVariants && filament::Variant::isShadowReceiverVariant(variant));
|
||||
CodeGenerator::generateDefine(out, "VARIANT_HAS_VSM",
|
||||
filament::Variant::isVSMVariant(variant));
|
||||
CodeGenerator::generateDefine(out, "VARIANT_HAS_INSTANCED_STEREO",
|
||||
hasInstancedStereo(variant, featureLevel));
|
||||
CodeGenerator::generateDefine(out, "VARIANT_HAS_STEREO",
|
||||
hasStereo(variant, featureLevel));
|
||||
|
||||
switch (stage) {
|
||||
case ShaderStage::VERTEX:
|
||||
@@ -758,9 +758,9 @@ bool ShaderGenerator::hasSkinningOrMorphing(
|
||||
&& featureLevel > MaterialBuilder::FeatureLevel::FEATURE_LEVEL_0;
|
||||
}
|
||||
|
||||
bool ShaderGenerator::hasInstancedStereo(
|
||||
bool ShaderGenerator::hasStereo(
|
||||
filament::Variant variant, MaterialBuilder::FeatureLevel featureLevel) noexcept {
|
||||
return variant.hasInstancedStereo()
|
||||
return variant.hasStereo()
|
||||
// HACK(exv): Ignore stereo variant when targeting ESSL 1.0. We should properly build a
|
||||
// system in matc which allows the set of included variants to differ per-feature level.
|
||||
&& featureLevel > MaterialBuilder::FeatureLevel::FEATURE_LEVEL_0;
|
||||
|
||||
@@ -115,7 +115,7 @@ private:
|
||||
filament::Variant variant,
|
||||
MaterialBuilder::FeatureLevel featureLevel) noexcept;
|
||||
|
||||
static bool hasInstancedStereo(
|
||||
static bool hasStereo(
|
||||
filament::Variant variant,
|
||||
MaterialBuilder::FeatureLevel featureLevel) noexcept;
|
||||
|
||||
|
||||
@@ -395,10 +395,10 @@ public:
|
||||
}
|
||||
|
||||
PoolAllocator(void* begin, size_t size) noexcept
|
||||
: mFreeList(begin, static_cast<char *>(begin) + size, ELEMENT_SIZE, ALIGNMENT, OFFSET) {
|
||||
: PoolAllocator(begin, static_cast<char *>(begin) + size) {
|
||||
}
|
||||
|
||||
template <typename AREA>
|
||||
template<typename AREA>
|
||||
explicit PoolAllocator(const AREA& area) noexcept
|
||||
: PoolAllocator(area.begin(), area.end()) {
|
||||
}
|
||||
@@ -424,6 +424,53 @@ private:
|
||||
FREELIST mFreeList;
|
||||
};
|
||||
|
||||
template <
|
||||
size_t ELEMENT_SIZE,
|
||||
size_t ALIGNMENT = alignof(std::max_align_t),
|
||||
typename FREELIST = FreeList>
|
||||
class PoolAllocatorWithFallback :
|
||||
private PoolAllocator<ELEMENT_SIZE, ALIGNMENT, 0, FREELIST>,
|
||||
private HeapAllocator {
|
||||
using PoolAllocator = PoolAllocator<ELEMENT_SIZE, ALIGNMENT, 0, FREELIST>;
|
||||
void* mBegin;
|
||||
void* mEnd;
|
||||
public:
|
||||
PoolAllocatorWithFallback(void* begin, void* end) noexcept
|
||||
: PoolAllocator(begin, end), mBegin(begin), mEnd(end) {
|
||||
}
|
||||
|
||||
PoolAllocatorWithFallback(void* begin, size_t size) noexcept
|
||||
: PoolAllocatorWithFallback(begin, static_cast<char*>(begin) + size) {
|
||||
}
|
||||
|
||||
template<typename AREA>
|
||||
explicit PoolAllocatorWithFallback(const AREA& area) noexcept
|
||||
: PoolAllocatorWithFallback(area.begin(), area.end()) {
|
||||
}
|
||||
|
||||
bool isHeapAllocation(void* p) const noexcept {
|
||||
return p < mBegin || p >= mEnd;
|
||||
}
|
||||
|
||||
// our allocator concept
|
||||
void* alloc(size_t size = ELEMENT_SIZE, size_t alignment = ALIGNMENT) noexcept {
|
||||
void* p = PoolAllocator::alloc(size, alignment);
|
||||
if (UTILS_UNLIKELY(!p)) {
|
||||
p = HeapAllocator::alloc(size, alignment);
|
||||
}
|
||||
assert_invariant(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
void free(void* p, size_t size) noexcept {
|
||||
if (UTILS_LIKELY(!isHeapAllocation(p))) {
|
||||
PoolAllocator::free(p, size);
|
||||
} else {
|
||||
HeapAllocator::free(p);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#define UTILS_MAX(a,b) ((a) > (b) ? (a) : (b))
|
||||
|
||||
template <typename T, size_t OFFSET = 0>
|
||||
@@ -884,7 +931,7 @@ public:
|
||||
|
||||
public:
|
||||
// we don't make this explicit, so that we can initialize a vector using a STLAllocator
|
||||
// from an Arena, avoiding to have to repeat the vector type.
|
||||
// from an Arena, avoiding having to repeat the vector type.
|
||||
STLAllocator(ARENA& arena) : mArena(arena) { } // NOLINT(google-explicit-constructor)
|
||||
|
||||
template<typename U>
|
||||
|
||||
@@ -59,7 +59,8 @@ enum class ToneMapping : uint8_t {
|
||||
FILMIC = 3,
|
||||
AGX = 4,
|
||||
GENERIC = 5,
|
||||
DISPLAY_RANGE = 6,
|
||||
PBR_NEUTRAL = 6,
|
||||
DISPLAY_RANGE = 7,
|
||||
};
|
||||
|
||||
using AmbientOcclusionOptions = filament::View::AmbientOcclusionOptions;
|
||||
|
||||
@@ -88,6 +88,7 @@ static int parse(jsmntok_t const* tokens, int i, const char* jsonChunk, ToneMapp
|
||||
else if (0 == compare(tokens[i], jsonChunk, "FILMIC")) { *out = ToneMapping::FILMIC; }
|
||||
else if (0 == compare(tokens[i], jsonChunk, "AGX")) { *out = ToneMapping::AGX; }
|
||||
else if (0 == compare(tokens[i], jsonChunk, "GENERIC")) { *out = ToneMapping::GENERIC; }
|
||||
else if (0 == compare(tokens[i], jsonChunk, "PBR_NEUTRAL")) { *out = ToneMapping::PBR_NEUTRAL; }
|
||||
else if (0 == compare(tokens[i], jsonChunk, "DISPLAY_RANGE")) { *out = ToneMapping::DISPLAY_RANGE; }
|
||||
else {
|
||||
slog.w << "Invalid ToneMapping: '" << STR(tokens[i], jsonChunk) << "'" << io::endl;
|
||||
@@ -679,11 +680,12 @@ constexpr ToneMapper* createToneMapper(const ColorGradingSettings& settings) noe
|
||||
case ToneMapping::FILMIC: return new FilmicToneMapper;
|
||||
case ToneMapping::AGX: return new AgxToneMapper(settings.agxToneMapper.look);
|
||||
case ToneMapping::GENERIC: return new GenericToneMapper(
|
||||
settings.genericToneMapper.contrast,
|
||||
settings.genericToneMapper.midGrayIn,
|
||||
settings.genericToneMapper.midGrayOut,
|
||||
settings.genericToneMapper.hdrMax
|
||||
settings.genericToneMapper.contrast,
|
||||
settings.genericToneMapper.midGrayIn,
|
||||
settings.genericToneMapper.midGrayOut,
|
||||
settings.genericToneMapper.hdrMax
|
||||
);
|
||||
case ToneMapping::PBR_NEUTRAL: return new PBRNeutralToneMapper;
|
||||
case ToneMapping::DISPLAY_RANGE: return new DisplayRangeToneMapper;
|
||||
}
|
||||
}
|
||||
@@ -734,6 +736,7 @@ static std::ostream& operator<<(std::ostream& out, ToneMapping in) {
|
||||
case ToneMapping::FILMIC: return out << "\"FILMIC\"";
|
||||
case ToneMapping::AGX: return out << "\"AGX\"";
|
||||
case ToneMapping::GENERIC: return out << "\"GENERIC\"";
|
||||
case ToneMapping::PBR_NEUTRAL: return out << "\"PBR_NEUTRAL\"";
|
||||
case ToneMapping::DISPLAY_RANGE: return out << "\"DISPLAY_RANGE\"";
|
||||
}
|
||||
return out << "\"INVALID\"";
|
||||
|
||||
@@ -145,6 +145,9 @@ static void computeToneMapPlot(ColorGradingSettings& settings, float* plot) {
|
||||
);
|
||||
hdrMax = settings.genericToneMapper.hdrMax;
|
||||
break;
|
||||
case ToneMapping::PBR_NEUTRAL:
|
||||
mapper = new PBRNeutralToneMapper;
|
||||
break;
|
||||
case ToneMapping::DISPLAY_RANGE:
|
||||
mapper = new DisplayRangeToneMapper;
|
||||
break;
|
||||
@@ -196,7 +199,7 @@ static void colorGradingUI(Settings& settings, float* rangePlot, float* curvePlo
|
||||
|
||||
int toneMapping = (int) colorGrading.toneMapping;
|
||||
ImGui::Combo("Tone-mapping", &toneMapping,
|
||||
"Linear\0ACES (legacy)\0ACES\0Filmic\0AgX\0Generic\0Display Range\0\0");
|
||||
"Linear\0ACES (legacy)\0ACES\0Filmic\0AgX\0Generic\0PBR Neutral\0Display Range\0\0");
|
||||
colorGrading.toneMapping = (decltype(colorGrading.toneMapping)) toneMapping;
|
||||
if (colorGrading.toneMapping == ToneMapping::GENERIC) {
|
||||
if (ImGui::CollapsingHeader("Tonemap parameters")) {
|
||||
|
||||
@@ -24,7 +24,7 @@ highp mat4 getViewFromClipMatrix() {
|
||||
|
||||
/** @public-api */
|
||||
highp mat4 getClipFromWorldMatrix() {
|
||||
#if defined(VARIANT_HAS_INSTANCED_STEREO)
|
||||
#if defined(VARIANT_HAS_STEREO)
|
||||
int eye = instance_index % CONFIG_STEREO_EYE_COUNT;
|
||||
return frameUniforms.clipFromWorldMatrix[eye];
|
||||
#else
|
||||
|
||||
+3
-3
@@ -23,7 +23,7 @@ void main() {
|
||||
logical_instance_index = instance_index;
|
||||
#endif
|
||||
|
||||
#if defined(VARIANT_HAS_INSTANCED_STEREO)
|
||||
#if defined(VARIANT_HAS_STEREO)
|
||||
#if !defined(FILAMENT_HAS_FEATURE_INSTANCING)
|
||||
#error Instanced stereo not supported at this feature level
|
||||
#endif
|
||||
@@ -215,7 +215,7 @@ void main() {
|
||||
// this must happen before we compensate for vulkan below
|
||||
vertex_position = position;
|
||||
|
||||
#if defined(VARIANT_HAS_INSTANCED_STEREO)
|
||||
#if defined(VARIANT_HAS_STEREO)
|
||||
// We're transforming a vertex whose x coordinate is within the range (-w to w).
|
||||
// To move it to the correct portion of the viewport, we need to modify the x coordinate.
|
||||
// It's important to do this after computing vertex_position.
|
||||
@@ -253,7 +253,7 @@ void main() {
|
||||
// some PowerVR drivers crash when gl_Position is written more than once
|
||||
gl_Position = position;
|
||||
|
||||
#if defined(VARIANT_HAS_INSTANCED_STEREO)
|
||||
#if defined(VARIANT_HAS_STEREO)
|
||||
// Fragment shaders filter out the stereo variant, so we need to set instance_index here.
|
||||
instance_index = logical_instance_index;
|
||||
#endif
|
||||
|
||||
@@ -34,7 +34,7 @@ LAYOUT_LOCATION(11) VARYING highp vec4 vertex_lightSpacePosition;
|
||||
|
||||
// Note that fragColor is an output and is not declared here; see main.fs and depth_main.fs
|
||||
|
||||
#if defined(VARIANT_HAS_INSTANCED_STEREO)
|
||||
#if defined(VARIANT_HAS_STEREO)
|
||||
#if defined(GL_ES) && defined(FILAMENT_GLSLANG)
|
||||
// On ES, gl_ClipDistance is not a built-in, so we have to rely on EXT_clip_cull_distance
|
||||
// However, this extension is not supported by glslang, so we instead write to
|
||||
@@ -46,4 +46,4 @@ LAYOUT_LOCATION(100) out float filament_gl_ClipDistance[2];
|
||||
// If we're on Desktop GL (or not running shaders through glslang), we're free to use gl_ClipDistance
|
||||
#define FILAMENT_CLIPDISTANCE gl_ClipDistance
|
||||
#endif // GL_ES && FILAMENT_GLSLANG
|
||||
#endif // VARIANT_HAS_INSTANCED_STEREO
|
||||
#endif // VARIANT_HAS_STEREO
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "filament",
|
||||
"version": "1.50.4",
|
||||
"version": "1.50.5",
|
||||
"description": "Real-time physically based rendering engine",
|
||||
"main": "filament.js",
|
||||
"module": "filament.js",
|
||||
|
||||
Reference in New Issue
Block a user