diff --git a/README.md b/README.md
index 66f0976391..38792b3936 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index f7fa37e7f5..f538299884 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -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
diff --git a/android/build.gradle b/android/build.gradle
index 960c67d4e8..4d6ebb91f8 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -80,7 +80,7 @@ buildscript {
ext.versions = [
'jdk': 17,
- 'minSdk': 19,
+ 'minSdk': 21,
'targetSdk': 34,
'compileSdk': 34,
'kotlin': '1.9.21',
diff --git a/android/filament-android/src/main/cpp/ToneMapper.cpp b/android/filament-android/src/main/cpp/ToneMapper.cpp
index 21c7e7ab24..860b95508b 100644
--- a/android/filament-android/src/main/cpp/ToneMapper.cpp
+++ b/android/filament-android/src/main/cpp/ToneMapper.cpp
@@ -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));
diff --git a/android/filament-android/src/main/java/com/google/android/filament/Renderer.java b/android/filament-android/src/main/java/com/google/android/filament/Renderer.java
index 3a9b6ab94d..ab70e9ed38 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/Renderer.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/Renderer.java
@@ -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.
diff --git a/android/filament-android/src/main/java/com/google/android/filament/ToneMapper.java b/android/filament-android/src/main/java/com/google/android/filament/ToneMapper.java
index 58ffc501ec..c3d3490441 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/ToneMapper.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/ToneMapper.java
@@ -16,12 +16,14 @@ package com.google.android.filament;
*
Configurable tone mapping operators
*
* - GenericToneMapper
+ * - AgXToneMapper
*
* Fixed-aesthetic tone mapping operators
*
* - ACESToneMapper
* - ACESLegacyToneMapper
* - FilmicToneMapper
+ * - PBRNeutralToneMapper
*
* Debug/validation tone mapping operators
*
@@ -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);
diff --git a/android/gradle.properties b/android/gradle.properties
index 34340a20b8..0c82646753 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -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.
diff --git a/android/samples/sample-gltf-viewer/build.gradle b/android/samples/sample-gltf-viewer/build.gradle
index f934f01901..c2d0699017 100644
--- a/android/samples/sample-gltf-viewer/build.gradle
+++ b/android/samples/sample-gltf-viewer/build.gradle
@@ -30,7 +30,7 @@ android {
compileSdkVersion versions.compileSdk
defaultConfig {
applicationId "com.google.android.filament.gltf"
- minSdkVersion 19
+ minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
}
diff --git a/build/android/ndk.version b/build/android/ndk.version
index 46e2cdd860..365a3c4156 100644
--- a/build/android/ndk.version
+++ b/build/android/ndk.version
@@ -1 +1 @@
-25.1.8937393
\ No newline at end of file
+26.1.10909125
\ No newline at end of file
diff --git a/build/toolchain-arm7-linux-android.cmake b/build/toolchain-arm7-linux-android.cmake
index ce94ab5659..a3f6036c32 100644
--- a/build/toolchain-arm7-linux-android.cmake
+++ b/build/toolchain-arm7-linux-android.cmake
@@ -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)
diff --git a/build/toolchain-x86-linux-android.cmake b/build/toolchain-x86-linux-android.cmake
index 88e1b1a79a..8c50712337 100644
--- a/build/toolchain-x86-linux-android.cmake
+++ b/build/toolchain-x86-linux-android.cmake
@@ -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)
diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt
index 3b3babcd29..698c139332 100644
--- a/filament/CMakeLists.txt
+++ b/filament/CMakeLists.txt
@@ -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
diff --git a/filament/backend/include/backend/Handle.h b/filament/backend/include/backend/Handle.h
index ffc16133fd..4b63607a1c 100644
--- a/filament/backend/include/backend/Handle.h
+++ b/filament/backend/include/backend/Handle.h
@@ -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;
-using FenceHandle = Handle;
-using IndexBufferHandle = Handle;
-using ProgramHandle = Handle;
-using RenderPrimitiveHandle = Handle;
-using RenderTargetHandle = Handle;
-using SamplerGroupHandle = Handle;
-using StreamHandle = Handle;
-using SwapChainHandle = Handle;
-using TextureHandle = Handle;
-using TimerQueryHandle = Handle;
-using VertexBufferHandle = Handle;
+using BufferObjectHandle = Handle;
+using FenceHandle = Handle;
+using IndexBufferHandle = Handle;
+using ProgramHandle = Handle;
+using RenderPrimitiveHandle = Handle;
+using RenderTargetHandle = Handle;
+using SamplerGroupHandle = Handle;
+using StreamHandle = Handle;
+using SwapChainHandle = Handle;
+using TextureHandle = Handle;
+using TimerQueryHandle = Handle;
+using VertexBufferHandle = Handle;
+using VertexBufferInfoHandle = Handle;
} // namespace filament::backend
diff --git a/filament/backend/include/backend/PipelineState.h b/filament/backend/include/backend/PipelineState.h
index aa98dda606..04001345d8 100644
--- a/filament/backend/include/backend/PipelineState.h
+++ b/filament/backend/include/backend/PipelineState.h
@@ -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
diff --git a/filament/backend/include/backend/platforms/PlatformEGL.h b/filament/backend/include/backend/platforms/PlatformEGL.h
index cb1246657b..61b7ec8c45 100644
--- a/filament/backend/include/backend/platforms/PlatformEGL.h
+++ b/filament/backend/include/backend/platforms/PlatformEGL.h
@@ -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:
diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc
index 37fcb6e2ea..d3f9dd43ca 100644
--- a/filament/backend/include/private/backend/DriverAPI.inc
+++ b/filament/backend/include/private/backend/DriverAPI.inc
@@ -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
diff --git a/filament/backend/include/private/backend/HandleAllocator.h b/filament/backend/include/private/backend/HandleAllocator.h
index 04e66d8577..14e70fd06e 100644
--- a/filament/backend/include/private/backend/HandleAllocator.h
+++ b/filament/backend/include/private/backend/HandleAllocator.h
@@ -37,7 +37,7 @@
#include
#include
-#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
diff --git a/filament/backend/src/DriverBase.h b/filament/backend/src/DriverBase.h
index abf6890116..43cd3ef90d 100644
--- a/filament/backend/src/DriverBase.h
+++ b/filament/backend/src/DriverBase.h
@@ -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;
};
diff --git a/filament/backend/src/metal/MetalDriver.h b/filament/backend/src/metal/MetalDriver.h
index 036c89db12..51cd7902dd 100644
--- a/filament/backend/src/metal/MetalDriver.h
+++ b/filament/backend/src/metal/MetalDriver.h
@@ -133,12 +133,9 @@ private:
mHandleAllocator.deallocate(handle, p);
}
- inline void setRenderPrimitiveBuffer(Handle rph,
+ inline void setRenderPrimitiveBuffer(Handle rph, PrimitiveType pt,
Handle vbh, Handle ibh);
- inline void setRenderPrimitiveRange(Handle 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& f);
diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm
index b1e3d7574f..1bf3fc9337 100644
--- a/filament/backend/src/metal/MetalDriver.mm
+++ b/filament/backend/src/metal/MetalDriver.mm
@@ -279,10 +279,16 @@ void MetalDriver::finish(int) {
[oneOffBuffer waitUntilCompleted];
}
-void MetalDriver::createVertexBufferR(Handle vbh, uint8_t bufferCount,
- uint8_t attributeCount, uint32_t vertexCount, AttributeArray attributes) {
- construct_handle(vbh, *mContext, bufferCount,
- attributeCount, vertexCount, attributes);
+void MetalDriver::createVertexBufferInfoR(Handle vbih, uint8_t bufferCount,
+ uint8_t attributeCount, AttributeArray attributes) {
+ construct_handle(vbih, *mContext,
+ bufferCount, attributeCount, attributes);
+}
+
+void MetalDriver::createVertexBufferR(Handle vbh,
+ uint32_t vertexCount, Handle vbih) {
+ MetalVertexBufferInfo const* const vbi = handle_cast(vbih);
+ construct_handle(vbh, *mContext, vertexCount, vbi->bufferCount, vbih);
}
void MetalDriver::createIndexBufferR(Handle ibh, ElementType elementType,
@@ -350,11 +356,9 @@ void MetalDriver::createSamplerGroupR(
void MetalDriver::createRenderPrimitiveR(Handle rph,
Handle vbh, Handle ibh,
- PrimitiveType pt, uint32_t offset,
- uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
+ PrimitiveType pt) {
construct_handle(rph);
- MetalDriver::setRenderPrimitiveBuffer(rph, vbh, ibh);
- MetalDriver::setRenderPrimitiveRange(rph, pt, offset, minIndex, maxIndex, count);
+ MetalDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh);
}
void MetalDriver::createProgramR(Handle rph, Program&& program) {
@@ -440,6 +444,10 @@ void MetalDriver::createTimerQueryR(Handle tqh, int) {
// nothing to do, timer query was constructed in createTimerQueryS
}
+Handle MetalDriver::createVertexBufferInfoS() noexcept {
+ return alloc_handle();
+}
+
Handle MetalDriver::createVertexBufferS() noexcept {
return alloc_handle();
}
@@ -504,6 +512,12 @@ Handle MetalDriver::createTimerQueryS() noexcept {
return alloc_and_construct_handle();
}
+void MetalDriver::destroyVertexBufferInfo(Handle vbih) {
+ if (vbih) {
+ destruct_handle(vbih);
+ }
+}
+
void MetalDriver::destroyVertexBuffer(Handle vbh) {
if (vbh) {
destruct_handle(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 rph,
+void MetalDriver::setRenderPrimitiveBuffer(Handle rph, PrimitiveType pt,
Handle vbh, Handle ibh) {
auto primitive = handle_cast(rph);
auto vertexBuffer = handle_cast(vbh);
auto indexBuffer = handle_cast(ibh);
- primitive->setBuffers(vertexBuffer, indexBuffer);
-}
-
-void MetalDriver::setRenderPrimitiveRange(Handle rph,
- PrimitiveType pt, uint32_t offset, uint32_t minIndex, uint32_t maxIndex,
- uint32_t count) {
- auto primitive = handle_cast(rph);
+ MetalVertexBufferInfo const* const vbi = handle_cast(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 schDraw, Handle schRead) {
@@ -1581,7 +1592,8 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
}
}
-void MetalDriver::draw(PipelineState ps, Handle rph, uint32_t instanceCount) {
+void MetalDriver::draw(PipelineState ps, Handle 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(rph);
@@ -1598,7 +1610,7 @@ void MetalDriver::draw(PipelineState ps, Handle 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 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(attachmentSize.x);
- const float abottom = static_cast(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(left),
- .y = static_cast(top),
- .width = static_cast(right - left),
- .height = static_cast(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 rph, uint32_t
id cmdBuffer = getPendingCommandBuffer(mContext);
id 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 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(attachmentSize.x);
+ const float abottom = static_cast(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(left),
+ .y = static_cast(top),
+ .width = static_cast(right - left),
+ .height = static_cast(bottom - top)
+ };
+
+ [mContext->currentRenderPassEncoder setScissorRect:scissorRect];
+}
+
void MetalDriver::beginTimerQuery(Handle tqh) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"beginTimerQuery must be called outside of a render pass.");
diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h
index fdc4a5e6a9..534d75044a 100644
--- a/filament/backend/src/metal/MetalHandles.h
+++ b/filament/backend/src/metal/MetalHandles.h
@@ -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 vbih);
+
+ Handle vbih;
utils::FixedCapacityVector 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.
diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm
index e28689ba4f..eef4087cc5 100644
--- a/filament/backend/src/metal/MetalHandles.mm
+++ b/filament/backend/src/metal/MetalHandles.mm
@@ -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 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::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.
diff --git a/filament/backend/src/metal/MetalShaderCompiler.h b/filament/backend/src/metal/MetalShaderCompiler.h
index c3b3d4e1b8..5d967081e4 100644
--- a/filament/backend/src/metal/MetalShaderCompiler.h
+++ b/filament/backend/src/metal/MetalShaderCompiler.h
@@ -30,6 +30,8 @@
#include
#include
+#include
+#include
namespace filament::backend {
@@ -41,36 +43,69 @@ class MetalShaderCompiler {
public:
class MetalFunctionBundle {
public:
- MetalFunctionBundle() = default;
- MetalFunctionBundle(id fragment, id vertex)
- : functions{fragment, vertex} {
+ using Raster = std::tuple, id>;
+ using Compute = id;
+ using Error = std::tuple; // error message, Program name
+ struct None {};
+
+ MetalFunctionBundle() : mPrograms{None{}} {}
+
+ explicit operator bool() const { return isValid(); }
+
+ bool isValid() const noexcept {
+ return std::holds_alternative(mPrograms) ||
+ std::holds_alternative(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(mPrograms));
+ return std::get(mPrograms);
+ }
+
+ Compute getComputeFunction() const {
+ assert_invariant(std::holds_alternative(mPrograms));
+ return std::get(mPrograms);
+ }
+
+ static MetalFunctionBundle none() {
+ return MetalFunctionBundle(None{});
+ }
+
+ static MetalFunctionBundle raster(id fragment, id vertex) {
assert_invariant(fragment && vertex);
assert_invariant(fragment.functionType == MTLFunctionTypeFragment);
assert_invariant(vertex.functionType == MTLFunctionTypeVertex);
+ return MetalFunctionBundle(Raster{fragment, vertex});
}
- explicit MetalFunctionBundle(id compute) : functions{compute, nil} {
+
+ static MetalFunctionBundle compute(id compute) {
assert_invariant(compute);
assert_invariant(compute.functionType == MTLFunctionTypeKernel);
+ return MetalFunctionBundle(Compute{compute});
}
- std::pair, id> 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 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 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 mPrograms;
};
using program_token_t = std::shared_ptr;
diff --git a/filament/backend/src/metal/MetalShaderCompiler.mm b/filament/backend/src/metal/MetalShaderCompiler.mm
index 7bea1e2103..7af9c65944 100644
--- a/filament/backend/src/metal/MetalShaderCompiler.mm
+++ b/filament/backend/src/metal/MetalShaderCompiler.mm
@@ -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(mPrograms))) {
+ auto [errorMessage, programName] = std::get(mPrograms);
+ NSString* reason =
+ [NSString stringWithFormat:
+ @"Attempting to draw with an id that failed to compile.\n"
+ @"Program: %@\n"
+ @"%@", programName, errorMessage];
+ [[NSException exceptionWithName:@"MetalCompilationFailure"
+ reason:reason
+ userInfo:nil] raise];
+ } else if (UTILS_UNLIKELY(std::holds_alternative(mPrograms))) {
+ NSString* reason = @"Attempting to draw with an empty id.";
+ [[NSException exceptionWithName:@"MetalEmptyFunctionBundle"
+ reason:reason
+ userInfo:nil] raise];
+ }
+}
+
} // namespace filament::backend
diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp
index 30e44d1447..797a1a61b0 100644
--- a/filament/backend/src/noop/NoopDriver.cpp
+++ b/filament/backend/src/noop/NoopDriver.cpp
@@ -77,6 +77,9 @@ void NoopDriver::finish(int) {
void NoopDriver::destroyRenderPrimitive(Handle rph) {
}
+void NoopDriver::destroyVertexBufferInfo(Handle vbih) {
+}
+
void NoopDriver::destroyVertexBuffer(Handle 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 rph,
- uint32_t instanceCount) {
+ uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
}
void NoopDriver::dispatchCompute(Handle program, math::uint3 workGroupCount) {
}
+void NoopDriver::scissor(
+ Viewport scissor) {
+}
+
void NoopDriver::beginTimerQuery(Handle tqh) {
}
diff --git a/filament/backend/src/opengl/OpenGLContext.cpp b/filament/backend/src/opengl/OpenGLContext.cpp
index 6d2b08eddc..8d7a6a4b22 100644
--- a/filament/backend/src/opengl/OpenGLContext.cpp
+++ b/filament/backend/src/opengl/OpenGLContext.cpp
@@ -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;
diff --git a/filament/backend/src/opengl/OpenGLContext.h b/filament/backend/src/opengl/OpenGLContext.h
index 0db583344c..315f949031 100644
--- a/filament/backend/src/opengl/OpenGLContext.h
+++ b/filament/backend/src/opengl/OpenGLContext.h
@@ -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;
diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp
index 047f28383d..98031d6ff8 100644
--- a/filament/backend/src/opengl/OpenGLDriver.cpp
+++ b/filament/backend/src/opengl/OpenGLDriver.cpp
@@ -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 OpenGLDriver::createVertexBufferInfoS() noexcept {
+ return initHandle();
+}
+
Handle OpenGLDriver::createVertexBufferS() noexcept {
return initHandle();
}
@@ -446,14 +452,21 @@ Handle OpenGLDriver::createTimerQueryS() noexcept {
return initHandle();
}
-void OpenGLDriver::createVertexBufferR(
- Handle vbh,
+void OpenGLDriver::createVertexBufferInfoR(
+ Handle vbih,
uint8_t bufferCount,
uint8_t attributeCount,
- uint32_t elementCount,
AttributeArray attributes) {
DEBUG_MARKER()
- construct(vbh, bufferCount, attributeCount, elementCount, attributes);
+ construct(vbih, bufferCount, attributeCount, attributes);
+}
+
+void OpenGLDriver::createVertexBufferR(
+ Handle vbh,
+ uint32_t vertexCount,
+ Handle vbih) {
+ DEBUG_MARKER()
+ construct(vbh, vertexCount, vbih);
}
void OpenGLDriver::createIndexBufferR(
@@ -501,13 +514,11 @@ void OpenGLDriver::createBufferObjectR(Handle boh,
void OpenGLDriver::createRenderPrimitiveR(Handle rph,
Handle vbh, Handle 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(vbh);
GLIndexBuffer const* const ib = handle_cast(ibh);
assert_invariant(ib->elementSize == 2 || ib->elementSize == 4);
@@ -515,17 +526,14 @@ void OpenGLDriver::createRenderPrimitiveR(Handle 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(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(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 tqh, int) {
// Destroying driver objects
// ------------------------------------------------------------------------------------------------
+void OpenGLDriver::destroyVertexBufferInfo(Handle vbih) {
+ DEBUG_MARKER()
+ if (vbih) {
+ GLVertexBufferInfo const* vbi = handle_cast(vbih);
+ destruct(vbih, vbi);
+ }
+}
+
void OpenGLDriver::destroyVertexBuffer(Handle 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 rph, uint32_t instanceCount) {
+void OpenGLDriver::draw(PipelineState state, Handle 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 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(rp->offset));
+ glDrawElements(GLenum(rp->type), (GLsizei)indexCount, rp->gl.getIndicesType(),
+ reinterpret_cast(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(rp->offset),
+ glDrawElementsInstanced(GLenum(rp->type), (GLsizei)indexCount,
+ rp->gl.getIndicesType(),
+ reinterpret_cast(indexOffset * rp->gl.indicesSize),
(GLsizei)instanceCount);
#endif
}
@@ -3754,6 +3784,10 @@ void OpenGLDriver::draw(PipelineState state, Handle rph, uint
#endif
}
+void OpenGLDriver::scissor(Viewport scissor) {
+ setScissor(scissor);
+}
+
void OpenGLDriver::dispatchCompute(Handle program, math::uint3 workGroupCount) {
getShaderCompilerService().tick();
diff --git a/filament/backend/src/opengl/OpenGLDriver.h b/filament/backend/src/opengl/OpenGLDriver.h
index 74f311a962..5365811b01 100644
--- a/filament/backend/src/opengl/OpenGLDriver.h
+++ b/filament/backend/src/opengl/OpenGLDriver.h
@@ -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 vbih)
+ : HwVertexBuffer(vertexCount), vbih(vbih) {
+ }
+ Handle vbih;
struct {
// 4 * MAX_VERTEX_ATTRIBUTE_COUNT bytes
std::array buffers{};
diff --git a/filament/backend/src/opengl/OpenGLPlatform.cpp b/filament/backend/src/opengl/OpenGLPlatform.cpp
index 837faecdc4..ccffdfa6d4 100644
--- a/filament/backend/src/opengl/OpenGLPlatform.cpp
+++ b/filament/backend/src/opengl/OpenGLPlatform.cpp
@@ -18,6 +18,14 @@
#include "OpenGLDriverFactory.h"
+#include
+#include
+#include
+
+#include
+
+#include
+
namespace filament::backend {
Driver* OpenGLPlatform::createDefaultDriver(OpenGLPlatform* platform,
diff --git a/filament/backend/src/opengl/ShaderCompilerService.cpp b/filament/backend/src/opengl/ShaderCompilerService.cpp
index cf5c900c9d..3c1f57f832 100644
--- a/filament/backend/src/opengl/ShaderCompilerService.cpp
+++ b/filament/backend/src/opengl/ShaderCompilerService.cpp
@@ -261,12 +261,11 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram(
[this, &gl, program = std::move(program), token]() mutable {
// compile the shaders
std::array shaders{};
- std::array shaderSourceCode;
compileShaders(gl,
std::move(program.getShadersSource()),
program.getSpecializationConstants(),
shaders,
- shaderSourceCode);
+ token->shaderSourceCode);
// link the program
GLuint const glProgram = linkProgram(gl, shaders, token->attributes);
diff --git a/filament/backend/src/opengl/platforms/PlatformEGL.cpp b/filament/backend/src/opengl/platforms/PlatformEGL.cpp
index 76abd8d554..c05fa09786 100644
--- a/filament/backend/src/opengl/platforms/PlatformEGL.cpp
+++ b/filament/backend/src/opengl/platforms/PlatformEGL.cpp
@@ -18,16 +18,31 @@
#include "opengl/GLUtils.h"
+#include
+
+#include
+#include
+
#include
#include
-
#if defined(__ANDROID__)
+
#include
#endif
-
#include
+
+#include
#include
-#include
+#include
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
#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(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(drawSwapChain);
+ SwapChainEGL const* const rsc = static_cast(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(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;
diff --git a/filament/backend/src/vulkan/VulkanConstants.h b/filament/backend/src/vulkan/VulkanConstants.h
index 429060535d..a2b62bd34c 100644
--- a/filament/backend/src/vulkan/VulkanConstants.h
+++ b/filament/backend/src/vulkan/VulkanConstants.h
@@ -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)
diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp
index c700ce9c6a..05a892dedc 100644
--- a/filament/backend/src/vulkan/VulkanDriver.cpp
+++ b/filament/backend/src/vulkan/VulkanDriver.cpp
@@ -24,6 +24,7 @@
#include "VulkanHandles.h"
#include "VulkanImageUtility.h"
#include "VulkanMemory.h"
+#include "VulkanTexture.h"
#include
@@ -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(mPlatform->getDevice());
mCommands = std::make_unique(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 sbh, uint32_t coun
void VulkanDriver::createRenderPrimitiveR(Handle rph,
Handle vbh, Handle ibh,
- PrimitiveType pt, uint32_t offset,
- uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
+ PrimitiveType pt) {
auto rp = mResourceAllocator.construct(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 rph) {
@@ -375,10 +424,26 @@ void VulkanDriver::destroyRenderPrimitive(Handle rph) {
mResourceManager.release(rp);
}
-void VulkanDriver::createVertexBufferR(Handle vbh, uint8_t bufferCount,
- uint8_t attributeCount, uint32_t elementCount, AttributeArray attributes) {
- auto vertexBuffer = mResourceAllocator.construct(vbh, mContext, mStagePool,
- &mResourceAllocator, bufferCount, attributeCount, elementCount, attributes);
+void VulkanDriver::createVertexBufferInfoR(Handle vbih, uint8_t bufferCount,
+ uint8_t attributeCount, AttributeArray attributes) {
+ auto vbi = mResourceAllocator.construct(vbih,
+ bufferCount, attributeCount, attributes);
+ mResourceManager.acquire(vbi);
+}
+
+void VulkanDriver::destroyVertexBufferInfo(Handle vbih) {
+ if (!vbih) {
+ return;
+ }
+ auto vbi = mResourceAllocator.handle_cast(vbih);
+ mResourceManager.release(vbi);
+}
+
+
+void VulkanDriver::createVertexBufferR(Handle vbh,
+ uint32_t vertexCount, Handle vbih) {
+ auto vertexBuffer = mResourceAllocator.construct(vbh,
+ mContext, mStagePool, &mResourceAllocator, vertexCount, vbih);
mResourceManager.acquire(vertexBuffer);
}
@@ -585,6 +650,10 @@ void VulkanDriver::createTimerQueryR(Handle tqh, int) {
// nothing to do, timer query was constructed in createTimerQueryS
}
+Handle VulkanDriver::createVertexBufferInfoS() noexcept {
+ return mResourceAllocator.allocHandle();
+}
+
Handle VulkanDriver::createVertexBufferS() noexcept {
return mResourceAllocator.allocHandle();
}
@@ -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 vbh, uint32_t in
auto bo = mResourceAllocator.handle_cast(boh);
assert_invariant(bo->bindingType == BufferObjectBinding::VERTEX);
- vb->setBuffer(bo, index);
+ vb->setBuffer(mResourceAllocator, bo, index);
}
void VulkanDriver::updateIndexBuffer(Handle ibh, BufferDescriptor&& p,
@@ -1063,7 +1138,7 @@ void VulkanDriver::beginRenderPass(Handle rth, const RenderPassP
VulkanRenderTarget* const rt = mResourceAllocator.handle_cast(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 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(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(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 rph,
+void VulkanDriver::setRenderPrimitiveBuffer(Handle rph, PrimitiveType pt,
Handle vbh, Handle ibh) {
auto primitive = mResourceAllocator.handle_cast(rph);
primitive->setBuffers(mResourceAllocator.handle_cast(vbh),
mResourceAllocator.handle_cast(ibh));
-}
-
-void VulkanDriver::setRenderPrimitiveRange(Handle rph,
- PrimitiveType pt, uint32_t offset,
- uint32_t minIndex, uint32_t maxIndex, uint32_t count) {
- auto& primitive = *mResourceAllocator.handle_cast(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 drawSch, Handle readSch) {
@@ -1648,7 +1705,7 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
}
void VulkanDriver::draw(PipelineState pipelineState, Handle 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 r
Handle programHandle = pipelineState.program;
RasterState rasterState = pipelineState.rasterState;
PolygonOffset depthOffset = pipelineState.polygonOffset;
- Viewport const& scissorBox = pipelineState.scissor;
auto* program = mResourceAllocator.handle_cast(programHandle);
commands->acquire(program);
@@ -1698,7 +1754,9 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle 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(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 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 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