diff --git a/README.md b/README.md
index 3bff570f9c..8a4adea8f2 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
- implementation 'com.google.android.filament:filament-android:1.50.6'
+ implementation 'com.google.android.filament:filament-android:1.51.0'
}
```
@@ -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.6'
+pod 'Filament', '~> 1.51.0'
```
### Snapshots
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index 64ab3f4769..e3b32ecc38 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -7,6 +7,10 @@ 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.51.0
+
+- materials: add support for post-lighting mix factor (b/328498606) [⚠️ **New Material Version**]
+
## v1.50.6
- Add new API `SwapChain::getFrameScheduledCallback`
diff --git a/android/filament-android/src/main/java/com/google/android/filament/Engine.java b/android/filament-android/src/main/java/com/google/android/filament/Engine.java
index a858330eb4..aee4c2b34a 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/Engine.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/Engine.java
@@ -654,7 +654,7 @@ public class Engine {
*/
@NonNull
public SwapChain createSwapChain(@NonNull Object surface) {
- return createSwapChain(surface, SwapChain.CONFIG_DEFAULT);
+ return createSwapChain(surface, SwapChainFlags.CONFIG_DEFAULT);
}
/**
@@ -662,15 +662,15 @@ public class Engine {
*
* @param surface on Android, must be an instance of {@link android.view.Surface}
*
- * @param flags configuration flags, see {@link SwapChain}
+ * @param flags configuration flags, see {@link SwapChainFlags}
*
* @return a newly created {@link SwapChain} object
*
* @exception IllegalStateException can be thrown if the SwapChain couldn't be created
*
- * @see SwapChain#CONFIG_DEFAULT
- * @see SwapChain#CONFIG_TRANSPARENT
- * @see SwapChain#CONFIG_READABLE
+ * @see SwapChainFlags#CONFIG_DEFAULT
+ * @see SwapChainFlags#CONFIG_TRANSPARENT
+ * @see SwapChainFlags#CONFIG_READABLE
*
*/
@NonNull
@@ -688,21 +688,22 @@ public class Engine {
*
* @param width width of the rendering buffer
* @param height height of the rendering buffer
- * @param flags configuration flags, see {@link SwapChain}
+ * @param flags configuration flags, see {@link SwapChainFlags}
*
* @return a newly created {@link SwapChain} object
*
* @exception IllegalStateException can be thrown if the SwapChain couldn't be created
*
- * @see SwapChain#CONFIG_DEFAULT
- * @see SwapChain#CONFIG_TRANSPARENT
- * @see SwapChain#CONFIG_READABLE
+ * @see SwapChainFlags#CONFIG_DEFAULT
+ * @see SwapChainFlags#CONFIG_TRANSPARENT
+ * @see SwapChainFlags#CONFIG_READABLE
*
*/
@NonNull
public SwapChain createSwapChain(int width, int height, long flags) {
if (width >= 0 && height >= 0) {
- long nativeSwapChain = nCreateSwapChainHeadless(getNativeObject(), width, height, flags);
+ long nativeSwapChain =
+ nCreateSwapChainHeadless(getNativeObject(), width, height, flags);
if (nativeSwapChain == 0) throw new IllegalStateException("Couldn't create SwapChain");
return new SwapChain(nativeSwapChain, null);
}
@@ -714,11 +715,12 @@ public class Engine {
*
* @param surface a properly initialized {@link NativeSurface}
*
- * @param flags configuration flags, see {@link SwapChain}
+ * @param flags configuration flags, see {@link SwapChainFlags}
*
* @return a newly created {@link SwapChain} object
*
- * @exception IllegalStateException can be thrown if the {@link SwapChain} couldn't be created
+ * @exception IllegalStateException can be thrown if the {@link SwapChainFlags} couldn't be
+ * created
*/
@NonNull
public SwapChain createSwapChainFromNativeSurface(@NonNull NativeSurface surface, long flags) {
diff --git a/android/filament-android/src/main/java/com/google/android/filament/SwapChain.java b/android/filament-android/src/main/java/com/google/android/filament/SwapChain.java
index e3d1cf43aa..db47d21563 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/SwapChain.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/SwapChain.java
@@ -68,72 +68,6 @@ public class SwapChain {
private final Object mSurface;
private long mNativeObject;
- public static final long CONFIG_DEFAULT = 0x0;
-
- /**
- * This flag indicates that the SwapChain must be allocated with an
- * alpha-channel.
- */
- public static final long CONFIG_TRANSPARENT = 0x1;
-
- /**
- * This flag indicates that the SwapChain may be used as a source surface
- * for reading back render results. This config must be set when creating
- * any SwapChain that will be used as the source for a blit operation.
- *
- * @see Renderer#copyFrame
- */
- public static final long CONFIG_READABLE = 0x2;
-
- /**
- * Indicates that the native X11 window is an XCB window rather than an XLIB window.
- * This is ignored on non-Linux platforms and in builds that support only one X11 API.
- */
- public static final long CONFIG_ENABLE_XCB = 0x4;
-
- /**
- * Indicates that the SwapChain must automatically perform linear to sRGB encoding.
- *
- * This flag is ignored if isSRGBSwapChainSupported() is false.
- *
- * When using this flag, post-processing should be disabled.
- *
- * @see SwapChain#isSRGBSwapChainSupported
- * @see View#setPostProcessingEnabled
- */
- public static final long CONFIG_SRGB_COLORSPACE = 0x10;
-
- /**
- * Indicates that this SwapChain should allocate a stencil buffer in addition to a depth buffer.
- *
- * This flag is necessary when using View::setStencilBufferEnabled and rendering directly into
- * the SwapChain (when post-processing is disabled).
- *
- * The specific format of the stencil buffer depends on platform support. The following pixel
- * formats are tried, in order of preference:
- *
- * Depth only (without CONFIG_HAS_STENCIL_BUFFER):
- * - DEPTH32F
- * - DEPTH24
- *
- * Depth + stencil (with CONFIG_HAS_STENCIL_BUFFER):
- * - DEPTH32F_STENCIL8
- * - DEPTH24F_STENCIL8
- *
- * Note that enabling the stencil buffer may hinder depth precision and should only be used if
- * necessary.
- *
- * @see View#setStencilBufferEnabled
- * @see View#setPostProcessingEnabled
- */
- public static final long CONFIG_HAS_STENCIL_BUFFER = 0x20;
-
- /**
- * The SwapChain contains protected content. Only supported when isProtectedContentSupported()
- * is true.
- */
- public static final long CONFIG_PROTECTED_CONTENT = 0x40;
-
SwapChain(long nativeSwapChain, Object surface) {
mNativeObject = nativeSwapChain;
mSurface = surface;
@@ -145,17 +79,19 @@ public class SwapChain {
*
* @param engine A reference to the filament Engine
* @return true if CONFIG_PROTECTED_CONTENT is supported, false otherwise.
+ * @see SwapChainFlags#CONFIG_PROTECTED_CONTENT
*/
public static boolean isProtectedContentSupported(@NonNull Engine engine) {
return nIsProtectedContentSupported(engine.getNativeObject());
}
/**
- * Return whether createSwapChain supports the SWAP_CHAIN_CONFIG_SRGB_COLORSPACE flag.
+ * Return whether createSwapChain supports the CONFIG_SRGB_COLORSPACE flag.
* The default implementation returns false.
*
* @param engine A reference to the filament Engine
- * @return true if SWAP_CHAIN_CONFIG_SRGB_COLORSPACE is supported, false otherwise.
+ * @return true if CONFIG_SRGB_COLORSPACE is supported, false otherwise.
+ * @see SwapChainFlags#CONFIG_SRGB_COLORSPACE
*/
public static boolean isSRGBSwapChainSupported(@NonNull Engine engine) {
return nIsSRGBSwapChainSupported(engine.getNativeObject());
diff --git a/android/filament-android/src/main/java/com/google/android/filament/SwapChainFlags.java b/android/filament-android/src/main/java/com/google/android/filament/SwapChainFlags.java
new file mode 100644
index 0000000000..daef2dd15f
--- /dev/null
+++ b/android/filament-android/src/main/java/com/google/android/filament/SwapChainFlags.java
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+package com.google.android.filament;
+
+// Note: SwapChainFlags is kept separate from SwapChain so that UiHelper does not need to depend
+// on SwapChain. This allows clients to use UiHelper without requiring all of Filament's Java
+// classes.
+
+/**
+ * Flags that a SwapChain can be created with to control behavior.
+ *
+ * @see Engine#createSwapChain
+ * @see Engine#createSwapChainFromNativeSurface
+ */
+public final class SwapChainFlags {
+
+ public static final long CONFIG_DEFAULT = 0x0;
+
+ /**
+ * This flag indicates that the SwapChain must be allocated with an
+ * alpha-channel.
+ */
+ public static final long CONFIG_TRANSPARENT = 0x1;
+
+ /**
+ * This flag indicates that the SwapChain may be used as a source surface
+ * for reading back render results. This config must be set when creating
+ * any SwapChain that will be used as the source for a blit operation.
+ *
+ * @see Renderer#copyFrame
+ */
+ public static final long CONFIG_READABLE = 0x2;
+
+ /**
+ * Indicates that the native X11 window is an XCB window rather than an XLIB window.
+ * This is ignored on non-Linux platforms and in builds that support only one X11 API.
+ */
+ public static final long CONFIG_ENABLE_XCB = 0x4;
+
+ /**
+ * Indicates that the SwapChain must automatically perform linear to sRGB encoding.
+ *
+ * This flag is ignored if isSRGBSwapChainSupported() is false.
+ *
+ * When using this flag, post-processing should be disabled.
+ *
+ * @see SwapChain#isSRGBSwapChainSupported
+ * @see View#setPostProcessingEnabled
+ */
+ public static final long CONFIG_SRGB_COLORSPACE = 0x10;
+
+ /**
+ * Indicates that this SwapChain should allocate a stencil buffer in addition to a depth buffer.
+ *
+ * This flag is necessary when using View::setStencilBufferEnabled and rendering directly into
+ * the SwapChain (when post-processing is disabled).
+ *
+ * The specific format of the stencil buffer depends on platform support. The following pixel
+ * formats are tried, in order of preference:
+ *
+ * Depth only (without CONFIG_HAS_STENCIL_BUFFER):
+ * - DEPTH32F
+ * - DEPTH24
+ *
+ * Depth + stencil (with CONFIG_HAS_STENCIL_BUFFER):
+ * - DEPTH32F_STENCIL8
+ * - DEPTH24F_STENCIL8
+ *
+ * Note that enabling the stencil buffer may hinder depth precision and should only be used if
+ * necessary.
+ *
+ * @see View#setStencilBufferEnabled
+ * @see View#setPostProcessingEnabled
+ */
+ public static final long CONFIG_HAS_STENCIL_BUFFER = 0x20;
+
+ /**
+ * The SwapChain contains protected content. Only supported when isProtectedContentSupported()
+ * is true.
+ */
+ public static final long CONFIG_PROTECTED_CONTENT = 0x40;
+}
+
diff --git a/android/filament-android/src/main/java/com/google/android/filament/android/UiHelper.java b/android/filament-android/src/main/java/com/google/android/filament/android/UiHelper.java
index 451c09a33b..4d6f3a140f 100644
--- a/android/filament-android/src/main/java/com/google/android/filament/android/UiHelper.java
+++ b/android/filament-android/src/main/java/com/google/android/filament/android/UiHelper.java
@@ -27,7 +27,7 @@ import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
-import com.google.android.filament.SwapChain;
+import com.google.android.filament.SwapChainFlags;
/**
* UiHelper is a simple class that can manage either a SurfaceView, TextureView, or a SurfaceHolder
@@ -538,7 +538,7 @@ public class UiHelper {
* the options set on this UiHelper.
*/
public long getSwapChainFlags() {
- return isOpaque() ? SwapChain.CONFIG_DEFAULT : SwapChain.CONFIG_TRANSPARENT;
+ return isOpaque() ? SwapChainFlags.CONFIG_DEFAULT : SwapChainFlags.CONFIG_TRANSPARENT;
}
/**
diff --git a/android/gradle.properties b/android/gradle.properties
index 5dd1904abe..535a300264 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
-VERSION_NAME=1.50.6
+VERSION_NAME=1.51.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt
index 698c139332..25318ec0fa 100644
--- a/filament/CMakeLists.txt
+++ b/filament/CMakeLists.txt
@@ -252,8 +252,8 @@ set(MATERIAL_SRCS
)
set(MATERIAL_FL0_SRCS
- src/materials/defaultMaterial0.mat
- src/materials/skybox0.mat
+ src/materials/defaultMaterial.mat
+ src/materials/skybox.mat
)
# Embed the binary resource blob for materials.
@@ -316,32 +316,30 @@ foreach (mat_src ${MATERIAL_SRCS})
get_filename_component(fullname "${mat_src}" ABSOLUTE)
set(output_path "${MATERIAL_DIR}/${localname}.filamat")
- add_custom_command(
- OUTPUT ${output_path}
- COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
- MAIN_DEPENDENCY ${fullname}
- DEPENDS matc
- COMMENT "Compiling material ${mat_src} to ${output_path}"
- )
- list(APPEND MATERIAL_BINS ${output_path})
-endforeach()
-
-if (FILAMENT_ENABLE_FEATURE_LEVEL_0)
- foreach (mat_src ${MATERIAL_FL0_SRCS})
- get_filename_component(localname "${mat_src}" NAME_WE)
- get_filename_component(fullname "${mat_src}" ABSOLUTE)
- set(output_path "${MATERIAL_DIR}/${localname}.filamat")
-
+ list(FIND MATERIAL_FL0_SRCS ${mat_src} index)
+ if (${index} GREATER -1 AND FILAMENT_ENABLE_FEATURE_LEVEL_0)
+ string(REGEX REPLACE "[.]filamat$" "0.filamat" output_path0 ${output_path})
+ add_custom_command(
+ OUTPUT ${output_path} ${output_path0}
+ COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
+ COMMAND matc ${MATC_BASE_FLAGS} -PfeatureLevel=0 -o ${output_path0} ${fullname}
+ MAIN_DEPENDENCY ${fullname}
+ DEPENDS matc
+ COMMENT "Compiling material ${fullname} to ${output_path} and ${output_path0}"
+ )
+ list(APPEND MATERIAL_BINS ${output_path0})
+ else ()
add_custom_command(
OUTPUT ${output_path}
COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname}
MAIN_DEPENDENCY ${fullname}
DEPENDS matc
- COMMENT "Compiling material ${mat_src} to ${output_path}"
+ COMMENT "Compiling material ${fullname} to ${output_path}"
)
- list(APPEND MATERIAL_BINS ${output_path})
- endforeach ()
-endif ()
+ endif ()
+
+ list(APPEND MATERIAL_BINS ${output_path})
+endforeach()
# Additional dependencies on included files for materials
diff --git a/filament/backend/include/backend/DriverEnums.h b/filament/backend/include/backend/DriverEnums.h
index c2deaf547c..6b0424ca06 100644
--- a/filament/backend/include/backend/DriverEnums.h
+++ b/filament/backend/include/backend/DriverEnums.h
@@ -1186,11 +1186,27 @@ struct StencilState {
//! Stencil operations for front-facing polygons
StencilOperations front = {
- .stencilFunc = StencilFunction::A, .ref = 0, .readMask = 0xff, .writeMask = 0xff };
+ .stencilFunc = StencilFunction::A,
+ .stencilOpStencilFail = StencilOperation::KEEP,
+ .padding0 = 0,
+ .stencilOpDepthFail = StencilOperation::KEEP,
+ .stencilOpDepthStencilPass = StencilOperation::KEEP,
+ .padding1 = 0,
+ .ref = 0,
+ .readMask = 0xff,
+ .writeMask = 0xff };
//! Stencil operations for back-facing polygons
StencilOperations back = {
- .stencilFunc = StencilFunction::A, .ref = 0, .readMask = 0xff, .writeMask = 0xff };
+ .stencilFunc = StencilFunction::A,
+ .stencilOpStencilFail = StencilOperation::KEEP,
+ .padding0 = 0,
+ .stencilOpDepthFail = StencilOperation::KEEP,
+ .stencilOpDepthStencilPass = StencilOperation::KEEP,
+ .padding1 = 0,
+ .ref = 0,
+ .readMask = 0xff,
+ .writeMask = 0xff };
//! Whether stencil-buffer writes are enabled
bool stencilWrite = false;
diff --git a/filament/backend/include/backend/PipelineState.h b/filament/backend/include/backend/PipelineState.h
index 04001345d8..220d04bbf2 100644
--- a/filament/backend/include/backend/PipelineState.h
+++ b/filament/backend/include/backend/PipelineState.h
@@ -29,10 +29,13 @@ namespace filament::backend {
//! \privatesection
struct PipelineState {
- Handle program;
- RasterState rasterState;
- StencilState stencilState;
- PolygonOffset polygonOffset;
+ Handle program; // 4
+ Handle vertexBufferInfo; // 4
+ RasterState rasterState; // 4
+ StencilState stencilState; // 12
+ PolygonOffset polygonOffset; // 8
+ PrimitiveType primitiveType = PrimitiveType::TRIANGLES; // 1
+ uint8_t padding[3] = {}; // 3
};
} // namespace filament::backend
diff --git a/filament/backend/include/backend/Platform.h b/filament/backend/include/backend/Platform.h
index b89fb98f7f..1d85568a88 100644
--- a/filament/backend/include/backend/Platform.h
+++ b/filament/backend/include/backend/Platform.h
@@ -56,7 +56,7 @@ public:
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
- * Currently only honored by the GL backend.
+ * Currently only honored by the GL and Metal backends.
*/
bool disableParallelShaderCompile = false;
};
diff --git a/filament/backend/include/private/backend/DriverAPI.inc b/filament/backend/include/private/backend/DriverAPI.inc
index 05170ac8d4..ac1dc256c8 100644
--- a/filament/backend/include/private/backend/DriverAPI.inc
+++ b/filament/backend/include/private/backend/DriverAPI.inc
@@ -493,6 +493,17 @@ DECL_DRIVER_API_N(blit,
math::uint2, srcOrigin,
math::uint2, size)
+DECL_DRIVER_API_N(bindPipeline,
+ backend::PipelineState, state)
+
+DECL_DRIVER_API_N(bindRenderPrimitive,
+ backend::RenderPrimitiveHandle, rph)
+
+DECL_DRIVER_API_N(draw2,
+ uint32_t, indexOffset,
+ uint32_t, indexCount,
+ uint32_t, instanceCount)
+
DECL_DRIVER_API_N(draw,
backend::PipelineState, state,
backend::RenderPrimitiveHandle, rph,
@@ -500,6 +511,7 @@ DECL_DRIVER_API_N(draw,
uint32_t, indexCount,
uint32_t, instanceCount)
+
DECL_DRIVER_API_N(dispatchCompute,
backend::ProgramHandle, program,
math::uint3, workGroupCount)
diff --git a/filament/backend/include/private/backend/HandleAllocator.h b/filament/backend/include/private/backend/HandleAllocator.h
index 14e70fd06e..53bab21077 100644
--- a/filament/backend/include/private/backend/HandleAllocator.h
+++ b/filament/backend/include/private/backend/HandleAllocator.h
@@ -37,9 +37,9 @@
#include
#include
-#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
+#define HandleAllocatorGL HandleAllocator<32, 64, 136> // ~4520 / pool / MiB
+#define HandleAllocatorVK HandleAllocator<64, 160, 312> // ~1820 / pool / MiB
+#define HandleAllocatorMTL HandleAllocator<32, 48, 552> // ~1660 / pool / MiB
namespace filament::backend {
diff --git a/filament/backend/src/metal/MetalContext.h b/filament/backend/src/metal/MetalContext.h
index c98419b167..33b8f92c82 100644
--- a/filament/backend/src/metal/MetalContext.h
+++ b/filament/backend/src/metal/MetalContext.h
@@ -99,6 +99,7 @@ struct MetalContext {
std::array ssboState;
CullModeStateTracker cullModeState;
WindingStateTracker windingState;
+ Handle currentRenderPrimitive;
// State caches.
DepthStencilStateCache depthStencilStateCache;
diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm
index e291ab60fd..8dfbb27c51 100644
--- a/filament/backend/src/metal/MetalDriver.mm
+++ b/filament/backend/src/metal/MetalDriver.mm
@@ -48,24 +48,26 @@ Driver* MetalDriverFactory::create(MetalPlatform* const platform, const Platform
// For reference on a 64-bits machine in Release mode:
// MetalTimerQuery : 16 few
// HwStream : 24 few
+ // MetalRenderPrimitive : 24 many
+ // MetalVertexBuffer : 32 moderate
+ // -- less than or equal 32 bytes
// MetalIndexBuffer : 40 moderate
// MetalFence : 48 few
// MetalBufferObject : 48 many
// -- less than or equal 48 bytes
// MetalSamplerGroup : 112 few
- // MetalProgram : 144 moderate
+ // MetalProgram : 152 moderate
// MetalTexture : 152 moderate
- // MetalVertexBuffer : 152 moderate
- // -- less than or equal 160 bytes
// MetalSwapChain : 184 few
// MetalRenderTarget : 272 few
- // MetalRenderPrimitive : 584 many
- // -- less than or equal to 592 bytes
+ // MetalVertexBufferInfo : 552 moderate
+ // -- less than or equal to 552 bytes
utils::slog.d
<< "\nMetalSwapChain: " << sizeof(MetalSwapChain)
<< "\nMetalBufferObject: " << sizeof(MetalBufferObject)
<< "\nMetalVertexBuffer: " << sizeof(MetalVertexBuffer)
+ << "\nMetalVertexBufferInfo: " << sizeof(MetalVertexBufferInfo)
<< "\nMetalIndexBuffer: " << sizeof(MetalIndexBuffer)
<< "\nMetalSamplerGroup: " << sizeof(MetalSamplerGroup)
<< "\nMetalRenderPrimitive: " << sizeof(MetalRenderPrimitive)
@@ -178,7 +180,10 @@ MetalDriver::MetalDriver(MetalPlatform* platform, const Platform::DriverConfig&
mContext->eventListener = [[MTLSharedEventListener alloc] initWithDispatchQueue:queue];
}
- mContext->shaderCompiler = new MetalShaderCompiler(mContext->device, *this);
+ const MetalShaderCompiler::Mode compilerMode = driverConfig.disableParallelShaderCompile
+ ? MetalShaderCompiler::Mode::SYNCHRONOUS
+ : MetalShaderCompiler::Mode::ASYNCHRONOUS;
+ mContext->shaderCompiler = new MetalShaderCompiler(mContext->device, *this, compilerMode);
mContext->shaderCompiler->init();
#if defined(FILAMENT_METAL_PROFILING)
@@ -786,7 +791,7 @@ bool MetalDriver::isStereoSupported(backend::StereoscopicType stereoscopicType)
}
bool MetalDriver::isParallelShaderCompileSupported() {
- return true;
+ return mContext->shaderCompiler->isParallelShaderCompileSupported();
}
bool MetalDriver::isDepthStencilResolveSupported() {
@@ -1602,11 +1607,13 @@ void MetalDriver::finalizeSamplerGroup(MetalSamplerGroup* samplerGroup) {
}
}
-void MetalDriver::draw(PipelineState ps, Handle rph,
- uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
+void MetalDriver::bindPipeline(PipelineState ps) {
ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
- "Attempted to draw without a valid command encoder.");
- auto primitive = handle_cast(rph);
+ "bindPipeline() without a valid command encoder.");
+
+ MetalVertexBufferInfo const* const vbi =
+ handle_cast(ps.vertexBufferInfo);
+
auto program = handle_cast(ps.program);
const auto& rs = ps.rasterState;
@@ -1620,7 +1627,7 @@ void MetalDriver::draw(PipelineState ps, Handle rph,
return;
}
- functions.validate();
+ ASSERT_PRECONDITION(bool(functions), "Attempting to bind an invalid Metal program.");
auto [fragment, vertex] = functions.getRasterFunctions();
@@ -1647,7 +1654,7 @@ void MetalDriver::draw(PipelineState ps, Handle rph,
MetalPipelineState const pipelineState {
.vertexFunction = vertex,
.fragmentFunction = fragment,
- .vertexDescription = primitive->vertexDescription,
+ .vertexDescription = vbi->vertexDescription,
.colorAttachmentPixelFormat = {
colorPixelFormat[0],
colorPixelFormat[1],
@@ -1741,20 +1748,6 @@ void MetalDriver::draw(PipelineState ps, Handle rph,
mContext->currentPolygonOffset = ps.polygonOffset;
}
- // Bind uniform buffers.
- MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
- NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
-
- enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
- [&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer,
- uint32_t index) {
- uniformsToBind[index] = buffer;
- offsets[index] = state.offset;
- });
- MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder,
- UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT,
- uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT);
-
// Bind sampler groups (argument buffers).
for (size_t s = 0; s < Program::SAMPLER_BINDING_COUNT; s++) {
MetalSamplerGroup* const samplerGroup = mContext->samplerBindings[s];
@@ -1785,19 +1778,29 @@ void MetalDriver::draw(PipelineState ps, Handle rph,
atIndex:(SAMPLER_GROUP_BINDING_START + s)];
}
}
+}
+
+void MetalDriver::bindRenderPrimitive(Handle rph) {
+ ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
+ "bindRenderPrimitive() without a valid command encoder.");
// Bind the user vertex buffers.
-
MetalBuffer* vertexBuffers[MAX_VERTEX_BUFFER_COUNT] = {};
size_t vertexBufferOffsets[MAX_VERTEX_BUFFER_COUNT] = {};
size_t maxBufferIndex = 0;
+ MetalRenderPrimitive const* const primitive = handle_cast(rph);
+ MetalVertexBufferInfo const* const vbi =
+ handle_cast(primitive->vertexBuffer->vbih);
+
+ mContext->currentRenderPrimitive = rph;
+
auto vb = primitive->vertexBuffer;
- for (auto m : primitive->bufferMapping) {
+ for (auto m : vbi->bufferMapping) {
assert_invariant(
m.bufferArgumentIndex >= USER_VERTEX_BUFFER_BINDING_START &&
m.bufferArgumentIndex < USER_VERTEX_BUFFER_BINDING_START + MAX_VERTEX_BUFFER_COUNT);
- size_t vertexBufferIndex = m.bufferArgumentIndex - USER_VERTEX_BUFFER_BINDING_START;
+ size_t const vertexBufferIndex = m.bufferArgumentIndex - USER_VERTEX_BUFFER_BINDING_START;
vertexBuffers[vertexBufferIndex] = vb->buffers[m.sourceBufferIndex];
maxBufferIndex = std::max(maxBufferIndex, vertexBufferIndex);
}
@@ -1812,6 +1815,27 @@ void MetalDriver::draw(PipelineState ps, Handle rph,
[mContext->currentRenderPassEncoder setVertexBytes:bytes
length:16
atIndex:ZERO_VERTEX_BUFFER_BINDING];
+}
+
+void MetalDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
+ ASSERT_PRECONDITION(mContext->currentRenderPassEncoder != nullptr,
+ "draw() without a valid command encoder.");
+
+ // Bind uniform buffers.
+ MetalBuffer* uniformsToBind[Program::UNIFORM_BINDING_COUNT] = { nil };
+ NSUInteger offsets[Program::UNIFORM_BINDING_COUNT] = { 0 };
+
+ enumerateBoundBuffers(BufferObjectBinding::UNIFORM,
+ [&uniformsToBind, &offsets](const BufferState& state, MetalBuffer* buffer,
+ uint32_t index) {
+ uniformsToBind[index] = buffer;
+ offsets[index] = state.offset;
+ });
+ MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder,
+ UNIFORM_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX | MetalBuffer::Stage::FRAGMENT,
+ uniformsToBind, offsets, Program::UNIFORM_BINDING_COUNT);
+
+ auto primitive = handle_cast(mContext->currentRenderPrimitive);
MetalIndexBuffer* indexBuffer = primitive->indexBuffer;
@@ -1825,6 +1849,16 @@ void MetalDriver::draw(PipelineState ps, Handle rph,
instanceCount:instanceCount];
}
+void MetalDriver::draw(PipelineState ps, Handle rph,
+ uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
+ MetalRenderPrimitive const* const rp = handle_cast(rph);
+ ps.primitiveType = rp->type;
+ ps.vertexBufferInfo = rp->vertexBuffer->vbih;
+ bindPipeline(ps);
+ bindRenderPrimitive(rph);
+ draw2(indexOffset, indexCount, instanceCount);
+}
+
void MetalDriver::dispatchCompute(Handle program, math::uint3 workGroupCount) {
ASSERT_PRECONDITION(!isInRenderPass(mContext),
"dispatchCompute 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 534d75044a..fea6f0947f 100644
--- a/filament/backend/src/metal/MetalHandles.h
+++ b/filament/backend/src/metal/MetalHandles.h
@@ -149,7 +149,21 @@ struct MetalVertexBufferInfo : public HwVertexBufferInfo {
MetalVertexBufferInfo(MetalContext& context,
uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes);
- AttributeArray attributes;
+ // This struct is used to create the pipeline description to describe vertex assembly.
+ VertexDescription vertexDescription = {};
+
+ struct Entry {
+ uint8_t sourceBufferIndex = 0;
+ uint8_t stride = 0;
+ // maps to ->
+ uint8_t bufferArgumentIndex = 0;
+
+ Entry(uint8_t sourceBufferIndex, uint8_t stride, uint8_t bufferArgumentIndex)
+ : sourceBufferIndex(sourceBufferIndex),
+ stride(stride),
+ bufferArgumentIndex(bufferArgumentIndex) {}
+ };
+ utils::FixedCapacityVector bufferMapping;
};
struct MetalVertexBuffer : public HwVertexBuffer {
@@ -176,22 +190,6 @@ struct MetalRenderPrimitive : public HwRenderPrimitive {
MetalVertexBuffer* vertexBuffer = nullptr;
MetalIndexBuffer* indexBuffer = nullptr;
-
- // This struct is used to create the pipeline description to describe vertex assembly.
- VertexDescription vertexDescription = {};
-
- struct Entry {
- uint8_t sourceBufferIndex = 0;
- uint8_t stride = 0;
- // maps to ->
- uint8_t bufferArgumentIndex = 0;
-
- Entry(uint8_t sourceBufferIndex, uint8_t stride, uint8_t bufferArgumentIndex)
- : sourceBufferIndex(sourceBufferIndex),
- stride(stride),
- bufferArgumentIndex(bufferArgumentIndex) {}
- };
- utils::FixedCapacityVector bufferMapping;
};
class MetalProgram : public HwProgram {
diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm
index eef4087cc5..18d8311825 100644
--- a/filament/backend/src/metal/MetalHandles.mm
+++ b/filament/backend/src/metal/MetalHandles.mm
@@ -343,27 +343,9 @@ void MetalBufferObject::updateBufferUnsynchronized(void* data, size_t size, uint
MetalVertexBufferInfo::MetalVertexBufferInfo(MetalContext& context, uint8_t bufferCount,
uint8_t attributeCount, AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
- attributes(attributes) {
-}
+ bufferMapping(utils::FixedCapacityVector::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {
-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),
- buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { }
-
-MetalRenderPrimitive::MetalRenderPrimitive()
- : bufferMapping(utils::FixedCapacityVector::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {}
-
-void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
- MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) {
- this->vertexBuffer = vertexBuffer;
- this->indexBuffer = indexBuffer;
-
- const size_t attributeCount = vbi->attributes.size();
+ const size_t maxAttributeCount = attributes.size();
auto& mapping = bufferMapping;
mapping.clear();
@@ -403,8 +385,8 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
}
};
- for (uint32_t attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) {
- const auto& attribute = vbi->attributes[attributeIndex];
+ for (uint32_t attributeIndex = 0; attributeIndex < maxAttributeCount; attributeIndex++) {
+ const auto& attribute = 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.
@@ -431,6 +413,24 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
}
}
+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),
+ buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { }
+
+MetalRenderPrimitive::MetalRenderPrimitive() {
+}
+
+void MetalRenderPrimitive::setBuffers(MetalVertexBufferInfo const* const vbi,
+ MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) {
+ this->vertexBuffer = vertexBuffer;
+ this->indexBuffer = indexBuffer;
+}
+
MetalProgram::MetalProgram(MetalContext& context, Program&& program) noexcept
: HwProgram(program.getName()), mContext(context) {
diff --git a/filament/backend/src/metal/MetalShaderCompiler.h b/filament/backend/src/metal/MetalShaderCompiler.h
index 5d967081e4..f0b827ad6a 100644
--- a/filament/backend/src/metal/MetalShaderCompiler.h
+++ b/filament/backend/src/metal/MetalShaderCompiler.h
@@ -41,6 +41,11 @@ class MetalShaderCompiler {
struct MetalProgramToken;
public:
+ enum class Mode {
+ SYNCHRONOUS, // synchronous shader compilation
+ ASYNCHRONOUS // asynchronous shader compilation
+ };
+
class MetalFunctionBundle {
public:
using Raster = std::tuple, id>;
@@ -110,7 +115,7 @@ public:
using program_token_t = std::shared_ptr;
- explicit MetalShaderCompiler(id device, MetalDriver& driver);
+ explicit MetalShaderCompiler(id device, MetalDriver& driver, Mode mode);
MetalShaderCompiler(MetalShaderCompiler const& rhs) = delete;
MetalShaderCompiler(MetalShaderCompiler&& rhs) = delete;
@@ -120,15 +125,15 @@ public:
void init() noexcept;
void terminate() noexcept;
- // Creates a program asynchronously
+ bool isParallelShaderCompileSupported() const noexcept;
+
+ // Creates a program, either synchronously or asynchronously, depending on the Mode
+ // MetalShaderCompiler was constructed with.
program_token_t createProgram(utils::CString const& name, Program&& program);
// Returns the functions, blocking if necessary. The Token is destroyed and becomes invalid.
MetalFunctionBundle getProgram(program_token_t& token);
- // Destroys a valid token and all associated resources. Used to "cancel" a program compilation.
- static void terminate(program_token_t& token);
-
void notifyWhenAllProgramsAreReady(
CallbackHandler* handler, CallbackHandler::Callback callback, void* user);
@@ -138,6 +143,7 @@ private:
CompilerThreadPool mCompilerThreadPool;
id mDevice;
CallbackManager mCallbackManager;
+ Mode mMode;
};
} // namespace filament::backend
diff --git a/filament/backend/src/metal/MetalShaderCompiler.mm b/filament/backend/src/metal/MetalShaderCompiler.mm
index 713133b1db..7741ae5448 100644
--- a/filament/backend/src/metal/MetalShaderCompiler.mm
+++ b/filament/backend/src/metal/MetalShaderCompiler.mm
@@ -70,22 +70,31 @@ struct MetalShaderCompiler::MetalProgramToken : ProgramToken {
MetalShaderCompiler::MetalProgramToken::~MetalProgramToken() = default;
-MetalShaderCompiler::MetalShaderCompiler(id device, MetalDriver& driver)
+MetalShaderCompiler::MetalShaderCompiler(id device, MetalDriver& driver, Mode mode)
: mDevice(device),
- mCallbackManager(driver) {
+ mCallbackManager(driver),
+ mMode(mode) {
}
void MetalShaderCompiler::init() noexcept {
const uint32_t poolSize = 1;
- mCompilerThreadPool.init(poolSize, []() {}, []() {});
+ if (mMode == Mode::ASYNCHRONOUS) {
+ mCompilerThreadPool.init(poolSize, []() {}, []() {});
+ }
}
void MetalShaderCompiler::terminate() noexcept {
- mCompilerThreadPool.terminate();
+ if (mMode == Mode::ASYNCHRONOUS) {
+ mCompilerThreadPool.terminate();
+ }
mCallbackManager.terminate();
}
+bool MetalShaderCompiler::isParallelShaderCompileSupported() const noexcept {
+ return mMode == Mode::ASYNCHRONOUS;
+}
+
/* static */ MetalShaderCompiler::MetalFunctionBundle MetalShaderCompiler::compileProgram(
const Program& program, id device) {
std::array, Program::SHADER_TYPE_COUNT> functions = { nil };
@@ -180,14 +189,26 @@ MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram(
token->handle = mCallbackManager.get();
- CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
- mCompilerThreadPool.queue(priorityQueue, token,
- [this, name, device = mDevice, program = std::move(program), token]() {
- MetalFunctionBundle compiledProgram = compileProgram(program, device);
+ switch (mMode) {
+ case Mode::ASYNCHRONOUS: {
+ CompilerPriorityQueue const priorityQueue = program.getPriorityQueue();
+ mCompilerThreadPool.queue(priorityQueue, token,
+ [this, name, device = mDevice, program = std::move(program), token]() {
+ MetalFunctionBundle compiledProgram = compileProgram(program, device);
+ token->set(compiledProgram);
+ mCallbackManager.put(token->handle);
+ });
- token->set(compiledProgram);
- mCallbackManager.put(token->handle);
- });
+ break;
+ }
+
+ case Mode::SYNCHRONOUS: {
+ MetalFunctionBundle compiledProgram = compileProgram(program, mDevice);
+ token->set(compiledProgram);
+ mCallbackManager.put(token->handle);
+ break;
+ }
+ }
return token;
}
@@ -195,35 +216,23 @@ MetalShaderCompiler::program_token_t MetalShaderCompiler::createProgram(
MetalShaderCompiler::MetalFunctionBundle MetalShaderCompiler::getProgram(program_token_t& token) {
assert_invariant(token);
- if (!token->isReady()) {
- auto job = mCompilerThreadPool.dequeue(token);
- if (job) {
- job();
+ if (mMode == Mode::ASYNCHRONOUS) {
+ if (!token->isReady()) {
+ auto job = mCompilerThreadPool.dequeue(token);
+ if (job) {
+ job();
+ }
}
}
+ // The job isn't guaranteed to have finished yet. We may have failed to dequeue it above,
+ // which means it's currently running. In that case get() will block until it finishes.
+
MetalShaderCompiler::MetalFunctionBundle program = token->get();
-
token = nullptr;
-
return program;
}
-/* static */ void MetalShaderCompiler::terminate(program_token_t& token) {
- assert_invariant(token);
-
- auto job = token->compiler.mCompilerThreadPool.dequeue(token);
- if (!job) {
- // The job is being executed right now (or has already executed).
- token->wait();
- } else {
- // The job has not executed yet.
- token->compiler.mCallbackManager.put(token->handle);
- }
-
- token.reset();
-}
-
void MetalShaderCompiler::notifyWhenAllProgramsAreReady(
CallbackHandler* handler, CallbackHandler::Callback callback, void* user) {
mCallbackManager.setCallback(handler, callback, user);
diff --git a/filament/backend/src/noop/NoopDriver.cpp b/filament/backend/src/noop/NoopDriver.cpp
index 9f5a7b644d..7a150a3e74 100644
--- a/filament/backend/src/noop/NoopDriver.cpp
+++ b/filament/backend/src/noop/NoopDriver.cpp
@@ -350,6 +350,15 @@ void NoopDriver::blit(
math::uint2 size) {
}
+void NoopDriver::bindPipeline(PipelineState pipelineState) {
+}
+
+void NoopDriver::bindRenderPrimitive(Handle rph) {
+}
+
+void NoopDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
+}
+
void NoopDriver::draw(PipelineState pipelineState, Handle rph,
uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
}
diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp
index a4a0ea2b3f..cb6958a253 100644
--- a/filament/backend/src/opengl/OpenGLDriver.cpp
+++ b/filament/backend/src/opengl/OpenGLDriver.cpp
@@ -94,10 +94,10 @@ Driver* OpenGLDriver::create(OpenGLPlatform* const platform,
// GLSamplerGroup : 16 few
// GLSwapChain : 16 few
// GLTimerQuery : 16 few
- // GLRenderPrimitive : 20 many
// GLFence : 24 few
- // -- less than or equal 24 bytes
+ // GLRenderPrimitive : 32 many
// GLBufferObject : 32 many
+ // -- less than or equal 32 bytes
// OpenGLProgram : 56 moderate
// GLTexture : 64 moderate
// -- less than or equal 64 bytes
@@ -518,10 +518,12 @@ void OpenGLDriver::createRenderPrimitiveR(Handle rph,
GLIndexBuffer const* const ib = handle_cast(ibh);
assert_invariant(ib->elementSize == 2 || ib->elementSize == 4);
- GLRenderPrimitive* rp = handle_cast(rph);
+ GLVertexBuffer* const vb = handle_cast(vbh);
+ GLRenderPrimitive* const rp = handle_cast(rph);
rp->gl.indicesSize = (ib->elementSize == 4u) ? 4u : 2u;
rp->gl.vertexBufferWithObjects = vbh;
rp->type = pt;
+ rp->vbih = vb->vbih;
// create a name for this VAO in the current context
gl.procs.genVertexArrays(1, &rp->gl.vao[gl.contextIndex]);
@@ -3784,39 +3786,42 @@ void OpenGLDriver::updateTextureLodRange(GLTexture* texture, int8_t targetLevel)
#endif
}
-void OpenGLDriver::draw(PipelineState state, Handle rph,
- uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
+void OpenGLDriver::bindPipeline(PipelineState state) {
DEBUG_MARKER()
auto& gl = mContext;
-
+ setRasterState(state.rasterState);
+ setStencilState(state.stencilState);
+ gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant);
OpenGLProgram* const p = handle_cast(state.program);
+ mValidProgram = useProgram(p);
+}
- bool const success = useProgram(p);
- if (UTILS_UNLIKELY(!success)) {
- // Avoid fatal (or cascading) errors that can occur during the draw call when the program
- // is invalid. The shader compile error has already been dumped to the console at this
- // point, so it's fine to simply return early.
- return;
- }
+void OpenGLDriver::bindRenderPrimitive(Handle rph) {
+ DEBUG_MARKER()
+ auto& gl = mContext;
GLRenderPrimitive* const rp = handle_cast(rph);
// Gracefully do nothing if the render primitive has not been set up.
VertexBufferHandle vb = rp->gl.vertexBufferWithObjects;
if (UTILS_UNLIKELY(!vb)) {
+ mBoundRenderPrimitive = nullptr;
return;
}
- gl.bindVertexArray(&rp->gl);
-
// If necessary, mutate the bindings in the VAO.
+ gl.bindVertexArray(&rp->gl);
GLVertexBuffer const* const glvb = handle_cast(vb);
updateVertexArrayObject(rp, glvb);
- setRasterState(state.rasterState);
- setStencilState(state.stencilState);
+ mBoundRenderPrimitive = rp;
+}
- gl.polygonOffset(state.polygonOffset.slope, state.polygonOffset.constant);
+void OpenGLDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
+ GLRenderPrimitive const* const rp = mBoundRenderPrimitive;
+ if (UTILS_UNLIKELY(!rp || !mValidProgram)) {
+ return;
+ }
if (UTILS_LIKELY(instanceCount <= 1)) {
glDrawElements(GLenum(rp->type), (GLsizei)indexCount, rp->gl.getIndicesType(),
@@ -3842,6 +3847,17 @@ void OpenGLDriver::scissor(Viewport scissor) {
setScissor(scissor);
}
+void OpenGLDriver::draw(PipelineState state, Handle rph,
+ uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
+ DEBUG_MARKER()
+ GLRenderPrimitive* const rp = handle_cast(rph);
+ state.primitiveType = rp->type;
+ state.vertexBufferInfo = rp->vbih;
+ bindPipeline(state);
+ bindRenderPrimitive(rph);
+ draw2(indexOffset, indexCount, instanceCount);
+}
+
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 7ef5bc4f20..ccaddeefbf 100644
--- a/filament/backend/src/opengl/OpenGLDriver.h
+++ b/filament/backend/src/opengl/OpenGLDriver.h
@@ -142,6 +142,7 @@ public:
struct GLRenderPrimitive : public HwRenderPrimitive {
using HwRenderPrimitive::HwRenderPrimitive;
OpenGLContext::RenderPrimitive gl;
+ Handle vbih;
};
struct GLTexture : public HwTexture {
@@ -366,6 +367,10 @@ private:
GLboolean mRenderPassDepthWrite{};
GLboolean mRenderPassStencilWrite{};
+ GLRenderPrimitive const* mBoundRenderPrimitive = nullptr;
+ bool mValidProgram = false;
+
+
void clearWithRasterPipe(TargetBufferFlags clearFlags,
math::float4 const& linearColor, GLfloat depth, GLint stencil) noexcept;
diff --git a/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp b/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp
index b61bbeab9a..81d390397f 100644
--- a/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp
+++ b/filament/backend/src/opengl/platforms/PlatformEGLAndroid.cpp
@@ -14,21 +14,34 @@
* limitations under the License.
*/
+#include
+#include
+#include
#include
#include "opengl/GLUtils.h"
#include "ExternalStreamManagerAndroid.h"
#include
+#include
+
+#include
+#include
+#include
#include
#include
-#include
-#include
-
#include
+#include
+
+#include
+
+#include
+#include
+#include
+
// We require filament to be built with an API 19 toolchain, before that, OpenGLES 3.0 didn't exist
// Actually, OpenGL ES 3.0 was added to API 18, but API 19 is the better target and
// the minimum for Jetpack at the time of this comment.
@@ -165,14 +178,28 @@ int PlatformEGLAndroid::getOSVersion() const noexcept {
AcquiredImage PlatformEGLAndroid::transformAcquiredImage(AcquiredImage source) noexcept {
// Convert the AHardwareBuffer to EGLImage.
- EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID((const AHardwareBuffer*)source.image);
+ AHardwareBuffer const* const pHardwareBuffer = (const AHardwareBuffer*)source.image;
+
+ EGLClientBuffer clientBuffer = eglGetNativeClientBufferANDROID(pHardwareBuffer);
if (!clientBuffer) {
slog.e << "Unable to get EGLClientBuffer from AHardwareBuffer." << io::endl;
return {};
}
- // Note that this cannot be used to stream protected video (for now) because we do not set EGL_PROTECTED_CONTENT_EXT.
- EGLint attrs[] = { EGL_NONE, EGL_NONE };
- EGLImageKHR eglImage = eglCreateImageKHR(mEGLDisplay, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attrs);
+
+ PlatformEGL::Config attributes;
+
+ if (__builtin_available(android 26, *)) {
+ AHardwareBuffer_Desc desc;
+ AHardwareBuffer_describe(pHardwareBuffer, &desc);
+ bool const isProtectedContent =
+ desc.usage & AHardwareBuffer_UsageFlags::AHARDWAREBUFFER_USAGE_PROTECTED_CONTENT;
+ if (isProtectedContent) {
+ attributes[EGL_PROTECTED_CONTENT_EXT] = EGL_TRUE;
+ }
+ }
+
+ EGLImageKHR eglImage = eglCreateImageKHR(mEGLDisplay,
+ EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, clientBuffer, attributes.data());
if (eglImage == EGL_NO_IMAGE_KHR) {
slog.e << "eglCreateImageKHR returned no image." << io::endl;
return {};
diff --git a/filament/backend/src/vulkan/VulkanCommands.cpp b/filament/backend/src/vulkan/VulkanCommands.cpp
index f5b8e9029a..21d5944cbd 100644
--- a/filament/backend/src/vulkan/VulkanCommands.cpp
+++ b/filament/backend/src/vulkan/VulkanCommands.cpp
@@ -435,8 +435,8 @@ void VulkanCommands::popGroupMarker() {
auto const [marker, startTime] = mGroupMarkers->pop();
auto const endTime = std::chrono::high_resolution_clock::now();
std::chrono::duration diff = endTime - startTime;
- utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms\n"
- << utils::io::flush;
+ utils::slog.d << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms"
+ << utils::io::endl;
#else
mGroupMarkers->pop();
#endif
diff --git a/filament/backend/src/vulkan/VulkanConstants.h b/filament/backend/src/vulkan/VulkanConstants.h
index a2b62bd34c..5856e39164 100644
--- a/filament/backend/src/vulkan/VulkanConstants.h
+++ b/filament/backend/src/vulkan/VulkanConstants.h
@@ -63,11 +63,12 @@
// Enable the debug utils extension if it is available.
#define FVK_DEBUG_DEBUG_UTILS 0x00008000
+#define FVK_DEBUG_RESOURCE_LEAK 0x00010000
+
// Usefaul default combinations
#define FVK_DEBUG_EVERYTHING 0xFFFFFFFF
#define FVK_DEBUG_PERFORMANCE \
- FVK_DEBUG_SYSTRACE | \
- FVK_DEBUG_GROUP_MARKERS
+ FVK_DEBUG_SYSTRACE
#define FVK_DEBUG_CORRECTNESS \
FVK_DEBUG_VALIDATION | \
@@ -80,14 +81,12 @@
FVK_DEBUG_PRINT_GROUP_MARKERS
#ifndef NDEBUG
-#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_DEBUG_UTILS | FVK_DEBUG_VALIDATION)
+#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE)
#else
#define FVK_DEBUG_FLAGS 0
#endif
-#define FVK_ENABLED(flags) ((FVK_DEBUG_FLAGS) & (flags))
-#define FVK_ENABLED_BOOL(flags) ((bool) FVK_ENABLED(flags))
-
+#define FVK_ENABLED(flags) (((FVK_DEBUG_FLAGS) & (flags)) == (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
@@ -107,6 +106,15 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
// end dependcy checks
+// Shorthand for combination of enabled debug flags
+#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS) || FVK_ENABLED(FVK_DEBUG_TEXTURE)
+#define FVK_ENABLED_DEBUG_SAMPLER_NAME 1
+#else
+#define FVK_ENABLED_DEBUG_SAMPLER_NAME 0
+#endif
+
+// end shorthands
+
#if FVK_ENABLED(FVK_DEBUG_SYSTRACE)
#include
diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp
index c1e8f4c0de..05e85a6d48 100644
--- a/filament/backend/src/vulkan/VulkanDriver.cpp
+++ b/filament/backend/src/vulkan/VulkanDriver.cpp
@@ -264,26 +264,28 @@ Driver* VulkanDriver::create(VulkanPlatform* platform, VulkanContext const& cont
#if 0
// this is useful for development, but too verbose even for debug builds
// For reference on a 64-bits machine in Release mode:
- // VulkanSamplerGroup : 24 few
+ // VulkanSamplerGroup : 16 few
// HwStream : 24 few
- // VulkanFence : 40 few
- // VulkanProgram : 40 moderate
- // VulkanIndexBuffer : 72 moderate
- // VulkanBufferObject : 72 many
- // -- less than or equal 80 bytes
- // VulkanRenderPrimitive : 104 many
- // VulkanSwapChain : 112 few
- // VulkanTimerQuery : 168 few
- // -- less than or equal 176 bytes
- // VulkanTexture : 232 moderate
- // VulkanVertexBuffer : 312 moderate
- // VulkanRenderTarget : 320 few
- // -- less than or equal to 320 bytes
+ // VulkanFence : 32 few
+ // VulkanProgram : 32 moderate
+ // VulkanIndexBuffer : 64 moderate
+ // VulkanBufferObject : 64 many
+ // -- less than or equal 64 bytes
+ // VulkanRenderPrimitive : 72 many
+ // VulkanVertexBufferInfo : 96 moderate
+ // VulkanSwapChain : 104 few
+ // VulkanTimerQuery : 160 few
+ // -- less than or equal 160 bytes
+ // VulkanVertexBuffer : 192 moderate
+ // VulkanTexture : 224 moderate
+ // VulkanRenderTarget : 312 few
+ // -- less than or equal to 312 bytes
utils::slog.d
<< "\nVulkanSwapChain: " << sizeof(VulkanSwapChain)
<< "\nVulkanBufferObject: " << sizeof(VulkanBufferObject)
<< "\nVulkanVertexBuffer: " << sizeof(VulkanVertexBuffer)
+ << "\nVulkanVertexBufferInfo: " << sizeof(VulkanVertexBufferInfo)
<< "\nVulkanIndexBuffer: " << sizeof(VulkanIndexBuffer)
<< "\nVulkanSamplerGroup: " << sizeof(VulkanSamplerGroup)
<< "\nVulkanRenderPrimitive: " << sizeof(VulkanRenderPrimitive)
@@ -359,6 +361,11 @@ void VulkanDriver::collectGarbage() {
mCommands->gc();
mStagePool.gc();
mFramebufferCache.gc();
+
+#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
+ mResourceAllocator.print();
+#endif
+
FVK_SYSTRACE_END();
}
void VulkanDriver::beginFrame(int64_t monotonic_clock_ns, uint32_t frameId) {
@@ -411,9 +418,9 @@ void VulkanDriver::createSamplerGroupR(Handle sbh, uint32_t coun
void VulkanDriver::createRenderPrimitiveR(Handle rph,
Handle vbh, Handle ibh,
PrimitiveType pt) {
- auto rp = mResourceAllocator.construct(rph, &mResourceAllocator);
+ auto rp = mResourceAllocator.construct(rph,
+ &mResourceAllocator, pt, vbh, ibh);
mResourceManager.acquire(rp);
- VulkanDriver::setRenderPrimitiveBuffer(rph, pt, vbh, ibh);
}
void VulkanDriver::destroyRenderPrimitive(Handle rph) {
@@ -982,7 +989,6 @@ void VulkanDriver::setVertexBufferObject(Handle vbh, uint32_t in
auto vb = mResourceAllocator.handle_cast(vbh);
auto bo = mResourceAllocator.handle_cast(boh);
assert_invariant(bo->bindingType == BufferObjectBinding::VERTEX);
-
vb->setBuffer(mResourceAllocator, bo, index);
}
@@ -1460,14 +1466,6 @@ void VulkanDriver::nextSubpass(int) {
}
}
-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));
- primitive->setPrimitiveType(pt);
-}
-
void VulkanDriver::makeCurrent(Handle drawSch, Handle readSch) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("makeCurrent");
@@ -1713,14 +1711,13 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
FVK_SYSTRACE_END();
}
-void VulkanDriver::draw(PipelineState pipelineState, Handle rph,
- uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
+void VulkanDriver::bindPipeline(PipelineState pipelineState) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("draw");
VulkanCommandBuffer* commands = &mCommands->get();
- VkCommandBuffer cmdbuffer = commands->buffer();
- const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast(rph);
+ const VulkanVertexBufferInfo& vbi =
+ *mResourceAllocator.handle_cast(pipelineState.vertexBufferInfo);
Handle programHandle = pipelineState.program;
RasterState rasterState = pipelineState.rasterState;
@@ -1728,15 +1725,6 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r
auto* program = mResourceAllocator.handle_cast(programHandle);
commands->acquire(program);
- commands->acquire(prim.indexBuffer);
- commands->acquire(prim.vertexBuffer);
-
- // If this is a debug build, validate the current shader.
-#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
- if (program->bundle.vertex == VK_NULL_HANDLE || program->bundle.fragment == VK_NULL_HANDLE) {
- utils::slog.e << "Binding missing shader: " << program->name.c_str() << utils::io::endl;
- }
-#endif
// Update the VK raster state.
const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
@@ -1762,20 +1750,19 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r
.depthBiasSlopeFactor = depthOffset.slope
};
+ // unfortunately in Vulkan the topology is per pipeline
+ VkPrimitiveTopology const topology =
+ VulkanPipelineCache::getPrimitiveTopology(pipelineState.primitiveType);
+
// Declare fixed-size arrays that get passed to the pipeCache and to vkCmdBindVertexBuffers.
- 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();
- VkDeviceSize const* offsets = prim.vertexBuffer->getOffsets();
+ VkVertexInputAttributeDescription const* attribDesc = vbi.getAttribDescriptions();
+ VkVertexInputBindingDescription const* bufferDesc = vbi.getBufferDescriptions();
// Push state changes to the VulkanPipelineCache instance. This is fast and does not make VK calls.
mPipelineCache.bindProgram(program);
mPipelineCache.bindRasterState(vulkanRasterState);
- mPipelineCache.bindPrimitiveTopology(prim.primitiveTopology);
- mPipelineCache.bindVertexArray(attribDesc, bufferDesc, bufferCount);
+ mPipelineCache.bindPrimitiveTopology(topology);
+ mPipelineCache.bindVertexArray(attribDesc, bufferDesc, vbi.getAttributeCount());
// Query the program for the mapping from (SamplerGroupBinding,Offset) to (SamplerBinding),
// where "SamplerBinding" is the integer in the GLSL, and SamplerGroupBinding is the abstract
@@ -1787,6 +1774,10 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r
auto const& bindingToSamplerIndex = program->getBindingToSamplerIndex();
VulkanPipelineCache::UsageFlags usage = program->getUsage();
+#if FVK_ENABLED_DEBUG_SAMPLER_NAME
+ auto const& bindingToName = program->getBindingToName();
+#endif
+
UTILS_NOUNROLL
for (uint8_t binding = 0; binding < VulkanPipelineCache::SAMPLER_BINDING_COUNT; binding++) {
uint16_t const indexPair = bindingToSamplerIndex[binding];
@@ -1819,15 +1810,21 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r
// matching characteristics. (e.g. if the missing texture is a 3D texture)
if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VulkanLayout::UNDEFINED)) {
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
- utils::slog.w << "Uninitialized texture bound to '" << sampler.name.c_str() << "'";
+ utils::slog.w << "Uninitialized texture bound to '" << bindingToName[binding] << "'";
utils::slog.w << " in material '" << program->name.c_str() << "'";
- utils::slog.w << " at binding point " << +sampler.binding << utils::io::endl;
+ utils::slog.w << " at binding point " << +binding << utils::io::endl;
#endif
texture = mEmptyTexture.get();
}
SamplerParams const& samplerParams = boundSampler->s;
VkSampler const vksampler = mSamplerCache.getSampler(samplerParams);
+
+#if FVK_ENABLED_DEBUG_SAMPLER_NAME
+ VulkanDriver::DebugUtils::setName(VK_OBJECT_TYPE_SAMPLER,
+ reinterpret_cast(vksampler), bindingToName[binding].c_str());
+#endif
+
VkImageView imageView = VK_NULL_HANDLE;
VkImageSubresourceRange const range = texture->getPrimaryViewRange();
if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) &&
@@ -1851,19 +1848,33 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r
mPipelineCache.bindSamplers(samplerInfo, samplerTextures, usage);
- // Bind new descriptor sets if they need to change.
- // If descriptor set allocation failed, skip the draw call and bail. No need to emit an error
- // message since the validation layers already do so.
- if (!mPipelineCache.bindDescriptors(cmdbuffer)) {
- 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;
}
+ FVK_SYSTRACE_END();
+}
+
+void VulkanDriver::bindRenderPrimitive(Handle rph) {
+ FVK_SYSTRACE_CONTEXT();
+ FVK_SYSTRACE_START("bindRenderPrimitive");
+
+ VulkanCommandBuffer* commands = &mCommands->get();
+ VkCommandBuffer cmdbuffer = commands->buffer();
+ const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast(rph);
+ commands->acquire(prim.indexBuffer);
+ commands->acquire(prim.vertexBuffer);
+
+ // This *must* match the VulkanVertexBufferInfo that was bound in bindPipeline(). But we want
+ // to allow to call this before bindPipeline(), so the validation can only happen in draw()
+ VulkanVertexBufferInfo const* const vbi =
+ mResourceAllocator.handle_cast(prim.vertexBuffer->vbih);
+
+ uint32_t const bufferCount = vbi->getAttributeCount();
+ VkDeviceSize const* offsets = vbi->getOffsets();
+ VkBuffer const* buffers = prim.vertexBuffer->getVkBuffers();
// 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
@@ -1872,15 +1883,43 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r
vkCmdBindIndexBuffer(cmdbuffer, prim.indexBuffer->buffer.getGpuBuffer(), 0,
prim.indexBuffer->indexType);
+ FVK_SYSTRACE_END();
+}
+
+void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
+ FVK_SYSTRACE_CONTEXT();
+ FVK_SYSTRACE_START("draw2");
+
+ VulkanCommandBuffer* commands = &mCommands->get();
+ VkCommandBuffer cmdbuffer = commands->buffer();
+
+ // Bind new descriptor sets if they need to change.
+ // If descriptor set allocation failed, skip the draw call and bail. No need to emit an error
+ // message since the validation layers already do so.
+ if (!mPipelineCache.bindDescriptors(cmdbuffer)) {
+ return;
+ }
+
// 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::draw(PipelineState state, Handle rph,
+ uint32_t const indexOffset, uint32_t const indexCount, uint32_t const instanceCount) {
+ VulkanRenderPrimitive* const rp = mResourceAllocator.handle_cast(rph);
+ state.primitiveType = rp->type;
+ state.vertexBufferInfo = rp->vertexBuffer->vbih;
+ bindPipeline(state);
+ bindRenderPrimitive(rph);
+ draw2(indexOffset, indexCount, instanceCount);
+}
+
void VulkanDriver::dispatchCompute(Handle program, math::uint3 workGroupCount) {
// FIXME: implement me
}
diff --git a/filament/backend/src/vulkan/VulkanDriver.h b/filament/backend/src/vulkan/VulkanDriver.h
index 8e311b9bcc..d6398f8cd3 100644
--- a/filament/backend/src/vulkan/VulkanDriver.h
+++ b/filament/backend/src/vulkan/VulkanDriver.h
@@ -101,10 +101,6 @@ private:
VulkanDriver& operator=(VulkanDriver const&) = delete;
private:
- inline void setRenderPrimitiveBuffer(Handle rph, PrimitiveType pt,
- Handle vbh,
- Handle ibh);
-
void collectGarbage();
VulkanPlatform* mPlatform = nullptr;
diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp
index 7712115082..48dc56a2b1 100644
--- a/filament/backend/src/vulkan/VulkanHandles.cpp
+++ b/filament/backend/src/vulkan/VulkanHandles.cpp
@@ -99,6 +99,10 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
#endif
}
+#if FVK_ENABLED_DEBUG_SAMPLER_NAME
+ auto& bindingToName = mInfo->bindingToName;
+#endif
+
auto& groupInfo = builder.getSamplerGroupInfo();
auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex;
auto& usage = mInfo->usage;
@@ -109,12 +113,16 @@ VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
uint32_t const binding = samplers[i].binding;
bindingToSamplerIndex[binding] = (groupInd << 8) | (0xff & i);
usage = VulkanPipelineCache::getUsageFlags(binding, group.stageFlags, usage);
+
+#if FVK_ENABLED_DEBUG_SAMPLER_NAME
+ bindingToName[binding] = samplers[i].name.c_str();
+#endif
}
}
#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
- utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << bundle.vertex
- << ", " << bundle.fragment << ")" << utils::io::endl;
+ utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << modules[0]
+ << ", " << modules[1] << ")" << utils::io::endl;
#endif
}
@@ -281,32 +289,21 @@ 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) {
-}
+VulkanVertexBufferInfo::VulkanVertexBufferInfo(
+ uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes)
+ : HwVertexBufferInfo(bufferCount, attributeCount),
+ VulkanResource(VulkanResourceType::VERTEX_BUFFER_INFO),
+ mInfo(attributes.size()) {
-VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
- VulkanResourceAllocator* allocator, uint32_t vertexCount, Handle vbih)
- : HwVertexBuffer(vertexCount),
- VulkanResource(VulkanResourceType::VERTEX_BUFFER),
- vbih(vbih),
- mResources(allocator) {
+ auto attribDesc = mInfo.mSoa.data();
+ auto bufferDesc = mInfo.mSoa.data();
+ auto offsets = mInfo.mSoa.data();
+ auto attribToBufferIndex = mInfo.mSoa.data();
+ std::fill(mInfo.mSoa.begin(),
+ mInfo.mSoa.end(), -1);
- VulkanVertexBufferInfo const* const vbi = allocator->handle_cast(vbih);
- mInfo = new PipelineInfo(vbi->attributes.size());
-
- auto attribDesc = mInfo->mSoa.data();
- auto bufferDesc = mInfo->mSoa.data();
- auto offsets = mInfo->mSoa.data();
- auto attribToBufferIndex = mInfo->mSoa.data();
- std::fill(mInfo->mSoa.begin(),
- mInfo->mSoa.end(), -1);
-
- for (uint32_t attribIndex = 0; attribIndex < vbi->attributes.size(); attribIndex++) {
- Attribute attrib = vbi->attributes[attribIndex];
+ for (uint32_t attribIndex = 0; attribIndex < attributes.size(); attribIndex++) {
+ Attribute attrib = 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);
@@ -318,7 +315,7 @@ VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool&
// expects to receive floats or ints.
if (attrib.buffer == Attribute::BUFFER_UNUSED) {
vkformat = isInteger ? VK_FORMAT_R8G8B8A8_UINT : VK_FORMAT_R8G8B8A8_SNORM;
- attrib = vbi->attributes[0];
+ attrib = attributes[0];
}
offsets[attribIndex] = attrib.offset;
attribDesc[attribIndex] = {
@@ -334,18 +331,23 @@ VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool&
}
}
-VulkanVertexBuffer::~VulkanVertexBuffer() {
- delete mInfo;
+VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
+ VulkanResourceAllocator* allocator,
+ uint32_t vertexCount, Handle vbih)
+ : HwVertexBuffer(vertexCount),
+ VulkanResource(VulkanResourceType::VERTEX_BUFFER),
+ vbih(vbih),
+ mBuffers(MAX_VERTEX_BUFFER_COUNT), // TODO: can we do better here?
+ mResources(allocator) {
}
void VulkanVertexBuffer::setBuffer(VulkanResourceAllocator const& allocator,
VulkanBufferObject* bufferObject, uint32_t index) {
-
VulkanVertexBufferInfo const* const vbi =
const_cast(allocator).handle_cast(vbih);
- size_t count = vbi->attributes.size();
- auto vkbuffers = mInfo->mSoa.data();
- auto attribToBuffer = mInfo->mSoa.data();
+ size_t const count = vbi->getAttributeCount();
+ VkBuffer* const vkbuffers = getVkBuffers();
+ int8_t const* const attribToBuffer = vbi->getAttributeToBuffer();
for (uint8_t attribIndex = 0; attribIndex < count; attribIndex++) {
if (attribToBuffer[attribIndex] == static_cast(index)) {
vkbuffers[attribIndex] = bufferObject->buffer.getGpuBuffer();
@@ -361,35 +363,6 @@ VulkanBufferObject::VulkanBufferObject(VmaAllocator allocator, VulkanStagePool&
buffer(allocator, stagePool, getBufferObjectUsage(bindingType), byteCount),
bindingType(bindingType) {}
-void VulkanRenderPrimitive::setPrimitiveType(PrimitiveType pt) {
- this->type = pt;
- switch (pt) {
- case PrimitiveType::POINTS:
- primitiveTopology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
- break;
- case PrimitiveType::LINES:
- primitiveTopology = VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
- break;
- case PrimitiveType::LINE_STRIP:
- primitiveTopology = VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
- break;
- case PrimitiveType::TRIANGLES:
- primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
- break;
- case PrimitiveType::TRIANGLE_STRIP:
- primitiveTopology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
- break;
- }
-}
-
-void VulkanRenderPrimitive::setBuffers(VulkanVertexBuffer* vertexBuffer,
- VulkanIndexBuffer* indexBuffer) {
- this->vertexBuffer = vertexBuffer;
- this->indexBuffer = indexBuffer;
- mResources.acquire(vertexBuffer);
- mResources.acquire(indexBuffer);
-}
-
VulkanTimerQuery::VulkanTimerQuery(std::tuple indices)
: VulkanThreadSafeResource(VulkanResourceType::TIMER_QUERY),
mStartingQueryIndex(std::get<0>(indices)),
@@ -421,4 +394,15 @@ bool VulkanTimerQuery::isCompleted() noexcept {
VulkanTimerQuery::~VulkanTimerQuery() = default;
+VulkanRenderPrimitive::VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator,
+ PrimitiveType pt, Handle vbh, Handle ibh)
+ : VulkanResource(VulkanResourceType::RENDER_PRIMITIVE),
+ mResources(resourceAllocator) {
+ type = pt;
+ vertexBuffer = resourceAllocator->handle_cast(vbh);
+ indexBuffer = resourceAllocator->handle_cast(ibh);
+ mResources.acquire(vertexBuffer);
+ mResources.acquire(indexBuffer);
+}
+
} // namespace filament::backend
diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h
index 9e1d51b537..7c65915d51 100644
--- a/filament/backend/src/vulkan/VulkanHandles.h
+++ b/filament/backend/src/vulkan/VulkanHandles.h
@@ -64,6 +64,12 @@ struct VulkanProgram : public HwProgram, VulkanResource {
return mInfo->bindingToSamplerIndex;
}
+#if FVK_ENABLED_DEBUG_SAMPLER_NAME
+ inline utils::FixedCapacityVector const& getBindingToName() const {
+ return mInfo->bindingToName;
+ }
+#endif
+
private:
// TODO: handle compute shaders.
// The expected order of shaders - from frontend to backend - is vertex, fragment, compute.
@@ -80,6 +86,12 @@ private:
// We store the samplerGroupIndex as the top 8-bit and the index within each group as the lower 8-bit.
utils::FixedCapacityVector bindingToSamplerIndex;
VkShaderModule shaders[MAX_SHADER_MODULES] = { VK_NULL_HANDLE };
+
+#if FVK_ENABLED_DEBUG_SAMPLER_NAME
+ // We store the sampler name mapped from binding index (only for debug purposes).
+ utils::FixedCapacityVector bindingToName;
+#endif
+
};
PipelineInfo* mInfo;
@@ -132,7 +144,48 @@ struct VulkanBufferObject;
struct VulkanVertexBufferInfo : public HwVertexBufferInfo, VulkanResource {
VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes);
- AttributeArray attributes;
+
+ inline VkVertexInputAttributeDescription const* getAttribDescriptions() const {
+ return mInfo.mSoa.data();
+ }
+
+ inline VkVertexInputBindingDescription const* getBufferDescriptions() const {
+ return mInfo.mSoa.data();
+ }
+
+ inline int8_t const* getAttributeToBuffer() const {
+ return mInfo.mSoa.data();
+ }
+
+ inline VkDeviceSize const* getOffsets() const {
+ return mInfo.mSoa.data();
+ }
+
+ size_t getAttributeCount() const noexcept {
+ return mInfo.mSoa.size();
+ }
+
+private:
+ struct PipelineInfo {
+ PipelineInfo(size_t size) : mSoa(size /* capacity */) {
+ mSoa.resize(size);
+ }
+
+ // 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 OFFSETS = 2;
+ static constexpr uint8_t ATTRIBUTE_TO_BUFFER_INDEX = 3;
+
+ utils::StructureOfArrays<
+ VkVertexInputAttributeDescription,
+ VkVertexInputBindingDescription,
+ VkDeviceSize,
+ int8_t
+ > mSoa;
+ };
+
+ PipelineInfo mInfo;
};
struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
@@ -140,53 +193,20 @@ struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
VulkanResourceAllocator* allocator,
uint32_t vertexCount, Handle vbih);
- ~VulkanVertexBuffer();
-
void setBuffer(VulkanResourceAllocator const& allocator,
VulkanBufferObject* bufferObject, uint32_t index);
- inline VkVertexInputAttributeDescription const* getAttribDescriptions() {
- return mInfo->mSoa.data();
- }
-
- inline VkVertexInputBindingDescription const* getBufferDescriptions() {
- return mInfo->mSoa.data();
- }
-
inline VkBuffer const* getVkBuffers() const {
- return mInfo->mSoa.data();
+ return mBuffers.data();
}
- inline VkDeviceSize const* getOffsets() const {
- return mInfo->mSoa.data();
+ inline VkBuffer* getVkBuffers() {
+ return mBuffers.data();
}
Handle vbih;
-
private:
- struct PipelineInfo {
- PipelineInfo(size_t size)
- : mSoa(size /* capacity */) {
- mSoa.resize(size);
- }
-
- // 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;
- static constexpr uint8_t OFFSETS = 3;
- static constexpr uint8_t ATTRIBUTE_TO_BUFFER_INDEX = 4;
-
- utils::StructureOfArrays<
- VkVertexInputAttributeDescription,
- VkVertexInputBindingDescription,
- VkBuffer,
- VkDeviceSize,
- int8_t
- > mSoa;
- };
-
- PipelineInfo* mInfo;
+ utils::FixedCapacityVector mBuffers;
FixedSizeVulkanResourceManager mResources;
};
@@ -219,19 +239,15 @@ struct VulkanSamplerGroup : public HwSamplerGroup, VulkanResource {
};
struct VulkanRenderPrimitive : public HwRenderPrimitive, VulkanResource {
- VulkanRenderPrimitive(VulkanResourceAllocator* allocator)
- : VulkanResource(VulkanResourceType::RENDER_PRIMITIVE),
- mResources(allocator) {}
+ VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator,
+ PrimitiveType pt, Handle vbh, Handle ibh);
~VulkanRenderPrimitive() {
mResources.clear();
}
- void setPrimitiveType(PrimitiveType pt);
- void setBuffers(VulkanVertexBuffer* vertexBuffer, VulkanIndexBuffer* indexBuffer);
VulkanVertexBuffer* vertexBuffer = nullptr;
VulkanIndexBuffer* indexBuffer = nullptr;
- VkPrimitiveTopology primitiveTopology;
private:
// Keep references to the vertex buffer and the index buffer.
diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.cpp b/filament/backend/src/vulkan/VulkanPipelineCache.cpp
index 5b96344c0c..2cb474d7bb 100644
--- a/filament/backend/src/vulkan/VulkanPipelineCache.cpp
+++ b/filament/backend/src/vulkan/VulkanPipelineCache.cpp
@@ -563,6 +563,14 @@ VulkanPipelineCache::PipelineLayoutCacheEntry* VulkanPipelineCache::getOrCreateP
void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
mPipelineRequirements.shaders[0] = program->getVertexShader();
mPipelineRequirements.shaders[1] = program->getFragmentShader();
+
+ // If this is a debug build, validate the current shader.
+#if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
+ if (mPipelineRequirements.shaders[0] == VK_NULL_HANDLE ||
+ mPipelineRequirements.shaders[1] == VK_NULL_HANDLE) {
+ utils::slog.e << "Binding missing shader: " << program->name.c_str() << utils::io::endl;
+ }
+#endif
}
void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexcept {
diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.h b/filament/backend/src/vulkan/VulkanPipelineCache.h
index b2acb37ec2..9888c59fb8 100644
--- a/filament/backend/src/vulkan/VulkanPipelineCache.h
+++ b/filament/backend/src/vulkan/VulkanPipelineCache.h
@@ -191,10 +191,19 @@ public:
mDummyTargetInfo.imageView = imageView;
}
- // Acquires a resource to be bound to the current pipeline. The ownership of the resource
- // will be transferred to the corresponding pipeline when pipeline is bound.
- void acquireResource(VulkanResource* resource) {
- mPipelineBoundResources.acquire(resource);
+ static VkPrimitiveTopology getPrimitiveTopology(PrimitiveType pt) noexcept {
+ switch (pt) {
+ case PrimitiveType::POINTS:
+ return VK_PRIMITIVE_TOPOLOGY_POINT_LIST;
+ case PrimitiveType::LINES:
+ return VK_PRIMITIVE_TOPOLOGY_LINE_LIST;
+ case PrimitiveType::LINE_STRIP:
+ return VK_PRIMITIVE_TOPOLOGY_LINE_STRIP;
+ case PrimitiveType::TRIANGLES:
+ return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
+ case PrimitiveType::TRIANGLE_STRIP:
+ return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
+ }
}
private:
diff --git a/filament/backend/src/vulkan/VulkanResourceAllocator.h b/filament/backend/src/vulkan/VulkanResourceAllocator.h
index 222c7be936..ac71f0ece0 100644
--- a/filament/backend/src/vulkan/VulkanResourceAllocator.h
+++ b/filament/backend/src/vulkan/VulkanResourceAllocator.h
@@ -17,6 +17,7 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H
#define TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H
+#include "VulkanConstants.h"
#include "VulkanHandles.h"
#include
@@ -29,18 +30,17 @@
namespace filament::backend {
-// RESOURCE_TYPE_COUNT matches the count of enum VulkanResourceType.
-#define RESOURCE_TYPE_COUNT 12
-#define DEBUG_RESOURCE_LEAKS 0
+#define RESOURCE_TYPE_COUNT (static_cast(VulkanResourceType::END_TYPE))
+#define DEBUG_RESOURCE_LEAKS FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
#if DEBUG_RESOURCE_LEAKS
- #define TRACK_INCREMENT() \
- if (!IS_MANAGED_TYPE(obj->mType)) { \
- mDebugOnlyResourceCount[static_cast(obj->mType)]++; \
+ #define TRACK_INCREMENT() \
+ if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \
+ mDebugOnlyResourceCount[static_cast(obj->getType())]++; \
}
- #define TRACK_DECREMENT() \
- if (!IS_MANAGED_TYPE(obj->mType)) { \
- mDebugOnlyResourceCount[static_cast(obj->mType)]--; \
+ #define TRACK_DECREMENT() \
+ if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \
+ mDebugOnlyResourceCount[static_cast(obj->getType())]--; \
}
#else
// No-op
diff --git a/filament/backend/src/vulkan/VulkanResources.cpp b/filament/backend/src/vulkan/VulkanResources.cpp
index bbc8472239..a0bfd79435 100644
--- a/filament/backend/src/vulkan/VulkanResources.cpp
+++ b/filament/backend/src/vulkan/VulkanResources.cpp
@@ -70,6 +70,7 @@ void deallocateResource(VulkanResourceAllocator* allocator, VulkanResourceType t
// destruction.
case VulkanResourceType::FENCE:
case VulkanResourceType::HEAP_ALLOCATED:
+ case VulkanResourceType::END_TYPE:
break;
}
}
diff --git a/filament/backend/src/vulkan/VulkanResources.h b/filament/backend/src/vulkan/VulkanResources.h
index 8364c232b0..dd40cbb3ab 100644
--- a/filament/backend/src/vulkan/VulkanResources.h
+++ b/filament/backend/src/vulkan/VulkanResources.h
@@ -51,6 +51,8 @@ enum class VulkanResourceType : uint8_t {
// Below are resources that are managed manually (i.e. not ref counted).
FENCE,
HEAP_ALLOCATED,
+
+ END_TYPE, // A placeholder
};
#define IS_HEAP_ALLOC_TYPE(f) \
diff --git a/filament/backend/src/vulkan/VulkanTexture.cpp b/filament/backend/src/vulkan/VulkanTexture.cpp
index 16ab0a2ecc..e595f3bec9 100644
--- a/filament/backend/src/vulkan/VulkanTexture.cpp
+++ b/filament/backend/src/vulkan/VulkanTexture.cpp
@@ -159,7 +159,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.samples = (VkSampleCountFlagBits) samples;
VkResult error = vkCreateImage(mDevice, &imageInfo, VKALLOC, &mTextureImage);
- if (error || FVK_ENABLED_BOOL(FVK_DEBUG_TEXTURE)) {
+ if (error || FVK_ENABLED(FVK_DEBUG_TEXTURE)) {
utils::slog.d << "vkCreateImage: "
<< "image = " << mTextureImage << ", "
<< "result = " << error << ", "
diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h
index 6eb1916f7a..04d71259b7 100644
--- a/filament/include/filament/Engine.h
+++ b/filament/include/filament/Engine.h
@@ -300,7 +300,7 @@ public:
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
- * Currently only honored by the GL backend.
+ * Currently only honored by the GL and Metal backends.
*/
bool disableParallelShaderCompile = false;
diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp
index 3a1b2b18a3..461acba3a2 100644
--- a/filament/src/PostProcessManager.cpp
+++ b/filament/src/PostProcessManager.cpp
@@ -41,6 +41,7 @@
#include "details/Material.h"
#include "details/MaterialInstance.h"
#include "details/Texture.h"
+#include "details/VertexBuffer.h"
#include "generated/resources/materials.h"
@@ -190,8 +191,9 @@ std::pair
FMaterial* const material = getMaterial(engine);
material->prepareProgram(Variant{ variantKey });
return {{
- .program = material->getProgram(Variant{ variantKey }),
- .rasterState = material->getRasterState() },
+ .program = material->getProgram(Variant{ variantKey }),
+ .vertexBufferInfo = engine.getFullScreenVertexBuffer()->getVertexBufferInfoHandle(),
+ .rasterState = material->getRasterState() },
material->getDefaultInstance()->getScissor() };
}
diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp
index 016c9976a4..de60680970 100644
--- a/filament/src/RenderPass.cpp
+++ b/filament/src/RenderPass.cpp
@@ -55,6 +55,7 @@
#include
#include
#include
+#include
using namespace utils;
using namespace filament::math;
@@ -866,6 +867,10 @@ void RenderPass::Executor::execute(FEngine& engine,
.polygonOffset = mPolygonOffset,
};
+ PipelineState currentPipeline{};
+ Handle currentPrimitiveHandle{};
+ bool rebindPipeline = true;
+
FMaterialInstance const* UTILS_RESTRICT mi = nullptr;
FMaterial const* UTILS_RESTRICT ma = nullptr;
auto const* UTILS_RESTRICT pCustomCommands = mCustomCommands.data();
@@ -918,7 +923,9 @@ void RenderPass::Executor::execute(FEngine& engine,
// per-renderable uniform
PrimitiveInfo const info = first->primitive;
pipeline.rasterState = info.rasterState;
- //pipeline.vertexBufferInfo = info.vertexBufferInfo;
+ pipeline.vertexBufferInfo = info.primitive->getVertexBufferInfoHandle();
+ pipeline.primitiveType = info.primitive->getPrimitiveType();
+ assert_invariant(pipeline.vertexBufferInfo);
if (UTILS_UNLIKELY(mi != info.primitive->getMaterialInstance())) {
// this is always taken the first time
@@ -940,6 +947,11 @@ void RenderPass::Executor::execute(FEngine& engine,
}
pipeline.stencilState = mi->getStencilState();
mi->use(driver);
+
+ // FIXME: MaterialInstance changed (not necessarily the program though),
+ // however, texture bindings may have changed and currently we need to
+ // rebind the pipeline when that happens.
+ rebindPipeline = true;
}
assert_invariant(ma);
@@ -989,6 +1001,10 @@ void RenderPass::Executor::execute(FEngine& engine,
// note: even if only skinning is enabled, binding morphTargetBuffer is needed.
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_MORPHING,
info.morphTargetBuffer);
+
+ // FIXME: Currently we need to rebind the PipelineState when texture or
+ // UBO binding change.
+ rebindPipeline = true;
}
if (UTILS_UNLIKELY(info.morphWeightBuffer)) {
@@ -1001,10 +1017,27 @@ void RenderPass::Executor::execute(FEngine& engine,
// note: even if only morphing is enabled, binding skinningTexture is needed.
driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_SKINNING,
info.skinningTexture);
+
+ // FIXME: Currently we need to rebind the PipelineState when texture or
+ // UBO binding change.
+ rebindPipeline = true;
}
- driver.draw(pipeline, info.primitive->getHwHandle(),
- info.primitive->getIndexOffset(), info.primitive->getIndexCount(),
+ if (rebindPipeline ||
+ (memcmp(&pipeline, ¤tPipeline, sizeof(PipelineState)) != 0)) {
+ rebindPipeline = false;
+ currentPipeline = pipeline;
+ driver.bindPipeline(pipeline);
+ }
+
+ if (info.primitive->getHwHandle() != currentPrimitiveHandle) {
+ currentPrimitiveHandle = info.primitive->getHwHandle();
+ driver.bindRenderPrimitive(info.primitive->getHwHandle());
+ }
+
+ driver.draw2(
+ info.primitive->getIndexOffset(),
+ info.primitive->getIndexCount(),
instanceCount);
}
}
diff --git a/filament/src/RenderPrimitive.cpp b/filament/src/RenderPrimitive.cpp
index 6a523b8b6e..8bd5e5ad57 100644
--- a/filament/src/RenderPrimitive.cpp
+++ b/filament/src/RenderPrimitive.cpp
@@ -16,14 +16,18 @@
#include "RenderPrimitive.h"
-#include "details/Engine.h"
+#include
+#include
+
#include "details/IndexBuffer.h"
-#include "details/Material.h"
+#include "details/MaterialInstance.h"
#include "details/VertexBuffer.h"
+#include
+#include
+
#include
-#include
#include
namespace filament {
@@ -39,18 +43,7 @@ void FRenderPrimitive::init(HwRenderPrimitiveFactory& factory, backend::DriverAp
if (entry.indices && entry.vertices) {
FVertexBuffer* vertexBuffer = downcast(entry.vertices);
FIndexBuffer* indexBuffer = downcast(entry.indices);
-
- AttributeBitset const enabledAttributes = vertexBuffer->getDeclaredAttributes();
-
- auto const& ebh = vertexBuffer->getHwHandle();
- auto const& ibh = indexBuffer->getHwHandle();
-
- mHandle = factory.create(driver, ebh, ibh, entry.type);
-
- mPrimitiveType = entry.type;
- mIndexOffset = entry.offset;
- mIndexCount = entry.count;
- mEnabledAttributes = enabledAttributes;
+ set(factory, driver, entry.type, vertexBuffer, indexBuffer, entry.offset, entry.count);
}
}
@@ -62,17 +55,19 @@ void FRenderPrimitive::terminate(HwRenderPrimitiveFactory& factory, backend::Dri
void FRenderPrimitive::set(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver,
RenderableManager::PrimitiveType type,
- FVertexBuffer* vertices, FIndexBuffer* indices, size_t offset, size_t count) noexcept {
+ FVertexBuffer* vertexBuffer, FIndexBuffer* indexBuffer,
+ size_t offset, size_t count) noexcept {
if (mHandle) {
factory.destroy(driver, mHandle);
}
- AttributeBitset const enabledAttributes = vertices->getDeclaredAttributes();
+ AttributeBitset const enabledAttributes = vertexBuffer->getDeclaredAttributes();
- auto const& ebh = vertices->getHwHandle();
- auto const& ibh = indices->getHwHandle();
+ auto const& ebh = vertexBuffer->getHwHandle();
+ auto const& ibh = indexBuffer->getHwHandle();
mHandle = factory.create(driver, ebh, ibh, type);
+ mVertexBufferInfoHandle = vertexBuffer->getVertexBufferInfoHandle();
mPrimitiveType = type;
mIndexOffset = offset;
diff --git a/filament/src/RenderPrimitive.h b/filament/src/RenderPrimitive.h
index bc1c897484..07d7431f02 100644
--- a/filament/src/RenderPrimitive.h
+++ b/filament/src/RenderPrimitive.h
@@ -45,14 +45,15 @@ public:
void set(HwRenderPrimitiveFactory& factory, backend::DriverApi& driver,
RenderableManager::PrimitiveType type,
- FVertexBuffer* vertices, FIndexBuffer* indices, size_t offset,
+ FVertexBuffer* vertexBuffer, FIndexBuffer* indexBuffer, size_t offset,
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 getHwHandle() const noexcept { return mHandle; }
+ backend::RenderPrimitiveHandle getHwHandle() const noexcept { return mHandle; }
+ backend::VertexBufferInfoHandle getVertexBufferInfoHandle() const { return mVertexBufferInfoHandle; }
uint32_t getIndexOffset() const noexcept { return mIndexOffset; }
uint32_t getIndexCount() const noexcept { return mIndexCount; }
@@ -76,7 +77,7 @@ private:
struct {
FMaterialInstance const* mMaterialInstance = nullptr;
backend::Handle mHandle = {};
- UTILS_UNUSED uint8_t padding[4]= {};
+ backend::Handle mVertexBufferInfoHandle = {};
uint32_t mIndexOffset = 0;
uint32_t mIndexCount = 0;
};
diff --git a/filament/src/details/VertexBuffer.h b/filament/src/details/VertexBuffer.h
index 56c5f5bb61..759c7fe019 100644
--- a/filament/src/details/VertexBuffer.h
+++ b/filament/src/details/VertexBuffer.h
@@ -52,6 +52,8 @@ public:
VertexBufferHandle getHwHandle() const noexcept { return mHandle; }
+ VertexBufferInfoHandle getVertexBufferInfoHandle() const { return mVertexBufferInfoHandle; }
+
size_t getVertexCount() const noexcept;
AttributeBitset getDeclaredAttributes() const noexcept {
diff --git a/filament/src/materials/defaultMaterial0.mat b/filament/src/materials/defaultMaterial0.mat
deleted file mode 100644
index 914d29c3d1..0000000000
--- a/filament/src/materials/defaultMaterial0.mat
+++ /dev/null
@@ -1,12 +0,0 @@
-material {
- name : "Filament Default Material",
- shadingModel : unlit,
- featureLevel: 0
-}
-
-fragment {
- void material(inout MaterialInputs material) {
- prepareMaterial(material);
- material.baseColor.rgb = vec3(0.8);
- }
-}
diff --git a/filament/src/materials/skybox.mat b/filament/src/materials/skybox.mat
index 1d36108a27..d692fc238c 100644
--- a/filament/src/materials/skybox.mat
+++ b/filament/src/materials/skybox.mat
@@ -35,10 +35,14 @@ fragment {
if (materialParams.constantColor != 0) {
sky = materialParams.color;
} else {
+#if MATERIAL_FEATURE_LEVEL == 0
+ sky = vec4(textureCube(materialParams_skybox, variable_eyeDirection.xyz).rgb, 1.0);
+#else
sky = vec4(textureLod(materialParams_skybox, variable_eyeDirection.xyz, 0.0).rgb, 1.0);
+#endif
sky.rgb *= frameUniforms.iblLuminance;
}
- if (materialParams.showSun != 0 && frameUniforms.sun.w >= 0.0f) {
+ if (materialParams.showSun != 0 && frameUniforms.sun.w >= 0.0) {
vec3 direction = normalize(variable_eyeDirection.xyz);
// Assume the sun is a sphere
vec3 sun = frameUniforms.lightColorIntensity.rgb *
diff --git a/filament/src/materials/skybox0.mat b/filament/src/materials/skybox0.mat
deleted file mode 100644
index 2891e32f4b..0000000000
--- a/filament/src/materials/skybox0.mat
+++ /dev/null
@@ -1,60 +0,0 @@
-material {
- name : Skybox,
- parameters : [
- {
- type : int,
- name : showSun
- },
- {
- type : int,
- name : constantColor
- },
- {
- type : samplerCubemap,
- name : skybox
- },
- {
- type : float4,
- name : color
- }
- ],
- variables : [
- eyeDirection
- ],
- vertexDomain : device,
- depthWrite : false,
- shadingModel : unlit,
- variantFilter : [ skinning, shadowReceiver, vsm ],
- culling: none,
- featureLevel: 0
-}
-
-fragment {
- void material(inout MaterialInputs material) {
- prepareMaterial(material);
- vec4 sky;
- if (materialParams.constantColor != 0) {
- sky = materialParams.color;
- } else {
- sky = vec4(textureCube(materialParams_skybox, variable_eyeDirection.xyz).rgb, 1.0);
- sky.rgb *= frameUniforms.iblLuminance;
- }
- if (materialParams.showSun != 0 && frameUniforms.sun.w >= 0.0) {
- vec3 direction = normalize(variable_eyeDirection.xyz);
- // Assume the sun is a sphere
- vec3 sun = frameUniforms.lightColorIntensity.rgb *
- (frameUniforms.lightColorIntensity.a * (4.0 * PI));
- float cosAngle = dot(direction, frameUniforms.lightDirection);
- float x = (cosAngle - frameUniforms.sun.x) * frameUniforms.sun.z;
- float gradient = pow(1.0 - saturate(x), frameUniforms.sun.w);
- sky.rgb = sky.rgb + gradient * sun;
- }
- material.baseColor = sky;
- }
-}
-
-vertex {
- void materialVertex(inout MaterialVertexInputs material) {
- material.eyeDirection.xyz = material.worldPosition.xyz;
- }
-}
diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec
index e92cedb1b4..513d6c1975 100644
--- a/ios/CocoaPods/Filament.podspec
+++ b/ios/CocoaPods/Filament.podspec
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
- spec.version = "1.50.6"
+ spec.version = "1.51.0"
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.6/filament-v1.50.6-ios.tgz" }
+ spec.source = { :http => "https://github.com/google/filament/releases/download/v1.51.0/filament-v1.51.0-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h
index f98c707e1c..ebe2ccce20 100644
--- a/libs/filabridge/include/filament/MaterialEnums.h
+++ b/libs/filabridge/include/filament/MaterialEnums.h
@@ -28,7 +28,7 @@
namespace filament {
// update this when a new version of filament wouldn't work with older materials
-static constexpr size_t MATERIAL_VERSION = 50;
+static constexpr size_t MATERIAL_VERSION = 51;
/**
* Supported shading models
@@ -201,7 +201,7 @@ enum class ReflectionMode : uint8_t {
// can't really use std::underlying_type::type because the driver takes a uint32_t
using AttributeBitset = utils::bitset32;
-static constexpr size_t MATERIAL_PROPERTIES_COUNT = 26;
+static constexpr size_t MATERIAL_PROPERTIES_COUNT = 27;
enum class Property : uint8_t {
BASE_COLOR, //!< float4, all shading models
ROUGHNESS, //!< float, lit shading models only
@@ -223,6 +223,7 @@ enum class Property : uint8_t {
EMISSIVE, //!< float4, all shading models
NORMAL, //!< float3, all shading models only, except unlit
POST_LIGHTING_COLOR, //!< float4, all shading models
+ POST_LIGHTING_MIX_FACTOR,//!< float, all shading models
CLIP_SPACE_TRANSFORM, //!< mat4, vertex shader only
ABSORPTION, //!< float3, how much light is absorbed by the material
TRANSMISSION, //!< float, how much light is refracted through the material
diff --git a/libs/filamat/include/filamat/MaterialBuilder.h b/libs/filamat/include/filamat/MaterialBuilder.h
index 541153112c..2e2d096309 100644
--- a/libs/filamat/include/filamat/MaterialBuilder.h
+++ b/libs/filamat/include/filamat/MaterialBuilder.h
@@ -410,7 +410,8 @@ public:
/**
* Set the blending mode of the post-lighting color for this material.
* Only OPAQUE, TRANSPARENT and ADD are supported, the default is TRANSPARENT.
- * This setting requires the material property "postLightingColor" to be set.
+ * This setting requires the material properties "postLightingColor" and
+ * "postLightingMixFactor" to be set.
*/
MaterialBuilder& postLightingBlending(BlendingMode blending) noexcept;
diff --git a/libs/filamat/src/Enums.cpp b/libs/filamat/src/Enums.cpp
index 01c4011c7e..583d376f95 100644
--- a/libs/filamat/src/Enums.cpp
+++ b/libs/filamat/src/Enums.cpp
@@ -21,32 +21,33 @@
namespace filamat {
std::unordered_map Enums::mStringToProperty = {
- { "baseColor", Property::BASE_COLOR },
- { "roughness", Property::ROUGHNESS },
- { "metallic", Property::METALLIC },
- { "reflectance", Property::REFLECTANCE },
- { "ambientOcclusion", Property::AMBIENT_OCCLUSION },
- { "clearCoat", Property::CLEAR_COAT },
- { "clearCoatRoughness", Property::CLEAR_COAT_ROUGHNESS },
- { "clearCoatNormal", Property::CLEAR_COAT_NORMAL },
- { "anisotropy", Property::ANISOTROPY },
- { "anisotropyDirection", Property::ANISOTROPY_DIRECTION },
- { "thickness", Property::THICKNESS },
- { "subsurfacePower", Property::SUBSURFACE_POWER },
- { "subsurfaceColor", Property::SUBSURFACE_COLOR },
- { "sheenColor", Property::SHEEN_COLOR },
- { "sheenRoughness", Property::SHEEN_ROUGHNESS },
- { "glossiness", Property::GLOSSINESS },
- { "specularColor", Property::SPECULAR_COLOR },
- { "emissive", Property::EMISSIVE },
- { "normal", Property::NORMAL },
- { "postLightingColor", Property::POST_LIGHTING_COLOR },
- { "clipSpaceTransform", Property::CLIP_SPACE_TRANSFORM },
- { "absorption", Property::ABSORPTION },
- { "transmission", Property::TRANSMISSION },
- { "ior", Property::IOR },
- { "microThickness", Property::MICRO_THICKNESS },
- { "bentNormal", Property::BENT_NORMAL },
+ { "baseColor", Property::BASE_COLOR },
+ { "roughness", Property::ROUGHNESS },
+ { "metallic", Property::METALLIC },
+ { "reflectance", Property::REFLECTANCE },
+ { "ambientOcclusion", Property::AMBIENT_OCCLUSION },
+ { "clearCoat", Property::CLEAR_COAT },
+ { "clearCoatRoughness", Property::CLEAR_COAT_ROUGHNESS },
+ { "clearCoatNormal", Property::CLEAR_COAT_NORMAL },
+ { "anisotropy", Property::ANISOTROPY },
+ { "anisotropyDirection", Property::ANISOTROPY_DIRECTION },
+ { "thickness", Property::THICKNESS },
+ { "subsurfacePower", Property::SUBSURFACE_POWER },
+ { "subsurfaceColor", Property::SUBSURFACE_COLOR },
+ { "sheenColor", Property::SHEEN_COLOR },
+ { "sheenRoughness", Property::SHEEN_ROUGHNESS },
+ { "glossiness", Property::GLOSSINESS },
+ { "specularColor", Property::SPECULAR_COLOR },
+ { "emissive", Property::EMISSIVE },
+ { "normal", Property::NORMAL },
+ { "postLightingColor", Property::POST_LIGHTING_COLOR },
+ { "postLightingMixFactor", Property::POST_LIGHTING_MIX_FACTOR },
+ { "clipSpaceTransform", Property::CLIP_SPACE_TRANSFORM },
+ { "absorption", Property::ABSORPTION },
+ { "transmission", Property::TRANSMISSION },
+ { "ior", Property::IOR },
+ { "microThickness", Property::MICRO_THICKNESS },
+ { "bentNormal", Property::BENT_NORMAL },
};
template <>
diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp
index c242eee6d5..13c6bd0768 100644
--- a/libs/filamat/src/shaders/CodeGenerator.cpp
+++ b/libs/filamat/src/shaders/CodeGenerator.cpp
@@ -1115,32 +1115,33 @@ io::sstream& CodeGenerator::generateShaderReflections(utils::io::sstream& out, S
char const* CodeGenerator::getConstantName(MaterialBuilder::Property property) noexcept {
using Property = MaterialBuilder::Property;
switch (property) {
- case Property::BASE_COLOR: return "BASE_COLOR";
- case Property::ROUGHNESS: return "ROUGHNESS";
- case Property::METALLIC: return "METALLIC";
- case Property::REFLECTANCE: return "REFLECTANCE";
- case Property::AMBIENT_OCCLUSION: return "AMBIENT_OCCLUSION";
- case Property::CLEAR_COAT: return "CLEAR_COAT";
- case Property::CLEAR_COAT_ROUGHNESS: return "CLEAR_COAT_ROUGHNESS";
- case Property::CLEAR_COAT_NORMAL: return "CLEAR_COAT_NORMAL";
- case Property::ANISOTROPY: return "ANISOTROPY";
- case Property::ANISOTROPY_DIRECTION: return "ANISOTROPY_DIRECTION";
- case Property::THICKNESS: return "THICKNESS";
- case Property::SUBSURFACE_POWER: return "SUBSURFACE_POWER";
- case Property::SUBSURFACE_COLOR: return "SUBSURFACE_COLOR";
- case Property::SHEEN_COLOR: return "SHEEN_COLOR";
- case Property::SHEEN_ROUGHNESS: return "SHEEN_ROUGHNESS";
- case Property::GLOSSINESS: return "GLOSSINESS";
- case Property::SPECULAR_COLOR: return "SPECULAR_COLOR";
- case Property::EMISSIVE: return "EMISSIVE";
- case Property::NORMAL: return "NORMAL";
- case Property::POST_LIGHTING_COLOR: return "POST_LIGHTING_COLOR";
- case Property::CLIP_SPACE_TRANSFORM: return "CLIP_SPACE_TRANSFORM";
- case Property::ABSORPTION: return "ABSORPTION";
- case Property::TRANSMISSION: return "TRANSMISSION";
- case Property::IOR: return "IOR";
- case Property::MICRO_THICKNESS: return "MICRO_THICKNESS";
- case Property::BENT_NORMAL: return "BENT_NORMAL";
+ case Property::BASE_COLOR: return "BASE_COLOR";
+ case Property::ROUGHNESS: return "ROUGHNESS";
+ case Property::METALLIC: return "METALLIC";
+ case Property::REFLECTANCE: return "REFLECTANCE";
+ case Property::AMBIENT_OCCLUSION: return "AMBIENT_OCCLUSION";
+ case Property::CLEAR_COAT: return "CLEAR_COAT";
+ case Property::CLEAR_COAT_ROUGHNESS: return "CLEAR_COAT_ROUGHNESS";
+ case Property::CLEAR_COAT_NORMAL: return "CLEAR_COAT_NORMAL";
+ case Property::ANISOTROPY: return "ANISOTROPY";
+ case Property::ANISOTROPY_DIRECTION: return "ANISOTROPY_DIRECTION";
+ case Property::THICKNESS: return "THICKNESS";
+ case Property::SUBSURFACE_POWER: return "SUBSURFACE_POWER";
+ case Property::SUBSURFACE_COLOR: return "SUBSURFACE_COLOR";
+ case Property::SHEEN_COLOR: return "SHEEN_COLOR";
+ case Property::SHEEN_ROUGHNESS: return "SHEEN_ROUGHNESS";
+ case Property::GLOSSINESS: return "GLOSSINESS";
+ case Property::SPECULAR_COLOR: return "SPECULAR_COLOR";
+ case Property::EMISSIVE: return "EMISSIVE";
+ case Property::NORMAL: return "NORMAL";
+ case Property::POST_LIGHTING_COLOR: return "POST_LIGHTING_COLOR";
+ case Property::POST_LIGHTING_MIX_FACTOR: return "POST_LIGHTING_MIX_FACTOR";
+ case Property::CLIP_SPACE_TRANSFORM: return "CLIP_SPACE_TRANSFORM";
+ case Property::ABSORPTION: return "ABSORPTION";
+ case Property::TRANSMISSION: return "TRANSMISSION";
+ case Property::IOR: return "IOR";
+ case Property::MICRO_THICKNESS: return "MICRO_THICKNESS";
+ case Property::BENT_NORMAL: return "BENT_NORMAL";
}
}
diff --git a/libs/filamat/tests/test_filamat.cpp b/libs/filamat/tests/test_filamat.cpp
index 91e4a9adeb..1d63fc15dc 100644
--- a/libs/filamat/tests/test_filamat.cpp
+++ b/libs/filamat/tests/test_filamat.cpp
@@ -701,6 +701,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerOutputFactor) {
void material(inout MaterialInputs material) {
prepareMaterial(material);
material.postLightingColor = vec4(1.0);
+ material.postLightingMixFactor = 0.5;
}
)");
@@ -712,6 +713,7 @@ TEST_F(MaterialCompiler, StaticCodeAnalyzerOutputFactor) {
glslTools.findProperties(ShaderStage::FRAGMENT, shaderCode, properties);
MaterialBuilder::PropertyList expected{ false };
expected[size_t(filamat::MaterialBuilder::Property::POST_LIGHTING_COLOR)] = true;
+ expected[size_t(filamat::MaterialBuilder::Property::POST_LIGHTING_MIX_FACTOR)] = true;
EXPECT_TRUE(PropertyListsMatch(expected, properties));
}
diff --git a/libs/iblprefilter/include/filament-iblprefilter/IBLPrefilterContext.h b/libs/iblprefilter/include/filament-iblprefilter/IBLPrefilterContext.h
index 815ff613ff..903c42589f 100644
--- a/libs/iblprefilter/include/filament-iblprefilter/IBLPrefilterContext.h
+++ b/libs/iblprefilter/include/filament-iblprefilter/IBLPrefilterContext.h
@@ -88,12 +88,23 @@ public:
*/
class EquirectangularToCubemap {
public:
+
+ struct Config {
+ bool mirror = true; //!< mirror the source horizontally
+ };
+
/**
- * Creates a EquirectangularToCubemap processor.
+ * Creates a EquirectangularToCubemap processor using the default Config
* @param context IBLPrefilterContext to use
*/
explicit EquirectangularToCubemap(IBLPrefilterContext& context);
+ /**
+ * Creates a EquirectangularToCubemap processor using the provided Config
+ * @param context IBLPrefilterContext to use
+ */
+ EquirectangularToCubemap(IBLPrefilterContext& context, Config const& config);
+
/**
* Destroys all GPU resources created during initialization.
*/
@@ -125,6 +136,7 @@ public:
private:
IBLPrefilterContext& mContext;
filament::Material* mEquirectMaterial = nullptr;
+ Config mConfig{};
};
/**
diff --git a/libs/iblprefilter/src/IBLPrefilterContext.cpp b/libs/iblprefilter/src/IBLPrefilterContext.cpp
index fc66e250eb..33705b140d 100644
--- a/libs/iblprefilter/src/IBLPrefilterContext.cpp
+++ b/libs/iblprefilter/src/IBLPrefilterContext.cpp
@@ -19,6 +19,7 @@
#include
#include
#include
+#include
#include
#include
#include
@@ -29,12 +30,22 @@
#include
#include
-#include
+#include
+
+#include
#include
+#include
#include
-#include