Merge branch 'rc/1.50.5' into release

This commit is contained in:
Powei Feng
2024-03-06 13:01:20 -08:00
93 changed files with 1840 additions and 823 deletions
+2 -2
View File
@@ -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
View File
@@ -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
+1 -1
View File
@@ -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 -1
View File
@@ -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
View File
@@ -1 +1 @@
25.1.8937393
26.1.10909125
+1 -1
View File
@@ -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)
+1 -1
View File
@@ -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)
+4
View File
@@ -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
+14 -12
View File
@@ -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
+16 -17
View File
@@ -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;
};
+1 -4
View File
@@ -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);
+70 -56
View File
@@ -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.");
+13 -4
View File
@@ -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.
+14 -7
View File
@@ -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
+9 -2
View File
@@ -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;
+83 -49
View File
@@ -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();
+15 -1
View File
@@ -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;
+21 -1
View File
@@ -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)
+154 -91
View File
@@ -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) {
+26 -5
View File
@@ -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;
+46 -13
View File
@@ -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++) {
+13 -4
View File
@@ -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;
+11 -8
View File
@@ -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);
+1 -1
View File
@@ -143,7 +143,7 @@ void BackendTest::renderTriangle(Handle<HwRenderTarget> renderTarget,
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
api.draw(state, triangle.getRenderPrimitive(), 1);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
}
+5 -2
View File
@@ -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;
+1 -1
View File
@@ -405,7 +405,7 @@ TEST_F(BackendTest, ColorResolve) {
api.beginFrame(0, 0);
api.beginRenderPass(srcRenderTarget, params);
api.bindUniformBuffer(0, ubuffer);
api.draw(state, triangle.getRenderPrimitive(), 1);
api.draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
api.endRenderPass();
api.endFrame(0);
+1 -1
View File
@@ -172,7 +172,7 @@ TEST_F(BackendTest, VertexBufferUpdate) {
}
getDriverApi().beginRenderPass(defaultRenderTarget, params);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 1);
getDriverApi().draw(state, triangle.getRenderPrimitive(), 0, 3, 1);
getDriverApi().endRenderPass();
triangleIndex++;
+2 -2
View File
@@ -218,7 +218,7 @@ TEST_F(BackendTest, FeedbackLoops) {
.sourceLevel = float(sourceLevel),
});
api.beginRenderPass(renderTargets[targetLevel], params);
api.draw(state, triangle.getRenderPrimitive(), 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();
}
+1 -1
View File
@@ -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();
+5 -4
View File
@@ -168,7 +168,7 @@ TEST_F(BackendTest, SetMinMaxLevel) {
ps.rasterState.colorWrite = true;
ps.rasterState.depthWrite = false;
api.beginRenderPass(renderTarget, params);
api.draw(ps, triangle.getRenderPrimitive(), 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();
+2 -2
View File
@@ -309,7 +309,7 @@ TEST_F(ReadPixelsTest, ReadPixels) {
state.rasterState.depthWrite = false;
state.rasterState.depthFunc = RasterState::DepthFunc::A;
state.rasterState.culling = CullingMode::NONE;
getDriverApi().draw(state, triangle.getRenderPrimitive(), 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();
+6 -5
View File
@@ -134,13 +134,13 @@ TEST_F(BackendTest, ScissorViewportRegion) {
ps.program = program;
ps.rasterState.colorWrite = true;
ps.rasterState.depthWrite = false;
ps.scissor = scissor;
api.makeCurrent(swapChain, swapChain);
api.beginFrame(0, 0);
api.beginRenderPass(srcRenderTarget, params);
api.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(
+4 -4
View File
@@ -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);
+3 -3
View File
@@ -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;
+15 -5
View File
@@ -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;
+128
View File
@@ -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
+2 -2
View File
@@ -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 {
+42 -68
View File
@@ -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);
}
}
+45 -50
View File
@@ -17,17 +17,17 @@
#ifndef TNT_FILAMENT_HWRENDERPRIMITIVEFACTORY_H
#define TNT_FILAMENT_HWRENDERPRIMITIVEFACTORY_H
#include "Bimap.h"
#include <backend/DriverApiForward.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <private/backend/DriverApi.h>
#include <utils/Allocator.h>
#include <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
+119
View File
@@ -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
+129 -43
View File
@@ -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,
+17 -2
View File
@@ -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
View File
@@ -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);
}
}
+13 -7
View File
@@ -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);
+16 -13
View File
@@ -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 -5
View File
@@ -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
View File
@@ -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);
}
}
}
+1 -1
View File
@@ -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.
+9 -1
View File
@@ -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" };
+2 -1
View File
@@ -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);
}
+2 -2
View File
@@ -679,7 +679,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
RenderPass::RenderFlags renderFlags = 0;
if (view.hasShadowing()) renderFlags |= RenderPass::HAS_SHADOWING;
if (view.isFrontFaceWindingInverted()) renderFlags |= RenderPass::HAS_INVERSE_FRONT_FACES;
if (view.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
+167 -99
View File
@@ -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
+3 -6
View File
@@ -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;
+1 -1
View File
@@ -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();
+1 -1
View File
@@ -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;
}
+38
View File
@@ -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);
}
}
+2 -2
View File
@@ -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); }
+1 -1
View File
@@ -894,7 +894,7 @@ bool MaterialBuilder::generateShaders(JobSystem& jobSystem, const std::vector<Va
.domain = mMaterialDomain,
.materialInfo = &info,
.hasFramebufferFetch = mEnableFramebufferFetch,
.usesClipDistance = v.variant.hasInstancedStereo(),
.usesClipDistance = v.variant.hasStereo(),
.glsl = {},
};
+1 -1
View File
@@ -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,
+4 -4
View File
@@ -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;
+1 -1
View File
@@ -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;
+50 -3
View File
@@ -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>
+2 -1
View File
@@ -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;
+7 -4
View File
@@ -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\"";
+4 -1
View File
@@ -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")) {
+1 -1
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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 -1
View File
@@ -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",