Compare commits

..
Author SHA1 Message Date
Powei Feng c3fa1c7035 renderdiff: Use single lib for osmesa (#8255)
Previously, we linked against libOSMesa through the linker and
then used dlopen to link against libGL. This is not how libOSMesa
is intended to be used.  Instead, we use dlopen on libOSMesa
(via bluegl), which then will map the correct GL methods for us,
and for the OSMesa functions, we also map those functions
from libOSMesa (instead of relying on compile-time linker).
2024-11-07 15:42:44 -08:00
106 changed files with 1621 additions and 3229 deletions
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.56.1'
implementation 'com.google.android.filament:filament-android:1.55.1'
}
```
@@ -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.56.1'
pod 'Filament', '~> 1.55.1'
```
## Documentation
+1 -7
View File
@@ -7,12 +7,6 @@ A new header is inserted each time a *tag* is created.
Instead, if you are authoring a PR for the main branch, add your release note to
[NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md).
## v1.56.1
## v1.56.0
- backend: descriptor layouts distinguish samplers and external samplers (b/376089915) [⚠️ **New Material Version**]
## v1.55.1
@@ -660,7 +654,7 @@ Instead, if you are authoring a PR for the main branch, add your release note to
- engine: Binary size improvements.
- engine: Add basic support for instanced renderables [**NEW API**].
- engine: Fix, first imaged passed to `Stream::SetAcquiredImage` is ignored and leaked.
- engine: Fix, first imaged passsed to `Stream::SetAcquiredImage` is ignored and leaked.
- Vulkan: Robustness improvements.
- Java: Fix, lookAt z axis negated.
- gltfio: Be graceful when model has > 4 weights per vert.
+5 -5
View File
@@ -83,12 +83,12 @@ buildscript {
'minSdk': 21,
'targetSdk': 34,
'compileSdk': 34,
'kotlin': '2.0.21',
'kotlin_coroutines': '1.9.0',
'buildTools': '35.0.0',
'kotlin': '2.0.0',
'kotlin_coroutines': '1.9.0-RC',
'buildTools': '34.0.0',
'ndk': '27.0.11718014',
'androidx_core': '1.13.1',
'androidx_annotations': '1.9.0'
'androidx_annotations': '1.8.0'
]
ext.deps = [
@@ -104,7 +104,7 @@ buildscript {
]
dependencies {
classpath 'com.android.tools.build:gradle:8.6.1'
classpath 'com.android.tools.build:gradle:8.4.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
}
@@ -499,37 +499,6 @@ Java_com_google_android_filament_Engine_nGetActiveFeatureLevel(JNIEnv *, jclass,
return (jint)engine->getActiveFeatureLevel();
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nHasFeatureFlag(JNIEnv *env, jclass clazz,
jlong nativeEngine, jstring name_) {
Engine* engine = (Engine*) nativeEngine;
const char *name = env->GetStringUTFChars(name_, 0);
std::optional<bool> result = engine->getFeatureFlag(name);
env->ReleaseStringUTFChars(name_, name);
return result.has_value();
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nSetFeatureFlag(JNIEnv *env, jclass clazz,
jlong nativeEngine, jstring name_, jboolean value) {
Engine* engine = (Engine*) nativeEngine;
const char *name = env->GetStringUTFChars(name_, 0);
jboolean result = engine->setFeatureFlag(name, (bool)value);
env->ReleaseStringUTFChars(name_, name);
return result;
}
extern "C"
JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_Engine_nGetFeatureFlag(JNIEnv *env, jclass clazz,
jlong nativeEngine, jstring name_) {
Engine* engine = (Engine*) nativeEngine;
const char *name = env->GetStringUTFChars(name_, 0);
std::optional<bool> result = engine->getFeatureFlag(name);
env->ReleaseStringUTFChars(name_, name);
return result.value_or(false); // we should never fail here
}
extern "C" JNIEXPORT jlong JNICALL Java_com_google_android_filament_Engine_nCreateBuilder(JNIEnv*,
jclass) {
Engine::Builder* builder = new Engine::Builder{};
@@ -596,16 +565,6 @@ extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBu
builder->paused((bool) paused);
}
extern "C"
JNIEXPORT void JNICALL
Java_com_google_android_filament_Engine_nSetBuilderFeature(JNIEnv *env, jclass clazz,
jlong nativeBuilder, jstring name_, jboolean value) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
const char *name = env->GetStringUTFChars(name_, 0);
builder->feature(name, (bool)value);
env->ReleaseStringUTFChars(name_, name);
}
extern "C" JNIEXPORT jlong JNICALL
Java_com_google_android_filament_Engine_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder) {
Engine::Builder* builder = (Engine::Builder*) nativeBuilder;
@@ -222,20 +222,6 @@ Java_com_google_android_filament_View_nIsFrontFaceWindingInverted(JNIEnv*,
return static_cast<jboolean>(view->isFrontFaceWindingInverted());
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nSetTransparentPickingEnabled(JNIEnv*,
jclass, jlong nativeView, jboolean enabled) {
View* view = (View*) nativeView;
view->setTransparentPickingEnabled(enabled);
}
extern "C" JNIEXPORT jboolean JNICALL
Java_com_google_android_filament_View_nIsTransparentPickingEnabled(JNIEnv*,
jclass, jlong nativeView) {
View* view = (View*) nativeView;
return static_cast<jboolean>(view->isTransparentPickingEnabled());
}
extern "C" JNIEXPORT void JNICALL
Java_com_google_android_filament_View_nSetAmbientOcclusion(JNIEnv*, jclass, jlong nativeView, jint ordinal) {
View* view = (View*) nativeView;
@@ -258,17 +258,6 @@ public class Engine {
return this;
}
/**
* Set a feature flag value. This is the only way to set constant feature flags.
* @param name feature name
* @param value true to enable, false to disable
* @return A reference to this Builder for chaining calls.
*/
public Builder feature(@NonNull String name, boolean value) {
nSetBuilderFeature(mNativeBuilder, name, value);
return this;
}
/**
* Creates an instance of Engine
*
@@ -404,7 +393,6 @@ public class Engine {
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL backend.
* @Deprecated use "backend.disable_parallel_shader_compile" feature flag instead
*/
public boolean disableParallelShaderCompile = false;
@@ -445,7 +433,6 @@ public class Engine {
/**
* Disable backend handles use-after-free checks.
* @Deprecated use "backend.disable_handle_use_after_free_check" feature flag instead
*/
public boolean disableHandleUseAfterFreeCheck = false;
@@ -482,7 +469,6 @@ public class Engine {
* Assert the native window associated to a SwapChain is valid when calling makeCurrent().
* This is only supported for:
* - PlatformEGLAndroid
* @Deprecated use "backend.opengl.assert_native_window_is_valid" feature flag instead
*/
public boolean assertNativeWindowIsValid = false;
}
@@ -707,11 +693,11 @@ public class Engine {
/**
* Returns the maximum number of stereoscopic eyes supported by Filament. The actual number of
* eyes rendered is set at Engine creation time with the {@link Config#stereoscopicEyeCount}
* setting.
* eyes rendered is set at Engine creation time with the {@link
* Engine#Config#stereoscopicEyeCount} setting.
*
* @return the max number of stereoscopic eyes supported
* @see Config#stereoscopicEyeCount
* @see Engine#Config#stereoscopicEyeCount
*/
public long getMaxStereoscopicEyes() {
return nGetMaxStereoscopicEyes(getNativeObject());
@@ -908,8 +894,7 @@ public class Engine {
/**
* Returns whether the object is valid.
* @param ma Material
* @param mi MaterialInstance to check for validity
* @param object Object to check for validity
* @return returns true if the specified object is valid.
*/
public boolean isValidMaterialInstance(@NonNull Material ma, MaterialInstance mi) {
@@ -1331,39 +1316,6 @@ public class Engine {
*/
public static native long getSteadyClockTimeNano();
/**
* Checks if a feature flag exists
* @param name name of the feature flag to check
* @return true if it exists false otherwise
*/
public boolean hasFeatureFlag(@NonNull String name) {
return nHasFeatureFlag(mNativeObject, name);
}
/**
* Set the value of a non-constant feature flag.
* @param name name of the feature flag to set
* @param value value to set
* @return true if the value was set, false if the feature flag is constant or doesn't exist.
*/
public boolean setFeatureFlag(@NonNull String name, boolean value) {
return nSetFeatureFlag(mNativeObject, name, value);
}
/**
* Retrieves the value of any feature flag.
* @param name name of the feature flag
* @return the value of the flag if it exists
* @exception IllegalArgumentException is thrown if the feature flag doesn't exist
*/
public boolean getFeatureFlag(@NonNull String name) {
if (!hasFeatureFlag(name)) {
throw new IllegalArgumentException("The feature flag \"" + name + "\" doesn't exist");
}
return nGetFeatureFlag(mNativeObject, name);
}
@UsedByReflection("TextureHelper.java")
public long getNativeObject() {
if (mNativeObject == 0) {
@@ -1453,9 +1405,6 @@ public class Engine {
private static native int nGetSupportedFeatureLevel(long nativeEngine);
private static native int nSetActiveFeatureLevel(long nativeEngine, int ordinal);
private static native int nGetActiveFeatureLevel(long nativeEngine);
private static native boolean nHasFeatureFlag(long nativeEngine, String name);
private static native boolean nSetFeatureFlag(long nativeEngine, String name, boolean value);
private static native boolean nGetFeatureFlag(long nativeEngine, String name);
private static native long nCreateBuilder();
private static native void nDestroyBuilder(long nativeBuilder);
@@ -1471,6 +1420,5 @@ public class Engine {
private static native void nSetBuilderFeatureLevel(long nativeBuilder, int ordinal);
private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext);
private static native void nSetBuilderPaused(long nativeBuilder, boolean paused);
private static native void nSetBuilderFeature(long nativeBuilder, String name, boolean value);
private static native long nBuilderBuild(long nativeBuilder);
}
@@ -745,33 +745,6 @@ public class View {
nSetFrontFaceWindingInverted(getNativeObject(), inverted);
}
/**
* Returns true if transparent picking is enabled.
*
* @see #setTransparentPickingEnabled
*/
public boolean isTransparentPickingEnabled() {
return nIsTransparentPickingEnabled(getNativeObject());
}
/**
* Enables or disables transparent picking. Disabled by default.
*
* When transparent picking is enabled, View::pick() will pick from both
* transparent and opaque renderables. When disabled, View::pick() will only
* pick from opaque renderables.
*
* <p>
* Transparent picking will create an extra pass for rendering depth
* from both transparent and opaque renderables.
* </p>
*
* @param enabled true enables transparent picking, false disables it.
*/
public void setTransparentPickingEnabled(boolean enabled) {
nSetTransparentPickingEnabled(getNativeObject(), enabled);
}
/**
* Sets options relative to dynamic lighting for this view.
*
@@ -1308,8 +1281,6 @@ public class View {
private static native boolean nIsPostProcessingEnabled(long nativeView);
private static native void nSetFrontFaceWindingInverted(long nativeView, boolean inverted);
private static native boolean nIsFrontFaceWindingInverted(long nativeView);
private static native void nSetTransparentPickingEnabled(long nativeView, boolean enabled);
private static native boolean nIsTransparentPickingEnabled(long nativeView);
private static native void nSetAmbientOcclusion(long nativeView, int ordinal);
private static native int nGetAmbientOcclusion(long nativeView);
private static native void nSetAmbientOcclusionOptions(long nativeView, float radius, float bias, float power, float resolution, float intensity, float bilateralThreshold, int quality, int lowPassFilter, int upsampling, boolean enabled, boolean bentNormals, float minHorizonAngleRad);
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.56.1
VERSION_NAME=1.55.1
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
+1 -1
View File
@@ -1,6 +1,6 @@
#Wed Nov 17 10:40:18 PST 2021
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
@@ -6,10 +6,6 @@ plugins {
project.ext.isSample = true
kotlin {
jvmToolchain(versions.jdk)
}
filamentTools {
cmgenArgs = "-q --format=ktx --size=256 --extract-blur=0.1 --deploy=src/main/assets/envs/default_env"
iblInputFile = project.layout.projectDirectory.file("../../../third_party/environments/lightroom_14b.hdr")
@@ -17,7 +13,7 @@ filamentTools {
}
// don't forget to update MainACtivity.kt when/if changing this.
tasks.register('copyMesh', Copy) {
task copyMesh(type: Copy) {
from "../../../third_party/models/BusterDrone"
into "src/main/assets/models"
}
@@ -112,13 +112,7 @@ class MainActivity : Activity() {
}
private fun setupFilament() {
val config = Engine.Config()
//config.forceGLES2Context = true
engine = Engine.Builder()
.config(config)
.featureLevel(Engine.FeatureLevel.FEATURE_LEVEL_0)
.build()
engine = Engine.Builder().featureLevel(Engine.FeatureLevel.FEATURE_LEVEL_0).build()
renderer = engine.createRenderer()
scene = engine.createScene()
view = engine.createView()
@@ -129,9 +123,7 @@ class MainActivity : Activity() {
scene.skybox = Skybox.Builder().color(0.035f, 0.035f, 0.035f, 1.0f).build(engine)
// post-processing is not supported at feature level 0
if (engine.activeFeatureLevel == Engine.FeatureLevel.FEATURE_LEVEL_0) {
view.isPostProcessingEnabled = false
}
view.isPostProcessingEnabled = false
// Tell the view which camera we want to use
view.camera = camera
File diff suppressed because one or more lines are too long
Binary file not shown.
-7
View File
@@ -607,13 +607,6 @@ else()
-Wover-aligned
-Werror
)
if (CMAKE_CXX_STANDARD EQUAL 20)
# The lambdas for passes in PostProcessManager.cpp capture this
# implicitly in a way that's deprecated in c++20, but can't easily be
# fixed in a way that's backwards compatible with c++17:
# https://www.nextptr.com/tutorial/ta1430524603/capture-this-in-lambda-expression-timeline-of-change
list(APPEND FILAMENT_WARNINGS -Wno-deprecated-this-capture)
endif()
endif()
target_compile_options(${TARGET} PRIVATE
-14
View File
@@ -179,18 +179,11 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/caching/VulkanDescriptorSetManager.h
src/vulkan/caching/VulkanPipelineLayoutCache.cpp
src/vulkan/caching/VulkanPipelineLayoutCache.h
src/vulkan/memory/ResourceManager.cpp
src/vulkan/memory/ResourceManager.h
src/vulkan/memory/ResourcePointer.h
src/vulkan/memory/Resource.cpp
src/vulkan/memory/Resource.h
src/vulkan/platform/VulkanPlatform.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.cpp
src/vulkan/platform/VulkanPlatformSwapChainImpl.h
src/vulkan/spirv/VulkanSpirvUtils.cpp
src/vulkan/spirv/VulkanSpirvUtils.h
src/vulkan/VulkanAsyncHandles.cpp
src/vulkan/VulkanAsyncHandles.h
src/vulkan/VulkanBlitter.cpp
src/vulkan/VulkanBlitter.h
src/vulkan/VulkanBuffer.cpp
@@ -378,10 +371,6 @@ set(LINUX_LINKER_OPTIMIZATION_FLAGS
if (LINUX AND FILAMENT_SUPPORTS_OSMESA)
set(OSMESA_COMPILE_FLAGS
-I${FILAMENT_OSMESA_PATH}/include/GL)
set(OSMESA_LINKER_FLAGS
-Wl,-L${FILAMENT_OSMESA_PATH}/lib/x86_64-linux-gnu/
-lOSMesa
)
endif()
if (MSVC)
@@ -395,9 +384,6 @@ else()
-Wover-aligned
-Werror
)
if (CMAKE_CXX_STANDARD EQUAL 20)
list(APPEND FILAMENT_WARNINGS -Wno-deprecated-this-capture)
endif()
endif()
if (APPLE)
+1 -13
View File
@@ -227,7 +227,6 @@ enum class DescriptorType : uint8_t {
SHADER_STORAGE_BUFFER,
SAMPLER,
INPUT_ATTACHMENT,
SAMPLER_EXTERNAL
};
enum class DescriptorFlags : uint8_t {
@@ -319,20 +318,9 @@ struct Viewport {
int32_t right() const noexcept { return left + int32_t(width); }
//! get the top coordinate in window space of the viewport
int32_t top() const noexcept { return bottom + int32_t(height); }
friend bool operator==(Viewport const& lhs, Viewport const& rhs) noexcept {
// clang can do this branchless with xor/or
return lhs.left == rhs.left && lhs.bottom == rhs.bottom &&
lhs.width == rhs.width && lhs.height == rhs.height;
}
friend bool operator!=(Viewport const& lhs, Viewport const& rhs) noexcept {
// clang is being dumb and uses branches
return bool(((lhs.left ^ rhs.left) | (lhs.bottom ^ rhs.bottom)) |
((lhs.width ^ rhs.width) | (lhs.height ^ rhs.height)));
}
};
/**
* Specifies the mapping of the near and far clipping plane to window coordinates.
*/
@@ -21,7 +21,7 @@
#include "bluegl/BlueGL.h"
#include "osmesa.h"
#include <osmesa.h>
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h>
@@ -56,6 +56,7 @@ protected:
private:
OSMesaContext mContext;
void* mOsMesaApi = nullptr;
};
} // namespace filament::backend
@@ -88,7 +88,6 @@ public:
VkFormat colorFormat = VK_FORMAT_UNDEFINED;
VkFormat depthFormat = VK_FORMAT_UNDEFINED;
VkExtent2D extent = {0, 0};
bool isProtected = false;
};
struct ImageSyncData {
@@ -200,13 +199,6 @@ public:
*/
virtual bool hasResized(SwapChainPtr handle);
/**
* Check if the surface is protected.
* @param handle The handle returned by createSwapChain()
* @return Whether the swapchain is protected
*/
virtual bool isProtected(SwapChainPtr handle);
/**
* Carry out a recreation of the swapchain.
* @param handle The handle returned by createSwapChain()
@@ -275,23 +267,6 @@ public:
*/
VkQueue getGraphicsQueue() const noexcept;
/**
* @return The family index of the protected graphics queue selected for the
* Vulkan backend.
*/
uint32_t getProtectedGraphicsQueueFamilyIndex() const noexcept;
/**
* @return The index of the protected graphics queue (if there are multiple
* graphics queues) selected for the Vulkan backend.
*/
uint32_t getProtectedGraphicsQueueIndex() const noexcept;
/**
* @return The protected queue that was selected for the Vulkan backend.
*/
VkQueue getProtectedGraphicsQueue() const noexcept;
private:
static ExtensionSet getSwapchainInstanceExtensions();
-28
View File
@@ -1,28 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_SYSTRACEPROFILE_H
#define TNT_FILAMENT_BACKEND_SYSTRACEPROFILE_H
#include <utils/Systrace.h>
#define PROFILE_SCOPE(marker) SYSTRACE_NAME(marker)
#define PROFILE_NAME_BEGINFRAME "backend::beginFrame"
#define PROFILE_NAME_ENDFRAME "backend::endFrame"
#endif // TNT_FILAMENT_BACKEND_SYSTRACEPROFILE_H
@@ -161,7 +161,6 @@ struct MetalContext {
CullModeStateTracker cullModeState;
WindingStateTracker windingState;
DepthClampStateTracker depthClampState;
ScissorRectStateTracker scissorRectState;
Handle<HwRenderPrimitive> currentRenderPrimitive;
// State caches.
+15 -49
View File
@@ -623,8 +623,6 @@ const char* toString(DescriptorType type) {
return "SAMPLER";
case DescriptorType::INPUT_ATTACHMENT:
return "INPUT_ATTACHMENT";
case DescriptorType::SAMPLER_EXTERNAL:
return "SAMPLER_EXTERNAL";
}
}
@@ -821,24 +819,17 @@ void MetalDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
}
void MetalDriver::destroySwapChain(Handle<HwSwapChain> sch) {
if (UTILS_UNLIKELY(!sch)) {
return;
}
auto* swapChain = handle_cast<MetalSwapChain>(sch);
if (mContext->currentDrawSwapChain == swapChain) {
mContext->currentDrawSwapChain = nullptr;
}
if (mContext->currentReadSwapChain == swapChain) {
mContext->currentReadSwapChain = nullptr;
}
// If the SwapChain is a pixel buffer, we need to wait for the current command buffer to
// complete before destroying it. This is because pixel buffer SwapChains hold a
// MetalExternalImage that could still being rendered into.
if (UTILS_UNLIKELY(swapChain->isPixelBuffer())) {
executeAfterCurrentCommandBufferCompletes(
[this, sch]() mutable { destruct_handle<MetalSwapChain>(sch); });
} else {
destruct_handle<MetalSwapChain>(sch);
if (sch) {
auto* swapChain = handle_cast<MetalSwapChain>(sch);
// If the SwapChain is a pixel buffer, we need to wait for the current command buffer to
// complete before destroying it. This is because pixel buffer SwapChains hold a
// MetalExternalImage that could still being rendered into.
if (UTILS_UNLIKELY(swapChain->isPixelBuffer())) {
executeAfterCurrentCommandBufferCompletes(
[this, sch]() mutable { destruct_handle<MetalSwapChain>(sch); });
} else {
destruct_handle<MetalSwapChain>(sch);
}
}
}
@@ -861,20 +852,10 @@ void MetalDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> dslh)
void MetalDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
DEBUG_LOG("destroyDescriptorSet(dsh = %d)\n", dsh.getId());
if (!dsh) {
return;
if (dsh) {
executeAfterCurrentCommandBufferCompletes(
[this, dsh]() mutable { destruct_handle<MetalDescriptorSet>(dsh); });
}
// Unbind this descriptor set.
auto* descriptorSet = handle_cast<MetalDescriptorSet>(dsh);
for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) {
if (UTILS_UNLIKELY(mContext->currentDescriptorSets[i] == descriptorSet)) {
mContext->currentDescriptorSets[i] = nullptr;
}
}
executeAfterCurrentCommandBufferCompletes(
[this, dsh]() mutable { destruct_handle<MetalDescriptorSet>(dsh); });
}
void MetalDriver::terminate() {
@@ -1246,7 +1227,6 @@ void MetalDriver::beginRenderPass(Handle<HwRenderTarget> rth,
mContext->depthStencilState.invalidate();
mContext->cullModeState.invalidate();
mContext->windingState.invalidate();
mContext->scissorRectState.invalidate();
mContext->currentPolygonOffset = {0.0f, 0.0f};
mContext->finalizedDescriptorSets.clear();
@@ -1813,16 +1793,6 @@ void MetalDriver::bindDescriptorSet(
backend::DescriptorSetHandle dsh,
backend::descriptor_set_t set,
backend::DescriptorSetOffsetArray&& offsets) {
if (UTILS_UNLIKELY(!dsh)) {
DEBUG_LOG("bindDescriptorSet(dsh = null, set = %d, offsets = [])\n", set);
mContext->currentDescriptorSets[set] = nullptr;
mContext->vertexDescriptorBindings.setBuffer(nil, 0, set);
mContext->fragmentDescriptorBindings.setBuffer(nil, 0, set);
mContext->dynamicOffsets.setOffsets(set, nullptr, 0);
return;
}
auto descriptorSet = handle_cast<MetalDescriptorSet>(dsh);
const size_t dynamicBindings = descriptorSet->layout->getDynamicOffsetCount();
utils::FixedCapacityVector<size_t> offsetsVector(dynamicBindings, 0);
@@ -1983,11 +1953,7 @@ void MetalDriver::scissor(Viewport scissorBox) {
.height = static_cast<NSUInteger>(bottom - top)
};
auto& srs = mContext->scissorRectState;
srs.updateState(scissorRect);
if (srs.stateChanged()) {
[mContext->currentRenderPassEncoder setScissorRect:scissorRect];
}
[mContext->currentRenderPassEncoder setScissorRect:scissorRect];
}
void MetalDriver::beginTimerQuery(Handle<HwTimerQuery> tqh) {
+1 -2
View File
@@ -71,8 +71,7 @@ constexpr inline MTLIndexType getIndexType(size_t elementSize) noexcept {
} else if (elementSize == 4) {
return MTLIndexTypeUInt32;
}
assert_invariant(false);
return MTLIndexTypeUInt16;
FILAMENT_CHECK_POSTCONDITION(false) << "Index element size not supported.";
}
constexpr inline MTLVertexFormat getMetalFormat(ElementType type, bool normalized) noexcept {
+2 -4
View File
@@ -1290,8 +1290,7 @@ id<MTLArgumentEncoder> MetalDescriptorSetLayout::getArgumentEncoderSlow(id<MTLDe
[arguments addObject:bufferArgument];
break;
}
case DescriptorType::SAMPLER:
case DescriptorType::SAMPLER_EXTERNAL: {
case DescriptorType::SAMPLER: {
MTLArgumentDescriptor* textureArgument = [MTLArgumentDescriptor argumentDescriptor];
textureArgument.index = binding.binding * 2;
textureArgument.dataType = MTLDataTypeTexture;
@@ -1406,8 +1405,7 @@ id<MTLBuffer> MetalDescriptorSet::finalizeAndGetBuffer(MetalDriver* driver, Shad
atIndex:binding.binding * 2];
break;
}
case DescriptorType::SAMPLER:
case DescriptorType::SAMPLER_EXTERNAL: {
case DescriptorType::SAMPLER: {
auto found = textures.find(binding.binding);
if (found == textures.end()) {
[encoder setTexture:driver->mContext->emptyTexture atIndex:binding.binding * 2];
+4 -12
View File
@@ -204,8 +204,9 @@ private:
// Different kinds of state, like pipeline state, uniform buffer state, etc., are passed to the
// current Metal command encoder and persist throughout the lifetime of the encoder (a frame).
// StateTracker is used to prevent calling redundant state change methods.
template <typename StateType, typename StateEqual = std::equal_to<StateType>>
template<typename StateType>
class StateTracker {
public:
// Call to force the state to dirty at the beginning of each frame, as all state must be
@@ -213,7 +214,7 @@ public:
void invalidate() noexcept { mStateDirty = true; }
void updateState(const StateType& newState) noexcept {
if (!StateEqual()(mCurrentState, newState)) {
if (mCurrentState != newState) {
mCurrentState = newState;
mStateDirty = true;
}
@@ -234,6 +235,7 @@ private:
bool mStateDirty = true;
StateType mCurrentState = {};
};
// Pipeline state
@@ -341,16 +343,6 @@ using DepthStencilStateTracker = StateTracker<DepthStencilState>;
using DepthStencilStateCache = StateCache<DepthStencilState, id<MTLDepthStencilState>,
DepthStateCreator>;
struct MtlScissorRectEqual {
bool operator()(const MTLScissorRect& lhs, const MTLScissorRect& rhs) const {
return lhs.height == rhs.height &&
lhs.width == rhs.width &&
lhs.x == rhs.x &&
lhs.y == rhs.y;
}
};
using ScissorRectStateTracker = StateTracker<MTLScissorRect, MtlScissorRectEqual>;
// Uniform buffers
class MetalBufferObject;
+1 -3
View File
@@ -69,9 +69,7 @@ public:
assert_invariant(set < MAX_DESCRIPTOR_SET_COUNT);
assert_invariant(binding < MAX_DESCRIPTOR_COUNT);
assert_invariant(entry.binding < 128); // we reserve 1 bit for the type right now
mStorage[set][binding] = { (uint8_t)entry.binding,
entry.type == DescriptorType::SAMPLER ||
entry.type == DescriptorType::SAMPLER_EXTERNAL };
mStorage[set][binding] = { (uint8_t)entry.binding, entry.type == DescriptorType::SAMPLER };
mActiveDescriptors[set].set(binding);
}
@@ -96,7 +96,6 @@ GLDescriptorSet::GLDescriptorSet(OpenGLContext& gl, DescriptorSetLayoutHandle ds
break;
}
case DescriptorType::SAMPLER:
case DescriptorType::SAMPLER_EXTERNAL:
if (UTILS_UNLIKELY(gl.isES2())) {
desc.emplace<SamplerGLES2>();
} else {
+10 -8
View File
@@ -614,7 +614,7 @@ FeatureLevel OpenGLContext::resolveFeatureLevel(GLint major, GLint minor,
featureLevel = FeatureLevel::FEATURE_LEVEL_2;
if (gets.max_texture_image_units >= MAX_FRAGMENT_SAMPLER_COUNT &&
gets.max_combined_texture_image_units >=
(MAX_FRAGMENT_SAMPLER_COUNT + MAX_VERTEX_SAMPLER_COUNT)) {
(MAX_FRAGMENT_SAMPLER_COUNT + MAX_VERTEX_SAMPLER_COUNT)) {
featureLevel = FeatureLevel::FEATURE_LEVEL_3;
}
}
@@ -623,13 +623,15 @@ FeatureLevel OpenGLContext::resolveFeatureLevel(GLint major, GLint minor,
# ifndef IOS // IOS is guaranteed to have ES3.x
else if (UTILS_UNLIKELY(major == 2)) {
// Runtime OpenGL version is ES 2.x
// note: mandatory extensions (all supported by Mali-400 and Adreno 304)
// OES_depth_texture
// OES_depth24
// OES_packed_depth_stencil
// OES_rgb8_rgba8
// OES_standard_derivatives
// OES_texture_npot
# if defined(BACKEND_OPENGL_LEVEL_GLES30)
// mandatory extensions (all supported by Mali-400 and Adreno 304)
assert_invariant(exts.OES_depth_texture);
assert_invariant(exts.OES_depth24);
assert_invariant(exts.OES_packed_depth_stencil);
assert_invariant(exts.OES_rgb8_rgba8);
assert_invariant(exts.OES_standard_derivatives);
assert_invariant(exts.OES_texture_npot);
# endif
featureLevel = FeatureLevel::FEATURE_LEVEL_0;
}
# endif // IOS
+25 -42
View File
@@ -23,7 +23,6 @@
#include "OpenGLDriverFactory.h"
#include "OpenGLProgram.h"
#include "OpenGLTimerQuery.h"
#include "SystraceProfile.h"
#include "gl_headers.h"
#include <backend/platforms/OpenGLPlatform.h>
@@ -92,35 +91,24 @@
#define DEBUG_GROUP_MARKER_NONE 0x00 // no debug marker
#define DEBUG_GROUP_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
#define DEBUG_GROUP_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
#define DEBUG_GROUP_MARKER_ALL 0xFF // all markers
#define DEBUG_GROUP_MARKER_ALL 0x03 // all markers
#define DEBUG_MARKER_NONE 0x00 // no debug marker
#define DEBUG_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
#define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
#define DEBUG_MARKER_PROFILE 0x04 // profiling on the backend side (systrace)
#define DEBUG_MARKER_ALL (0xFF & ~DEBUG_MARKER_PROFILE) // all markers
#define DEBUG_MARKER_ALL 0x03 // all markers
// set to the desired debug marker level (for user markers [default: All])
#define DEBUG_GROUP_MARKER_LEVEL DEBUG_GROUP_MARKER_ALL
// set to the desired debug level (for internal debugging [Default: None])
#define DEBUG_MARKER_LEVEL DEBUG_MARKER_NONE
#define DEBUG_MARKER_LEVEL DEBUG_MARKER_NONE
#if DEBUG_MARKER_LEVEL == DEBUG_MARKER_PROFILE
# define DEBUG_MARKER()
# define PROFILE_MARKER(marker) PROFILE_SCOPE(marker);
# if DEBUG_GROUP_MARKER_LEVEL != DEBUG_GROUP_MARKER_NONE
# error PROFILING is exclusive; group markers must be disabled.
# endif
#elif DEBUG_MARKER_LEVEL > DEBUG_MARKER_NONE
# define DEBUG_MARKER() DebugMarker _debug_marker(*this, __func__);
# define PROFILE_MARKER(marker) DEBUG_MARKER()
# if DEBUG_MARKER_LEVEL & DEBUG_MARKER_PROFILE
# error PROFILING is exclusive; all other debug features must be disabled.
# endif
#if DEBUG_MARKER_LEVEL > DEBUG_MARKER_NONE
# define DEBUG_MARKER() \
DebugMarker _debug_marker(*this, __func__);
#else
# define DEBUG_MARKER()
# define PROFILE_MARKER(marker)
#endif
using namespace filament::math;
@@ -394,21 +382,24 @@ void OpenGLDriver::bindTexture(GLuint unit, GLTexture const* t) noexcept {
}
bool OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
bool success = true;
if (mBoundProgram != p) {
// compile/link the program if needed and call glUseProgram
success = p->use(this, mContext);
assert_invariant(success == p->isValid());
if (success) {
// TODO: we could even improve this if the program could tell us which of the descriptors
// bindings actually changed. In practice, it is likely that set 0 or 1 might not
// change often.
decltype(mInvalidDescriptorSetBindings) changed;
changed.setValue((1 << MAX_DESCRIPTOR_SET_COUNT) - 1);
mInvalidDescriptorSetBindings |= changed;
if (UTILS_UNLIKELY(mBoundProgram == p)) {
// program didn't change, don't do anything.
return true;
}
mBoundProgram = p;
}
// compile/link the program if needed and call glUseProgram
bool const success = p->use(this, mContext);
assert_invariant(success == p->isValid());
if (success) {
// TODO: we could even improve this if the program could tell us which of the descriptors
// bindings actually changed. In practice, it is likely that set 0 or 1 might not
// change often.
decltype(mInvalidDescriptorSetBindings) changed;
changed.setValue((1 << MAX_DESCRIPTOR_SET_COUNT) - 1);
mInvalidDescriptorSetBindings |= changed;
mBoundProgram = p;
}
if (UTILS_UNLIKELY(mContext.isES2() && success)) {
@@ -3457,7 +3448,7 @@ void OpenGLDriver::beginFrame(
UTILS_UNUSED int64_t monotonic_clock_ns,
UTILS_UNUSED int64_t refreshIntervalNs,
UTILS_UNUSED uint32_t frameId) {
PROFILE_MARKER(PROFILE_NAME_BEGINFRAME)
DEBUG_MARKER()
auto& gl = mContext;
insertEventMarker("beginFrame");
mPlatform.beginFrame(monotonic_clock_ns, refreshIntervalNs, frameId);
@@ -3492,7 +3483,7 @@ void OpenGLDriver::setPresentationTime(int64_t monotonic_clock_ns) {
}
void OpenGLDriver::endFrame(UTILS_UNUSED uint32_t frameId) {
PROFILE_MARKER(PROFILE_NAME_ENDFRAME)
DEBUG_MARKER()
#if defined(__EMSCRIPTEN__)
// WebGL builds are single-threaded so users might manipulate various GL state after we're
// done with the frame. We do NOT officially support using Filament in this way, but we can
@@ -3884,14 +3875,6 @@ void OpenGLDriver::bindDescriptorSet(
backend::DescriptorSetHandle dsh,
backend::descriptor_set_t set,
backend::DescriptorSetOffsetArray&& offsets) {
if (UTILS_UNLIKELY(!dsh)) {
mBoundDescriptorSets[set].dsh = dsh;
mInvalidDescriptorSetBindings.set(set, true);
mInvalidDescriptorSetBindingOffsets.set(set, true);
return;
}
// handle_cast<> here also serves to validate the handle (it actually cannot return nullptr)
GLDescriptorSet const* const ds = handle_cast<GLDescriptorSet*>(dsh);
if (ds) {
@@ -174,8 +174,7 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
}
break;
}
case DescriptorType::SAMPLER:
case DescriptorType::SAMPLER_EXTERNAL: {
case DescriptorType::SAMPLER: {
if (!entry.name.empty()) {
GLint const loc = glGetUniformLocation(program, entry.name.c_str());
if (loc >= 0) {
@@ -255,9 +254,7 @@ void OpenGLProgram::updateUniforms(
for (size_t i = 0, c = records.uniforms.size(); i < c; i++) {
Program::Uniform const& u = records.uniforms[i];
GLint const loc = records.locations[i];
// mRec709Location is special, it is handled by setRec709ColorSpace() and the corresponding
// entry in `buffer` is typically not initialized, so we skip it.
if (loc < 0 || loc == mRec709Location) {
if (loc < 0) {
continue;
}
// u.offset is in 'uint32_t' units
@@ -19,6 +19,7 @@
#include <utils/Log.h>
#include <utils/Panic.h>
#include <dlfcn.h>
#include <memory>
namespace filament::backend {
@@ -41,13 +42,56 @@ struct OSMesaSwapchain {
std::unique_ptr<uint8_t[]> buffer;
};
} // anonymous namespace
struct OSMesaAPI {
private:
using CreateContextFunc = OSMesaContext (*)(GLenum format, OSMesaContext);
using DestroyContextFunc = GLboolean (*)(OSMesaContext);
using MakeCurrentFunc = GLboolean (*)(OSMesaContext ctx, void* buffer, GLenum type,
GLsizei width, GLsizei height);
using GetProcAddressFunc = OSMESAproc (*)(const char* funcName);
public:
CreateContextFunc OSMesaCreateContext;
DestroyContextFunc OSMesaDestroyContext;
MakeCurrentFunc OSMesaMakeCurrent;
GetProcAddressFunc OSMesaGetProcAddress;
OSMesaAPI() {
constexpr char const* libraryNames[] = {"libOSMesa.so", "libosmesa.so"};
for (char const* libName: libraryNames) {
mLib = dlopen(libName, RTLD_GLOBAL | RTLD_NOW);
if (mLib) {
break;
}
}
FILAMENT_CHECK_PRECONDITION(mLib)
<< "Unable to dlopen libOSMesa to create a software GL context";
OSMesaGetProcAddress = (GetProcAddressFunc) dlsym(mLib, "OSMesaGetProcAddress");
OSMesaCreateContext = (CreateContextFunc) OSMesaGetProcAddress("OSMesaCreateContext");
OSMesaDestroyContext =
(DestroyContextFunc) OSMesaGetProcAddress("OSMesaDestroyContext");
OSMesaMakeCurrent = (MakeCurrentFunc) OSMesaGetProcAddress("OSMesaMakeCurrent");
}
~OSMesaAPI() {
dlclose(mLib);
}
private:
void* mLib = nullptr;
};
}// anonymous namespace
Driver* PlatformOSMesa::createDriver(void* const sharedGLContext,
const DriverConfig& driverConfig) noexcept {
OSMesaAPI* api = new OSMesaAPI();
mOsMesaApi = api;
FILAMENT_CHECK_PRECONDITION(sharedGLContext == nullptr)
<< "shared GL context is not supported with PlatformOSMesa";
mContext = OSMesaCreateContext(GL_RGBA, NULL);
mContext = api->OSMesaCreateContext(GL_RGBA, NULL);
// We need to do a no-op makecurrent here so that the context will be in a correct state before
// any GL calls.
@@ -62,7 +106,11 @@ Driver* PlatformOSMesa::createDriver(void* const sharedGLContext,
}
void PlatformOSMesa::terminate() noexcept {
OSMesaDestroyContext(mContext);
OSMesaAPI* api = (OSMesaAPI*) mOsMesaApi;
api->OSMesaDestroyContext(mContext);
delete api;
mOsMesaApi = nullptr;
bluegl::unbind();
}
@@ -84,11 +132,12 @@ void PlatformOSMesa::destroySwapChain(Platform::SwapChain* swapChain) noexcept {
bool PlatformOSMesa::makeCurrent(ContextType type, SwapChain* drawSwapChain,
SwapChain* readSwapChain) noexcept {
OSMesaAPI* api = (OSMesaAPI*) mOsMesaApi;
OSMesaSwapchain* impl = (OSMesaSwapchain*) drawSwapChain;
auto result = OSMesaMakeCurrent(mContext, (BackingType*) impl->buffer.get(), BACKING_GL_TYPE,
impl->width, impl->height);
FILAMENT_CHECK_POSTCONDITION(result) << "OSMesaMakeCurrent failed!";
auto result = api->OSMesaMakeCurrent(mContext, (BackingType*) impl->buffer.get(),
BACKING_GL_TYPE, impl->width, impl->height);
FILAMENT_CHECK_POSTCONDITION(result == GL_TRUE) << "OSMesaMakeCurrent failed!";
return true;
}
@@ -19,8 +19,8 @@
#include <Wingdi.h>
#ifdef _MSC_VER
// this variable is checked in BlueGL.h (included from "gl_headers.h" right after this),
// and prevents duplicate definition of OpenGL apis when building this file.
// this variable is checked in BlueGL.h (included from "gl_headers.h" right after this),
// and prevents duplicate definition of OpenGL apis when building this file.
// However, GL_GLEXT_PROTOTYPES need to be defined in BlueGL.h when included from other files.
#define FILAMENT_PLATFORM_WGL
#endif
@@ -37,8 +37,9 @@
namespace {
void reportWindowsError(DWORD dwError) {
void reportLastWindowsError() {
LPSTR lpMessageBuffer = nullptr;
DWORD dwError = GetLastError();
if (dwError == 0) {
return;
@@ -79,7 +80,6 @@ Driver* PlatformWGL::createDriver(void* const sharedGLContext,
const Platform::DriverConfig& driverConfig) noexcept {
int result = 0;
int pixelFormat = 0;
DWORD dwError = 0;
mPfd = {
sizeof(PIXELFORMATDESCRIPTOR),
@@ -105,7 +105,6 @@ Driver* PlatformWGL::createDriver(void* const sharedGLContext,
mHWnd = CreateWindowA("STATIC", "dummy", 0, 0, 0, 1, 1, NULL, NULL, NULL, NULL);
HDC whdc = mWhdc = GetDC(mHWnd);
if (whdc == NULL) {
dwError = GetLastError();
utils::slog.e << "CreateWindowA() failed" << utils::io::endl;
goto error;
}
@@ -116,7 +115,6 @@ Driver* PlatformWGL::createDriver(void* const sharedGLContext,
// We need a tmp context to retrieve and call wglCreateContextAttribsARB.
tempContext = wglCreateContext(whdc);
if (!wglMakeCurrent(whdc, tempContext)) {
dwError = GetLastError();
utils::slog.e << "wglMakeCurrent() failed, whdc=" << whdc << ", tempContext=" <<
tempContext << utils::io::endl;
goto error;
@@ -138,7 +136,6 @@ Driver* PlatformWGL::createDriver(void* const sharedGLContext,
if (mContext) {
break;
}
dwError = GetLastError();
}
if (!mContext) {
@@ -151,7 +148,6 @@ Driver* PlatformWGL::createDriver(void* const sharedGLContext,
tempContext = NULL;
if (!wglMakeCurrent(whdc, mContext)) {
dwError = GetLastError();
utils::slog.e << "wglMakeCurrent() failed, whdc=" << whdc << ", mContext=" <<
mContext << utils::io::endl;
goto error;
@@ -166,7 +162,7 @@ error:
if (tempContext) {
wglDeleteContext(tempContext);
}
reportWindowsError(dwError);
reportLastWindowsError();
terminate();
return NULL;
}
@@ -209,11 +205,9 @@ Platform::SwapChain* PlatformWGL::createSwapChain(void* nativeWindow, uint64_t f
// on Windows, the nativeWindow maps to a HWND
swapChain->hWnd = (HWND) nativeWindow;
swapChain->hDc = GetDC(swapChain->hWnd);
if (!swapChain->hDc) {
DWORD dwError = GetLastError();
ASSERT_POSTCONDITION_NON_FATAL(swapChain->hDc,
"Unable to create the SwapChain (nativeWindow = %p)", nativeWindow);
reportWindowsError(dwError);
if (!ASSERT_POSTCONDITION_NON_FATAL(swapChain->hDc,
"Unable to create the SwapChain (nativeWindow = %p)", nativeWindow)) {
reportLastWindowsError();
}
// We have to match pixel formats across the HDC and HGLRC (mContext)
@@ -270,10 +264,8 @@ bool PlatformWGL::makeCurrent(ContextType type, SwapChain* drawSwapChain,
HDC hdc = wglSwapChain->hDc;
if (hdc != NULL) {
BOOL success = wglMakeCurrent(hdc, mContext);
if (!success) {
DWORD dwError = GetLastError();
ASSERT_POSTCONDITION_NON_FATAL(success, "wglMakeCurrent() failed. hdc = %p", hdc);
reportWindowsError(dwError);
if (!ASSERT_POSTCONDITION_NON_FATAL(success, "wglMakeCurrent() failed. hdc = %p", hdc)) {
reportLastWindowsError();
wglMakeCurrent(0, NULL);
}
}
@@ -1,44 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VulkanAsyncHandles.h"
namespace filament::backend {
VulkanTimerQuery::VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices)
: VulkanThreadSafeResource(VulkanResourceType::TIMER_QUERY),
mStartingQueryIndex(std::get<0>(indices)),
mStoppingQueryIndex(std::get<1>(indices)) {}
void VulkanTimerQuery::setFence(std::shared_ptr<VulkanCmdFence> fence) noexcept {
std::unique_lock<utils::Mutex> lock(mFenceMutex);
mFence = fence;
}
bool VulkanTimerQuery::isCompleted() noexcept {
std::unique_lock<utils::Mutex> lock(mFenceMutex);
// QueryValue is a synchronous call and might occur before beginTimerQuery has written anything
// into the command buffer, which is an error according to the validation layer that ships in
// the Android NDK. Even when AVAILABILITY_BIT is set, validation seems to require that the
// timestamp has at least been written into a processed command buffer.
// This fence indicates that the corresponding buffer has been completed.
return mFence && mFence->getStatus() == VK_SUCCESS;
}
VulkanTimerQuery::~VulkanTimerQuery() = default;
} // namespace filament::backend
@@ -1,86 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKANASYNCHANDLES_H
#define TNT_FILAMENT_BACKEND_VULKANASYNCHANDLES_H
#include <bluevk/BlueVK.h>
#include "DriverBase.h"
#include "VulkanResources.h"
#include <utils/Mutex.h>
#include <utils/Condition.h>
namespace filament::backend {
// Wrapper to enable use of shared_ptr for implementing shared ownership of low-level Vulkan fences.
struct VulkanCmdFence {
VulkanCmdFence(VkResult initialStatus) {
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence
// gets submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually
// finishes executing the command buffer, the status changes to VK_SUCCESS.
status.store(initialStatus);
}
~VulkanCmdFence() = default;
void setStatus(VkResult value) { status.store(value); }
VkResult getStatus() { return status.load(std::memory_order_acquire); }
private:
std::atomic<VkResult> status;
};
struct VulkanFence : public HwFence, VulkanResource {
VulkanFence() : VulkanResource(VulkanResourceType::FENCE) {}
explicit VulkanFence(std::shared_ptr<VulkanCmdFence> fence)
: VulkanResource(VulkanResourceType::FENCE),
fence(fence) {}
std::shared_ptr<VulkanCmdFence> fence;
};
struct VulkanTimerQuery : public HwTimerQuery, VulkanThreadSafeResource {
explicit VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices);
~VulkanTimerQuery();
void setFence(std::shared_ptr<VulkanCmdFence> fence) noexcept;
bool isCompleted() noexcept;
uint32_t getStartingQueryIndex() const {
return mStartingQueryIndex;
}
uint32_t getStoppingQueryIndex() const {
return mStoppingQueryIndex;
}
private:
uint32_t mStartingQueryIndex;
uint32_t mStoppingQueryIndex;
std::shared_ptr<VulkanCmdFence> mFence;
utils::Mutex mFenceMutex;
};
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKANHASYNCANDLES_H
+291 -333
View File
@@ -33,28 +33,48 @@ using namespace utils;
namespace filament::backend {
namespace {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
using Timestamp = VulkanGroupMarkers::Timestamp;
#endif
VkCommandBuffer createCommandBuffer(VkDevice device, VkCommandPool pool) {
VkCommandBuffer cmdbuffer;
// Create the low-level command buffer.
VkCommandBufferAllocateInfo const allocateInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = pool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
// The buffer allocated here will be implicitly reset when vkBeginCommandBuffer is called.
// We don't need to deallocate since destroying the pool will free all of the buffers.
vkAllocateCommandBuffers(device, &allocateInfo, &cmdbuffer);
return cmdbuffer;
VulkanCmdFence::VulkanCmdFence(VkFence ifence)
: fence(ifence) {
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence gets
// submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually finishes
// executing the command buffer, the status changes to VK_SUCCESS.
status.store(VK_INCOMPLETE);
}
} // anonymous namespace
VulkanCommandBuffer::VulkanCommandBuffer(VulkanResourceAllocator* allocator, VkDevice device,
VkCommandPool pool)
: mResourceManager(allocator),
mPipeline(VK_NULL_HANDLE) {
// Create the low-level command buffer.
const VkCommandBufferAllocateInfo allocateInfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
.commandPool = pool,
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
.commandBufferCount = 1,
};
// The buffer allocated here will be implicitly reset when vkBeginCommandBuffer is called.
// We don't need to deallocate since destroying the pool will free all of the buffers.
vkAllocateCommandBuffers(device, &allocateInfo, &mBuffer);
}
CommandBufferObserver::~CommandBufferObserver() {}
static VkCommandPool createPool(VkDevice device, uint32_t queueFamilyIndex) {
VkCommandPoolCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT
| VK_COMMAND_POOL_CREATE_TRANSIENT_BIT,
.queueFamilyIndex = queueFamilyIndex,
};
VkCommandPool pool;
vkCreateCommandPool(device, &createInfo, VKALLOC, &pool);
return pool;
}
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
void VulkanGroupMarkers::push(std::string const& marker, Timestamp start) noexcept {
@@ -110,142 +130,178 @@ bool VulkanGroupMarkers::empty() const noexcept {
#endif // FVK_DEBUG_GROUP_MARKERS
VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext* context, VulkanResourceAllocator* allocator,
VkDevice device, VkQueue queue, VkCommandPool pool, bool isProtected)
: mContext(context),
mMarkerCount(0),
isProtected(isProtected),
mDevice(device),
VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
VulkanContext* context, VulkanResourceAllocator* allocator)
: mDevice(device),
mQueue(queue),
mResourceManager(allocator),
mBuffer(createCommandBuffer(device, pool)),
mFenceStatus(std::make_shared<VulkanCmdFence>(VK_INCOMPLETE)) {
mPool(createPool(mDevice, queueFamilyIndex)),
mContext(context),
mStorage(CAPACITY) {
VkSemaphoreCreateInfo sci{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
vkCreateSemaphore(mDevice, &sci, VKALLOC, &mSubmission);
for (auto& semaphore: mSubmissionSignals) {
vkCreateSemaphore(mDevice, &sci, nullptr, &semaphore);
}
VkFenceCreateInfo fenceCreateInfo{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &mFence);
}
VulkanCommandBuffer::~VulkanCommandBuffer() {
vkDestroySemaphore(mDevice, mSubmission, VKALLOC);
vkDestroyFence(mDevice, mFence, VKALLOC);
}
void VulkanCommandBuffer::reset() noexcept {
mMarkerCount = 0;
mResourceManager.clear();
mWaitSemaphores.clear();
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence
// gets, gets submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually
// finishes executing the command buffer, the status changes to VK_SUCCESS.
mFenceStatus = std::make_shared<VulkanCmdFence>(VK_INCOMPLETE);
vkResetFences(mDevice, 1, &mFence);
}
void VulkanCommandBuffer::pushMarker(char const* marker) noexcept {
if (mContext->isDebugUtilsSupported()) {
VkDebugUtilsLabelEXT labelInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
.pLabelName = marker,
.color = {0, 1, 0, 1},
};
vkCmdBeginDebugUtilsLabelEXT(mBuffer, &labelInfo);
} else if (mContext->isDebugMarkersSupported()) {
VkDebugMarkerMarkerInfoEXT markerInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT,
.pMarkerName = marker,
.color = {0.0f, 1.0f, 0.0f, 1.0f},
};
vkCmdDebugMarkerBeginEXT(mBuffer, &markerInfo);
for (auto& fence: mFences) {
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &fence);
}
mMarkerCount++;
}
void VulkanCommandBuffer::popMarker() noexcept{
assert_invariant(mMarkerCount > 0);
if (mContext->isDebugUtilsSupported()) {
vkCmdEndDebugUtilsLabelEXT(mBuffer);
} else if (mContext->isDebugMarkersSupported()) {
vkCmdDebugMarkerEndEXT(mBuffer);
for (size_t i = 0; i < CAPACITY; ++i) {
mStorage[i] = std::make_unique<VulkanCommandBuffer>(allocator, mDevice, mPool);
}
mMarkerCount--;
#if !FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
(void) mContext;
#endif
}
void VulkanCommandBuffer::insertEvent(char const* marker) noexcept {
if (mContext->isDebugUtilsSupported()) {
VkDebugUtilsLabelEXT labelInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
.pLabelName = marker,
.color = {1, 1, 0, 1},
};
vkCmdInsertDebugUtilsLabelEXT(mBuffer, &labelInfo);
} else if (mContext->isDebugMarkersSupported()) {
VkDebugMarkerMarkerInfoEXT markerInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT,
.pMarkerName = marker,
.color = {0.0f, 1.0f, 0.0f, 1.0f},
};
vkCmdDebugMarkerInsertEXT(mBuffer, &markerInfo);
void VulkanCommands::terminate() {
wait();
gc();
vkDestroyCommandPool(mDevice, mPool, VKALLOC);
for (VkSemaphore sema: mSubmissionSignals) {
vkDestroySemaphore(mDevice, sema, VKALLOC);
}
for (VkFence fence: mFences) {
vkDestroyFence(mDevice, fence, VKALLOC);
}
}
void VulkanCommandBuffer::begin() noexcept {
// Begin writing into the command buffer.
VkCommandBufferBeginInfo const binfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
vkBeginCommandBuffer(mBuffer, &binfo);
}
VkSemaphore VulkanCommandBuffer::submit() {
while (mMarkerCount > 0) {
popMarker();
}
vkEndCommandBuffer(mBuffer);
VkPipelineStageFlags const waitDestStageMasks[2] = {
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
};
VkSubmitInfo submitInfo{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = mWaitSemaphores.size(),
.pWaitSemaphores = mWaitSemaphores.data(),
.pWaitDstStageMask = waitDestStageMasks,
.commandBufferCount = 1u,
.pCommandBuffers = &mBuffer,
.signalSemaphoreCount = 1u,
.pSignalSemaphores = &mSubmission,
};
// add submit protection if needed
VkProtectedSubmitInfo protectedSubmitInfo{
.sType = VK_STRUCTURE_TYPE_PROTECTED_SUBMIT_INFO,
.protectedSubmit = VK_TRUE,
};
if (isProtected) {
submitInfo.pNext = &protectedSubmitInfo;
VulkanCommandBuffer& VulkanCommands::get() {
if (mCurrentCommandBufferIndex >= 0) {
return *mStorage[mCurrentCommandBufferIndex].get();
}
// If we ran out of available command buffers, stall until one finishes. This is very rare.
// It occurs only when Filament invokes commit() or endFrame() a large number of times without
// presenting the swap chain or waiting on a fence.
while (mAvailableBufferCount == 0) {
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
FVK_LOGI << "Submitting cmdbuffer=" << mBuffer
<< " wait=(";
for (size_t s = 0, count = mWaitSemaphores.size(); s < count; ++s) {
FVK_LOGI << mWaitSemaphores[s] << " ";
FVK_LOGI << "VulkanCommands has stalled. "
<< "If this occurs frequently, consider increasing VK_MAX_COMMAND_BUFFERS."
<< io::endl;
#endif
wait();
gc();
}
VulkanCommandBuffer* currentbuf = nullptr;
// Find an available slot.
for (size_t i = 0; i < CAPACITY; ++i) {
auto wrapper = mStorage[i].get();
if (wrapper->buffer() == VK_NULL_HANDLE) {
mCurrentCommandBufferIndex = static_cast<int8_t>(i);
currentbuf = wrapper;
break;
}
}
assert_invariant(currentbuf);
mAvailableBufferCount--;
// Note that the fence wrapper uses shared_ptr because a DriverAPI fence can also have ownership
// over it. The destruction of the low-level fence occurs either in VulkanCommands::gc(), or in
// VulkanDriver::destroyFence(), both of which are safe spots.
currentbuf->fence = std::make_shared<VulkanCmdFence>(mFences[mCurrentCommandBufferIndex]);
// Begin writing into the command buffer.
const VkCommandBufferBeginInfo binfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
};
vkBeginCommandBuffer(currentbuf->buffer(), &binfo);
// Notify the observer that a new command buffer has been activated.
if (mObserver) {
mObserver->onCommandBuffer(*currentbuf);
}
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
// We push the current markers onto a temporary stack. This must be placed after currentbuf is
// set to the new command buffer since pushGroupMarker also calls get().
while (mCarriedOverMarkers && !mCarriedOverMarkers->empty()) {
auto [marker, time] = mCarriedOverMarkers->pop();
pushGroupMarker(marker.c_str(), time);
}
#endif
return *currentbuf;
}
bool VulkanCommands::flush() {
// It's perfectly fine to call flush when no commands have been written.
if (mCurrentCommandBufferIndex < 0) {
return false;
}
// Before actually submitting, we need to pop any leftover group markers.
// Note that this needs to occur before vkEndCommandBuffer.
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
while (mGroupMarkers && !mGroupMarkers->empty()) {
if (!mCarriedOverMarkers) {
mCarriedOverMarkers = std::make_unique<VulkanGroupMarkers>();
}
auto const [marker, time] = mGroupMarkers->top();
mCarriedOverMarkers->push(marker, time);
// We still need to call through to vkCmdEndDebugUtilsLabelEXT.
popGroupMarker();
}
FVK_LOGI << ") "
<< " signal=" << mSubmission
<< " fence=" << mFence << utils::io::endl;
#endif
mFenceStatus->setStatus(VK_NOT_READY);
UTILS_UNUSED_IN_RELEASE VkResult result =
vkQueueSubmit(mQueue, 1, &submitInfo, mFence);
int8_t const index = mCurrentCommandBufferIndex;
VulkanCommandBuffer const* currentbuf = mStorage[index].get();
VkSemaphore const renderingFinished = mSubmissionSignals[index];
vkEndCommandBuffer(currentbuf->buffer());
// If the injected semaphore is an "image available" semaphore that has not yet been signaled,
// it is sometimes fine to start executing commands anyway, as along as we stall the GPU at the
// VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT stage. However we need to assume the worst
// here and use VK_PIPELINE_STAGE_ALL_COMMANDS_BIT. This is a more aggressive stall, but it is
// the only safe option because the previously submitted command buffer might have set up some
// state that the new command buffer depends on.
VkPipelineStageFlags waitDestStageMasks[2] = {
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
};
VkSemaphore signals[2] = {
VK_NULL_HANDLE,
VK_NULL_HANDLE,
};
uint32_t waitSemaphoreCount = 0;
if (mSubmissionSignal) {
signals[waitSemaphoreCount++] = mSubmissionSignal;
}
if (mInjectedSignal) {
signals[waitSemaphoreCount++] = mInjectedSignal;
}
VkCommandBuffer const cmdbuffer = currentbuf->buffer();
VkSubmitInfo submitInfo{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
.waitSemaphoreCount = waitSemaphoreCount,
.pWaitSemaphores = waitSemaphoreCount > 0 ? signals : nullptr,
.pWaitDstStageMask = waitDestStageMasks,
.commandBufferCount = 1,
.pCommandBuffers = &cmdbuffer,
.signalSemaphoreCount = 1u,
.pSignalSemaphores = &renderingFinished,
};
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
FVK_LOGI << "Submitting cmdbuffer=" << cmdbuffer
<< " wait=(" << signals[0] << ", " << signals[1] << ") "
<< " signal=" << renderingFinished
<< " fence=" << currentbuf->fence->fence
<< utils::io::endl;
#endif
auto& cmdfence = currentbuf->fence;
UTILS_UNUSED_IN_RELEASE VkResult result = VK_SUCCESS;
{
auto scope = cmdfence->setValue(VK_NOT_READY);
result = vkQueueSubmit(mQueue, 1, &submitInfo, cmdfence->getFence());
}
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
if (result != VK_SUCCESS) {
@@ -253,212 +309,86 @@ VkSemaphore VulkanCommandBuffer::submit() {
}
#endif
assert_invariant(result == VK_SUCCESS);
mWaitSemaphores.clear();
return mSubmission;
}
CommandBufferPool::CommandBufferPool(VulkanContext* context, VulkanResourceAllocator* allocator,
VkDevice device, VkQueue queue, uint8_t queueFamilyIndex, bool isProtected)
: mDevice(device),
mRecording(INVALID) {
VkCommandPoolCreateInfo createInfo = {
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT |
VK_COMMAND_POOL_CREATE_TRANSIENT_BIT |
(isProtected ? VK_COMMAND_POOL_CREATE_PROTECTED_BIT : 0u),
.queueFamilyIndex = queueFamilyIndex,
};
vkCreateCommandPool(device, &createInfo, VKALLOC, &mPool);
for (size_t i = 0; i < CAPACITY; ++i) {
mBuffers.emplace_back(std::make_unique<VulkanCommandBuffer>(context, allocator, device,
queue, mPool, isProtected));
}
}
CommandBufferPool::~CommandBufferPool() {
wait();
gc();
vkDestroyCommandPool(mDevice, mPool, VKALLOC);
}
VulkanCommandBuffer& CommandBufferPool::getRecording() {
if (isRecording()) {
return *mBuffers[mRecording];
}
auto const findNext = [this]() {
for (int8_t i = 0; i < CAPACITY; ++i) {
if (!mSubmitted[i]) {
return i;
}
}
return INVALID;
};
while ((mRecording = findNext()) == INVALID) {
wait();
gc();
}
auto& recording = *mBuffers[mRecording];
recording.begin();
return recording;
}
void CommandBufferPool::gc() {
ActiveBuffers reclaimed;
mSubmitted.forEachSetBit([this,&reclaimed] (size_t index) {
auto& buffer = mBuffers[index];
if (buffer->getStatus() == VK_SUCCESS) {
reclaimed.set(index, true);
buffer->reset();
}
});
mSubmitted &= ~reclaimed;
}
void CommandBufferPool::update() {
mSubmitted.forEachSetBit([this] (size_t index) {
auto& buffer = mBuffers[index];
VkResult status = vkGetFenceStatus(mDevice, buffer->getVkFence());
if (status == VK_SUCCESS) {
buffer->setComplete();
}
});
}
VkSemaphore CommandBufferPool::flush() {
// We're not recording right now.
if (!isRecording()) {
return VK_NULL_HANDLE;
}
auto submitSemaphore = mBuffers[mRecording]->submit();
mSubmitted.set(mRecording, true);
mRecording = INVALID;
return submitSemaphore;
}
void CommandBufferPool::wait() {
uint8_t count = 0;
VkFence fences[CAPACITY];
mSubmitted.forEachSetBit([this, &count, &fences] (size_t index) {
fences[count++] = mBuffers[index]->getVkFence();
});
vkWaitForFences(mDevice, count, fences, VK_TRUE, UINT64_MAX);
update();
}
void CommandBufferPool::waitFor(VkSemaphore previousAction) {
if (!isRecording()) {
return;
}
auto& recording = mBuffers[mRecording];
recording->insertWait(previousAction);
}
void CommandBufferPool::pushMarker(char const* marker) {
getRecording().pushMarker(marker);
}
void CommandBufferPool::popMarker() {
getRecording().popMarker();
}
void CommandBufferPool::insertEvent(char const* marker) {
getRecording().insertEvent(marker);
}
VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context,
VulkanResourceAllocator* allocator)
: mDevice(device),
mProtectedQueue(protectedQueue),
mProtectedQueueFamilyIndex(protectedQueueFamilyIndex),
mAllocator(allocator),
mContext(context),
mPool(std::make_unique<CommandBufferPool>(context, allocator, device, queue, queueFamilyIndex,
false)) {
}
void VulkanCommands::terminate() {
mPool.reset();
mProtectedPool.reset();
}
VulkanCommandBuffer& VulkanCommands::get() {
auto& ret = mPool->getRecording();
return ret;
}
VulkanCommandBuffer& VulkanCommands::getProtected() {
assert_invariant(mProtectedQueue != VK_NULL_HANDLE);
if (!mProtectedPool) {
mProtectedPool = std::make_unique<CommandBufferPool>(mContext, mAllocator, mDevice,
mProtectedQueue, mProtectedQueueFamilyIndex, true);
}
auto& ret = mProtectedPool->getRecording();
return ret;
}
bool VulkanCommands::flush() {
VkSemaphore dependency = mInjectedDependency;
VkSemaphore lastSubmit = mLastSubmit;
bool hasFlushed = false;
// Note that we've ordered it so that the non-protected commands are followed by the protected
// commands. This assumes that the protected commands will be that one doing the rendering into
// the protected memory (i.e. protected render target).
for (auto pool: {mPool.get(), mProtectedPool.get()}) {
if (!pool || !pool->isRecording()) {
continue;
}
if (dependency != VK_NULL_HANDLE) {
pool->waitFor(dependency);
}
if (lastSubmit != VK_NULL_HANDLE) {
pool->waitFor(lastSubmit);
lastSubmit = VK_NULL_HANDLE;
}
dependency = pool->flush();
hasFlushed = true;
}
if (hasFlushed) {
mInjectedDependency = VK_NULL_HANDLE;
mLastSubmit = dependency;
}
mSubmissionSignal = renderingFinished;
mInjectedSignal = VK_NULL_HANDLE;
mCurrentCommandBufferIndex = -1;
return true;
}
void VulkanCommands::wait() {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("commands::wait");
VkSemaphore VulkanCommands::acquireFinishedSignal() {
VkSemaphore semaphore = mSubmissionSignal;
mSubmissionSignal = VK_NULL_HANDLE;
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
FVK_LOGI << "Acquiring " << semaphore << " (e.g. for vkQueuePresentKHR)" << io::endl;
#endif
return semaphore;
}
mPool->wait();
if (mProtectedPool) {
mProtectedPool->wait();
void VulkanCommands::injectDependency(VkSemaphore next) {
assert_invariant(mInjectedSignal == VK_NULL_HANDLE);
mInjectedSignal = next;
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
FVK_LOGI << "Injecting " << next << " (e.g. due to vkAcquireNextImageKHR)" << io::endl;
#endif
}
void VulkanCommands::wait() {
VkFence fences[CAPACITY];
size_t count = 0;
for (size_t i = 0; i < CAPACITY; i++) {
auto wrapper = mStorage[i].get();
if (wrapper->buffer() != VK_NULL_HANDLE
&& mCurrentCommandBufferIndex != static_cast<int8_t>(i)) {
fences[count++] = wrapper->fence->getFence();
}
}
if (count > 0) {
vkWaitForFences(mDevice, count, fences, VK_TRUE, UINT64_MAX);
updateFences();
}
FVK_SYSTRACE_END();
}
void VulkanCommands::gc() {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("commands::gc");
mPool->gc();
if (mProtectedPool) {
mProtectedPool->gc();
VkFence fences[CAPACITY];
size_t count = 0;
for (size_t i = 0; i < CAPACITY; i++) {
auto wrapper = mStorage[i].get();
if (wrapper->buffer() == VK_NULL_HANDLE) {
continue;
}
auto const vkfence = wrapper->fence->getFence();
VkResult const result = vkGetFenceStatus(mDevice, vkfence);
if (result != VK_SUCCESS) {
continue;
}
fences[count++] = vkfence;
wrapper->fence->setValue(VK_SUCCESS);
wrapper->reset();
mAvailableBufferCount++;
}
if (count > 0) {
vkResetFences(mDevice, count, fences);
}
FVK_SYSTRACE_END();
}
void VulkanCommands::updateFences() {
mPool->update();
if (mProtectedPool) {
mProtectedPool->update();
for (size_t i = 0; i < CAPACITY; i++) {
auto wrapper = mStorage[i].get();
if (wrapper->buffer() != VK_NULL_HANDLE) {
VulkanCmdFence* fence = wrapper->fence.get();
if (fence) {
VkResult status = vkGetFenceStatus(mDevice, fence->getFence());
// This is either VK_SUCCESS, VK_NOT_READY, or VK_ERROR_DEVICE_LOST.
fence->setValue(status);
}
}
}
}
@@ -472,14 +402,29 @@ void VulkanCommands::pushGroupMarker(char const* str, VulkanGroupMarkers::Timest
FVK_LOGD << "----> " << str << utils::io::endl;
}
#endif
// TODO: Add group marker color to the Driver API
VkCommandBuffer const cmdbuffer = get().buffer();
if (!mGroupMarkers) {
mGroupMarkers = std::make_unique<VulkanGroupMarkers>();
}
mGroupMarkers->push(str, timestamp);
mPool->pushMarker(str);
if (mProtectedPool) {
mProtectedPool->pushMarker(str);
if (mContext->isDebugUtilsSupported()) {
VkDebugUtilsLabelEXT labelInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
.pLabelName = str,
.color = {0, 1, 0, 1},
};
vkCmdBeginDebugUtilsLabelEXT(cmdbuffer, &labelInfo);
} else if (mContext->isDebugMarkersSupported()) {
VkDebugMarkerMarkerInfoEXT markerInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT,
.pMarkerName = str,
.color = {0.0f, 1.0f, 0.0f, 1.0f},
};
vkCmdDebugMarkerBeginEXT(cmdbuffer, &markerInfo);
}
}
@@ -497,9 +442,10 @@ void VulkanCommands::popGroupMarker() {
#else
mGroupMarkers->pop();
#endif
mPool->popMarker();
if (mProtectedPool) {
mProtectedPool->popMarker();
if (mContext->isDebugUtilsSupported()) {
vkCmdEndDebugUtilsLabelEXT(cmdbuffer);
} else if (mContext->isDebugMarkersSupported()) {
vkCmdDebugMarkerEndEXT(cmdbuffer);
}
} else if (mCarriedOverMarkers && !mCarriedOverMarkers->empty()) {
// It could be that pop is called between flush() and get() (new command buffer), in which
@@ -509,10 +455,22 @@ void VulkanCommands::popGroupMarker() {
}
}
void VulkanCommands::insertEventMarker(char const* str, uint32_t len) {
mPool->insertEvent(str);
if (mProtectedPool) {
mProtectedPool->insertEvent(str);
void VulkanCommands::insertEventMarker(char const* string, uint32_t len) {
VkCommandBuffer const cmdbuffer = get().buffer();
if (mContext->isDebugUtilsSupported()) {
VkDebugUtilsLabelEXT labelInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT,
.pLabelName = string,
.color = {1, 1, 0, 1},
};
vkCmdInsertDebugUtilsLabelEXT(cmdbuffer, &labelInfo);
} else if (mContext->isDebugMarkersSupported()) {
VkDebugMarkerMarkerInfoEXT markerInfo = {
.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT,
.pMarkerName = string,
.color = {0.0f, 1.0f, 0.0f, 1.0f},
};
vkCmdDebugMarkerInsertEXT(cmdbuffer, &markerInfo);
}
}
+90 -96
View File
@@ -21,10 +21,8 @@
#include "DriverBase.h"
#include "VulkanAsyncHandles.h"
#include "VulkanConstants.h"
#include "VulkanResources.h"
#include "VulkanUtility.h"
#include <utils/Condition.h>
#include <utils/FixedCapacityVector.h>
@@ -61,18 +59,57 @@ private:
#endif // FVK_DEBUG_GROUP_MARKERS
// Wrapper to enable use of shared_ptr for implementing shared ownership of low-level Vulkan fences.
struct VulkanCmdFence {
struct SetValueScope {
public:
~SetValueScope() {
mHolder->mutex.unlock();
mHolder->condition.notify_all();
}
private:
SetValueScope(VulkanCmdFence* fenceHolder, VkResult result) :
mHolder(fenceHolder) {
mHolder->mutex.lock();
mHolder->status.store(result);
}
VulkanCmdFence* mHolder;
friend struct VulkanCmdFence;
};
VulkanCmdFence(VkFence ifence);
~VulkanCmdFence() = default;
SetValueScope setValue(VkResult value) {
return {this, value};
}
VkFence& getFence() {
return fence;
}
VkResult getStatus() {
std::unique_lock<utils::Mutex> lock(mutex);
return status.load(std::memory_order_acquire);
}
private:
VkFence fence;
utils::Condition condition;
utils::Mutex mutex;
std::atomic<VkResult> status;
};
// The submission fence has shared ownership semantics because it is potentially wrapped by a
// DriverApi fence object and should not be destroyed until both the DriverApi object is freed and
// we're done waiting on the most recent submission of the given command buffer.
struct VulkanCommandBuffer {
VulkanCommandBuffer(VulkanContext* mContext, VulkanResourceAllocator* allocator,
VkDevice device, VkQueue queue, VkCommandPool pool, bool isProtected);
VulkanCommandBuffer(VulkanResourceAllocator* allocator, VkDevice device, VkCommandPool pool);
VulkanCommandBuffer(VulkanCommandBuffer const&) = delete;
VulkanCommandBuffer& operator=(VulkanCommandBuffer const&) = delete;
~VulkanCommandBuffer();
inline void acquire(VulkanResource* resource) {
mResourceManager.acquire(resource);
}
@@ -80,87 +117,41 @@ struct VulkanCommandBuffer {
inline void acquire(VulkanAcquireOnlyResourceManager* srcResources) {
mResourceManager.acquireAll(srcResources);
}
void reset() noexcept;
inline void insertWait(VkSemaphore sem) {
mWaitSemaphores.insert(sem);
inline void reset() {
fence.reset();
mResourceManager.clear();
mPipeline = VK_NULL_HANDLE;
}
void pushMarker(char const* marker) noexcept;
void popMarker() noexcept;
void insertEvent(char const* marker) noexcept;
void begin() noexcept;
VkSemaphore submit();
inline void setComplete() {
mFenceStatus->setStatus(VK_SUCCESS);
inline void setPipeline(VkPipeline pipeline) {
mPipeline = pipeline;
}
VkResult getStatus() {
return mFenceStatus->getStatus();
inline VkPipeline pipeline() const {
return mPipeline;
}
std::shared_ptr<VulkanCmdFence> getFenceStatus() const {
return mFenceStatus;
inline VkCommandBuffer buffer() const {
if (fence) {
return mBuffer;
}
return VK_NULL_HANDLE;
}
VkFence getVkFence() const {
return mFence;
}
VkCommandBuffer buffer() const {
return mBuffer;
}
std::shared_ptr<VulkanCmdFence> fence;
private:
VulkanContext* mContext;
uint8_t mMarkerCount;
bool const isProtected;
VkDevice mDevice;
VkQueue mQueue;
VulkanAcquireOnlyResourceManager mResourceManager;
CappedArray<VkSemaphore, 2> mWaitSemaphores;
VkCommandBuffer mBuffer;
VkSemaphore mSubmission;
VkFence mFence;
std::shared_ptr<VulkanCmdFence> mFenceStatus;
VkPipeline mPipeline;
};
struct CommandBufferPool {
using ActiveBuffers = utils::bitset32;
static constexpr int8_t INVALID = -1;
CommandBufferPool(VulkanContext* context, VulkanResourceAllocator* allocator, VkDevice device,
VkQueue queue, uint8_t queueFamilyIndex, bool isProtected);
~CommandBufferPool();
VulkanCommandBuffer& getRecording();
void gc();
void update();
VkSemaphore flush();
void wait();
void waitFor(VkSemaphore previousAction);
void pushMarker(char const* marker);
void popMarker();
void insertEvent(char const* marker);
inline bool isRecording() const { return mRecording != INVALID; }
private:
static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS;
// int8 only goes up to 127, therefore capacity must be less than that.
static_assert(CAPACITY < 128);
using BufferList = utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>>;
VkDevice mDevice;
VkCommandPool mPool;
ActiveBuffers mSubmitted;
std::vector<std::unique_ptr<VulkanCommandBuffer>> mBuffers;
int8_t mRecording;
// Allows classes to be notified after a new command buffer has been activated.
class CommandBufferObserver {
public:
virtual void onCommandBuffer(const VulkanCommandBuffer& cmdbuffer) = 0;
virtual ~CommandBufferObserver();
};
// Manages a set of command buffers and semaphores, exposing an API that is significantly simpler
@@ -174,6 +165,9 @@ private:
// - This creates a guarantee of in-order execution.
// - Semaphores are recycled to prevent create / destroy churn.
//
// - Notifies listeners when recording begins in a new VkCommandBuffer.
// - Used by PipelineCache so that it knows when to clear out its shadow state.
//
// - Allows 1 user to inject a "dependency" semaphore that stalls the next flush.
// - This is used for asynchronous acquisition of a swap chain image, since the GPU
// might require a valid swap chain image when it starts executing the command buffer.
@@ -189,18 +183,13 @@ private:
class VulkanCommands {
public:
VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context,
VulkanResourceAllocator* allocator);
VulkanContext* context, VulkanResourceAllocator* allocator);
void terminate();
// Creates a "current" command buffer if none exists, otherwise returns the current one.
VulkanCommandBuffer& get();
// Creates a "current" protected capable command buffer if none exists, otherwise
// returns the current one.
VulkanCommandBuffer& getProtected();
// Submits the current command buffer if it exists, then sets "current" to null.
// If there are no outstanding commands then nothing happens and this returns false.
bool flush();
@@ -208,17 +197,11 @@ public:
// Returns the "rendering finished" semaphore for the most recent flush and removes
// it from the existing dependency chain. This is especially useful for setting up
// vkQueuePresentKHR.
VkSemaphore acquireFinishedSignal() {
VkSemaphore ret= mLastSubmit;
mLastSubmit = VK_NULL_HANDLE;
return ret;
}
VkSemaphore acquireFinishedSignal();
// Takes a semaphore that signals when the next flush can occur. Only one injected
// semaphore is allowed per flush. Useful after calling vkAcquireNextImageKHR.
void injectDependency(VkSemaphore next) {
mInjectedDependency = next;
}
void injectDependency(VkSemaphore next);
// Destroys all command buffers that are no longer in use.
void gc();
@@ -229,26 +212,37 @@ public:
// Updates the atomic "status" variable in every extant fence.
void updateFences();
// Sets an observer who is notified every time a new command buffer has been made "current".
// The observer's event handler can only be called during get().
void setObserver(CommandBufferObserver* observer) { mObserver = observer; }
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
void pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp = {});
void popGroupMarker();
void insertEventMarker(char const* string, uint32_t len);
std::string getTopGroupMarker() const;
#endif
private:
static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS;
VkDevice const mDevice;
VkQueue const mProtectedQueue;
// For defered initialization if/when we need protected content
uint32_t const mProtectedQueueFamilyIndex;
VulkanResourceAllocator* mAllocator;
VulkanContext* mContext;
VkQueue const mQueue;
VkCommandPool const mPool;
VulkanContext const* mContext;
std::unique_ptr<CommandBufferPool> mPool;
std::unique_ptr<CommandBufferPool> mProtectedPool;
VkSemaphore mInjectedDependency = VK_NULL_HANDLE;
VkSemaphore mLastSubmit = VK_NULL_HANDLE;
// int8 only goes up to 127, therefore capacity must be less than that.
static_assert(CAPACITY < 128);
int8_t mCurrentCommandBufferIndex = -1;
VkSemaphore mSubmissionSignal = {};
VkSemaphore mInjectedSignal = {};
utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>> mStorage;
VkFence mFences[CAPACITY] = {};
VkSemaphore mSubmissionSignals[CAPACITY] = {};
uint8_t mAvailableBufferCount = CAPACITY;
CommandBufferObserver* mObserver = nullptr;
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
+9 -30
View File
@@ -77,12 +77,8 @@
// order of calls).
#define FVK_DEBUG_FORCE_LOG_TO_I 0x00020000
// Enable a minimal set of traces to assess the performance of the backend.
// All other debug features must be disabled.
#define FVK_DEBUG_PROFILING 0x00040000
// Useful default combinations
#define FVK_DEBUG_EVERYTHING (0xFFFFFFFF & ~FVK_DEBUG_PROFILING)
#define FVK_DEBUG_EVERYTHING 0xFFFFFFFF
#define FVK_DEBUG_PERFORMANCE \
FVK_DEBUG_SYSTRACE
@@ -116,10 +112,6 @@ static_assert(FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS));
static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
#endif
#if FVK_ENABLED(FVK_DEBUG_PROFILING) && FVK_DEBUG_FLAGS != FVK_DEBUG_PROFILING
#error PROFILING is exclusive; all other debug features must be disabled.
#endif
// end dependcy checks
// Shorthand for combination of enabled debug flags
@@ -131,30 +123,17 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
// end shorthands
#if FVK_DEBUG_FLAGS == FVK_DEBUG_PROFILING
#if FVK_ENABLED(FVK_DEBUG_SYSTRACE)
#define FVK_SYSTRACE_CONTEXT()
#define FVK_SYSTRACE_START(marker)
#define FVK_SYSTRACE_END()
#define FVK_SYSTRACE_SCOPE()
#define FVK_PROFILE_MARKER(marker) PROFILE_SCOPE(marker)
#elif FVK_ENABLED(FVK_DEBUG_SYSTRACE)
#include <utils/Systrace.h>
#define FVK_SYSTRACE_CONTEXT() SYSTRACE_CONTEXT()
#define FVK_SYSTRACE_START(marker) SYSTRACE_NAME_BEGIN(marker)
#define FVK_SYSTRACE_END() SYSTRACE_NAME_END()
#define FVK_SYSTRACE_SCOPE() SYSTRACE_NAME(__func__)
#define FVK_PROFILE_MARKER(marker) FVK_SYSTRACE_SCOPE()
#include <utils/Systrace.h>
#define FVK_SYSTRACE_CONTEXT() SYSTRACE_CONTEXT()
#define FVK_SYSTRACE_START(marker) SYSTRACE_NAME_BEGIN(marker)
#define FVK_SYSTRACE_END() SYSTRACE_NAME_END()
#else
#define FVK_SYSTRACE_CONTEXT()
#define FVK_SYSTRACE_START(marker)
#define FVK_SYSTRACE_END()
#define FVK_SYSTRACE_SCOPE()
#define FVK_PROFILE_MARKER(marker)
#define FVK_SYSTRACE_CONTEXT()
#define FVK_SYSTRACE_START(marker)
#define FVK_SYSTRACE_END()
#endif
#ifndef FVK_HANDLE_ARENA_SIZE_IN_MB
@@ -86,7 +86,7 @@ VulkanTimestamps::VulkanTimestamps(VkDevice device) : mDevice(device) {
VkQueryPoolCreateInfo tqpCreateInfo = {
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
.queryType = VK_QUERY_TYPE_TIMESTAMP,
};
};
std::unique_lock<utils::Mutex> lock(mMutex);
tqpCreateInfo.queryCount = mUsed.size() * 2;
VkResult result = vkCreateQueryPool(mDevice, &tqpCreateInfo, VKALLOC, &mPool);
@@ -101,7 +101,7 @@ std::tuple<uint32_t, uint32_t> VulkanTimestamps::getNextQuery() {
for (size_t timerIndex = 0; timerIndex < maxTimers; ++timerIndex) {
if (!mUsed.test(timerIndex)) {
mUsed.set(timerIndex);
return std::make_tuple(timerIndex * 2, timerIndex * 2 + 1);
return std::make_tuple(timerIndex * 2, timerIndex * 2 + 1);
}
}
FVK_LOGE << "More than " << maxTimers << " timers are not supported." << utils::io::endl;
@@ -121,7 +121,7 @@ void VulkanTimestamps::beginQuery(VulkanCommandBuffer const* commands,
vkCmdWriteTimestamp(cmdbuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index);
// We stash this because getResult might come before the query is actually processed.
query->setFence(commands->getFenceStatus());
query->setFence(commands->fence);
}
void VulkanTimestamps::endQuery(VulkanCommandBuffer const* commands,
+7 -19
View File
@@ -93,9 +93,6 @@ struct VulkanRenderPass {
struct VulkanContext {
public:
inline uint32_t selectMemoryType(uint32_t flags, VkFlags reqs) const {
if ((reqs & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) {
assert_invariant(isProtectedMemorySupported() == true);
}
for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) {
if (flags & 1) {
if ((mMemoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) {
@@ -116,19 +113,19 @@ public:
}
inline VkPhysicalDeviceLimits const& getPhysicalDeviceLimits() const noexcept {
return mPhysicalDeviceProperties.properties.limits;
return mPhysicalDeviceProperties.limits;
}
inline uint32_t getPhysicalDeviceVendorId() const noexcept {
return mPhysicalDeviceProperties.properties.vendorID;
return mPhysicalDeviceProperties.vendorID;
}
inline bool isImageCubeArraySupported() const noexcept {
return mPhysicalDeviceFeatures.features.imageCubeArray == VK_TRUE;
return mPhysicalDeviceFeatures.imageCubeArray == VK_TRUE;
}
inline bool isDepthClampSupported() const noexcept {
return mPhysicalDeviceFeatures.features.depthClamp == VK_TRUE;
return mPhysicalDeviceFeatures.depthClamp == VK_TRUE;
}
inline bool isDebugMarkersSupported() const noexcept {
@@ -144,30 +141,21 @@ public:
}
inline bool isClipDistanceSupported() const noexcept {
return mPhysicalDeviceFeatures.features.shaderClipDistance == VK_TRUE;
return mPhysicalDeviceFeatures.shaderClipDistance == VK_TRUE;
}
inline bool isLazilyAllocatedMemorySupported() const noexcept {
return mLazilyAllocatedMemorySupported;
}
inline bool isProtectedMemorySupported() const noexcept {
return mProtectedMemorySupported;
}
private:
VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2
};
VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2
};
VkPhysicalDeviceProperties mPhysicalDeviceProperties = {};
VkPhysicalDeviceFeatures mPhysicalDeviceFeatures = {};
bool mDebugMarkersSupported = false;
bool mDebugUtilsSupported = false;
bool mMultiviewEnabled = false;
bool mLazilyAllocatedMemorySupported = false;
bool mProtectedMemorySupported = false;
VkFormatList mDepthStencilFormats;
VkFormatList mBlittableDepthStencilFormats;
+199 -77
View File
@@ -18,8 +18,6 @@
#include "CommandStreamDispatcher.h"
#include "DataReshaper.h"
#include "SystraceProfile.h"
#include "VulkanAsyncHandles.h"
#include "VulkanBuffer.h"
#include "VulkanCommands.h"
#include "VulkanDriverFactory.h"
@@ -106,7 +104,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugReportCallback(VkDebugReportFlagsEXT flags,
FVK_LOGE << utils::io::endl;
return VK_FALSE;
}
#endif // FVK_ENABLED(FVK_DEBUG_VALIDATION)
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION)
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
@@ -127,7 +125,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFla
FVK_LOGE << utils::io::endl;
return VK_FALSE;
}
#endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
#endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS)
}// anonymous namespace
@@ -157,7 +155,7 @@ DebugUtils::DebugUtils(VkInstance instance, VkDevice device, VulkanContext const
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
<< "Unable to create Vulkan debug messenger.";
}
#endif // FVK_ENABLED(FVK_DEBUG_VALIDATION)
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION)
}
DebugUtils* DebugUtils::get() {
@@ -185,7 +183,7 @@ void DebugUtils::setName(VkObjectType type, uint64_t handle, char const* name) {
};
vkSetDebugUtilsObjectNameEXT(impl->mDevice, &info);
}
#endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
#endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS)
Dispatcher VulkanDriver::getDispatcher() const noexcept {
return ConcreteDispatcher<VulkanDriver>::make();
@@ -201,8 +199,7 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
mResourceManager(&mResourceAllocator),
mThreadSafeResourceManager(&mResourceAllocator),
mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(),
mPlatform->getGraphicsQueueFamilyIndex(),
nullptr, 0, &mContext, &mResourceAllocator),
mPlatform->getGraphicsQueueFamilyIndex(), &mContext, &mResourceAllocator),
mPipelineLayoutCache(mPlatform->getDevice(), &mResourceAllocator),
mPipelineCache(mPlatform->getDevice(), mAllocator),
mStagePool(mAllocator, &mCommands),
@@ -347,7 +344,8 @@ void VulkanDriver::tick(int) {
// rather than the wall clock, because we must wait 3 frames after a DriverAPI-level resource has
// been destroyed for safe destruction, due to outstanding command buffers and triple buffering.
void VulkanDriver::collectGarbage() {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("gc");
// Command buffers need to be submitted and completed before other resources can be gc'd. And
// its gc() function carrys out the *wait*.
mCommands.gc();
@@ -358,11 +356,15 @@ void VulkanDriver::collectGarbage() {
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
mResourceAllocator.print();
#endif
FVK_SYSTRACE_END();
}
void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
int64_t refreshIntervalNs, uint32_t frameId) {
FVK_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("beginFrame");
// Do nothing.
FVK_SYSTRACE_END();
}
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
@@ -377,9 +379,11 @@ void VulkanDriver::setPresentationTime(int64_t monotonic_clock_ns) {
}
void VulkanDriver::endFrame(uint32_t frameId) {
FVK_PROFILE_MARKER(PROFILE_NAME_ENDFRAME);
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("endframe");
mCommands.flush();
collectGarbage();
FVK_SYSTRACE_END();
}
void VulkanDriver::updateDescriptorSetBuffer(
@@ -388,13 +392,9 @@ void VulkanDriver::updateDescriptorSetBuffer(
backend::BufferObjectHandle boh,
uint32_t offset,
uint32_t size) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("updateDescriptorSetBuffer");
VulkanDescriptorSet* set = mResourceAllocator.handle_cast<VulkanDescriptorSet*>(dsh);
VulkanBufferObject* obj = mResourceAllocator.handle_cast<VulkanBufferObject*>(boh);
mDescriptorSetManager.updateBuffer(set, binding, obj, offset, size);
FVK_SYSTRACE_END();
}
void VulkanDriver::updateDescriptorSetTexture(
@@ -402,27 +402,28 @@ void VulkanDriver::updateDescriptorSetTexture(
backend::descriptor_binding_t binding,
backend::TextureHandle th,
SamplerParams params) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("updateDescriptorSetTexture");
VulkanDescriptorSet* set = mResourceAllocator.handle_cast<VulkanDescriptorSet*>(dsh);
VulkanTexture* texture = mResourceAllocator.handle_cast<VulkanTexture*>(th);
VkSampler const vksampler = mSamplerCache.getSampler(params);
mDescriptorSetManager.updateSampler(set, binding, texture, vksampler);
FVK_SYSTRACE_END();
}
void VulkanDriver::flush(int) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("flush");
mCommands.flush();
FVK_SYSTRACE_END();
}
void VulkanDriver::finish(int dummy) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("finish");
mCommands.flush();
mCommands.wait();
mReadPixels.runUntilComplete();
FVK_SYSTRACE_END();
}
void VulkanDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
@@ -506,16 +507,11 @@ void VulkanDriver::destroyBufferObject(Handle<HwBufferObject> boh) {
void VulkanDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage usage) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("createTexture");
auto vktexture = mResourceAllocator.construct<VulkanTexture>(th, mPlatform->getDevice(),
mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, &mResourceAllocator,
target, levels,
format, samples, w, h, depth, usage, mStagePool);
mResourceManager.acquire(vktexture);
FVK_SYSTRACE_END();
}
//void VulkanDriver::createTextureSwizzledR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
@@ -578,12 +574,9 @@ void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
}
void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("createProgram");
auto vkprogram
= mResourceAllocator.construct<VulkanProgram>(ph, mPlatform->getDevice(), program);
mResourceManager.acquire(vkprogram);
FVK_SYSTRACE_END();
}
void VulkanDriver::destroyProgram(Handle<HwProgram> ph) {
@@ -604,9 +597,6 @@ void VulkanDriver::createDefaultRenderTargetR(Handle<HwRenderTarget> rth, int) {
void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
TargetBufferFlags targets, uint32_t width, uint32_t height, uint8_t samples,
uint8_t layerCount, MRT color, TargetBufferInfo depth, TargetBufferInfo stencil) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("createRenderTarget");
UTILS_UNUSED_IN_RELEASE math::vec2<uint32_t> tmin = {std::numeric_limits<uint32_t>::max()};
UTILS_UNUSED_IN_RELEASE math::vec2<uint32_t> tmax = {0};
UTILS_UNUSED_IN_RELEASE size_t attachmentCount = 0;
@@ -665,8 +655,6 @@ void VulkanDriver::createRenderTargetR(Handle<HwRenderTarget> rth,
&mCommands, &mResourceAllocator, width, height, samples, colorTargets, depthStencil,
mStagePool, layerCount);
mResourceManager.acquire(renderTarget);
FVK_SYSTRACE_END();
}
void VulkanDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
@@ -682,8 +670,8 @@ void VulkanDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
}
void VulkanDriver::createFenceR(Handle<HwFence> fh, int) {
VulkanCommandBuffer* cmdbuf = &mCommands.get();
mResourceAllocator.construct<VulkanFence>(fh, cmdbuf->getFenceStatus());
VulkanCommandBuffer const& commandBuffer = mCommands.get();
mResourceAllocator.construct<VulkanFence>(fh, commandBuffer.fence);
}
void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow, uint64_t flags) {
@@ -727,16 +715,11 @@ void VulkanDriver::createDescriptorSetLayoutR(Handle<HwDescriptorSetLayout> dslh
void VulkanDriver::createDescriptorSetR(Handle<HwDescriptorSet> dsh,
Handle<HwDescriptorSetLayout> dslh) {
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("createDescriptorSet");
auto layout = mResourceAllocator.handle_cast<VulkanDescriptorSetLayout*>(dslh);
mDescriptorSetManager.createSet(dsh, layout);
auto set = mResourceAllocator.handle_cast<VulkanDescriptorSet*>(dsh);
mResourceManager.acquire(set);
FVK_SYSTRACE_END();
}
Handle<HwVertexBufferInfo> VulkanDriver::createVertexBufferInfoS() noexcept {
@@ -969,7 +952,8 @@ bool VulkanDriver::isSRGBSwapChainSupported() {
}
bool VulkanDriver::isProtectedContentSupported() {
return mContext.isProtectedMemorySupported();
// the SWAP_CHAIN_CONFIG_PROTECTED_CONTENT flag is not supported
return false;
}
bool VulkanDriver::isStereoSupported() {
@@ -1230,10 +1214,11 @@ void VulkanDriver::compilePrograms(CompilerPriorityQueue priority,
}
void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("beginRenderPass");
VulkanRenderTarget* const rt = mResourceAllocator.handle_cast<VulkanRenderTarget*>(rth);
VkExtent2D const& extent = rt->getExtent();
VkExtent2D const extent = rt->getExtent();
assert_invariant(rt == mDefaultRenderTarget || extent.width > 0 && extent.height > 0);
// Filament has the expectation that the contents of the swap chain are not preserved on the
@@ -1249,9 +1234,10 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
}
}
VulkanAttachment depth = rt->getSamples() == 1 ? rt->getDepth() : rt->getMsaaDepth();
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
if (rt->hasDepth()) {
auto depth = rt->getDepth();
if (depth.texture) {
depth.texture->print();
}
#endif
@@ -1268,36 +1254,129 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
VkRect2D const scissor{ .offset = { 0, 0 }, .extent = extent };
vkCmdSetScissor(cmdbuffer, 0, 1, &scissor);
VulkanLayout currentDepthLayout = VulkanLayout::UNDEFINED;
VulkanLayout currentDepthLayout = depth.getLayout();
TargetBufferFlags clearVal = params.flags.clear;
TargetBufferFlags discardEndVal = params.flags.discardEnd;
if (rt->hasDepth()) {
if (depth.texture) {
if (params.readOnlyDepthStencil & RenderPassParams::READONLY_DEPTH) {
discardEndVal &= ~TargetBufferFlags::DEPTH;
clearVal &= ~TargetBufferFlags::DEPTH;
}
// If the depth attachment texture was previously sampled, then we need to manually
// transition it to an attachment. This is necessary to also set up a barrier between the
// previous read and the potentially coming write.
depth.texture->transitionLayout(&commands, depth.getSubresourceRange(),
VulkanLayout::DEPTH_ATTACHMENT);
currentDepthLayout = VulkanLayout::DEPTH_ATTACHMENT;
}
uint8_t const renderTargetLayerCount = rt->getLayerCount();
// Create the VkRenderPass or fetch it from cache.
VulkanFboCache::RenderPassKey rpkey = rt->getRenderPassKey();
rpkey.clear = clearVal;
rpkey.discardStart = discardStart;
rpkey.discardEnd = discardEndVal;
rpkey.initialDepthLayout = currentDepthLayout;
rpkey.subpassMask = uint8_t(params.subpassMask);
VulkanFboCache::RenderPassKey rpkey = {
.depthFormat = depth.getFormat(),
.clear = clearVal,
.discardStart = discardStart,
.discardEnd = discardEndVal,
.initialDepthLayout = currentDepthLayout,
.samples = rt->getSamples(),
.needsResolveMask = 0,
.usesLazilyAllocatedMemory = 0,
.subpassMask = uint8_t(params.subpassMask),
.viewCount = renderTargetLayerCount,
};
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
VulkanAttachment const& info = rt->getColor(i);
if (info.texture) {
assert_invariant(info.layerCount == renderTargetLayerCount);
rpkey.colorFormat[i] = info.getFormat();
if (rpkey.samples > 1) {
const VulkanTexture* sidecar = info.texture->getSidecar();
if (sidecar && sidecar->isTransientAttachment()) {
rpkey.usesLazilyAllocatedMemory |= (1 << i);
}
if (info.texture->samples == 1) {
rpkey.needsResolveMask |= (1 << i);
}
}
} else {
rpkey.colorFormat[i] = VK_FORMAT_UNDEFINED;
}
}
VkRenderPass renderPass = mFramebufferCache.getRenderPass(rpkey);
mPipelineCache.bindRenderPass(renderPass, 0);
// Create the VkFramebuffer or fetch it from cache.
VulkanFboCache::FboKey fbkey = rt->getFboKey();
fbkey.renderPass = renderPass;
fbkey.layers = 1;
VulkanFboCache::FboKey fbkey {
.renderPass = renderPass,
.width = (uint16_t) extent.width,
.height = (uint16_t) extent.height,
.layers = 1,
.samples = rpkey.samples,
};
auto& renderPassAttachments = mRenderPassFboInfo.attachments;
for (int i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
VulkanAttachment& attachment = rt->getColor(i);
if (!attachment.texture) {
fbkey.color[i] = VK_NULL_HANDLE;
fbkey.resolve[i] = VK_NULL_HANDLE;
continue;
}
rt->emitBarriersBeginRenderPass(commands);
if (fbkey.samples == 1) {
auto const& range = attachment.getSubresourceRange();
auto tex = attachment.texture;
if (tex->getLayout(range.baseMipLevel, range.baseArrayLayer) !=
VulkanLayout::COLOR_ATTACHMENT &&
!tex->transitionLayout(&commands, range, VulkanLayout::COLOR_ATTACHMENT)) {
// If the layout transition did not emit a barrier, we do it manually here.
tex->samplerToAttachmentBarrier(&commands, range);
}
renderPassAttachments.insert(attachment);
fbkey.color[i] = attachment.getImageView();
fbkey.resolve[i] = VK_NULL_HANDLE;
assert_invariant(fbkey.color[i]);
} else {
auto& msaaColorAttachment = rt->getMsaaColor(i);
auto const& msaaRange = attachment.getSubresourceRange();
msaaColorAttachment.texture->transitionLayout(&commands,
msaaRange, VulkanLayout::COLOR_ATTACHMENT);
renderPassAttachments.insert(msaaColorAttachment);
fbkey.color[i] = msaaColorAttachment.getImageView();
VulkanTexture* texture = attachment.texture;
if (texture->samples == 1) {
auto const& range = attachment.getSubresourceRange();
attachment.texture->transitionLayout(&commands,
range, VulkanLayout::COLOR_ATTACHMENT);
renderPassAttachments.insert(attachment);
fbkey.resolve[i] = attachment.getImageView();
assert_invariant(fbkey.resolve[i]);
}
assert_invariant(fbkey.color[i]);
}
}
if (depth.texture) {
fbkey.depth = depth.getImageView();
assert_invariant(fbkey.depth);
renderPassAttachments.insert(depth);
UTILS_UNUSED_IN_RELEASE bool const depthDiscardEnd =
any(rpkey.discardEnd & TargetBufferFlags::DEPTH);
// Vulkan 1.1 does not support multisampled depth resolve, so let's check here
// and assert if this is requested. (c.f. isAutoDepthResolveSupported)
// Reminder: Filament's backend API works like this:
// - If the RT is SS then all attachments must be SS.
// - If the RT is MS then all SS attachments are auto resolved if not discarded.
assert_invariant(!(rt->getSamples() > 1 &&
rt->getDepth().texture->samples == 1 &&
!depthDiscardEnd));
}
VkFramebuffer vkfb = mFramebufferCache.getFramebuffer(fbkey);
// Assign a label to the framebuffer for debugging purposes.
@@ -1311,6 +1390,9 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
// The current command buffer now has references to the render target and its attachments.
commands.acquire(rt);
for (auto const& attachment: renderPassAttachments) {
commands.acquire(attachment.texture);
}
// Populate the structures required for vkCmdBeginRenderPass.
VkRenderPassBeginInfo renderPassInfo {
@@ -1329,6 +1411,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT + MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT +
1] = {};
if (clearVal != TargetBufferFlags::NONE) {
// NOTE: clearValues must be populated in the same order as the attachments array in
// VulkanFboCache::getFramebuffer. Values must be provided regardless of whether Vulkan is
// actually clearing that particular target.
@@ -1365,7 +1448,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
.maxDepth = params.depthRange.far
};
rt->transformViewportToPlatform(&viewport);
rt->transformClientRectToPlatform(&viewport);
vkCmdSetViewport(cmdbuffer, 0, 1, &viewport);
mCurrentRenderPass = {
@@ -1374,10 +1457,12 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
.params = params,
.currentSubpass = 0,
};
FVK_SYSTRACE_END();
}
void VulkanDriver::endRenderPass(int) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("endRenderPass");
VulkanCommandBuffer& commands = mCommands.get();
VkCommandBuffer cmdbuffer = commands.buffer();
@@ -1388,11 +1473,29 @@ void VulkanDriver::endRenderPass(int) {
// Since we might soon be sampling from the render target that we just wrote to, we need a
// pipeline barrier between framebuffer writes and shader reads.
rt->emitBarriersEndRenderPass(commands);
if (!rt->isSwapChain()) {
for (auto& attachment: mRenderPassFboInfo.attachments) {
auto const& range = attachment.getSubresourceRange();
bool const isDepth = attachment.isDepth();
auto texture = attachment.texture;
if (isDepth) {
texture->setLayout(range, VulkanFboCache::FINAL_DEPTH_ATTACHMENT_LAYOUT);
if (!texture->transitionLayout(&commands, range, VulkanLayout::DEPTH_SAMPLER)) {
texture->attachmentToSamplerBarrier(&commands, range);
}
} else {
texture->setLayout(range, VulkanFboCache::FINAL_COLOR_ATTACHMENT_LAYOUT);
if (!texture->transitionLayout(&commands, range, VulkanLayout::READ_WRITE)) {
texture->attachmentToSamplerBarrier(&commands, range);
}
}
}
}
mRenderPassFboInfo = {};
mCurrentRenderPass.renderTarget = nullptr;
mCurrentRenderPass.renderPass = VK_NULL_HANDLE;
FVK_SYSTRACE_END();
}
void VulkanDriver::nextSubpass(int) {
@@ -1409,13 +1512,14 @@ void VulkanDriver::nextSubpass(int) {
++mCurrentRenderPass.currentSubpass);
if (mCurrentRenderPass.params.subpassMask & 0x1) {
VulkanAttachment subpassInput = renderTarget->getColor0();
VulkanAttachment subpassInput = renderTarget->getColor(0);
mDescriptorSetManager.updateInputAttachment({}, subpassInput);
}
}
void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("makeCurrent");
ASSERT_PRECONDITION_NON_FATAL(drawSch == readSch,
"Vulkan driver does not support distinct draw/read swap chains.");
@@ -1432,15 +1536,19 @@ void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
if (UTILS_LIKELY(mDefaultRenderTarget)) {
mDefaultRenderTarget->bindToSwapChain(*swapChain);
}
FVK_SYSTRACE_END();
}
void VulkanDriver::commit(Handle<HwSwapChain> sch) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("commit");
VulkanSwapChain* swapChain = mResourceAllocator.handle_cast<VulkanSwapChain*>(sch);
// Present the backbuffer after the most recent command buffer submission has finished.
swapChain->present();
FVK_SYSTRACE_END();
}
void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
@@ -1502,7 +1610,8 @@ void VulkanDriver::readBufferSubData(backend::BufferObjectHandle boh,
void VulkanDriver::resolve(
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer,
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("resolve");
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
<< "resolve() cannot be invoked inside a render pass.";
@@ -1538,13 +1647,16 @@ void VulkanDriver::resolve(
mBlitter.resolve(
{ .texture = dstTexture, .level = dstLevel, .layer = dstLayer },
{ .texture = srcTexture, .level = srcLevel, .layer = srcLayer });
FVK_SYSTRACE_END();
}
void VulkanDriver::blit(
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer, math::uint2 dstOrigin,
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer, math::uint2 srcOrigin,
math::uint2 size) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("blit");
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
<< "blit() cannot be invoked inside a render pass.";
@@ -1578,13 +1690,16 @@ void VulkanDriver::blit(
mBlitter.blit(VK_FILTER_NEAREST,
{ .texture = dstTexture, .level = dstLevel, .layer = dstLayer }, dstOffsets,
{ .texture = srcTexture, .level = srcLevel, .layer = srcLayer }, srcOffsets);
FVK_SYSTRACE_END();
}
void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
Handle<HwRenderTarget> dst, Viewport dstRect,
Handle<HwRenderTarget> src, Viewport srcRect,
SamplerMagFilter filter) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("blitDEPRECATED");
// Note: blitDEPRECATED is only used for Renderer::copyFrame()
@@ -1621,12 +1736,15 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
VkOffset3D const dstOffsets[2] = { { dstLeft, dstTop, 0 }, { dstRight, dstBottom, 1 }};
mBlitter.blit(vkfilter,
dstTarget->getColor0(), dstOffsets,
srcTarget->getColor0(), srcOffsets);
dstTarget->getColor(0), dstOffsets,
srcTarget->getColor(0), srcOffsets);
FVK_SYSTRACE_END();
}
void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("draw");
VulkanCommandBuffer* commands = &mCommands.get();
const VulkanVertexBufferInfo& vbi =
@@ -1702,10 +1820,13 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
mPipelineCache.bindLayout(pipelineLayout);
mPipelineCache.bindPipeline(commands);
FVK_SYSTRACE_END();
}
void VulkanDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("bindRenderPrimitive");
VulkanCommandBuffer* commands = &mCommands.get();
VkCommandBuffer cmdbuffer = commands->buffer();
@@ -1728,22 +1849,21 @@ void VulkanDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
vkCmdBindVertexBuffers(cmdbuffer, 0, bufferCount, buffers, offsets);
vkCmdBindIndexBuffer(cmdbuffer, prim.indexBuffer->buffer.getGpuBuffer(), 0,
prim.indexBuffer->indexType);
FVK_SYSTRACE_END();
}
void VulkanDriver::bindDescriptorSet(
backend::DescriptorSetHandle dsh,
backend::descriptor_set_t setIndex,
backend::DescriptorSetOffsetArray&& offsets) {
if (dsh) {
VulkanDescriptorSet* set = mResourceAllocator.handle_cast<VulkanDescriptorSet*>(dsh);
mDescriptorSetManager.bind(setIndex, set, std::move(offsets));
} else {
mDescriptorSetManager.unbind(setIndex);
}
VulkanDescriptorSet* set = mResourceAllocator.handle_cast<VulkanDescriptorSet*>(dsh);
mDescriptorSetManager.bind(setIndex, set, std::move(offsets));
}
void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
FVK_SYSTRACE_SCOPE();
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("draw2");
VulkanCommandBuffer& commands = mCommands.get();
VkCommandBuffer cmdbuffer = commands.buffer();
@@ -1757,6 +1877,8 @@ void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
const uint32_t firstInstId = 0;
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
FVK_SYSTRACE_END();
}
void VulkanDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
@@ -69,8 +69,8 @@ VulkanFboCache::~VulkanFboCache() {
<< "Please explicitly call terminate() while the VkDevice is still alive.";
}
VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
FboMap::iterator iter = mFramebufferCache.find(config);
VkFramebuffer VulkanFboCache::getFramebuffer(FboKey config) noexcept {
auto iter = mFramebufferCache.find(config);
if (UTILS_LIKELY(iter != mFramebufferCache.end() && iter->second.handle != VK_NULL_HANDLE)) {
iter.value().timestamp = mCurrentTime;
return iter->second.handle;
@@ -121,7 +121,7 @@ VkFramebuffer VulkanFboCache::getFramebuffer(FboKey const& config) noexcept {
return framebuffer;
}
VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey const& config) noexcept {
VkRenderPass VulkanFboCache::getRenderPass(RenderPassKey config) noexcept {
auto iter = mRenderPassCache.find(config);
if (UTILS_LIKELY(iter != mRenderPassCache.end() && iter->second.handle != VK_NULL_HANDLE)) {
iter.value().timestamp = mCurrentTime;
@@ -362,7 +362,7 @@ void VulkanFboCache::gc() noexcept {
}
const uint32_t evictTime = mCurrentTime - TIME_BEFORE_EVICTION;
for (FboMap::iterator iter = mFramebufferCache.begin(); iter != mFramebufferCache.end(); ++iter) {
for (auto iter = mFramebufferCache.begin(); iter != mFramebufferCache.end(); ++iter) {
const FboVal fbo = iter->second;
if (fbo.timestamp < evictTime && fbo.handle) {
mRenderPassRefCount[iter->first.renderPass]--;
+4 -6
View File
@@ -54,7 +54,7 @@ public:
uint8_t usesLazilyAllocatedMemory; // 1 byte
uint8_t subpassMask; // 1 byte
uint8_t viewCount; // 1 byte
uint8_t padding[2];
uint8_t padding[2];
};
struct RenderPassVal {
VkRenderPass handle;
@@ -98,10 +98,10 @@ public:
~VulkanFboCache();
// Retrieves or creates a VkFramebuffer handle.
VkFramebuffer getFramebuffer(FboKey const& config) noexcept;
VkFramebuffer getFramebuffer(FboKey config) noexcept;
// Retrieves or creates a VkRenderPass handle.
VkRenderPass getRenderPass(RenderPassKey const& config) noexcept;
VkRenderPass getRenderPass(RenderPassKey config) noexcept;
// Evicts old unused Vulkan objects. Call this once per frame.
void gc() noexcept;
@@ -111,9 +111,7 @@ public:
private:
VkDevice mDevice;
using FboMap = tsl::robin_map<FboKey, FboVal, FboKeyHashFn, FboKeyEqualFn>;
FboMap mFramebufferCache;
tsl::robin_map<FboKey, FboVal, FboKeyHashFn, FboKeyEqualFn> mFramebufferCache;
tsl::robin_map<RenderPassKey, RenderPassVal, RenderPassHash, RenderPassEq> mRenderPassCache;
tsl::robin_map<VkRenderPass, uint32_t> mRenderPassRefCount;
uint32_t mCurrentTime = 0;
+123 -208
View File
@@ -36,12 +36,15 @@ namespace filament::backend {
namespace {
void flipVertically(VkRect2D* rect, uint32_t framebufferHeight) {
rect->offset.y = framebufferHeight - rect->offset.y - rect->extent.height;
}
void flipVertically(VkViewport* rect, uint32_t framebufferHeight) {
rect->y = framebufferHeight - rect->y - rect->height;
}
void clampToFramebuffer(VkRect2D* rect, uint32_t fbWidth, uint32_t fbHeight) {
rect->offset.y = fbHeight - rect->offset.y - rect->extent.height;
int32_t x = std::max(rect->offset.x, 0);
int32_t y = std::max(rect->offset.y, 0);
int32_t right = std::min(rect->offset.x + (int32_t) rect->extent.width, (int32_t) fbWidth);
@@ -87,8 +90,6 @@ BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) {
}
break;
}
// TODO: properly handle external sampler
case DescriptorType::SAMPLER_EXTERNAL:
case DescriptorType::SAMPLER: {
fromStageFlags(binding.stageFlags, binding.binding, mask.sampler);
break;
@@ -105,27 +106,6 @@ BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) {
return mask;
}
VulkanTexture* initMsaaTexture(VulkanTexture* texture, VkDevice device,
VkPhysicalDevice physicalDevice, VulkanContext const& context, VmaAllocator allocator,
VulkanCommands* commands, VulkanResourceAllocator* handleAllocator, uint8_t levels,
uint8_t samples, VulkanStagePool& stagePool) {
assert_invariant(texture);
auto msTexture = texture->getSidecar();
if (UTILS_UNLIKELY(!msTexture)) {
// Clear all usage flags that are not related to attachments, so that we can
// use the transient usage flag.
const TextureUsage usage = texture->usage & TextureUsage::ALL_ATTACHMENTS;
assert_invariant(static_cast<uint16_t>(usage) != 0U);
msTexture = new VulkanTexture(device, physicalDevice, context, allocator, commands,
handleAllocator, texture->target, levels,
texture->format, samples, texture->width, texture->height, texture->depth, usage,
stagePool, true /* heap allocated */);
texture->setSidecar(msTexture);
}
return msTexture;
}
} // anonymous namespace
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout const& layout)
@@ -254,240 +234,152 @@ VulkanProgram::~VulkanProgram() {
}
// Creates a special "default" render target (i.e. associated with the swap chain)
VulkanRenderTarget::VulkanRenderTarget()
: HwRenderTarget(0, 0),
VulkanResource(VulkanResourceType::RENDER_TARGET),
mOffscreen(false),
mProtected(false),
mResources(nullptr),
mInfo(std::make_unique<Auxiliary>()) {
mInfo->rpkey.samples = mInfo->fbkey.samples = 1;
}
VulkanRenderTarget::VulkanRenderTarget() :
HwRenderTarget(0, 0),
VulkanResource(VulkanResourceType::RENDER_TARGET),
mOffscreen(false), mSamples(1) {}
void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) {
assert_invariant(!mOffscreen);
VkExtent2D const extent = swapChain.getExtent();
mColor[0] = { .texture = swapChain.getCurrentColor() };
mDepth = { .texture = swapChain.getDepth() };
width = extent.width;
height = extent.height;
mProtected = swapChain.isProtected();
VulkanAttachment color = {.texture = swapChain.getCurrentColor()};
mInfo->attachments = {color};
auto& fbkey = mInfo->fbkey;
auto& rpkey = mInfo->rpkey;
rpkey.colorFormat[0] = color.getFormat();
fbkey.width = width;
fbkey.height = height;
fbkey.color[0] = color.getImageView();
fbkey.resolve[0] = VK_NULL_HANDLE;
VulkanAttachment depth = {};
rpkey.depthFormat = depth.getFormat();
fbkey.depth = VK_NULL_HANDLE;
if (swapChain.getDepth()) {
depth = {.texture = swapChain.getDepth()};
mInfo->attachments.push_back(depth);
mInfo->depthIndex = 1;
rpkey.depthFormat = depth.getFormat();
fbkey.depth = depth.getImageView();
}
mInfo->colors.set(0);
}
VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice,
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
VulkanResourceAllocator* handleAllocator, uint32_t width, uint32_t height, uint8_t samples,
VulkanResourceAllocator* handleAllocator,
uint32_t width, uint32_t height, uint8_t samples,
VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool, uint8_t layerCount)
: HwRenderTarget(width, height),
VulkanResource(VulkanResourceType::RENDER_TARGET),
mOffscreen(true),
mProtected(false),
mResources(handleAllocator),
mInfo(std::make_unique<Auxiliary>()) {
mSamples(samples),
mLayerCount(layerCount) {
for (int index = 0; index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; index++) {
mColor[index] = color[index];
}
mDepth = depthStencil[0];
VulkanTexture* depthTexture = (VulkanTexture*) mDepth.texture;
auto& depth = depthStencil[0];
if (samples == 1) {
return;
}
// Constrain the sample count according to both kinds of sample count masks obtained from
// VkPhysicalDeviceProperties. This is consistent with the VulkanTexture constructor.
auto const& limits = context.getPhysicalDeviceLimits();
samples = reduceSampleCount(samples, limits.framebufferDepthSampleCounts &
mSamples = samples = reduceSampleCount(samples, limits.framebufferDepthSampleCounts &
limits.framebufferColorSampleCounts);
auto& rpkey = mInfo->rpkey;
rpkey.samples = samples;
rpkey.depthFormat = depth.getFormat();
rpkey.viewCount = layerCount;
auto& fbkey = mInfo->fbkey;
fbkey.width = width;
fbkey.height = height;
fbkey.samples = samples;
std::vector<VulkanAttachment>& attachments = mInfo->attachments;
std::vector<VulkanAttachment> msaa;
// Create sidecar MSAA textures for color attachments if they don't already exist.
for (int index = 0; index < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; index++) {
VulkanAttachment& attachment = color[index];
auto texture = attachment.texture;
if (!texture) {
rpkey.colorFormat[index] = VK_FORMAT_UNDEFINED;
continue;
}
VulkanAttachment const& spec = color[index];
VulkanTexture* texture = (VulkanTexture*) spec.texture;
if (texture && texture->samples == 1) {
auto msTexture = texture->getSidecar();
if (UTILS_UNLIKELY(!msTexture)) {
// Clear all usage flags that are not related to attachments, so that we can
// use the transient usage flag.
const TextureUsage usage = texture->usage & TextureUsage::ALL_ATTACHMENTS;
assert_invariant(static_cast<uint16_t>(usage) != 0U);
attachments.push_back(attachment);
mInfo->colors.set(index);
rpkey.colorFormat[index] = attachment.getFormat();
fbkey.color[index] = attachment.getImageView();
fbkey.resolve[index] = VK_NULL_HANDLE;
mResources.acquire(attachment.texture);
if (samples > 1) {
VulkanAttachment msaaAttachment = {};
if (texture->samples == 1) {
auto msaaTexture = initMsaaTexture(texture, device, physicalDevice, context,
allocator, commands, handleAllocator,
((VulkanTexture const*) texture)->levels, samples, stagePool);
if (msaaTexture && msaaTexture->isTransientAttachment()) {
rpkey.usesLazilyAllocatedMemory |= (1 << index);
}
if (attachment.texture->samples == 1) {
rpkey.needsResolveMask |= (1 << index);
}
msaaAttachment = {
.texture = msaaTexture,
.layerCount = layerCount,
};
fbkey.resolve[index] = attachment.getImageView();
} else {
msaaAttachment = {
.texture = texture,
.layerCount = layerCount,
};
// TODO: This should be allocated with the ResourceAllocator.
msTexture = new VulkanTexture(device, physicalDevice, context, allocator, commands,
handleAllocator,
texture->target, ((VulkanTexture const*) texture)->levels, texture->format,
samples, texture->width, texture->height, texture->depth, usage,
stagePool, true /* heap allocated */);
texture->setSidecar(msTexture);
}
fbkey.color[index] = msaaAttachment.getImageView();
msaa.push_back(msaaAttachment);
mResources.acquire(msaaAttachment.texture);
mMsaaAttachments[index] = {.texture = msTexture};
}
if (texture && texture->samples > 1) {
mMsaaAttachments[index] = mColor[index];
}
}
if (attachments.size() > 0 && samples > 1 && msaa.size() > 0) {
mInfo->msaaIndex = (uint8_t) attachments.size();
attachments.insert(attachments.end(), msaa.begin(), msaa.end());
if (!depthTexture) {
return;
}
if (depth.texture) {
auto depthTexture = depth.texture;
mInfo->depthIndex = (uint8_t) attachments.size();
attachments.push_back(depth);
mResources.acquire(depthTexture);
fbkey.depth = depth.getImageView();
if (samples > 1) {
mInfo->msaaDepthIndex = mInfo->depthIndex;
if (depthTexture->samples == 1) {
// MSAA depth texture must have the mipmap count of 1
uint8_t const msLevel = 1;
// Create sidecar MSAA texture for the depth attachment if it does not already exist.
auto msaa = initMsaaTexture(depthTexture, device, physicalDevice, context, allocator,
commands, handleAllocator, msLevel, samples, stagePool);
mInfo->msaaDepthIndex = (uint8_t) attachments.size();
attachments.push_back({ .texture = msaa, .layerCount = layerCount });
mResources.acquire(msaa);
}
}
// There is no need for sidecar depth if the depth texture is already MSAA.
if (depthTexture->samples > 1) {
mMsaaDepthAttachment = mDepth;
return;
}
// MSAA depth texture must have the mipmap count of 1
uint8_t const msLevel = 1;
// Create sidecar MSAA texture for the depth attachment if it does not already exist.
VulkanTexture* msTexture = depthTexture->getSidecar();
if (UTILS_UNLIKELY(!msTexture)) {
msTexture = new VulkanTexture(device, physicalDevice, context, allocator,
commands, handleAllocator,
depthTexture->target, msLevel, depthTexture->format, samples,
depthTexture->width, depthTexture->height, depthTexture->depth, depthTexture->usage,
stagePool, true /* heap allocated */);
depthTexture->setSidecar(msTexture);
}
mMsaaDepthAttachment = {
.texture = msTexture,
.level = msLevel,
.layer = mDepth.layer,
};
}
void VulkanRenderTarget::transformClientRectToPlatform(VkRect2D* bounds) const {
auto const& extent = getExtent();
const auto& extent = getExtent();
flipVertically(bounds, extent.height);
clampToFramebuffer(bounds, extent.width, extent.height);
}
void VulkanRenderTarget::transformViewportToPlatform(VkViewport* bounds) const {
void VulkanRenderTarget::transformClientRectToPlatform(VkViewport* bounds) const {
flipVertically(bounds, getExtent().height);
}
VkExtent2D VulkanRenderTarget::getExtent() const {
return {width, height};
}
VulkanAttachment& VulkanRenderTarget::getColor(int target) {
return mColor[target];
}
VulkanAttachment& VulkanRenderTarget::getMsaaColor(int target) {
return mMsaaAttachments[target];
}
VulkanAttachment& VulkanRenderTarget::getDepth() {
return mDepth;
}
VulkanAttachment& VulkanRenderTarget::getMsaaDepth() {
return mMsaaDepthAttachment;
}
uint8_t VulkanRenderTarget::getColorTargetCount(const VulkanRenderPass& pass) const {
if (!mOffscreen) {
return 1;
}
if (pass.currentSubpass == 1) {
return mInfo->colors.count();
}
uint8_t count = 0;
mInfo->colors.forEachSetBit([&count, &pass](size_t index) {
if (!(pass.params.subpassMask & (1 << index))) {
for (uint8_t i = 0; i < MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT; i++) {
if (!mColor[i].texture) {
continue;
}
// NOTE: This must be consistent with VkRenderPass construction (see VulkanFboCache).
if (!(pass.params.subpassMask & (1 << i)) || pass.currentSubpass == 1) {
count++;
}
});
}
return count;
}
void VulkanRenderTarget::emitBarriersBeginRenderPass(VulkanCommandBuffer& commands) {
auto& attachments = mInfo->attachments;
auto samples = mInfo->fbkey.samples;
auto barrier = [&commands](VulkanAttachment& attachment, VulkanLayout const layout) {
auto tex = attachment.texture;
auto const& range = attachment.getSubresourceRange();
if (tex->getLayout(range.baseMipLevel, range.baseArrayLayer) != layout &&
!tex->transitionLayout(&commands, range, layout)) {
// If the layout transition did not emit a barrier, we do it manually here.
tex->samplerToAttachmentBarrier(&commands, range);
}
};
for (size_t i = 0, count = mInfo->colors.count(); i < count; ++i) {
auto& attachment = attachments[i];
auto tex = attachment.texture;
if (samples == 1 || tex->samples == 1) {
barrier(attachment, VulkanLayout::COLOR_ATTACHMENT);
}
}
if (mInfo->msaaIndex != Auxiliary::UNDEFINED_INDEX) {
for (size_t i = mInfo->msaaIndex, count = mInfo->msaaIndex + mInfo->colors.count();
i < count; ++i) {
barrier(attachments[i], VulkanLayout::COLOR_ATTACHMENT);
}
}
if (mInfo->depthIndex != Auxiliary::UNDEFINED_INDEX) {
barrier(attachments[mInfo->depthIndex], VulkanLayout::DEPTH_ATTACHMENT);
}
if (mInfo->msaaDepthIndex != Auxiliary::UNDEFINED_INDEX) {
barrier(attachments[mInfo->msaaDepthIndex], VulkanLayout::DEPTH_ATTACHMENT);
}
}
void VulkanRenderTarget::emitBarriersEndRenderPass(VulkanCommandBuffer& commands) {
if (isSwapChain()) {
return;
}
for (auto& attachment: mInfo->attachments) {
auto const& range = attachment.getSubresourceRange();
bool const isDepth = attachment.isDepth();
auto texture = attachment.texture;
if (isDepth) {
texture->setLayout(range, VulkanFboCache::FINAL_DEPTH_ATTACHMENT_LAYOUT);
if (!texture->transitionLayout(&commands, range, VulkanLayout::DEPTH_SAMPLER)) {
texture->attachmentToSamplerBarrier(&commands, range);
}
} else {
texture->setLayout(range, VulkanFboCache::FINAL_COLOR_ATTACHMENT_LAYOUT);
if (!texture->transitionLayout(&commands, range, VulkanLayout::READ_WRITE)) {
texture->attachmentToSamplerBarrier(&commands, range);
}
}
}
}
VulkanVertexBufferInfo::VulkanVertexBufferInfo(
uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
@@ -562,6 +454,29 @@ VulkanBufferObject::VulkanBufferObject(VmaAllocator allocator, VulkanStagePool&
buffer(allocator, stagePool, getBufferObjectUsage(bindingType), byteCount),
bindingType(bindingType) {}
VulkanTimerQuery::VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices)
: VulkanThreadSafeResource(VulkanResourceType::TIMER_QUERY),
mStartingQueryIndex(std::get<0>(indices)),
mStoppingQueryIndex(std::get<1>(indices)) {}
void VulkanTimerQuery::setFence(std::shared_ptr<VulkanCmdFence> fence) noexcept {
std::unique_lock<utils::Mutex> lock(mFenceMutex);
mFence = fence;
}
bool VulkanTimerQuery::isCompleted() noexcept {
std::unique_lock<utils::Mutex> lock(mFenceMutex);
// QueryValue is a synchronous call and might occur before beginTimerQuery has written anything
// into the command buffer, which is an error according to the validation layer that ships in
// the Android NDK. Even when AVAILABILITY_BIT is set, validation seems to require that the
// timestamp has at least been written into a processed command buffer.
// This fence indicates that the corresponding buffer has been completed.
return mFence && mFence->getStatus() == VK_SUCCESS;
}
VulkanTimerQuery::~VulkanTimerQuery() = default;
VulkanRenderPrimitive::VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator,
PrimitiveType pt, Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh)
: VulkanResource(VulkanResourceType::RENDER_PRIMITIVE),
+71 -77
View File
@@ -21,7 +21,6 @@
#include "DriverBase.h"
#include "VulkanBuffer.h"
#include "VulkanFboCache.h"
#include "VulkanResources.h"
#include "VulkanSwapChain.h"
#include "VulkanTexture.h"
@@ -136,13 +135,9 @@ public:
using OnRecycle = std::function<void(VulkanDescriptorSet*)>;
VulkanDescriptorSet(VulkanResourceAllocator* allocator, VkDescriptorSet rawSet,
UniformBufferBitmask const& dynamicUboMask,
uint8_t uniqueDynamicUboCount,
OnRecycle&& onRecycleFn)
: VulkanResource(VulkanResourceType::DESCRIPTOR_SET),
vkSet(rawSet),
dynamicUboMask(dynamicUboMask),
uniqueDynamicUboCount(uniqueDynamicUboCount),
mResources(allocator),
mOnRecycleFn(std::move(onRecycleFn)) {}
@@ -152,24 +147,13 @@ public:
}
}
void setOffsets(backend::DescriptorSetOffsetArray&& offsets) noexcept {
mOffsets = std::move(offsets);
}
backend::DescriptorSetOffsetArray const* getOffsets() {
return &mOffsets;
}
void acquire(VulkanTexture* texture);
void acquire(VulkanBufferObject* texture);
VkDescriptorSet const vkSet;
UniformBufferBitmask const dynamicUboMask;
uint8_t const uniqueDynamicUboCount;
private:
backend::DescriptorSetOffsetArray mOffsets;
VulkanAcquireOnlyResourceManager mResources;
OnRecycle mOnRecycleFn;
};
@@ -210,6 +194,10 @@ struct VulkanProgram : public HwProgram, VulkanResource {
inline VkShaderModule getFragmentShader() const { return mInfo->shaders[1]; }
// Get a list of the sampler binding indices so that we don't have to loop through all possible
// samplers.
inline BindingList const& getBindings() const { return mInfo->bindings; }
inline uint32_t getPushConstantRangeCount() const {
return mInfo->pushConstantDescription.getVkRangeCount();
}
@@ -237,10 +225,22 @@ private:
struct PipelineInfo {
explicit PipelineInfo(backend::Program const& program) noexcept
: pushConstantDescription(program)
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
, bindingToName(MAX_SAMPLER_COUNT, "")
#endif
{}
BindingList bindings;
VkShaderModule shaders[MAX_SHADER_MODULES] = { VK_NULL_HANDLE };
PushConstantDescription pushConstantDescription;
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
// We store the sampler name mapped from binding index (only for debug purposes).
utils::FixedCapacityVector<std::string> bindingToName;
#endif
};
PipelineInfo* mInfo;
@@ -269,70 +269,28 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
explicit VulkanRenderTarget();
void transformClientRectToPlatform(VkRect2D* bounds) const;
void transformViewportToPlatform(VkViewport* bounds) const;
inline VkExtent2D getExtent() const {
return {width, height};
}
inline VulkanAttachment const& getColor0() const {
assert_invariant(mInfo->colors[0]);
return mInfo->attachments[0];
}
inline VulkanAttachment const& getDepth() const {
assert_invariant(hasDepth());
if (mInfo->fbkey.samples == 1) {
return mInfo->attachments[mInfo->depthIndex];
}
return mInfo->attachments[mInfo->msaaDepthIndex];
}
inline VulkanFboCache::RenderPassKey const& getRenderPassKey() const {
return mInfo->rpkey;
}
inline VulkanFboCache::FboKey const& getFboKey() const {
return mInfo->fbkey;
}
inline uint8_t getSamples() const {
return mInfo->fbkey.samples;
}
uint8_t getColorTargetCount(VulkanRenderPass const& pass) const;
inline bool hasDepth() const { return mInfo->depthIndex != Auxiliary::UNDEFINED_INDEX; }
inline bool isSwapChain() const { return !mOffscreen; }
inline bool isProtected() const { return mProtected; }
void transformClientRectToPlatform(VkViewport* bounds) const;
VkExtent2D getExtent() const;
// We return references in the following methods to avoid a copy.
VulkanAttachment& getColor(int target);
VulkanAttachment& getMsaaColor(int target);
VulkanAttachment& getDepth();
VulkanAttachment& getMsaaDepth();
uint8_t getColorTargetCount(const VulkanRenderPass& pass) const;
uint8_t getSamples() const { return mSamples; }
uint8_t getLayerCount() const { return mLayerCount; }
bool hasDepth() const { return mDepth.texture; }
bool isSwapChain() const { return !mOffscreen; }
void bindToSwapChain(VulkanSwapChain& surf);
void emitBarriersBeginRenderPass(VulkanCommandBuffer& commands);
void emitBarriersEndRenderPass(VulkanCommandBuffer& commands);
private:
struct Auxiliary {
static constexpr int8_t UNDEFINED_INDEX = -1;
explicit Auxiliary() noexcept = default;
VulkanFboCache::RenderPassKey rpkey = {};
VulkanFboCache::FboKey fbkey = {};
std::vector<VulkanAttachment> attachments;
utils::bitset32 colors;
int8_t depthIndex = UNDEFINED_INDEX;
int8_t msaaDepthIndex = UNDEFINED_INDEX;
int8_t msaaIndex = UNDEFINED_INDEX;
};
bool const mOffscreen;
bool mProtected;
VulkanAcquireOnlyResourceManager mResources;
std::unique_ptr<Auxiliary> mInfo;
VulkanAttachment mColor[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
VulkanAttachment mDepth = {};
VulkanAttachment mMsaaAttachments[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT] = {};
VulkanAttachment mMsaaDepthAttachment = {};
const bool mOffscreen : 1;
uint8_t mSamples : 7;
uint8_t mLayerCount = 1;
};
struct VulkanBufferObject;
@@ -450,6 +408,42 @@ private:
FixedSizeVulkanResourceManager<2> mResources;
};
struct VulkanFence : public HwFence, VulkanResource {
VulkanFence()
: VulkanResource(VulkanResourceType::FENCE) {}
explicit VulkanFence(std::shared_ptr<VulkanCmdFence> fence)
: VulkanResource(VulkanResourceType::FENCE),
fence(fence) {}
std::shared_ptr<VulkanCmdFence> fence;
};
struct VulkanTimerQuery : public HwTimerQuery, VulkanThreadSafeResource {
explicit VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices);
~VulkanTimerQuery();
void setFence(std::shared_ptr<VulkanCmdFence> fence) noexcept;
bool isCompleted() noexcept;
uint32_t getStartingQueryIndex() const {
return mStartingQueryIndex;
}
uint32_t getStoppingQueryIndex() const {
return mStoppingQueryIndex;
}
private:
uint32_t mStartingQueryIndex;
uint32_t mStoppingQueryIndex;
std::shared_ptr<VulkanCmdFence> mFence;
utils::Mutex mFenceMutex;
};
inline constexpr VkBufferUsageFlagBits getBufferObjectUsage(
BufferObjectBinding bindingType) noexcept {
switch(bindingType) {
@@ -31,7 +31,6 @@
#pragma clang diagnostic ignored "-Wnullability-completeness"
#pragma clang diagnostic ignored "-Wextra-semi"
#pragma clang diagnostic ignored "-Wc++98-compat-extra-semi"
#pragma clang diagnostic ignored "-Wthread-safety-analysis"
static const PFN_vkGetInstanceProcAddr& vkGetInstanceProcAddr = bluevk::vkGetInstanceProcAddr;
static const PFN_vkGetDeviceProcAddr& vkGetDeviceProcAddr = bluevk::vkGetDeviceProcAddr;
@@ -66,12 +66,17 @@ void VulkanPipelineCache::bindPipeline(VulkanCommandBuffer* commands) {
VkCommandBuffer const cmdbuffer = commands->buffer();
PipelineCacheEntry* cacheEntry = getOrCreatePipeline();
// Check if the required pipeline is already bound.
if (cacheEntry->handle == commands->pipeline()) {
return;
}
// If an error occurred, allow higher levels to handle it gracefully.
assert_invariant(cacheEntry != nullptr && "Failed to create/find pipeline");
mBoundPipeline = mPipelineRequirements;
vkCmdBindPipeline(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, cacheEntry->handle);
commands->setPipeline(cacheEntry->handle);
}
VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() noexcept {
@@ -143,7 +143,7 @@ void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint
VkCommandPool& cmdpool = mCommandPool;
VulkanTexture* srcTexture = srcTarget->getColor0().texture;
VulkanTexture* srcTexture = srcTarget->getColor(0).texture;
assert_invariant(srcTexture);
VkFormat const srcFormat = srcTexture->getVkFormat();
bool const swizzle
@@ -230,7 +230,7 @@ void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint
},
});
VulkanAttachment const srcAttachment = srcTarget->getColor0();
VulkanAttachment const srcAttachment = srcTarget->getColor(0);
VkImageSubresourceRange const srcRange = srcAttachment.getSubresourceRange();
srcTexture->transitionLayout(cmdbuffer, srcRange, VulkanLayout::TRANSFER_SRC);
@@ -63,20 +63,14 @@ void VulkanSwapChain::update() {
mColors.reserve(bundle.colors.size());
VkDevice const device = mPlatform->getDevice();
TextureUsage depthUsage = TextureUsage::DEPTH_ATTACHMENT;
TextureUsage colorUsage = TextureUsage::COLOR_ATTACHMENT;
if (bundle.isProtected) {
depthUsage |= TextureUsage::PROTECTED;
colorUsage |= TextureUsage::PROTECTED;
}
for (auto const color: bundle.colors) {
mColors.push_back(std::make_unique<VulkanTexture>(device, mAllocator, mCommands, mHandleAllocator,
color, bundle.colorFormat, 1, bundle.extent.width, bundle.extent.height,
colorUsage, mStagePool, true /* heap allocated */));
TextureUsage::COLOR_ATTACHMENT, mStagePool, true /* heap allocated */));
}
mDepth = std::make_unique<VulkanTexture>(device, mAllocator, mCommands, mHandleAllocator,
bundle.depth, bundle.depthFormat, 1, bundle.extent.width, bundle.extent.height,
depthUsage, mStagePool, true /* heap allocated */);
TextureUsage::DEPTH_ATTACHMENT, mStagePool, true /* heap allocated */);
mExtent = bundle.extent;
}
@@ -74,9 +74,6 @@ struct VulkanSwapChain : public HwSwapChain, VulkanResource {
return mExtent;
}
inline bool isProtected() noexcept {
return mPlatform->isProtected(swapChain);
}
private:
static constexpr int IMAGE_READY_SEMAPHORE_COUNT = FVK_MAX_COMMAND_BUFFERS;
+4 -12
View File
@@ -147,14 +147,12 @@ inline VulkanLayout getDefaultLayoutImpl(VkImageUsageFlags vkusage) {
VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator,
VulkanCommands* commands, VulkanStagePool& stagePool, VkFormat format,
VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VulkanLayout defaultLayout,
bool isProtected)
VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VulkanLayout defaultLayout)
: VulkanResource(VulkanResourceType::HEAP_ALLOCATED),
mVkFormat(format),
mViewType(viewType),
mFullViewRange{filament::backend::getImageAspect(format), 0, levels, 0, layerCount},
mDefaultLayout(defaultLayout),
mIsProtected(isProtected),
mStagePool(stagePool),
mDevice(device),
mAllocator(allocator),
@@ -185,8 +183,7 @@ VulkanTexture::VulkanTexture(
mState(handleAllocator->initHandle<VulkanTextureState>(
device, allocator, commands, stagePool,
format, imgutil::getViewType(SamplerType::SAMPLER_2D), 1, 1,
getDefaultLayoutImpl(tusage),
any(usage& TextureUsage::PROTECTED))) {
getDefaultLayoutImpl(tusage))) {
auto* const state = getSharedState();
state->mTextureImage = image;
mPrimaryViewRange = state->mFullViewRange;
@@ -204,7 +201,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
mAllocator(handleAllocator),
mState(handleAllocator->initHandle<VulkanTextureState>(device, allocator, commands, stagePool,
backend::getVkFormat(tformat), imgutil::getViewType(target), levels,
getLayerCount(target, depth), VulkanLayout::UNDEFINED, any(usage& TextureUsage::PROTECTED))) {
getLayerCount(target, depth), VulkanLayout::UNDEFINED)) {
auto* const state = getSharedState();
// Create an appropriately-sized device-only VkImage, but do not fill it yet.
@@ -235,9 +232,6 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.arrayLayers = depth * 6;
imageInfo.extent.depth = 1;
}
if (any(usage & TextureUsage::PROTECTED)) {
imageInfo.flags |= VK_IMAGE_CREATE_PROTECTED_BIT;
}
if (any(usage & TextureUsage::BLIT_SRC)) {
imageInfo.usage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
@@ -337,8 +331,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
const VkFlags requiredMemoryFlags =
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
(useTransientAttachment ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0U) |
(state->mIsProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
(useTransientAttachment ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0U);
uint32_t memoryTypeIndex
= context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags);
@@ -423,7 +416,6 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
assert_invariant(depth <= this->depth * ((target == SamplerType::SAMPLER_CUBEMAP ||
target == SamplerType::SAMPLER_CUBEMAP_ARRAY) ? 6 : 1));
auto* const state = getSharedState();
assert_invariant(state->mIsProtected == false);
const PixelBufferDescriptor* hostData = &data;
PixelBufferDescriptor reshapedData;
+1 -8
View File
@@ -35,7 +35,7 @@ class VulkanResourceAllocator;
struct VulkanTextureState : public VulkanResource {
VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
VulkanStagePool& stagePool, VkFormat format, VkImageViewType viewType, uint8_t levels,
uint8_t layerCount, VulkanLayout defaultLayout, bool isProtected);
uint8_t layerCount, VulkanLayout defaultLayout);
struct ImageViewKey {
VkImageSubresourceRange range; // 4 * 5 bytes
@@ -70,8 +70,6 @@ struct VulkanTextureState : public VulkanResource {
VkImage mTextureImage = VK_NULL_HANDLE;
VulkanLayout mDefaultLayout;
bool mIsProtected = false;
// Track the image layout of each subresource using a sparse range map.
utils::RangeMap<uint32_t, VulkanLayout> mSubresourceLayouts;
@@ -181,11 +179,6 @@ struct VulkanTexture : public HwTexture, VulkanResource {
return state->mIsTransientAttachment;
}
bool getIsProtected() const {
VulkanTextureState const* state = getSharedState();
return state->mIsProtected;
}
bool transitionLayout(VulkanCommandBuffer* commands, VkImageSubresourceRange const& range,
VulkanLayout newLayout);
@@ -347,6 +347,70 @@ private:
mVkLayouts;
};
class VulkanDescriptorSetManager::DescriptorSetHistory {
private:
using TextureBundle = std::pair<VulkanTexture*, VkImageSubresourceRange>;
public:
DescriptorSetHistory()
: dynamicUboCount(0),
mResources(nullptr) {}
DescriptorSetHistory(UniformBufferBitmask const& dynamicUbo, uint8_t uniqueDynamicUboCount,
VulkanResourceAllocator* allocator, VulkanDescriptorSet* set)
: dynamicUboMask(dynamicUbo),
dynamicUboCount(uniqueDynamicUboCount),
mResources(allocator),
mSet(set),
mBound(false) {
assert_invariant(set);
// initial state is unbound.
unbind();
}
~DescriptorSetHistory() {
if (mSet) {
mResources.clear();
}
}
void setOffsets(backend::DescriptorSetOffsetArray&& offsets) noexcept {
mOffsets = std::move(offsets);
mBound = false;
}
void write(uint8_t binding) noexcept { mBound = false; }
// Ownership will be transfered to the commandbuffer.
void bind(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout,
uint8_t index) noexcept {
VkCommandBuffer const cmdbuffer = commands->buffer();
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index,
1, &mSet->vkSet, dynamicUboCount, mOffsets.data());
commands->acquire(mSet);
mResources.clear();
mBound = true;
}
void unbind() noexcept {
mResources.acquire(mSet);
mBound = false;
}
bool bound() const noexcept { return mBound; }
UniformBufferBitmask const dynamicUboMask;
uint8_t const dynamicUboCount;
private:
FixedSizeVulkanResourceManager<1> mResources;
VulkanDescriptorSet* mSet = nullptr;
backend::DescriptorSetOffsetArray mOffsets;
bool mBound = false;
};
VulkanDescriptorSetManager::VulkanDescriptorSetManager(VkDevice device,
VulkanResourceAllocator* resourceAllocator)
: mDevice(device),
@@ -359,57 +423,50 @@ VulkanDescriptorSetManager::~VulkanDescriptorSetManager() = default;
void VulkanDescriptorSetManager::terminate() noexcept{
mLayoutManager.reset();
mDescriptorPool.reset();
mHistory.clear();
}
// bind() is not really binding the set but just stashing until we have all the info
// (pipelinelayout).
void VulkanDescriptorSetManager::bind(uint8_t setIndex, VulkanDescriptorSet* set,
backend::DescriptorSetOffsetArray&& offsets) {
set->setOffsets(std::move(offsets));
mStashedSets[setIndex] = set;
}
auto history = mHistory[set].get();
history->setOffsets(std::move(offsets));
void VulkanDescriptorSetManager::unbind(uint8_t setIndex) {
mStashedSets[setIndex] = nullptr;
auto lastHistory = mStashedSets[setIndex];
if (lastHistory) {
lastHistory->unbind();
}
mStashedSets[setIndex] = history;
}
void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands,
VkPipelineLayout pipelineLayout, DescriptorSetMask const& setMask) {
DescriptorSetHistoryArray& updateSets = mStashedSets;
// setMask indicates the set of descriptor sets the driver wants to bind, curMask is the
// actual set of sets that *needs* to be bound.
DescriptorSetMask curMask = setMask;
auto& updateSets = mStashedSets;
auto& lastBoundSets = mLastBoundInfo.boundSets;
setMask.forEachSetBit([&](size_t index) {
if (!updateSets[index] || updateSets[index] == lastBoundSets[index]) {
if (!updateSets[index] || updateSets[index]->bound()) {
curMask.unset(index);
}
});
if (curMask.none() &&
(mLastBoundInfo.pipelineLayout == pipelineLayout && mLastBoundInfo.setMask == setMask &&
mLastBoundInfo.boundSets == updateSets)) {
return;
}
curMask.forEachSetBit([&updateSets, commands, pipelineLayout](size_t index) {
// This code actually binds the descriptor sets.
auto set = updateSets[index];
VkCommandBuffer const cmdbuffer = commands->buffer();
vkCmdBindDescriptorSets(cmdbuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, index,
1, &set->vkSet, set->uniqueDynamicUboCount, set->getOffsets()->data());
commands->acquire(set);
});
mStashedSets = {};
mLastBoundInfo = {
BoundInfo nextInfo = {
pipelineLayout,
setMask,
updateSets,
};
if (curMask.none() && mLastBoundInfo == nextInfo) {
return;
}
curMask.forEachSetBit([&updateSets, commands, pipelineLayout](size_t index) {
updateSets[index]->bind(commands, pipelineLayout, index);
});
mLastBoundInfo = nextInfo;
}
void VulkanDescriptorSetManager::updateBuffer(VulkanDescriptorSet* set, uint8_t binding,
@@ -421,8 +478,9 @@ void VulkanDescriptorSetManager::updateBuffer(VulkanDescriptorSet* set, uint8_t
};
VkDescriptorType type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
auto& history = mHistory[set];
if (set->dynamicUboMask.test(binding)) {
if (history->dynamicUboMask.test(binding)) {
type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
}
VkWriteDescriptorSet const descriptorWrite = {
@@ -436,6 +494,7 @@ void VulkanDescriptorSetManager::updateBuffer(VulkanDescriptorSet* set, uint8_t
};
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
set->acquire(bufferObject);
history->write(binding);
}
void VulkanDescriptorSetManager::updateSampler(VulkanDescriptorSet* set, uint8_t binding,
@@ -467,6 +526,7 @@ void VulkanDescriptorSetManager::updateSampler(VulkanDescriptorSet* set, uint8_t
};
vkUpdateDescriptorSets(mDevice, 1, &descriptorWrite, 0, nullptr);
set->acquire(texture);
mHistory[set]->write(binding);
}
void VulkanDescriptorSetManager::updateInputAttachment(VulkanDescriptorSet* set,
@@ -479,15 +539,18 @@ void VulkanDescriptorSetManager::createSet(Handle<HwDescriptorSet> handle,
auto const vkSet = mDescriptorPool->obtainSet(layout);
auto const& count = layout->count;
auto const vklayout = layout->getVkLayout();
mResourceAllocator->construct<VulkanDescriptorSet>(handle, mResourceAllocator, vkSet,
layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
[vkSet, count, vklayout, this](VulkanDescriptorSet* set) {
VulkanDescriptorSet* set = mResourceAllocator->construct<VulkanDescriptorSet>(handle,
mResourceAllocator, vkSet, [vkSet, count, vklayout, this](VulkanDescriptorSet* set) {
eraseSetFromHistory(set);
mDescriptorPool->recycle(count, vklayout, vkSet);
});
mHistory[set] = std::make_unique<DescriptorSetHistory>(layout->bitmask.dynamicUbo,
layout->count.dynamicUbo, mResourceAllocator, set);
}
void VulkanDescriptorSetManager::destroySet(Handle<HwDescriptorSet> handle) {
VulkanDescriptorSet* set = mResourceAllocator->handle_cast<VulkanDescriptorSet*>(handle);
eraseSetFromHistory(set);
}
void VulkanDescriptorSetManager::initVkLayout(VulkanDescriptorSetLayout* layout) {
@@ -495,8 +558,11 @@ void VulkanDescriptorSetManager::initVkLayout(VulkanDescriptorSetLayout* layout)
}
void VulkanDescriptorSetManager::eraseSetFromHistory(VulkanDescriptorSet* set) {
DescriptorSetHistory* history = mHistory[set].get();
mHistory.erase(set);
for (uint8_t i = 0; i < mStashedSets.size(); ++i) {
if (mStashedSets[i] == set) {
if (mStashedSets[i] == history) {
mStashedSets[i] = nullptr;
}
}
@@ -61,8 +61,6 @@ public:
void bind(uint8_t setIndex, VulkanDescriptorSet* set, backend::DescriptorSetOffsetArray&& offsets);
void unbind(uint8_t setIndex);
void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout,
DescriptorSetMask const& setMask);
@@ -76,26 +74,43 @@ public:
void initVkLayout(VulkanDescriptorSetLayout* layout);
private:
class DescriptorSetHistory;
class DescriptorSetLayoutManager;
class DescriptorInfinitePool;
void eraseSetFromHistory(VulkanDescriptorSet* set);
using DescriptorSetArray =
std::array<VulkanDescriptorSet*, UNIQUE_DESCRIPTOR_SET_COUNT>;
using DescriptorSetHistoryArray =
std::array<DescriptorSetHistory*, UNIQUE_DESCRIPTOR_SET_COUNT>;
struct BoundInfo {
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
DescriptorSetMask setMask;
DescriptorSetHistoryArray boundSets;
bool operator==(BoundInfo const& info) const {
if (pipelineLayout != info.pipelineLayout || setMask != info.setMask) {
return false;
}
bool equal = true;
setMask.forEachSetBit([&](size_t i) {
if (boundSets[i] != info.boundSets[i]) {
equal = false;
}
});
return equal;
}
};
VkDevice mDevice;
VulkanResourceAllocator* mResourceAllocator;
std::unique_ptr<DescriptorSetLayoutManager> mLayoutManager;
std::unique_ptr<DescriptorInfinitePool> mDescriptorPool;
std::pair<VulkanAttachment, VkDescriptorImageInfo> mInputAttachment;
DescriptorSetArray mStashedSets = {};
std::unordered_map<VulkanDescriptorSet*, std::unique_ptr<DescriptorSetHistory>> mHistory;
DescriptorSetHistoryArray mStashedSets = {};
struct {
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
DescriptorSetMask setMask;
DescriptorSetArray boundSets = {};
} mLastBoundInfo;
BoundInfo mLastBoundInfo;
};
}// namespace filament::backend
@@ -1,129 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "vulkan/memory/Resource.h"
#include "vulkan/memory/ResourceManager.h"
#include "vulkan/VulkanHandles.h"
namespace filament::backend::fvkmemory {
template ResourceType getTypeEnum<VulkanBufferObject>() noexcept;
template ResourceType getTypeEnum<VulkanIndexBuffer>() noexcept;
template ResourceType getTypeEnum<VulkanProgram>() noexcept;
template ResourceType getTypeEnum<VulkanRenderTarget>() noexcept;
template ResourceType getTypeEnum<VulkanSwapChain>() noexcept;
template ResourceType getTypeEnum<VulkanRenderPrimitive>() noexcept;
template ResourceType getTypeEnum<VulkanTexture>() noexcept;
template ResourceType getTypeEnum<VulkanTextureState>() noexcept;
template ResourceType getTypeEnum<VulkanTimerQuery>() noexcept;
template ResourceType getTypeEnum<VulkanVertexBuffer>() noexcept;
template ResourceType getTypeEnum<VulkanVertexBufferInfo>() noexcept;
template ResourceType getTypeEnum<VulkanDescriptorSetLayout>() noexcept;
template ResourceType getTypeEnum<VulkanDescriptorSet>() noexcept;
template ResourceType getTypeEnum<VulkanFence>() noexcept;
template<typename D>
ResourceType getTypeEnum() noexcept {
if constexpr (std::is_same_v<D, VulkanBufferObject>) {
return ResourceType::BUFFER_OBJECT;
}
if constexpr (std::is_same_v<D, VulkanIndexBuffer>) {
return ResourceType::INDEX_BUFFER;
}
if constexpr (std::is_same_v<D, VulkanProgram>) {
return ResourceType::PROGRAM;
}
if constexpr (std::is_same_v<D, VulkanRenderTarget>) {
return ResourceType::RENDER_TARGET;
}
if constexpr (std::is_same_v<D, VulkanSwapChain>) {
return ResourceType::SWAP_CHAIN;
}
if constexpr (std::is_same_v<D, VulkanRenderPrimitive>) {
return ResourceType::RENDER_PRIMITIVE;
}
if constexpr (std::is_same_v<D, VulkanTexture>) {
return ResourceType::TEXTURE;
}
if constexpr (std::is_same_v<D, VulkanTextureState>) {
return ResourceType::TEXTURE_STATE;
}
if constexpr (std::is_same_v<D, VulkanTimerQuery>) {
return ResourceType::TIMER_QUERY;
}
if constexpr (std::is_same_v<D, VulkanVertexBuffer>) {
return ResourceType::VERTEX_BUFFER;
}
if constexpr (std::is_same_v<D, VulkanVertexBufferInfo>) {
return ResourceType::VERTEX_BUFFER_INFO;
}
if constexpr (std::is_same_v<D, VulkanDescriptorSetLayout>) {
return ResourceType::DESCRIPTOR_SET_LAYOUT;
}
if constexpr (std::is_same_v<D, VulkanDescriptorSet>) {
return ResourceType::DESCRIPTOR_SET;
}
if constexpr (std::is_same_v<D, VulkanFence>) {
return ResourceType::FENCE;
}
return ResourceType::UNDEFINED_TYPE;
}
std::string getTypeStr(ResourceType type) {
switch (type) {
case ResourceType::BUFFER_OBJECT:
return "BufferObject";
case ResourceType::INDEX_BUFFER:
return "IndexBuffer";
case ResourceType::PROGRAM:
return "Program";
case ResourceType::RENDER_TARGET:
return "RenderTarget";
case ResourceType::SWAP_CHAIN:
return "SwapChain";
case ResourceType::RENDER_PRIMITIVE:
return "RenderPrimitive";
case ResourceType::TEXTURE:
return "Texture";
case ResourceType::TEXTURE_STATE:
return "TextureState";
case ResourceType::TIMER_QUERY:
return "TimerQuery";
case ResourceType::VERTEX_BUFFER:
return "VertexBuffer";
case ResourceType::VERTEX_BUFFER_INFO:
return "VertexBufferInfo";
case ResourceType::DESCRIPTOR_SET_LAYOUT:
return "DescriptorSetLayout";
case ResourceType::DESCRIPTOR_SET:
return "DescriptorSet";
case ResourceType::FENCE:
return "Fence";
case ResourceType::UNDEFINED_TYPE:
return "";
}
}
void Resource::destroy(ResourceType type, HandleId id) {
resManager->destructLaterWithType(type, id);
}
void ThreadSafeResource::destroy(ResourceType type, HandleId id) {
resManager->destructLaterWithType(type, id);
}
} // namespace filament::backend::fvkmemory
@@ -1,143 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCE_H
#define TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCE_H
#include <private/backend/HandleAllocator.h>
#include <utils/Mutex.h>
#include <atomic>
#include <cstdint>
namespace filament::backend::fvkmemory {
using CounterIndex = int32_t;
using HandleId = HandleBase::HandleId;
class ResourceManager;
template <typename D>
struct resource_ptr;
// Subclasses of VulkanResource must provide this enum in their construction.
enum class ResourceType : uint8_t {
BUFFER_OBJECT = 0,
INDEX_BUFFER = 1,
PROGRAM = 2,
RENDER_TARGET = 3,
SWAP_CHAIN = 4,
RENDER_PRIMITIVE = 5,
TEXTURE = 6,
TEXTURE_STATE = 7,
TIMER_QUERY = 8,
VERTEX_BUFFER = 9,
VERTEX_BUFFER_INFO = 10,
DESCRIPTOR_SET_LAYOUT = 11,
DESCRIPTOR_SET = 12,
FENCE = 13,
UNDEFINED_TYPE = 14, // Must be the last enum because we use it for iterating over the enums.
};
template<typename D>
ResourceType getTypeEnum() noexcept;
std::string getTypeStr(ResourceType type);
inline bool isThreadSafeType(ResourceType type) {
return type == ResourceType::FENCE || type == ResourceType::TIMER_QUERY;
}
struct Resource {
Resource()
: resManager(nullptr),
id(HandleBase::nullid),
mCount(0),
restype(ResourceType::UNDEFINED_TYPE) {}
private:
inline void inc() noexcept {
mCount++;
}
inline void dec() noexcept {
assert_invariant(mCount > 0);
if (--mCount == 0) {
destroy(restype, id);
}
}
template <typename T>
inline void init(HandleId id, ResourceManager* resManager) {
this->id = id;
this->resManager = resManager;
this->restype = getTypeEnum<T>();
}
void destroy(ResourceType type, HandleId id);
ResourceManager* resManager; // 8
HandleId id; // 4
uint32_t mCount : 24;
ResourceType restype : 6; // restype + mCount is 4 bytes.
friend class ResourceManager;
template <typename D>
friend struct resource_ptr;
};
struct ThreadSafeResource {
ThreadSafeResource()
: resManager(nullptr),
id(HandleBase::nullid),
mCount(0),
restype(ResourceType::UNDEFINED_TYPE) {}
private:
inline void inc() noexcept {
mCount.fetch_add(1, std::memory_order_relaxed);
}
inline void dec() noexcept {
if (mCount.fetch_sub(1, std::memory_order_acq_rel) == 1) {
destroy(restype, id);
}
}
template <typename T>
inline void init(HandleId id, ResourceManager* resManager) {
this->id = id;
this->resManager = resManager;
this->restype = getTypeEnum<T>();
}
void destroy(ResourceType type, HandleId id);
ResourceManager* resManager; // 8
HandleId id; // 4
std::atomic<uint32_t> mCount; // 4
ResourceType restype; // 1
friend class ResourceManager;
template <typename D>
friend struct resource_ptr;
};
} // namespace filament::backend::fvkmemory
#endif // TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCE_H
@@ -1,130 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "vulkan/memory/ResourceManager.h"
#include "vulkan/VulkanHandles.h"
#include <utils/Panic.h>
namespace filament::backend::fvkmemory {
namespace {
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
uint32_t COUNTER[(size_t) ResourceType::UNDEFINED_TYPE] = {};
#endif
}
ResourceManager::ResourceManager(size_t arenaSize, bool disableUseAfterFreeCheck)
: mHandleAllocatorImpl("Handles", arenaSize, disableUseAfterFreeCheck) {}
void ResourceManager::gc() noexcept {
auto destroyAll = [this](GcList& list) {
for (auto const& [type, id]: list) {
destroyWithType(type, id);
}
list.clear();
};
{
// Note that we're not copying mThreadSafeGcList because the objects here do not have
// resource_ptrs to other handle objects, so their desctruction would not add more elements
// to mThreadSafeGcList.
std::unique_lock<utils::Mutex> lock(mThreadSafeGcListMutex);
destroyAll(mThreadSafeGcList);
}
GcList gcs;
gcs.insert(gcs.end(), mGcList.begin(), mGcList.end());
mGcList.clear();
destroyAll(gcs);
}
void ResourceManager::terminate() noexcept {
while (!mThreadSafeGcList.empty() || !mGcList.empty()) {
gc();
}
}
void ResourceManager::destroyWithType(ResourceType type, HandleId id) {
switch (type) {
case ResourceType::BUFFER_OBJECT:
destruct<VulkanBufferObject>(Handle<VulkanBufferObject>(id));
break;
case ResourceType::INDEX_BUFFER:
destruct<VulkanIndexBuffer>(Handle<VulkanIndexBuffer>(id));
break;
case ResourceType::PROGRAM:
destruct<VulkanProgram>(Handle<VulkanProgram>(id));
break;
case ResourceType::RENDER_TARGET:
destruct<VulkanRenderTarget>(Handle<VulkanRenderTarget>(id));
break;
case ResourceType::SWAP_CHAIN:
destruct<VulkanSwapChain>(Handle<VulkanSwapChain>(id));
break;
case ResourceType::RENDER_PRIMITIVE:
destruct<VulkanRenderPrimitive>(Handle<VulkanRenderPrimitive>(id));
break;
case ResourceType::TEXTURE:
destruct<VulkanTexture>(Handle<VulkanTexture>(id));
break;
case ResourceType::TEXTURE_STATE:
destruct<VulkanTextureState>(Handle<VulkanTextureState>(id));
break;
case ResourceType::TIMER_QUERY:
destruct<VulkanTimerQuery>(Handle<VulkanTimerQuery>(id));
break;
case ResourceType::VERTEX_BUFFER:
destruct<VulkanVertexBuffer>(Handle<VulkanVertexBuffer>(id));
break;
case ResourceType::VERTEX_BUFFER_INFO:
destruct<VulkanVertexBufferInfo>(Handle<VulkanVertexBufferInfo>(id));
break;
case ResourceType::DESCRIPTOR_SET_LAYOUT:
destruct<VulkanDescriptorSetLayout>(Handle<VulkanDescriptorSetLayout>(id));
break;
case ResourceType::DESCRIPTOR_SET:
destruct<VulkanDescriptorSet>(Handle<VulkanDescriptorSet>(id));
break;
case ResourceType::FENCE:
destruct<VulkanFence>(Handle<VulkanFence>(id));
break;
case ResourceType::UNDEFINED_TYPE:
break;
}
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
COUNTER[(size_t) type]--;
#endif
}
void ResourceManager::traceConstruction(ResourceType type, HandleId id) {
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
assert_invariant(type != ResourceType::UNDEFINED_TYPE);
COUNTER[(size_t) type]++;
#endif
}
void ResourceManager::print() const noexcept {
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
utils::slog.e << "-------------------" << utils::io::endl;
for (size_t i = 0; i < (size_t) ResourceType::UNDEFINED_TYPE; ++i) {
utils::slog.e <<" " << getTypeStr((ResourceType) i) << "=" << COUNTER[i] << utils::io::endl;
}
utils::slog.e << "+++++++++++++++++++" << utils::io::endl;
#endif
}
}
@@ -1,112 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCEMANAGER_H
#define TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCEMANAGER_H
#include "vulkan/memory/Resource.h"
#include "vulkan/VulkanAsyncHandles.h"
#include <private/backend/HandleAllocator.h>
#include <utils/Panic.h>
namespace filament::backend::fvkmemory {
class ResourceManager {
public:
ResourceManager(size_t arenaSize, bool disableUseAfterFreeCheck);
template<typename D>
inline Handle<D> allocHandle() noexcept {
return mHandleAllocatorImpl.allocate<D>();
}
inline void associateHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
mHandleAllocatorImpl.associateTagToHandle(id, std::move(tag));
}
void gc() noexcept;
void print() const noexcept;
void terminate() noexcept;
private:
using AllocatorImpl = HandleAllocatorVK;
template<typename D>
using requires_thread_safety = typename std::disjunction<std::is_same<D, VulkanFence>,
std::is_same<D, VulkanTimerQuery>>;
template<typename D, typename B, typename... ARGS>
inline D* construct(Handle<B> const& handle, ARGS&&... args) noexcept {
constexpr bool THREAD_SAFETY = requires_thread_safety<D>::value;
D* obj = mHandleAllocatorImpl.construct<D, B>(handle, std::forward<ARGS>(args)...);
if constexpr (THREAD_SAFETY) {
((ThreadSafeResource*) obj)->template init<D>(handle.getId(), this);
} else {
((Resource*) obj)->template init<D>(handle.getId(), this);
}
traceConstruction(getTypeEnum<D>(), handle.getId());
return obj;
}
template<typename Dp, typename B>
inline typename std::enable_if_t<
std::is_pointer_v<Dp> && std::is_base_of_v<B, typename std::remove_pointer_t<Dp>>, Dp>
handle_cast(Handle<B> const& handle) noexcept {
return mHandleAllocatorImpl.handle_cast<Dp, B>(handle);
}
template<typename D, typename B>
inline void destruct(Handle<B> handle) noexcept {
D* obj = handle_cast<D*>(handle);
mHandleAllocatorImpl.deallocate(handle, obj);
}
// This will post the destruction to a list that will be processed when gc is called from the
// backend thread.
inline void destructLaterWithType(ResourceType type, HandleId id) {
if (isThreadSafeType(type)) {
std::unique_lock<utils::Mutex> lock(mThreadSafeGcListMutex);
mThreadSafeGcList.push_back({type, id});
} else {
mGcList.push_back({type, id});
}
}
void destroyWithType(ResourceType type, HandleId id);
void traceConstruction(ResourceType type, HandleId id);
AllocatorImpl mHandleAllocatorImpl;
using GcList = std::vector<std::pair<ResourceType, HandleId>>;
utils::Mutex mThreadSafeGcListMutex;
GcList mThreadSafeGcList;
GcList mGcList;
template<typename D>
friend struct resource_ptr;
friend struct Resource;
friend struct ThreadSafeResource;
};
} // namespace filament::backend::fvkmemory
#endif // TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCEMANAGER_H
@@ -1,194 +0,0 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCEPOINTER_H
#define TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCEPOINTER_H
#include "vulkan/memory/Resource.h"
#include "vulkan/memory/ResourceManager.h"
#include <backend/Handle.h>
#include <utility>
namespace filament::backend {
class VulkanDriver;
namespace fvkmemory {
// This is a ref-counting, reference holder class (similar to std::shared_ptr) that serves as the
// primary interface between the vk backend (VulkanDriver) and the handle allocator. All objects
// that are allocated from the handle allocator are expected to be contained within a
template<typename D>
struct resource_ptr {
private:
template<typename B>
using enabled_resource_ptr =
resource_ptr<typename std::enable_if<std::is_base_of<B, D>::value, D>::type>;
public:
template<typename B, typename... ARGS>
static enabled_resource_ptr<B> make(ResourceManager* resManager, Handle<B> const& handle,
ARGS&&... args) noexcept {
D* ptr = resManager->construct<D, B>(handle, std::forward<ARGS>(args)...);
return {ptr};
}
// This will alloc a handle and then construct the object.
template<typename... ARGS>
static resource_ptr<D> construct(ResourceManager* resManager, ARGS&&... args) noexcept {
auto handle = resManager->allocHandle<D>();
D* ptr = resManager->construct<D, D>(handle, std::forward<ARGS>(args)...);
return {ptr};
}
template<typename B>
static enabled_resource_ptr<B> cast(ResourceManager* resManager,
Handle<B> const& handle) noexcept {
D* ptr = resManager->handle_cast<D*, B>(handle);
return {ptr};
}
template<typename B>
static enabled_resource_ptr<B> cast(B* ptr) noexcept {
return {ptr};
}
~resource_ptr() {
if (mRef) {
mRef->dec();
}
}
inline resource_ptr() = default;
// move constructor operator
inline resource_ptr(resource_ptr<D>&& rhs) {
(*this) = std::move(rhs);
}
inline resource_ptr(resource_ptr<D> const& rhs) {
(*this) = rhs;
}
template<typename E>
using is_supported_t =
typename std::enable_if<std::is_base_of<D, E>::value>::type;
template<typename E, typename = is_supported_t<E>>
inline resource_ptr(resource_ptr<E> const& rhs) {
(*this) = rhs;
}
// move operator
inline resource_ptr<D>& operator=(resource_ptr<D> && rhs) {
std::swap(mRef, rhs.mRef);
return *this;
}
inline resource_ptr<D>& operator=(resource_ptr<D> const& rhs) {
if (mRef == rhs.mRef) {
return *this;
}
if (mRef) {
mRef->dec();
}
mRef = rhs.mRef;
mRef->inc();
return *this;
}
template<typename E, typename = is_supported_t<E>>
inline resource_ptr<D>& operator=(resource_ptr<E> const& rhs) {
if (mRef == rhs.mRef) {
return *this;
}
if (mRef) {
mRef->dec();
}
mRef = rhs.mRef;
mRef->inc();
return *this;
}
inline bool operator==(resource_ptr<D> const& other) const {
return id() == other.id() && type() == other.type();
}
inline explicit operator bool() const {
return bool(mRef);
}
inline D* operator->() {
return get();
}
inline D const* operator->() const {
return get();
}
inline HandleId id() const {
if (mRef) {
return mRef->id;
}
return HandleBase::nullid;
}
inline D* get() const {
assert_invariant(mRef);
return mRef;
}
private:
// inc() and dec() For tracking ref-count with respect to create/destroy backend APIs. They can
// only be used from VulkanDriver.
inline void dec() {
assert_invariant(mRef);
mRef->dec();
}
inline void inc() {
assert_invariant(mRef);
mRef->inc();
}
inline ResourceType type() const {
if (mRef) {
return mRef->restype;
}
return ResourceType::UNDEFINED_TYPE;
}
resource_ptr(D* ref)
: mRef(ref) {
mRef->inc();
}
D* mRef = nullptr;
// Allow generic structures to hold resources of different types. For example,
// std::vector<fvkmemory::Resource> resources;
friend struct resource_ptr<Resource>;
friend struct resource_ptr<ThreadSafeResource>;
// This enables access to resource_ptr's private inc() and dec() methods.
friend class filament::backend::VulkanDriver;
};
} // namespace fvkmemory
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCEPOINTER_H
@@ -300,10 +300,10 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
}
VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceFeatures2 const& features, uint32_t graphicsQueueFamilyIndex,
uint32_t protectedGraphicsQueueFamilyIndex, ExtensionSet const& deviceExtensions) {
VkPhysicalDeviceFeatures const& features, uint32_t graphicsQueueFamilyIndex,
ExtensionSet const& deviceExtensions) {
VkDevice device;
VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = {};
VkDeviceQueueCreateInfo deviceQueueCreateInfo[1] = {};
const float queuePriority[] = {1.0f};
VkDeviceCreateInfo deviceCreateInfo = {};
FixedCapacityVector<const char*> requestExtensions;
@@ -314,30 +314,22 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
for (auto const& ext: deviceExtensions) {
requestExtensions.push_back(ext.data());
}
deviceQueueCreateInfo[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
deviceQueueCreateInfo[0].queueFamilyIndex = graphicsQueueFamilyIndex;
deviceQueueCreateInfo[0].queueCount = 1;
deviceQueueCreateInfo[0].pQueuePriorities = &queuePriority[0];
// Protected queue
deviceQueueCreateInfo[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
deviceQueueCreateInfo[1].flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT;
deviceQueueCreateInfo[1].queueFamilyIndex = protectedGraphicsQueueFamilyIndex;
deviceQueueCreateInfo[1].queueCount = 1;
deviceQueueCreateInfo[1].pQueuePriorities = &queuePriority[0];
deviceQueueCreateInfo->sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
deviceQueueCreateInfo->queueFamilyIndex = graphicsQueueFamilyIndex;
deviceQueueCreateInfo->queueCount = 1;
deviceQueueCreateInfo->pQueuePriorities = &queuePriority[0];
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
bool const hasProtectedQueue = protectedGraphicsQueueFamilyIndex != INVALID_VK_INDEX;
deviceCreateInfo.queueCreateInfoCount = hasProtectedQueue ? 2 : 1;
deviceCreateInfo.queueCreateInfoCount = 1;
deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
// We could simply enable all supported features, but since that may have performance
// consequences let's just enable the features we need.
VkPhysicalDeviceFeatures enabledFeatures{
.depthClamp = features.features.depthClamp,
.samplerAnisotropy = features.features.samplerAnisotropy,
.textureCompressionETC2 = features.features.textureCompressionETC2,
.textureCompressionBC = features.features.textureCompressionBC,
.shaderClipDistance = features.features.shaderClipDistance,
.depthClamp = features.depthClamp,
.samplerAnisotropy = features.samplerAnisotropy,
.textureCompressionETC2 = features.textureCompressionETC2,
.textureCompressionBC = features.textureCompressionBC,
.shaderClipDistance = features.shaderClipDistance,
};
deviceCreateInfo.pEnabledFeatures = &enabledFeatures;
@@ -368,16 +360,6 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
pNext = &multiview;
}
VkPhysicalDeviceProtectedMemoryFeatures protectedMemory = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES,
};
if (hasProtectedQueue) {
// Enable protected memory, if requested.
protectedMemory.protectedMemory = VK_TRUE;
protectedMemory.pNext = pNext;
pNext = &protectedMemory;
}
deviceCreateInfo.pNext = pNext;
VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, VKALLOC, &device);
@@ -424,13 +406,13 @@ FixedCapacityVector<VkQueueFamilyProperties> getPhysicalDeviceQueueFamilyPropert
return queueFamiliesProperties;
}
uint32_t identifyGraphicsQueueFamilyIndex(VkPhysicalDevice physicalDevice, VkQueueFlags flags) {
uint32_t identifyGraphicsQueueFamilyIndex(VkPhysicalDevice physicalDevice) {
const FixedCapacityVector<VkQueueFamilyProperties> queueFamiliesProperties
= getPhysicalDeviceQueueFamilyPropertiesHelper(physicalDevice);
uint32_t graphicsQueueFamilyIndex = INVALID_VK_INDEX;
for (uint32_t j = 0; j < queueFamiliesProperties.size(); ++j) {
VkQueueFamilyProperties props = queueFamiliesProperties[j];
if (props.queueCount != 0 && props.queueFlags & flags) {
if (props.queueCount != 0 && props.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
graphicsQueueFamilyIndex = j;
break;
}
@@ -491,8 +473,7 @@ VkPhysicalDevice selectPhysicalDevice(VkInstance instance,
// Does the device have any command queues that support graphics?
// In theory, we should also ensure that the device supports presentation of our
// particular VkSurface, but we don't have a VkSurface yet, so we'll skip this requirement.
if (identifyGraphicsQueueFamilyIndex(candidateDevice, VK_QUEUE_GRAPHICS_BIT)
== INVALID_VK_INDEX) {
if (identifyGraphicsQueueFamilyIndex(candidateDevice) == INVALID_VK_INDEX) {
continue;
}
@@ -603,9 +584,6 @@ struct VulkanPlatformPrivate {
uint32_t mGraphicsQueueFamilyIndex = INVALID_VK_INDEX;
uint32_t mGraphicsQueueIndex = INVALID_VK_INDEX;
VkQueue mGraphicsQueue = VK_NULL_HANDLE;
uint32_t mProtectedGraphicsQueueFamilyIndex = INVALID_VK_INDEX;
uint32_t mProtectedGraphicsQueueIndex = INVALID_VK_INDEX;
VkQueue mProtectedGraphicsQueue = VK_NULL_HANDLE;
VulkanContext mContext = {};
// We use a map to both map a handle (i.e. SwapChainPtr) to the concrete type and also to
@@ -708,45 +686,22 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
printDeviceInfo(mImpl->mInstance, mImpl->mPhysicalDevice);
VkPhysicalDeviceProtectedMemoryFeatures queryProtectedMemoryFeatures = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES,
};
// Chain to the pNext linked list
queryProtectedMemoryFeatures.pNext = context.mPhysicalDeviceFeatures.pNext;
context.mPhysicalDeviceFeatures.pNext = &queryProtectedMemoryFeatures;
VkPhysicalDeviceProtectedMemoryProperties protectedMemoryProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES,
};
protectedMemoryProperties.pNext = context.mPhysicalDeviceProperties.pNext;
context.mPhysicalDeviceProperties.pNext = &protectedMemoryProperties;
// Initialize the following fields: physicalDeviceProperties, memoryProperties,
// physicalDeviceFeatures, graphicsQueueFamilyIndex.
vkGetPhysicalDeviceProperties2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceProperties);
vkGetPhysicalDeviceFeatures2(mImpl->mPhysicalDevice, &context.mPhysicalDeviceFeatures);
vkGetPhysicalDeviceProperties(mImpl->mPhysicalDevice, &context.mPhysicalDeviceProperties);
vkGetPhysicalDeviceFeatures(mImpl->mPhysicalDevice, &context.mPhysicalDeviceFeatures);
vkGetPhysicalDeviceMemoryProperties(mImpl->mPhysicalDevice, &context.mMemoryProperties);
mImpl->mGraphicsQueueFamilyIndex
= mImpl->mGraphicsQueueFamilyIndex == INVALID_VK_INDEX
? identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice,
VK_QUEUE_GRAPHICS_BIT)
? identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice)
: mImpl->mGraphicsQueueFamilyIndex;
assert_invariant(mImpl->mGraphicsQueueFamilyIndex != INVALID_VK_INDEX);
// We know we need to allocate the protected version of the VK objects
context.mProtectedMemorySupported =
static_cast<bool>(queryProtectedMemoryFeatures.protectedMemory);
if (context.mProtectedMemorySupported) {
mImpl->mProtectedGraphicsQueueFamilyIndex
= identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice,
(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_PROTECTED_BIT));
assert_invariant(mImpl->mProtectedGraphicsQueueFamilyIndex != INVALID_VK_INDEX);
}
// Only enable shaderClipDistance if we are doing instanced stereoscopic rendering.
if (context.mPhysicalDeviceFeatures.features.shaderClipDistance == VK_TRUE
if (context.mPhysicalDeviceFeatures.shaderClipDistance == VK_TRUE
&& driverConfig.stereoscopicType != StereoscopicType::INSTANCED) {
context.mPhysicalDeviceFeatures.features.shaderClipDistance = VK_FALSE;
context.mPhysicalDeviceFeatures.shaderClipDistance = VK_FALSE;
}
// At this point, we should have a family index that points to a family that has > 0 queues for
@@ -756,11 +711,6 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
mImpl->mGraphicsQueueIndex
= mImpl->mGraphicsQueueIndex == INVALID_VK_INDEX ? 0 : mImpl->mGraphicsQueueIndex;
// Applying the same logic to the protected queue index (Not sure about shared context and protection)
mImpl->mProtectedGraphicsQueueIndex
= mImpl->mProtectedGraphicsQueueIndex == INVALID_VK_INDEX ? 0 :
mImpl->mProtectedGraphicsQueueIndex;
ExtensionSet deviceExts;
// If using a shared context, we do not assume any extensions.
if (!mImpl->mSharedContext) {
@@ -777,8 +727,7 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
mImpl->mDevice
= mImpl->mDevice == VK_NULL_HANDLE ? createLogicalDevice(mImpl->mPhysicalDevice,
context.mPhysicalDeviceFeatures, mImpl->mGraphicsQueueFamilyIndex,
mImpl->mProtectedGraphicsQueueFamilyIndex, deviceExts)
context.mPhysicalDeviceFeatures, mImpl->mGraphicsQueueFamilyIndex, deviceExts)
: mImpl->mDevice;
assert_invariant(mImpl->mDevice != VK_NULL_HANDLE);
@@ -786,15 +735,6 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
&mImpl->mGraphicsQueue);
assert_invariant(mImpl->mGraphicsQueue != VK_NULL_HANDLE);
if (context.mProtectedMemorySupported) {
VkDeviceQueueInfo2 info = {};
info.queueFamilyIndex = mImpl->mProtectedGraphicsQueueFamilyIndex;
info.queueIndex = mImpl->mProtectedGraphicsQueueIndex;
info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_INFO_2;
info.flags = VK_DEVICE_QUEUE_CREATE_PROTECTED_BIT;
vkGetDeviceQueue2(mImpl->mDevice, &info, &mImpl->mProtectedGraphicsQueue);
}
// Store the extension support in the context
context.mDebugUtilsSupported = setContains(instExts, VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
context.mDebugMarkersSupported = setContains(deviceExts, VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
@@ -860,10 +800,6 @@ bool VulkanPlatform::hasResized(SwapChainPtr handle) {
SWAPCHAIN_RET_FUNC(hasResized, handle, )
}
bool VulkanPlatform::isProtected(SwapChainPtr handle) {
SWAPCHAIN_RET_FUNC(isProtected, handle, )
}
VkResult VulkanPlatform::recreate(SwapChainPtr handle) {
SWAPCHAIN_RET_FUNC(recreate, handle, )
}
@@ -893,13 +829,6 @@ SwapChainPtr VulkanPlatform::createSwapChain(void* nativeWindow, uint64_t flags,
flags |= SWAP_CHAIN_CONFIG_ENABLE_XCB;
}
if (flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) {
if (!mImpl->mContext.mProtectedMemorySupported) {
utils::slog.w << "protected swapchain requested, but VulkanPlatform does not support it"
<< utils::io::endl;
}
}
auto [surface, fallbackExtent] = createVkSurfaceKHR(nativeWindow, mImpl->mInstance, flags);
// The VulkanPlatformSurfaceSwapChain now `owns` the surface.
VulkanPlatformSurfaceSwapChain* swapchain = new VulkanPlatformSurfaceSwapChain(mImpl->mContext,
@@ -933,18 +862,6 @@ VkQueue VulkanPlatform::getGraphicsQueue() const noexcept {
return mImpl->mGraphicsQueue;
}
uint32_t VulkanPlatform::getProtectedGraphicsQueueFamilyIndex() const noexcept {
return mImpl->mProtectedGraphicsQueueFamilyIndex;
}
uint32_t VulkanPlatform::getProtectedGraphicsQueueIndex() const noexcept {
return mImpl->mProtectedGraphicsQueueIndex;
}
VkQueue VulkanPlatform::getProtectedGraphicsQueue() const noexcept {
return mImpl->mProtectedGraphicsQueue;
}
#undef SWAPCHAIN_RET_FUNC
}// namespace filament::backend
@@ -195,15 +195,6 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin
}
#endif
#elif defined(WIN32)
// On (at least) NVIDIA drivers, the Vulkan implementation (specifically the call to
// vkGetPhysicalDeviceSurfaceCapabilitiesKHR()) does not correctly handle the fact that
// each native window has its own DPI_AWARENESS_CONTEXT, and erroneously uses the context
// of the calling thread. As a workaround, we set the current thread's DPI_AWARENESS_CONTEXT
// to that of the native window we've been given. This isn't a perfect solution, because an
// application could create swap chains on multiple native windows with varying DPI-awareness,
// but even then, at least one of the windows would be guaranteed to work correctly.
SetThreadDpiAwarenessContext(GetWindowDpiAwarenessContext((HWND) nativeWindow));
VkWin32SurfaceCreateInfoKHR const createInfo = {
.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR,
.hinstance = GetModuleHandle(nullptr),
@@ -125,8 +125,7 @@ VulkanPlatformSurfaceSwapChain::VulkanPlatformSurfaceSwapChain(VulkanContext con
mSurface(surface),
mFallbackExtent(fallbackExtent),
mUsesRGB((flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0),
mHasStencil((flags & backend::SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0),
mIsProtected((flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) != 0) {
mHasStencil((flags & backend::SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0) {
assert_invariant(surface);
create();
}
@@ -213,8 +212,6 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
VkSwapchainCreateInfoKHR const createInfo{
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.flags = mIsProtected ? VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR :
VkSwapchainCreateFlagsKHR(0),
.surface = mSurface,
.minImageCount = desiredImageCount,
.imageFormat = surfaceFormat.format,
@@ -249,15 +246,13 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
mSwapChainBundle.depthFormat =
selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), mHasStencil);
mSwapChainBundle.depth = createImage(mSwapChainBundle.extent, mSwapChainBundle.depthFormat);
mSwapChainBundle.isProtected = mIsProtected;
FVK_LOGI << "vkCreateSwapchain"
<< ": " << mSwapChainBundle.extent.width << "x" << mSwapChainBundle.extent.height << ", "
<< surfaceFormat.format << ", " << surfaceFormat.colorSpace << ", "
<< "swapchain-size=" << mSwapChainBundle.colors.size() << ", "
<< "identity-transform=" << (caps.currentTransform == 1) << ", "
<< "depth=" << mSwapChainBundle.depthFormat << ", "
<< "protected=" << mSwapChainBundle.isProtected
<< "depth=" << mSwapChainBundle.depthFormat
<< io::endl;
VkSemaphoreCreateInfo const semaphoreCreateInfo = {
@@ -322,10 +317,6 @@ bool VulkanPlatformSurfaceSwapChain::hasResized() {
return !equivalent(mSwapChainBundle.extent, perceivedExtent);
}
bool VulkanPlatformSurfaceSwapChain::isProtected() {
return mIsProtected;
}
// Non-virtual override
VkResult VulkanPlatformSurfaceSwapChain::recreate() {
destroy();
@@ -64,11 +64,6 @@ struct VulkanPlatformSwapChainImpl : public Platform::SwapChain {
return false;
}
// Non-virtual override-able method
bool isProtected() {
return false;
}
protected:
// Non-virtual override-able method
void destroy();
@@ -102,9 +97,6 @@ struct VulkanPlatformSurfaceSwapChain : public VulkanPlatformSwapChainImpl {
// Non-virtual override-able method
bool hasResized();
// Non-virtual override-able method
bool isProtected();
protected:
// Non-virtual override-able method
void destroy();
@@ -125,7 +117,6 @@ private:
bool mUsesRGB = false;
bool mHasStencil = false;
bool mIsProtected = false;
bool mSuboptimal;
};
-74
View File
@@ -24,10 +24,6 @@
#include <utils/compiler.h>
#include <utils/Invocable.h>
#include <utils/Slice.h>
#include <initializer_list>
#include <optional>
#include <stdint.h>
#include <stddef.h>
@@ -313,7 +309,6 @@ public:
/**
* Set to `true` to forcibly disable parallel shader compilation in the backend.
* Currently only honored by the GL and Metal backends.
* @deprecated use "backend.disable_parallel_shader_compile" feature flag instead
*/
bool disableParallelShaderCompile = false;
@@ -354,7 +349,6 @@ public:
/*
* Disable backend handles use-after-free checks.
* @deprecated use "backend.disable_handle_use_after_free_check" feature flag instead
*/
bool disableHandleUseAfterFreeCheck = false;
@@ -391,32 +385,10 @@ public:
* Assert the native window associated to a SwapChain is valid when calling makeCurrent().
* This is only supported for:
* - PlatformEGLAndroid
* @deprecated use "backend.opengl.assert_native_window_is_valid" feature flag instead
*/
bool assertNativeWindowIsValid = false;
};
/**
* Feature flags can be enabled or disabled when the Engine is built. Some Feature flags can
* also be toggled at any time. Feature flags should alawys use their default value unless
* the feature enabled by the flag is faulty. Feature flags provide a last resort way to
* disable problematic features.
* Feature flags are intended to have a short life-time and are regularly removed as features
* mature.
*/
struct FeatureFlag {
char const* UTILS_NONNULL name; //!< name of the feature flag
char const* UTILS_NONNULL description; //!< short description
bool const* UTILS_NONNULL value; //!< pointer to the value of the flag
bool constant; //!< whether the flag is constant after construction
};
/**
* Returns the list of available feature flags
*/
utils::Slice<const FeatureFlag> getFeatureFlags() const noexcept;
#if UTILS_HAS_THREADING
using CreateCallback = void(void* UTILS_NULLABLE user, void* UTILS_NONNULL token);
#endif
@@ -489,21 +461,6 @@ public:
*/
Builder& paused(bool paused) noexcept;
/**
* Set a feature flag value. This is the only way to set constant feature flags.
* @param name feature name
* @param value true to enable, false to disable
* @return A reference to this Builder for chaining calls.
*/
Builder& feature(char const* UTILS_NONNULL name, bool value) noexcept;
/**
* Enables a list of features.
* @param list list of feature names to enable.
* @return A reference to this Builder for chaining calls.
*/
Builder& features(std::initializer_list<char const *> list) noexcept;
#if UTILS_HAS_THREADING
/**
* Creates the filament Engine asynchronously.
@@ -1121,37 +1078,6 @@ public:
DebugRegistry& getDebugRegistry() noexcept;
/**
* Check if a feature flag exists
* @param name name of the feature flag to check
* @return true if the feature flag exists, false otherwise
*/
inline bool hasFeatureFlag(char const* UTILS_NONNULL name) noexcept {
return getFeatureFlag(name).has_value();
}
/**
* Set the value of a non-constant feature flag.
* @param name name of the feature flag to set
* @param value value to set
* @return true if the value was set, false if the feature flag is constant or doesn't exist.
*/
bool setFeatureFlag(char const* UTILS_NONNULL name, bool value) noexcept;
/**
* Retrieves the value of any feature flag.
* @param name name of the feature flag
* @return the value of the flag if it exists
*/
std::optional<bool> getFeatureFlag(char const* UTILS_NONNULL name) const noexcept;
/**
* Returns a pointer to a non-constant feature flag value.
* @param name name of the feature flag
* @return a pointer to the feature flag value, or nullptr if the feature flag is constant or doesn't exist
*/
bool* UTILS_NULLABLE getFeatureFlagPtr(char const* UTILS_NONNULL name) const noexcept;
protected:
//! \privatesection
Engine() noexcept = default;
-20
View File
@@ -668,26 +668,6 @@ public:
*/
bool isFrontFaceWindingInverted() const noexcept;
/**
* Enables or disables transparent picking. Disabled by default.
*
* When transparent picking is enabled, View::pick() will pick from both
* transparent and opaque renderables. When disabled, View::pick() will only
* pick from opaque renderables.
*
* @param enabled true enables transparent picking, false disables it.
*
* @note Transparent picking will create an extra pass for rendering depth
* from both transparent and opaque renderables.
*/
void setTransparentPickingEnabled(bool enabled) noexcept;
/**
* Returns true if transparent picking is enabled.
* See setTransparentPickingEnabled() for more information.
*/
bool isTransparentPickingEnabled() const noexcept;
/**
* Enables use of the stencil buffer.
*
-17
View File
@@ -40,7 +40,6 @@
#include <utils/compiler.h>
#include <utils/Panic.h>
#include <utils/Slice.h>
#include <chrono>
@@ -443,22 +442,6 @@ uint64_t Engine::getSteadyClockTimeNano() noexcept {
return std::chrono::steady_clock::now().time_since_epoch().count();
}
utils::Slice<const Engine::FeatureFlag> Engine::getFeatureFlags() const noexcept {
return downcast(this)->getFeatureFlags();
}
bool Engine::setFeatureFlag(char const* name, bool value) noexcept {
return downcast(this)->setFeatureFlag(name, value);
}
std::optional<bool> Engine::getFeatureFlag(char const* name) const noexcept {
return downcast(this)->getFeatureFlag(name);
}
bool* Engine::getFeatureFlagPtr(char const* UTILS_NONNULL name) const noexcept {
return downcast(this)->getFeatureFlagPtr(name);
}
#if defined(__EMSCRIPTEN__)
void Engine::resetBackendState() noexcept {
downcast(this)->resetBackendState();
File diff suppressed because it is too large Load Diff
+43 -57
View File
@@ -21,12 +21,11 @@
#include "FrameHistory.h"
#include "ds/DescriptorSetLayout.h"
#include "ds/PostProcessDescriptorSet.h"
#include "ds/SsrPassDescriptorSet.h"
#include "ds/TypedUniformBuffer.h"
#include <private/filament/Variant.h>
#include <fg/FrameGraphId.h>
#include <fg/FrameGraphResources.h>
#include <fg/FrameGraphTexture.h>
@@ -34,23 +33,25 @@
#include <filament/Options.h>
#include <filament/Viewport.h>
#include <private/filament/EngineEnums.h>
#include <backend/DriverEnums.h>
#include <backend/Handle.h>
#include <backend/PipelineState.h>
#include <private/filament/Variant.h>
#include <math/vec2.h>
#include <math/vec4.h>
#include <utils/Slice.h>
#include <utils/debug.h>
#include <utils/FixedCapacityVector.h>
#include <tsl/robin_map.h>
#include <array>
#include <initializer_list>
#include <memory>
#include <random>
#include <string_view>
#include <utility>
#include <variant>
#include <stddef.h>
@@ -70,19 +71,16 @@ struct CameraInfo;
class PostProcessManager {
public:
struct ConstantInfo {
std::string_view name;
std::variant<int32_t, float, bool> value;
};
// This is intended to be used only to hold the static material data
struct StaticMaterialInfo {
struct ConstantInfo {
std::string_view name;
std::variant<int32_t, float, bool> value;
};
struct MaterialInfo {
std::string_view name;
uint8_t const* data;
size_t size;
// the life-time of objects pointed to by this initializer_list<> is extended to the
// life-time of the initializer_list
std::initializer_list<ConstantInfo> constants;
int size;
utils::FixedCapacityVector<ConstantInfo> constants = {};
};
struct ColorGradingConfig {
@@ -322,7 +320,8 @@ public:
class PostProcessMaterial {
public:
explicit PostProcessMaterial(StaticMaterialInfo const& info) noexcept;
PostProcessMaterial() noexcept;
PostProcessMaterial(MaterialInfo const& info) noexcept;
PostProcessMaterial(PostProcessMaterial const& rhs) = delete;
PostProcessMaterial& operator=(PostProcessMaterial const& rhs) = delete;
@@ -330,21 +329,15 @@ public:
PostProcessMaterial(PostProcessMaterial&& rhs) noexcept;
PostProcessMaterial& operator=(PostProcessMaterial&& rhs) noexcept;
~PostProcessMaterial() noexcept;
~PostProcessMaterial();
void terminate(FEngine& engine) noexcept;
FMaterial* getMaterial(FEngine& engine,
PostProcessVariant variant = PostProcessVariant::OPAQUE) const noexcept;
FMaterial* getMaterial(FEngine& engine) const noexcept;
FMaterialInstance* getMaterialInstance(FEngine& engine) const noexcept;
// Helper to get a MaterialInstance from a FMaterial
// This currently just call FMaterial::getDefaultInstance().
static FMaterialInstance* getMaterialInstance(FMaterial const* ma) noexcept;
// Helper to get a MaterialInstance from a PostProcessMaterial.
static FMaterialInstance* getMaterialInstance(FEngine& engine,
PostProcessMaterial const& material,
PostProcessVariant variant = PostProcessVariant::OPAQUE) noexcept;
std::pair<backend::PipelineState, backend::Viewport> getPipelineState(FEngine& engine,
Variant::type_t variantKey = 0u) const noexcept;
private:
void loadMaterial(FEngine& engine) const noexcept;
@@ -353,46 +346,39 @@ public:
mutable FMaterial* mMaterial;
uint8_t const* mData;
};
// mSize == 0 if mMaterial is valid, otherwise mSize > 0
mutable uint32_t mSize{};
// the objects' must outlive the Slice<>
utils::Slice<StaticMaterialInfo::ConstantInfo> mConstants{};
uint32_t mSize{};
mutable bool mHasMaterial{};
utils::FixedCapacityVector<ConstantInfo> mConstants{};
};
void registerPostProcessMaterial(std::string_view name, StaticMaterialInfo const& info);
void registerPostProcessMaterial(std::string_view name, MaterialInfo const& info);
PostProcessMaterial& getPostProcessMaterial(std::string_view name) noexcept;
void commitAndRender(FrameGraphResources::RenderPassInfo const& out,
PostProcessMaterial const& material, uint8_t variant,
backend::DriverApi& driver) const noexcept;
void commitAndRender(FrameGraphResources::RenderPassInfo const& out,
PostProcessMaterial const& material,
backend::DriverApi& driver) const noexcept;
void render(FrameGraphResources::RenderPassInfo const& out,
backend::PipelineState const& pipeline, backend::Viewport const& scissor,
backend::DriverApi& driver) const noexcept;
void render(FrameGraphResources::RenderPassInfo const& out,
std::pair<backend::PipelineState, backend::Viewport> const& combo,
backend::DriverApi& driver) const noexcept {
render(out, combo.first, combo.second, driver);
}
void setFrameUniforms(backend::DriverApi& driver,
TypedUniformBuffer<PerViewUib>& uniforms) noexcept;
void bindPostProcessDescriptorSet(backend::DriverApi& driver) const noexcept;
backend::PipelineState getPipelineState(
FMaterial const* ma,
PostProcessVariant variant = PostProcessVariant::OPAQUE) const noexcept;
void renderFullScreenQuad(FrameGraphResources::RenderPassInfo const& out,
backend::PipelineState const& pipeline,
backend::DriverApi& driver) const noexcept;
void renderFullScreenQuadWithScissor(FrameGraphResources::RenderPassInfo const& out,
backend::PipelineState const& pipeline,
backend::Viewport scissor,
backend::DriverApi& driver) const noexcept;
// Helper for a common case. Don't use in a loop because retrieving the PipelineState
// from FMaterialInstance is not trivial.
void commitAndRenderFullScreenQuad(backend::DriverApi& driver,
FrameGraphResources::RenderPassInfo const& out,
FMaterialInstance const* mi,
PostProcessVariant variant = PostProcessVariant::OPAQUE) const noexcept;
private:
backend::RenderPrimitiveHandle mFullScreenQuadRph;
backend::VertexBufferInfoHandle mFullScreenQuadVbih;
backend::DescriptorSetLayoutHandle mPerRenderableDslh;
FEngine& mEngine;
mutable SsrPassDescriptorSet mSsrPassDescriptorSet;
+57 -58
View File
@@ -68,6 +68,7 @@ namespace filament {
using namespace backend;
RenderPassBuilder& RenderPassBuilder::customCommand(
FEngine& engine,
uint8_t channel,
RenderPass::Pass pass,
RenderPass::CustomCommand custom,
@@ -75,17 +76,17 @@ RenderPassBuilder& RenderPassBuilder::customCommand(
RenderPass::Executor::CustomCommandFn const& command) {
if (!mCustomCommands.has_value()) {
// construct the vector the first time
mCustomCommands.emplace();
mCustomCommands.emplace(engine.getPerRenderPassArena());
}
mCustomCommands->emplace_back(channel, pass, custom, order, command);
return *this;
}
RenderPass RenderPassBuilder::build(FEngine const& engine, backend::DriverApi& driver) const {
RenderPass RenderPassBuilder::build(FEngine& engine) {
assert_invariant(mRenderableSoa);
assert_invariant(mScissorViewport.width <= std::numeric_limits<int32_t>::max());
assert_invariant(mScissorViewport.height <= std::numeric_limits<int32_t>::max());
return RenderPass{ engine, driver, *this };
return RenderPass{ engine, *this };
}
// ------------------------------------------------------------------------------------------------
@@ -106,11 +107,11 @@ void RenderPass::DescriptorSetHandleDeleter::operator()(
// ------------------------------------------------------------------------------------------------
RenderPass::RenderPass(FEngine const& engine, backend::DriverApi& driver,
RenderPassBuilder const& builder) noexcept
RenderPass::RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexcept
: mRenderableSoa(*builder.mRenderableSoa),
mColorPassDescriptorSet(builder.mColorPassDescriptorSet),
mScissorViewport(builder.mScissorViewport) {
mScissorViewport(builder.mScissorViewport),
mCustomCommands(engine.getPerRenderPassArena()) {
// compute the number of commands we need
updateSummedPrimitiveCounts(
@@ -126,12 +127,10 @@ RenderPass::RenderPass(FEngine const& engine, backend::DriverApi& driver,
uint32_t const customCommandCount =
builder.mCustomCommands.has_value() ? builder.mCustomCommands->size() : 0;
// FIXME: builder.mArena must be thread safe eventually
Command* const commandBegin = builder.mArena.alloc<Command>(commandCount + customCommandCount);
Command* commandEnd = commandBegin + (commandCount + customCommandCount);
assert_invariant(commandBegin);
// FIXME: builder.mArena must be thread safe eventually
if (UTILS_UNLIKELY(builder.mArena.getAllocator().isHeapAllocation(commandBegin))) {
static bool sLogOnce = true;
if (UTILS_UNLIKELY(sLogOnce)) {
@@ -151,7 +150,6 @@ RenderPass::RenderPass(FEngine const& engine, backend::DriverApi& driver,
builder.mCameraForwardVector);
if (builder.mCustomCommands.has_value()) {
mCustomCommands.reserve(customCommandCount);
Command* p = commandBegin + commandCount;
for (auto const& [channel, passId, command, order, fn]: builder.mCustomCommands.value()) {
appendCustomCommand(p++, channel, passId, command, order, fn);
@@ -168,9 +166,7 @@ RenderPass::RenderPass(FEngine const& engine, backend::DriverApi& driver,
stereoscopicEyeCount *= engine.getConfig().stereoscopicEyeCount;
}
commandEnd = resize(builder.mArena,
instanceify(driver,
engine.getPerRenderableDescriptorSetLayout().getHandle(),
commandBegin, commandEnd, stereoscopicEyeCount));
instanceify(engine, commandBegin, commandEnd, stereoscopicEyeCount));
}
// these are `const` from this point on...
@@ -186,7 +182,7 @@ RenderPass::Command* RenderPass::resize(Arena& arena, Command* const last) noexc
return last;
}
void RenderPass::appendCommands(FEngine const& engine,
void RenderPass::appendCommands(FEngine& engine,
Slice<Command> commands,
utils::Range<uint32_t> const vr,
CommandTypeFlags const commandTypeFlags,
@@ -287,8 +283,17 @@ RenderPass::Command* RenderPass::sortCommands(
return last;
}
RenderPass::Command* RenderPass::instanceify(backend::DriverApi& driver,
DescriptorSetLayoutHandle perRenderableDescriptorSetLayoutHandle,
void RenderPass::execute(RenderPass const& pass,
FEngine& engine, const char* name,
backend::Handle<backend::HwRenderTarget> renderTarget,
backend::RenderPassParams params) noexcept {
DriverApi& driver = engine.getDriverApi();
driver.beginRenderPass(renderTarget, params);
pass.getExecutor().execute(engine, name);
driver.endRenderPass();
}
RenderPass::Command* RenderPass::instanceify(FEngine& engine,
Command* curr, Command* const last,
int32_t eyeCount) const noexcept {
SYSTRACE_NAME("instanceify");
@@ -347,6 +352,8 @@ RenderPass::Command* RenderPass::instanceify(backend::DriverApi& driver,
if (UTILS_UNLIKELY(instanceCount > 1)) {
drawCallsSavedCount += instanceCount - 1;
auto& driver = engine.getDriverApi();
// allocate our staging buffer only if needed
if (UTILS_UNLIKELY(!stagingBuffer)) {
// Create a temporary UBO for holding the per-renderable data of each primitive,
@@ -375,7 +382,8 @@ RenderPass::Command* RenderPass::instanceify(backend::DriverApi& driver,
// in this case we would need to preserve the default descriptor-set content).
// This has the same lifetime as the UBO (see above).
mInstancedDescriptorSetHandle = DescriptorSetSharedHandle{
driver.createDescriptorSet(perRenderableDescriptorSetLayoutHandle),
driver.createDescriptorSet(
engine.getPerRenderableDescriptorSetLayout().getHandle()),
driver
};
driver.updateDescriptorSetBuffer(mInstancedDescriptorSetHandle,
@@ -410,7 +418,10 @@ RenderPass::Command* RenderPass::instanceify(backend::DriverApi& driver,
if (UTILS_UNLIKELY(firstSentinel)) {
//slog.d << "auto-instancing, saving " << drawCallsSavedCount << " draw calls, out of "
// << count << io::endl;
// we have instanced primitives
DriverApi& driver = engine.getDriverApi();
// copy our instanced ubo data
driver.updateBufferObjectUnsynchronized(mInstancedUboHandle, {
stagingBuffer, sizeof(PerRenderableData) * instancedPrimitiveOffset,
@@ -806,15 +817,13 @@ RenderPass::Command* RenderPass::generateCommandsImpl(RenderPass::CommandTypeFla
const BlendingMode blendingMode = ma->getBlendingMode();
const bool translucent = (blendingMode != BlendingMode::OPAQUE
&& blendingMode != BlendingMode::MASKED);
const bool isPickingVariant = Variant::isPickingVariant(variant);
cmd.key |= mi->getSortingKey(); // already all set-up for direct or'ing
cmd.info.rasterState.culling = mi->getCullingMode();
// FIXME: should writeDepthForShadowCasters take precedence over mi->getDepthWrite()?
cmd.info.rasterState.depthWrite = (1 // only keep bit 0
& (mi->isDepthWriteEnabled() | (mode == TransparencyMode::TWO_PASSES_ONE_SIDE)
| isPickingVariant)
& (mi->isDepthWriteEnabled() | (mode == TransparencyMode::TWO_PASSES_ONE_SIDE))
& !(filterTranslucentObjects & translucent)
& !(depthFilterAlphaMaskedObjects & rs.alphaToCoverage))
| writeDepthForShadowCasters;
@@ -850,13 +859,19 @@ void RenderPass::Executor::overridePolygonOffset(backend::PolygonOffset const* p
}
}
void RenderPass::Executor::overrideScissor(backend::Viewport const* scissor) noexcept {
if ((mScissorOverride = (scissor != nullptr))) { // NOLINT(*-assignment-in-if-condition)
mScissor = *scissor;
}
}
void RenderPass::Executor::overrideScissor(backend::Viewport const& scissor) noexcept {
mScissorOverride = true;
mScissor = scissor;
}
void RenderPass::Executor::execute(FEngine const& engine, backend::DriverApi& driver) const noexcept {
execute(engine, driver, mCommands.begin(), mCommands.end());
void RenderPass::Executor::execute(FEngine& engine, const char*) const noexcept {
execute(engine, mCommands.begin(), mCommands.end());
}
UTILS_NOINLINE // no need to be inlined
@@ -866,15 +881,15 @@ backend::Viewport RenderPass::Executor::applyScissorViewport(
// clang vectorizes this!
constexpr int32_t maxvali = std::numeric_limits<int32_t>::max();
// compute new left/bottom, assume no overflow
int32_t const l = scissor.left + scissorViewport.left;
int32_t const b = scissor.bottom + scissorViewport.bottom;
int32_t l = scissor.left + scissorViewport.left;
int32_t b = scissor.bottom + scissorViewport.bottom;
// compute right/top without overflowing, scissor.width/height guaranteed
// to convert to int32
int32_t r = (l > maxvali - int32_t(scissor.width)) ? maxvali : l + int32_t(scissor.width);
int32_t t = (b > maxvali - int32_t(scissor.height)) ? maxvali : b + int32_t(scissor.height);
// clip to the viewport
assert_invariant(l == std::max(l, scissorViewport.left));
assert_invariant(b == std::max(b, scissorViewport.bottom));
l = std::max(l, scissorViewport.left);
b = std::max(b, scissorViewport.bottom);
r = std::min(r, scissorViewport.left + int32_t(scissorViewport.width));
t = std::min(t, scissorViewport.bottom + int32_t(scissorViewport.height));
assert_invariant(r >= l && t >= b);
@@ -882,26 +897,23 @@ backend::Viewport RenderPass::Executor::applyScissorViewport(
}
UTILS_NOINLINE // no need to be inlined
void RenderPass::Executor::execute(FEngine const& engine, backend::DriverApi& driver,
Command const* first, Command const* last) const noexcept {
void RenderPass::Executor::execute(FEngine& engine,
const Command* first, const Command* last) const noexcept {
SYSTRACE_CALL();
SYSTRACE_CONTEXT();
DriverApi& driver = engine.getDriverApi();
size_t const capacity = engine.getMinCommandBufferSize();
CircularBuffer const& circularBuffer = driver.getCircularBuffer();
if (first != last) {
SYSTRACE_VALUE32("commandCount", last - first);
// The scissor rectangle is associated to a render pass, so the tracking can be local.
backend::Viewport currentScissor{ 0, 0, INT32_MAX, INT32_MAX };
bool const hasScissorOverride = mScissorOverride;
bool const hasScissorViewport = mHasScissorViewport;
if (UTILS_UNLIKELY(hasScissorViewport || hasScissorOverride)) {
// we should never have both an override and scissor-viewport
assert_invariant(!hasScissorViewport || !hasScissorOverride);
currentScissor = mScissor;
bool const scissorOverride = mScissorOverride;
if (UTILS_UNLIKELY(scissorOverride)) {
// initialize with scissor override
driver.scissor(mScissor);
}
@@ -952,9 +964,7 @@ void RenderPass::Executor::execute(FEngine const& engine, backend::DriverApi& dr
// check we have enough capacity to write these commandCount commands, if not,
// request a new CircularBuffer allocation of `capacity` bytes.
if (UTILS_UNLIKELY(circularBuffer.getUsed() > capacity - commandSizeInBytes)) {
// FIXME: eventually we can't flush here because this will be a secondary
// command buffer. We will need another solution for overflows.
const_cast<FEngine&>(engine).flush();
engine.flush(); // TODO: we should use a "fast" flush if possible
}
first--;
@@ -987,24 +997,17 @@ void RenderPass::Executor::execute(FEngine const& engine, backend::DriverApi& dr
if (UTILS_UNLIKELY(mi != info.mi)) {
// this is always taken the first time
assert_invariant(info.mi);
mi = info.mi;
assert_invariant(mi);
ma = mi->getMaterial();
// if we have the scissor override, the material instance and scissor-viewport
// are ignored (typically used for shadow maps).
if (!hasScissorOverride) {
// apply the MaterialInstance scissor
if (UTILS_LIKELY(!scissorOverride)) {
backend::Viewport scissor = mi->getScissor();
if (hasScissorViewport) {
// apply the scissor viewport if any
scissor = applyScissorViewport(mScissor, scissor);
}
if (scissor != currentScissor) {
currentScissor = scissor;
driver.scissor(scissor);
if (UTILS_UNLIKELY(mi->hasScissor())) {
scissor = applyScissorViewport(mScissorViewport, scissor);
}
driver.scissor(scissor);
}
if (UTILS_LIKELY(!polygonOffsetOverride)) {
@@ -1082,9 +1085,7 @@ void RenderPass::Executor::execute(FEngine const& engine, backend::DriverApi& dr
// If the remaining space is less than half the capacity, we flush right away to
// allow some headroom for commands that might come later.
if (UTILS_UNLIKELY(circularBuffer.getUsed() > capacity / 2)) {
// FIXME: eventually we can't flush here because this will be a secondary
// command buffer.
const_cast<FEngine&>(engine).flush();
engine.flush();
}
}
}
@@ -1097,18 +1098,16 @@ RenderPass::Executor::Executor(RenderPass const& pass, Command const* b, Command
mInstancedUboHandle(pass.mInstancedUboHandle),
mInstancedDescriptorSetHandle(pass.mInstancedDescriptorSetHandle),
mColorPassDescriptorSet(pass.mColorPassDescriptorSet),
mScissor(pass.mScissorViewport),
mScissorViewport(pass.mScissorViewport),
mPolygonOffsetOverride(false),
mScissorOverride(false) {
mHasScissorViewport = mScissor != backend::Viewport{ 0, 0, INT32_MAX, INT32_MAX };
assert_invariant(b >= pass.begin());
assert_invariant(e <= pass.end());
}
RenderPass::Executor::Executor() noexcept
: mPolygonOffsetOverride(false),
mScissorOverride(false),
mHasScissorViewport(false) {
mScissorOverride(false) {
}
RenderPass::Executor::Executor(Executor&& rhs) noexcept = default;
+28 -28
View File
@@ -32,7 +32,6 @@
#include <utils/Allocator.h>
#include <utils/BitmaskEnum.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Range.h>
#include <utils/Slice.h>
#include <utils/architecture.h>
@@ -310,6 +309,13 @@ public:
Command const* end() const noexcept { return mCommandEnd; }
bool empty() const noexcept { return begin() == end(); }
// Helper to execute all the commands generated by this RenderPass
static void execute(RenderPass const& pass,
FEngine& engine, const char* name,
backend::Handle<backend::HwRenderTarget> renderTarget,
backend::RenderPassParams params) noexcept;
class BufferObjectHandleDeleter {
std::reference_wrapper<backend::DriverApi> driver;
public:
@@ -344,22 +350,16 @@ public:
BufferObjectSharedHandle mInstancedUboHandle;
DescriptorSetSharedHandle mInstancedDescriptorSetHandle;
ColorPassDescriptorSet const* mColorPassDescriptorSet = nullptr;
// this stores either the scissor-viewport or the scissor override
backend::Viewport mScissor{ 0, 0, INT32_MAX, INT32_MAX };
backend::Viewport mScissorViewport;
// value of the polygon offset override
backend::PolygonOffset mPolygonOffset{};
// whether to override the polygon offset from the MaterialInstance
bool mPolygonOffsetOverride : 1;
// whether to override the scissor rectangle from the MaterialInstance
bool mScissorOverride : 1;
// whether the scissor-viewport is set
bool mHasScissorViewport : 1;
backend::Viewport mScissor{}; // value of scissor override
backend::PolygonOffset mPolygonOffset{}; // value of the override
bool mPolygonOffsetOverride : 1; // whether to override the polygon offset setting
bool mScissorOverride : 1; // whether to override the polygon offset setting
Executor(RenderPass const& pass, Command const* b, Command const* e) noexcept;
void execute(FEngine const& engine, backend::DriverApi& driver,
Command const* first, Command const* last) const noexcept;
void execute(FEngine& engine, const Command* first, const Command* last) const noexcept;
static backend::Viewport applyScissorViewport(
backend::Viewport const& scissorViewport,
@@ -382,9 +382,11 @@ public:
// if non-null, overrides the material's polygon offset
void overridePolygonOffset(backend::PolygonOffset const* polygonOffset) noexcept;
// if non-null, overrides the material's scissor
void overrideScissor(backend::Viewport const* scissor) noexcept;
void overrideScissor(backend::Viewport const& scissor) noexcept;
void execute(FEngine const& engine, backend::DriverApi& driver) const noexcept;
void execute(FEngine& engine, const char* name) const noexcept;
};
// returns a new executor for this pass
@@ -399,12 +401,11 @@ public:
private:
friend class FRenderer;
friend class RenderPassBuilder;
RenderPass(FEngine const& engine, backend::DriverApi& driver,
RenderPassBuilder const& builder) noexcept;
RenderPass(FEngine& engine, RenderPassBuilder const& builder) noexcept;
// This is the main function of this class, this appends commands to the pass using
// the current camera, geometry and flags set. This can be called multiple times if needed.
void appendCommands(FEngine const& engine,
void appendCommands(FEngine& engine,
utils::Slice<Command> commands,
utils::Range<uint32_t> visibleRenderables,
CommandTypeFlags commandTypeFlags,
@@ -419,15 +420,14 @@ private:
uint8_t channel, Pass pass, CustomCommand custom, uint32_t order,
Executor::CustomCommandFn command);
static Command* resize(Arena& arena, Command* last) noexcept;
static Command* resize(Arena& arena, Command* const last) noexcept;
// sorts commands then trims sentinels
static Command* sortCommands(
Command* begin, Command* end) noexcept;
// instanceify commands then trims sentinels
RenderPass::Command* instanceify(backend::DriverApi& driver,
backend::DescriptorSetLayoutHandle perRenderableDescriptorSetLayoutHandle,
RenderPass::Command* instanceify(FEngine& engine,
Command* begin, Command* end,
int32_t eyeCount) const noexcept;
@@ -461,13 +461,14 @@ private:
FScene::RenderableSoa const& mRenderableSoa;
ColorPassDescriptorSet const* const mColorPassDescriptorSet;
backend::Viewport const mScissorViewport{ 0, 0, INT32_MAX, INT32_MAX };
backend::Viewport const mScissorViewport;
Command const* /* const */ mCommandBegin = nullptr; // Pointer to the first command
Command const* /* const */ mCommandEnd = nullptr; // Pointer to one past the last command
mutable BufferObjectSharedHandle mInstancedUboHandle; // ubo for instanced primitives
mutable DescriptorSetSharedHandle mInstancedDescriptorSetHandle; // a descriptor-set to hold the ubo
// a vector for our custom commands
using CustomCommandVector = utils::FixedCapacityVector<Executor::CustomCommandFn>;
using CustomCommandVector = std::vector<Executor::CustomCommandFn,
utils::STLAllocator<Executor::CustomCommandFn, LinearAllocatorArena>>;
mutable CustomCommandVector mCustomCommands;
};
@@ -493,10 +494,11 @@ class RenderPassBuilder {
uint32_t,
RenderPass::Executor::CustomCommandFn>;
using CustomCommandContainer = std::vector<CustomCommandRecord>;
using CustomCommandContainer = std::vector<CustomCommandRecord,
utils::STLAllocator<CustomCommandRecord, LinearAllocatorArena>>;
// we make this optional because it's not used often, and we don't want to have
// to construct it by default. For the same reason we use a std::vector<>
// to construct it by default.
std::optional<CustomCommandContainer> mCustomCommands;
public:
@@ -507,8 +509,6 @@ public:
return *this;
}
// Specifies the viewport for the scissor rectangle, that is, the final scissor rect is
// offset by the viewport's left-top and clipped to the viewport's width/height.
RenderPassBuilder& scissorViewport(backend::Viewport viewport) noexcept {
mScissorViewport = viewport;
return *this;
@@ -564,14 +564,14 @@ public:
return *this;
}
RenderPassBuilder& customCommand(
RenderPassBuilder& customCommand(FEngine& engine,
uint8_t channel,
RenderPass::Pass pass,
RenderPass::CustomCommand custom,
uint32_t order,
const RenderPass::Executor::CustomCommandFn& command);
RenderPass build(FEngine const& engine, backend::DriverApi& driver) const;
RenderPass build(FEngine& engine);
};
+1 -1
View File
@@ -251,7 +251,7 @@ RendererUtils::ColorPassOutput RendererUtils::colorPass(
}
driver.beginRenderPass(out.target, out.params);
passExecutor.execute(engine, driver);
passExecutor.execute(engine, resources.getPassName());
driver.endRenderPass();
// color pass is typically heavy, and we don't have much CPU work left after
+4 -5
View File
@@ -260,7 +260,7 @@ void ResourceAllocator::gc(bool skippedFrame) noexcept {
if ((ageDiff >= MAX_AGE_SKIPPED_FRAME && skippedFrame) ||
(ageDiff >= mCacheMaxAge && evictedCount < MAX_EVICTION_COUNT)) {
evictedCount++;
it = purge(it);
purge(it);
} else {
// build the set of ages present in the cache after eviction
ages.set(std::min(size_t(31), ageDiff));
@@ -280,7 +280,7 @@ void ResourceAllocator::gc(bool skippedFrame) noexcept {
for (auto it = textureCache.begin(); it != textureCache.end();) {
const size_t ageDiff = age - it->second.age;
if (ageDiff >= maxAge) {
it = purge(it);
purge(it);
} else {
++it;
}
@@ -306,13 +306,12 @@ void ResourceAllocator::dump(bool brief) const noexcept {
}
}
ResourceAllocator::CacheContainer::iterator
ResourceAllocator::purge(
void ResourceAllocator::purge(
ResourceAllocator::CacheContainer::iterator const& pos) {
//slog.d << "purging " << pos->second.handle.getId() << ", age=" << pos->second.age << io::endl;
mBackend.destroyTexture(pos->second.handle);
mCacheSize -= pos->second.size;
return mTextureCache.erase(pos);
mTextureCache.erase(pos);
}
// ------------------------------------------------------------------------------------------------
+1 -2
View File
@@ -215,8 +215,7 @@ private:
using CacheContainer = AssociativeContainer<TextureKey, TextureCachePayload>;
ResourceAllocator::CacheContainer::iterator
purge(ResourceAllocator::CacheContainer::iterator const& pos);
void purge(ResourceAllocator::CacheContainer::iterator const& pos);
backend::DriverApi& mBackend;
std::shared_ptr<ResourceAllocatorDisposer> mDisposer;
+3 -3
View File
@@ -1361,8 +1361,8 @@ math::float4 ShadowMap::getClampToEdgeCoords(ShadowMapInfo const& shadowMapInfo)
// ------------------------------------------------------------------------------------------------
void ShadowMap::prepareCamera(Transaction const& transaction,
backend::DriverApi& driver, const CameraInfo& cameraInfo) noexcept {
ShadowMapDescriptorSet::prepareCamera(transaction, driver, cameraInfo);
FEngine& engine, const CameraInfo& cameraInfo) noexcept {
ShadowMapDescriptorSet::prepareCamera(transaction, engine, cameraInfo);
ShadowMapDescriptorSet::prepareLodBias(transaction, 0.0f);
}
@@ -1372,7 +1372,7 @@ void ShadowMap::prepareViewport(Transaction const& transaction,
}
void ShadowMap::prepareTime(Transaction const& transaction,
FEngine const& engine, math::float4 const& userTime) noexcept {
FEngine& engine, math::float4 const& userTime) noexcept {
ShadowMapDescriptorSet::prepareTime(transaction, engine, userTime);
}
+2 -2
View File
@@ -193,11 +193,11 @@ public:
using Transaction = ShadowMapDescriptorSet::Transaction;
static void prepareCamera(Transaction const& transaction,
backend::DriverApi& driver, const CameraInfo& cameraInfo) noexcept;
FEngine& engine, const CameraInfo& cameraInfo) noexcept;
static void prepareViewport(Transaction const& transaction,
backend::Viewport const& viewport) noexcept;
static void prepareTime(Transaction const& transaction,
FEngine const& engine, math::float4 const& userTime) noexcept;
FEngine& engine, math::float4 const& userTime) noexcept;
static void prepareShadowMapping(Transaction const& transaction,
bool highPrecision) noexcept;
static ShadowMapDescriptorSet::Transaction open(backend::DriverApi& driver) noexcept;
+19 -21
View File
@@ -314,10 +314,9 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
// "read" from one of its resource (only writes), so the FrameGraph culls it.
builder.sideEffect();
},
[=, passBuilder = passBuilder,
&engine = const_cast<FEngine /*const*/ &>(engine), // FIXME: we want this const
&view = const_cast<FView const&>(view)]
(FrameGraphResources const&, auto const& data, DriverApi& driver) mutable {
[this, &engine, &view, vsmShadowOptions,
scene, mainCameraInfo, userTime, passBuilder = passBuilder](
FrameGraphResources const&, auto const& data, DriverApi& driver) mutable {
// Note: we could almost parallel_for the loop below, the problem currently is
// that updatePrimitivesLod() updates temporary global state.
@@ -364,7 +363,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
const CameraInfo cameraInfo{ shadowMap.getCamera(), mainCameraInfo };
auto transaction = ShadowMap::open(driver);
ShadowMap::prepareCamera(transaction, driver, cameraInfo);
ShadowMap::prepareCamera(transaction, engine, cameraInfo);
ShadowMap::prepareViewport(transaction, shadowMap.getViewport());
ShadowMap::prepareTime(transaction, engine, userTime);
ShadowMap::prepareShadowMapping(transaction,
@@ -372,8 +371,8 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
shadowMap.commit(transaction, engine, driver);
// updatePrimitivesLod must be run before RenderPass::appendCommands.
FView::updatePrimitivesLod(scene->getRenderableData(),
engine, cameraInfo, entry.range);
view.updatePrimitivesLod(engine,
cameraInfo, scene->getRenderableData(), entry.range);
// generate and sort the commands for rendering the shadow map
@@ -395,7 +394,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
.visibilityMask(entry.visibilityMask)
.geometry(scene->getRenderableData(), entry.range)
.commandTypeFlags(RenderPass::CommandTypeFlags::SHADOW)
.build(engine, driver);
.build(engine);
entry.executor = pass.getExecutor();
@@ -526,7 +525,7 @@ FrameGraphId<FrameGraphTexture> ShadowMapManager::render(FEngine& engine, FrameG
if (entry.shadowMap->hasVisibleShadows()) {
entry.shadowMap->bind(driver);
entry.executor.overrideScissor(entry.shadowMap->getScissor());
entry.executor.execute(engine, driver);
entry.executor.execute(engine, "Shadow Pass");
}
driver.endRenderPass();
});
@@ -792,10 +791,9 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap, FEngine& engin
}
}
void ShadowMapManager::cullSpotShadowMap(ShadowMap const& shadowMap,
FEngine const& engine, FView const& view,
void ShadowMapManager::cullSpotShadowMap(ShadowMap const& shadowMap, FEngine& engine, FView& view,
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
FScene::LightSoa const& lightData) noexcept {
FScene::LightSoa& lightData) noexcept {
auto& lcm = engine.getLightManager();
const size_t lightIndex = shadowMap.getLightIndex();
@@ -883,23 +881,23 @@ void ShadowMapManager::preparePointShadowMap(ShadowMap& shadowMap,
}
}
void ShadowMapManager::cullPointShadowMap(ShadowMap const& shadowMap, FView const& view,
void ShadowMapManager::cullPointShadowMap(ShadowMap const& shadowMap, FView& view,
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
FScene::LightSoa const& lightData) noexcept {
FScene::LightSoa& lightData) noexcept {
uint8_t const face = shadowMap.getFace();
size_t const lightIndex = shadowMap.getLightIndex();
const uint8_t face = shadowMap.getFace();
const size_t lightIndex = shadowMap.getLightIndex();
// compute the frustum for this light
// for spotlights, we cull shadow casters first because we already know the frustum,
// this will help us find better near/far plane later
auto const position = lightData.elementAt<FScene::POSITION_RADIUS>(lightIndex).xyz;
auto const radius = lightData.elementAt<FScene::POSITION_RADIUS>(lightIndex).w;
const auto position = lightData.elementAt<FScene::POSITION_RADIUS>(lightIndex).xyz;
const auto radius = lightData.elementAt<FScene::POSITION_RADIUS>(lightIndex).w;
// compute shadow map frustum for culling
mat4f const Mv = ShadowMap::getPointLightViewMatrix(TextureCubemapFace(face), position);
mat4f const Mp = mat4f::perspective(90.0f, 1.0f, 0.01f, radius);
Frustum const frustum{ math::highPrecisionMultiply(Mp, Mv) };
const mat4f Mv = ShadowMap::getPointLightViewMatrix(TextureCubemapFace(face), position);
const mat4f Mp = mat4f::perspective(90.0f, 1.0f, 0.01f, radius);
const Frustum frustum{ math::highPrecisionMultiply(Mp, Mv) };
// Cull shadow casters
float3 const* worldAABBCenter = renderableData.data<FScene::WORLD_AABB_CENTER>();
+6 -6
View File
@@ -165,17 +165,17 @@ private:
FScene::LightSoa& lightData, ShadowMap::SceneInfo const& sceneInfo) noexcept;
static void cullSpotShadowMap(ShadowMap const& map,
FEngine const& engine, FView const& view,
FEngine& engine, FView& view,
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
FScene::LightSoa const& lightData) noexcept;
FScene::LightSoa& lightData) noexcept;
void preparePointShadowMap(ShadowMap& map,
FEngine& engine, FView& view, CameraInfo const& mainCameraInfo,
FScene::LightSoa& lightData) noexcept;
static void cullPointShadowMap(ShadowMap const& shadowMap, FView const& view,
static void cullPointShadowMap(ShadowMap const& shadowMap, FView& view,
FScene::RenderableSoa& renderableData, utils::Range<uint32_t> range,
FScene::LightSoa const& lightData) noexcept;
FScene::LightSoa& lightData) noexcept;
static void updateSpotVisibilityMasks(
uint8_t visibleLayers,
@@ -253,8 +253,8 @@ private:
return const_cast<ShadowMapManager*>(this)->getCascadedShadowMap();
}
utils::Slice<ShadowMap> getSpotShadowMaps() const noexcept {
ShadowMap const* const p = &getShadowMap(CONFIG_MAX_SHADOW_CASCADES);
utils::Slice<ShadowMap> getSpotShadowMaps() noexcept {
ShadowMap* const p = &getShadowMap(CONFIG_MAX_SHADOW_CASCADES);
return { p, mSpotShadowMapCount };
}
};
-8
View File
@@ -185,14 +185,6 @@ bool View::isFrontFaceWindingInverted() const noexcept {
return downcast(this)->isFrontFaceWindingInverted();
}
void View::setTransparentPickingEnabled(bool enabled) noexcept {
downcast(this)->setTransparentPickingEnabled(enabled);
}
bool View::isTransparentPickingEnabled() const noexcept {
return downcast(this)->isTransparentPickingEnabled();
}
void View::setDynamicLightingOptions(float zLightNear, float zLightFar) noexcept {
downcast(this)->setDynamicLightingOptions(zLightNear, zLightFar);
}
@@ -807,10 +807,7 @@ void FRenderableManager::setMaterialInstanceAt(Instance instance, uint8_t level,
primitives[primitiveIndex].setMaterialInstance(mi);
AttributeBitset const required = material->getRequiredAttributes();
AttributeBitset const declared = primitives[primitiveIndex].getEnabledAttributes();
// Print the warning only when the handle is available. Otherwise this may end up
// emitting many invalid warnings as the `declared` bitset is not populated yet.
bool const isPrimitiveInitialized = !!primitives[primitiveIndex].getHwHandle();
if (UTILS_UNLIKELY(isPrimitiveInitialized && (declared & required) != required)) {
if (UTILS_UNLIKELY((declared & required) != required)) {
slog.w << "[instance=" << instance.asValue() << ", primitive @ " << primitiveIndex
<< "] missing required attributes ("
<< required << "), declared=" << declared << io::endl;
+9 -89
View File
@@ -46,7 +46,6 @@
#include <utils/compiler.h>
#include <utils/debug.h>
#include <utils/Invocable.h>
#include <utils/Log.h>
#include <utils/Panic.h>
#include <utils/PrivateImplementation-impl.h>
@@ -54,17 +53,8 @@
#include <utils/ThreadUtils.h>
#include <algorithm>
#include <initializer_list>
#include <memory>
#include <optional>
#include <thread>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "generated/resources/materials.h"
@@ -83,8 +73,6 @@ struct Engine::BuilderDetails {
FeatureLevel mFeatureLevel = FeatureLevel::FEATURE_LEVEL_1;
void* mSharedContext = nullptr;
bool mPaused = false;
std::unordered_map<std::string_view, bool> mFeatureFlags;
static Config validateConfig(Config config) noexcept;
};
@@ -116,11 +104,11 @@ Engine* FEngine::create(Engine::Builder const& builder) {
DriverConfig const driverConfig{
.handleArenaSize = instance->getRequestedDriverHandleArenaSize(),
.metalUploadBufferSizeBytes = instance->getConfig().metalUploadBufferSizeBytes,
.disableParallelShaderCompile = instance->features.backend.disable_parallel_shader_compile,
.disableHandleUseAfterFreeCheck = instance->features.backend.disable_handle_use_after_free_check,
.disableParallelShaderCompile = instance->getConfig().disableParallelShaderCompile,
.disableHandleUseAfterFreeCheck = instance->getConfig().disableHandleUseAfterFreeCheck,
.forceGLES2Context = instance->getConfig().forceGLES2Context,
.stereoscopicType = instance->getConfig().stereoscopicType,
.assertNativeWindowIsValid = instance->features.backend.opengl.assert_native_window_is_valid,
.assertNativeWindowIsValid = instance->getConfig().assertNativeWindowIsValid,
};
instance->mDriver = platform->createDriver(sharedContext, driverConfig);
@@ -231,34 +219,7 @@ FEngine::FEngine(Engine::Builder const& builder) :
mMainThreadId(ThreadUtils::getThreadId()),
mConfig(builder->mConfig)
{
// update a feature flag from Engine::Config if the flag is not specified in the Builder
auto const featureFlagsBackwardCompatibility =
[this, &builder](std::string_view name, bool value) {
if (builder->mFeatureFlags.find(name) == builder->mFeatureFlags.end()) {
auto* const p = getFeatureFlagPtr(name, true);
if (p) {
*p = value;
}
}
};
// update all the features flags specified in the builder
for (auto const& feature : builder->mFeatureFlags) {
auto* const p = getFeatureFlagPtr(feature.first, true);
if (p) {
*p = feature.second;
}
}
// update "old" feature flags that were specified in Engine::Config
featureFlagsBackwardCompatibility("backend.disable_parallel_shader_compile",
mConfig.disableParallelShaderCompile);
featureFlagsBackwardCompatibility("backend.disable_handle_use_after_free_check",
mConfig.disableHandleUseAfterFreeCheck);
featureFlagsBackwardCompatibility("backend.opengl.assert_native_window_is_valid",
mConfig.assertNativeWindowIsValid);
// We're assuming we're on the main thread here.
// we're assuming we're on the main thread here.
// (it may not be the case)
mJobSystem.adopt();
@@ -741,11 +702,11 @@ int FEngine::loop() {
DriverConfig const driverConfig {
.handleArenaSize = getRequestedDriverHandleArenaSize(),
.metalUploadBufferSizeBytes = mConfig.metalUploadBufferSizeBytes,
.disableParallelShaderCompile = features.backend.disable_parallel_shader_compile,
.disableHandleUseAfterFreeCheck = features.backend.disable_handle_use_after_free_check,
.disableParallelShaderCompile = mConfig.disableParallelShaderCompile,
.disableHandleUseAfterFreeCheck = mConfig.disableHandleUseAfterFreeCheck,
.forceGLES2Context = mConfig.forceGLES2Context,
.stereoscopicType = mConfig.stereoscopicType,
.assertNativeWindowIsValid = features.backend.opengl.assert_native_window_is_valid,
.assertNativeWindowIsValid = mConfig.assertNativeWindowIsValid,
};
mDriver = mPlatform->createDriver(mSharedGLContext, driverConfig);
@@ -874,9 +835,8 @@ FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material,
return p;
}
FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material,
const char* name) noexcept {
FMaterialInstance* p = mHeapAllocator.make<FMaterialInstance>(*this, material, name);
FMaterialInstance* FEngine::createMaterialInstance(const FMaterial* material) noexcept {
FMaterialInstance* p = mHeapAllocator.make<FMaterialInstance>(*this, material);
if (UTILS_UNLIKELY(p)) { // should never happen
auto pos = mMaterialInstances.emplace(material, "MaterialInstance");
pos.first->second.insert(p);
@@ -1355,32 +1315,6 @@ void FEngine::unprotected() noexcept {
mUnprotectedDummySwapchain->makeCurrent(getDriverApi());
}
bool FEngine::setFeatureFlag(char const* name, bool value) noexcept {
auto* const p = getFeatureFlagPtr(name);
if (p) {
*p = value;
}
return p != nullptr;
}
std::optional<bool> FEngine::getFeatureFlag(char const* name) const noexcept {
auto* const p = getFeatureFlagPtr(name, true);
if (p) {
return *p;
}
return std::nullopt;
}
bool* FEngine::getFeatureFlagPtr(std::string_view name, bool allowConstant) const noexcept {
auto pos = std::find_if(mFeatures.begin(), mFeatures.end(),
[name](FeatureFlag const& entry) {
return name == entry.name;
});
return (pos != mFeatures.end() && (!pos->constant || allowConstant)) ?
const_cast<bool*>(pos->value) : nullptr;
}
// ------------------------------------------------------------------------------------------------
Engine::Builder::Builder() noexcept = default;
@@ -1420,20 +1354,6 @@ Engine::Builder& Engine::Builder::paused(bool paused) noexcept {
return *this;
}
Engine::Builder& Engine::Builder::feature(char const* name, bool value) noexcept {
mImpl->mFeatureFlags[name] = value;
return *this;
}
Engine::Builder& Engine::Builder::features(std::initializer_list<char const *> list) noexcept {
for (auto name : list) {
if (name) {
feature(name, true);
}
}
return *this;
}
#if UTILS_HAS_THREADING
void Engine::Builder::build(Invocable<void(void*)>&& callback) const {
+5 -50
View File
@@ -56,24 +56,21 @@
#include <filament/Engine.h>
#include <filament/IndirectLight.h>
#include <filament/Material.h>
#include <filament/MaterialEnums.h>
#include <filament/Skybox.h>
#include <filament/Stream.h>
#include <filament/Texture.h>
#include <filament/VertexBuffer.h>
#include <utils/Allocator.h>
#include <utils/compiler.h>
#include <utils/CountDownLatch.h>
#include <utils/FixedCapacityVector.h>
#include <utils/JobSystem.h>
#include <utils/Slice.h>
#include <utils/compiler.h>
#include <array>
#include <chrono>
#include <memory>
#include <new>
#include <optional>
#include <string_view>
#include <random>
#include <thread>
#include <type_traits>
@@ -211,18 +208,10 @@ public:
return mRenderableManager;
}
FRenderableManager const& getRenderableManager() const noexcept {
return mRenderableManager;
}
FLightManager& getLightManager() noexcept {
return mLightManager;
}
FLightManager const& getLightManager() const noexcept {
return mLightManager;
}
FCameraManager& getCameraManager() noexcept {
return mCameraManager;
}
@@ -314,8 +303,7 @@ public:
FMaterialInstance* createMaterialInstance(const FMaterial* material,
const FMaterialInstance* other, const char* name) noexcept;
FMaterialInstance* createMaterialInstance(const FMaterial* material,
const char* name) noexcept;
FMaterialInstance* createMaterialInstance(const FMaterial* material) noexcept;
FScene* createScene() noexcept;
FView* createView() noexcept;
@@ -434,11 +422,8 @@ public:
bool execute();
utils::JobSystem& getJobSystem() const noexcept {
// JobSystem is thread-safe, and it's always okay to return a non-const one,
// it's conceptually the same as if we were holding a non-const reference, as opposed
// to by-value class attribute.
return const_cast<utils::JobSystem&>(mJobSystem);
utils::JobSystem& getJobSystem() noexcept {
return mJobSystem;
}
std::default_random_engine& getRandomEngine() {
@@ -675,36 +660,6 @@ public:
} stereo;
matdbg::DebugServer* server = nullptr;
} debug;
struct {
struct {
struct {
bool assert_native_window_is_valid = false;
} opengl;
bool disable_parallel_shader_compile = false;
bool disable_handle_use_after_free_check = false;
} backend;
} features;
std::array<Engine::FeatureFlag, sizeof(features)> const mFeatures{{
{ "backend.disable_parallel_shader_compile",
"Disable parallel shader compilation in GL and Metal backends.",
&features.backend.disable_parallel_shader_compile, true },
{ "backend.disable_handle_use_after_free_check",
"Disable Handle<> use-after-free checks.",
&features.backend.disable_handle_use_after_free_check, true },
{ "backend.opengl.assert_native_window_is_valid",
"Asserts that the ANativeWindow is valid when rendering starts.",
&features.backend.opengl.assert_native_window_is_valid, true }
}};
utils::Slice<const Engine::FeatureFlag> getFeatureFlags() const noexcept {
return { mFeatures.data(), mFeatures.size() };
}
bool setFeatureFlag(char const* name, bool value) noexcept;
std::optional<bool> getFeatureFlag(char const* name) const noexcept;
bool* getFeatureFlagPtr(std::string_view name, bool allowConstant = false) const noexcept;
};
FILAMENT_DOWNCAST(Engine)
-5
View File
@@ -55,11 +55,6 @@ IndexBuffer* IndexBuffer::Builder::build(Engine& engine) {
FIndexBuffer::FIndexBuffer(FEngine& engine, const IndexBuffer::Builder& builder)
: mIndexCount(builder->mIndexCount) {
auto& name = builder.getName();
const char* const tag = name.empty() ? "(no tag)" : name.c_str_safe();
FILAMENT_CHECK_PRECONDITION(
builder->mIndexType == IndexType::UINT || builder->mIndexType == IndexType::USHORT)
<< "Invalid index type " << static_cast<int>(builder->mIndexType) << ", tag=" << tag;
FEngine::DriverApi& driver = engine.getDriverApi();
mHandle = driver.createIndexBuffer(
(backend::ElementType)builder->mIndexType,
+4 -3
View File
@@ -346,8 +346,8 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder,
FMaterial::~FMaterial() noexcept = default;
void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantValue) noexcept {
DriverApi& driverApi = mEngine.getDriverApi();
if (mMaterialDomain == MaterialDomain::SURFACE) {
DriverApi& driverApi = mEngine.getDriverApi();
auto& cachedPrograms = mCachedPrograms;
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
Variant const variant(k);
@@ -377,6 +377,7 @@ void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantV
}
}
} else if (mMaterialDomain == MaterialDomain::POST_PROCESS) {
DriverApi& driverApi = mEngine.getDriverApi();
auto& cachedPrograms = mCachedPrograms;
for (size_t k = 0, n = POST_PROCESS_VARIANT_COUNT; k < n; ++k) {
if ((k & variantMask) == variantValue) {
@@ -461,13 +462,13 @@ FMaterialInstance* FMaterial::createInstance(const char* name) const noexcept {
return FMaterialInstance::duplicate(mDefaultMaterialInstance, name);
} else {
// but if we don't, just create an instance with all the default parameters
return mEngine.createMaterialInstance(this, name);
return mEngine.createMaterialInstance(this);
}
}
FMaterialInstance* FMaterial::getDefaultInstance() noexcept {
if (UTILS_UNLIKELY(!mDefaultMaterialInstance)) {
mDefaultMaterialInstance = mEngine.createMaterialInstance(this, mName.c_str());
mDefaultMaterialInstance = mEngine.createMaterialInstance(this);
mDefaultMaterialInstance->setDefaultInstance(true);
}
return mDefaultMaterialInstance;
+2 -4
View File
@@ -56,8 +56,7 @@ namespace filament {
using namespace backend;
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
const char* name) noexcept
FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material) noexcept
: mMaterial(material),
mDescriptorSet(material->getDescriptorSetLayout()),
mCulling(CullingMode::BACK),
@@ -67,8 +66,7 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, FMaterial const* material,
mHasScissor(false),
mIsDoubleSided(false),
mIsDefaultInstance(false),
mTransparencyMode(TransparencyMode::DEFAULT),
mName(name ? CString(name) : material->getName()) {
mTransparencyMode(TransparencyMode::DEFAULT) {
FEngine::DriverApi& driver = engine.getDriverApi();
+1 -2
View File
@@ -53,8 +53,7 @@ class FTexture;
class FMaterialInstance : public MaterialInstance {
public:
FMaterialInstance(FEngine& engine, FMaterial const* material,
const char* name) noexcept;
FMaterialInstance(FEngine& engine, FMaterial const* material) noexcept;
FMaterialInstance(FEngine& engine, FMaterialInstance const* other, const char* name);
FMaterialInstance(const FMaterialInstance& rhs) = delete;
FMaterialInstance& operator=(const FMaterialInstance& rhs) = delete;
+9 -73
View File
@@ -407,9 +407,6 @@ void FRenderer::endFrame() {
driver.debugThreading();
}
FILAMENT_CHECK_PRECONDITION(engine.isValid(mSwapChain))
<< "SwapChain must remain valid until endFrame is called.";
if (mSwapChain) {
mSwapChain->commit(driver);
mSwapChain = nullptr;
@@ -921,8 +918,8 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
// updatePrimitivesLod must be run before appendCommands and once for each set
// of RenderPass::setCamera / RenderPass::setGeometry calls.
FView::updatePrimitivesLod(scene.getRenderableData(),
engine, cameraInfo, view.getVisibleRenderables());
view.updatePrimitivesLod(engine, cameraInfo,
scene.getRenderableData(), view.getVisibleRenderables());
passBuilder.camera(cameraInfo);
passBuilder.geometry(scene.getRenderableData(), view.getVisibleRenderables());
@@ -965,74 +962,17 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
// --------------------------------------------------------------------------------------------
// structure pass -- automatically culled if not used
// Currently it consists of a simple depth pass.
// This is normally used by SSAO and contact-shadows.
// Also, picking is handled here if transparent picking is disabled.
// This is normally used by SSAO and contact-shadows
// TODO: the scaling should depends on all passes that need the structure pass
const auto [structure, picking_] = ppm.structure(fg,
passBuilder, renderFlags, svp.width, svp.height, {
.scale = aoOptions.resolution,
.picking = view.hasPicking() && !view.isTransparentPickingEnabled()
.picking = view.hasPicking()
});
auto picking = picking_;
// --------------------------------------------------------------------------------------------
// Picking pass -- automatically culled if not used
// Picking is handled here if transparent picking is enabled.
const auto picking = picking_;
if (view.hasPicking()) {
if (view.isTransparentPickingEnabled()) {
struct PickingRenderPassData {
FrameGraphId<FrameGraphTexture> depth;
FrameGraphId<FrameGraphTexture> picking;
};
auto& pickingRenderPass = fg.addPass<PickingRenderPassData>("Picking Render Pass",
[&](FrameGraph::Builder& builder, auto& data) {
bool const isFL0 = mEngine.getDriverApi().getFeatureLevel() ==
FeatureLevel::FEATURE_LEVEL_0;
// TODO: Specify the precision for picking pass
uint32_t const width = std::max(32u,
(uint32_t)std::ceil(float(svp.width) * aoOptions.resolution));
uint32_t const height = std::max(32u,
(uint32_t)std::ceil(float(svp.height) * aoOptions.resolution));
data.depth = builder.createTexture("Depth Buffer", {
.width = width, .height = height,
.format = isFL0 ? TextureFormat::DEPTH24 : TextureFormat::DEPTH32F });
data.depth = builder.write(data.depth,
FrameGraphTexture::Usage::DEPTH_ATTACHMENT);
data.picking = builder.createTexture("Picking Buffer", {
.width = width, .height = height,
.format = isFL0 ? TextureFormat::RGBA8 : TextureFormat::RG32F });
data.picking = builder.write(data.picking,
FrameGraphTexture::Usage::COLOR_ATTACHMENT);
builder.declareRenderPass("Picking Render Target", {
.attachments = {.color = { data.picking }, .depth = data.depth },
.clearFlags = TargetBufferFlags::COLOR0 | TargetBufferFlags::DEPTH
});
},
[=, passBuilder = passBuilder](FrameGraphResources const& resources,
auto const& data, DriverApi& driver) mutable {
Variant pickingVariant(Variant::DEPTH_VARIANT);
pickingVariant.setPicking(true);
auto out = resources.getRenderPassInfo();
passBuilder.renderFlags(renderFlags);
passBuilder.variant(pickingVariant);
passBuilder.commandTypeFlags(RenderPass::CommandTypeFlags::DEPTH);
RenderPass const pass{ passBuilder.build(mEngine, driver) };
driver.beginRenderPass(out.target, out.params);
pass.getExecutor().execute(mEngine, driver);
driver.endRenderPass();
});
picking = pickingRenderPass->picking;
}
struct PickingResolvePassData {
FrameGraphId<FrameGraphTexture> picking;
};
@@ -1113,11 +1053,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
// FIXME: we should use 'vp' when rendering directly into the swapchain, but that's hard to
// know at this point. This will usually be the case when post-process is disabled.
// FIXME: we probably should take the dynamic scaling into account too
// if MSAA is enabled, we end-up rendering in an intermediate buffer. This is the only case where
// "!hasPostProcess" doesn't guarantee rendering into the swapchain.
const bool useIntermediateBuffer = hasPostProcess || msaaOptions.enabled ||
(isRenderingMultiview && engine.debug.stereo.combine_multiview_images);
passBuilder.scissorViewport(useIntermediateBuffer ? xvp : vp);
passBuilder.scissorViewport(hasPostProcess ? xvp : vp);
// This one doesn't need to be a FrameGraph pass because it always happens by construction
// (i.e. it won't be culled, unless everything is culled), so no need to complexify things.
@@ -1132,7 +1068,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
if (colorGradingConfigForColor.asSubpass) {
// append color grading subpass after all other passes
passBuilder.customCommand(3,
passBuilder.customCommand(engine, 3,
RenderPass::Pass::BLENDED,
RenderPass::CustomCommand::EPILOG,
0, [&ppm, &driver, colorGradingConfigForColor]() {
@@ -1140,7 +1076,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
});
} else if (colorGradingConfig.customResolve) {
// append custom resolve subpass after all other passes
passBuilder.customCommand(3,
passBuilder.customCommand(engine, 3,
RenderPass::Pass::BLENDED,
RenderPass::CustomCommand::EPILOG,
0, [&ppm, &driver]() {
@@ -1158,7 +1094,7 @@ void FRenderer::renderJob(RootArenaScope& rootArenaScope, FView& view) {
passBuilder.renderFlags(renderFlags);
}
RenderPass const pass{ passBuilder.build(engine, driver) };
RenderPass const pass{ passBuilder.build(engine) };
FrameGraphTexture::Descriptor colorBufferDesc = {
.width = config.physicalViewport.width,
+2 -2
View File
@@ -1065,8 +1065,8 @@ inline void FView::computeLightCameraDistances(
}
}
void FView::updatePrimitivesLod(FScene::RenderableSoa& renderableData,
FEngine const& engine, CameraInfo const&, Range visible) noexcept {
void FView::updatePrimitivesLod(FEngine& engine, const CameraInfo&,
FScene::RenderableSoa& renderableData, Range visible) noexcept {
FRenderableManager const& rcm = engine.getRenderableManager();
for (uint32_t const index : visible) {
uint8_t const level = 0; // TODO: pick the proper level of detail
+3 -7
View File
@@ -133,9 +133,6 @@ public:
void setFrontFaceWindingInverted(bool inverted) noexcept { mFrontFaceWindingInverted = inverted; }
bool isFrontFaceWindingInverted() const noexcept { return mFrontFaceWindingInverted; }
void setTransparentPickingEnabled(bool enabled) noexcept { mIsTransparentPickingEnabled = enabled; }
bool isTransparentPickingEnabled() const noexcept { return mIsTransparentPickingEnabled; }
void setVisibleLayers(uint8_t select, uint8_t values) noexcept;
uint8_t getVisibleLayers() const noexcept {
@@ -203,9 +200,9 @@ public:
CameraInfo const& cameraInfo, math::float4 const& userTime,
RenderPassBuilder const& passBuilder) noexcept;
static void updatePrimitivesLod(FScene::RenderableSoa& renderableData,
FEngine const& engine, CameraInfo const& camera,
Range visible) noexcept;
void updatePrimitivesLod(
FEngine& engine, const CameraInfo& camera,
FScene::RenderableSoa& renderableData, Range visible) noexcept;
void setShadowingEnabled(bool enabled) noexcept { mShadowingEnabled = enabled; }
@@ -534,7 +531,6 @@ private:
Viewport mViewport;
bool mCulling = true;
bool mFrontFaceWindingInverted = false;
bool mIsTransparentPickingEnabled = true;
FRenderTarget* mRenderTarget = nullptr;
+2 -5
View File
@@ -35,11 +35,8 @@ DescriptorSetLayout::DescriptorSetLayout(
backend::DescriptorSetLayout descriptorSetLayout) noexcept {
for (auto&& desc : descriptorSetLayout.bindings) {
mMaxDescriptorBinding = std::max(mMaxDescriptorBinding, desc.binding);
mSamplers.set(desc.binding,
desc.type == backend::DescriptorType::SAMPLER ||
desc.type == backend::DescriptorType::SAMPLER_EXTERNAL);
mUniformBuffers.set(desc.binding,
desc.type == backend::DescriptorType::UNIFORM_BUFFER);
mSamplers.set(desc.binding, desc.type == backend::DescriptorType::SAMPLER);
mUniformBuffers.set(desc.binding, desc.type == backend::DescriptorType::UNIFORM_BUFFER);
}
mDescriptorSetLayoutHandle = factory.create(driver,
+3 -3
View File
@@ -61,7 +61,7 @@ PerViewUib& ShadowMapDescriptorSet::edit(Transaction const& transaction) noexcep
}
void ShadowMapDescriptorSet::prepareCamera(Transaction const& transaction,
backend::DriverApi& driver, const CameraInfo& camera) noexcept {
FEngine& engine, const CameraInfo& camera) noexcept {
mat4f const& viewFromWorld = camera.view;
mat4f const& worldFromView = camera.model;
mat4f const& clipFromView = camera.projection;
@@ -85,7 +85,7 @@ void ShadowMapDescriptorSet::prepareCamera(Transaction const& transaction,
// with a clip-space of [-w, w] ==> z' = -z
// with a clip-space of [0, w] ==> z' = (w - z)/2
s.clipControl = driver.getClipSpaceParams();
s.clipControl = engine.getDriverApi().getClipSpaceParams();
}
void ShadowMapDescriptorSet::prepareLodBias(Transaction const& transaction, float bias) noexcept {
@@ -103,7 +103,7 @@ void ShadowMapDescriptorSet::prepareViewport(Transaction const& transaction,
}
void ShadowMapDescriptorSet::prepareTime(Transaction const& transaction,
FEngine const& engine, math::float4 const& userTime) noexcept {
FEngine& engine, math::float4 const& userTime) noexcept {
auto& s = edit(transaction);
const uint64_t oneSecondRemainder = engine.getEngineTime().count() % 1'000'000'000;
const float fraction = float(double(oneSecondRemainder) / 1'000'000'000.0);
+2 -2
View File
@@ -56,7 +56,7 @@ public:
void terminate(backend::DriverApi& driver);
static void prepareCamera(Transaction const& transaction,
backend::DriverApi& driver, const CameraInfo& camera) noexcept;
FEngine& engine, const CameraInfo& camera) noexcept;
static void prepareLodBias(Transaction const& transaction,
float bias) noexcept;
@@ -65,7 +65,7 @@ public:
backend::Viewport const& viewport) noexcept;
static void prepareTime(Transaction const& transaction,
FEngine const& engine, math::float4 const& userTime) noexcept;
FEngine& engine, math::float4 const& userTime) noexcept;
static void prepareShadowMapping(Transaction const& transaction,
bool highPrecision) noexcept;
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.56.1"
spec.version = "1.55.1"
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.56.1/filament-v1.56.1-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.55.1/filament-v1.55.1-ios.tgz" }
# Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon.
spec.pod_target_xcconfig = {
+2
View File
@@ -36,6 +36,8 @@ elseif (APPLE AND NOT IOS)
elseif(LINUX)
if(FILAMENT_SUPPORTS_EGL_ON_LINUX)
set(SRCS ${SRCS} src/BlueGLLinuxEGL.cpp)
elseif(FILAMENT_SUPPORTS_OSMESA)
set(SRCS ${SRCS} src/BlueGLLinuxOSMesa.cpp)
else()
set(SRCS ${SRCS} src/BlueGLLinux.cpp)
endif()
+59
View File
@@ -0,0 +1,59 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <GL/gl.h>
#include <dlfcn.h>
#include <string.h>
namespace bluegl {
namespace {
using ProcAddressFunc = void*(*)(char const* funcName);
}
struct Driver {
ProcAddressFunc OSMesaGetProcAddress;
void* library;
} g_driver = {nullptr, nullptr};
bool initBinder() {
constexpr char const* libraryNames[] = {"libOSMesa.so", "libosmesa.so"};
for (char const* name : libraryNames) {
g_driver.library = dlopen(name, RTLD_GLOBAL | RTLD_NOW);
if (g_driver.library) {
break;
}
}
if (!g_driver.library) {
return false;
}
g_driver.OSMesaGetProcAddress = (ProcAddressFunc)
dlsym(g_driver.library, "OSMesaGetProcAddress");
return g_driver.OSMesaGetProcAddress;
}
void* loadFunction(const char* name) {
return (void*) g_driver.OSMesaGetProcAddress(name);
}
void shutdownBinder() {
dlclose(g_driver.library);
memset(&g_driver, 0, sizeof(g_driver));
}
} // namespace bluegl
@@ -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 = 56;
static constexpr size_t MATERIAL_VERSION = 55;
/**
* Supported shading models
+4 -11
View File
@@ -72,8 +72,6 @@ const char* toString(DescriptorType type) {
return "SAMPLER";
case DescriptorType::INPUT_ATTACHMENT:
return "INPUT_ATTACHMENT";
case DescriptorType::SAMPLER_EXTERNAL:
return "SAMPLER_EXTERNAL";
}
}
@@ -137,9 +135,7 @@ DescriptorSetLayout getPerMaterialDescriptorSet(SamplerInterfaceBlock const& sib
+PerMaterialBindingPoints::MATERIAL_PARAMS, DescriptorFlags::NONE, 0 });
for (auto const& sampler : samplers) {
layout.bindings.push_back(DescriptorSetLayoutBinding {
(sampler.type == SamplerInterfaceBlock::Type::SAMPLER_EXTERNAL) ?
DescriptorType::SAMPLER_EXTERNAL : DescriptorType::SAMPLER,
layout.bindings.push_back(DescriptorSetLayoutBinding { DescriptorType::SAMPLER,
ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT, sampler.binding,
DescriptorFlags::NONE, 0 });
}
@@ -215,8 +211,7 @@ static void collectDescriptorsForSet(filament::DescriptorSetBindingPoints set,
for (size_t i = 0; i < info.bindings.size(); i++) {
backend::descriptor_binding_t binding = info.bindings[i].binding;
auto name = getDescriptorName(set, binding);
if (info.bindings[i].type == DescriptorType::SAMPLER ||
info.bindings[i].type == DescriptorType::SAMPLER_EXTERNAL) {
if (info.bindings[i].type == DescriptorType::SAMPLER) {
auto pos = std::find_if(samplerList.begin(), samplerList.end(),
[&](const auto& entry) { return entry.binding == binding; });
assert_invariant(pos != samplerList.end());
@@ -250,8 +245,7 @@ void prettyPrintDescriptorSetInfoVector(DescriptorSets const& sets) {
printf("[DS] info (%s) = [\n", getName(setIndex));
for (auto const& descriptor : descriptors) {
auto const& [name, info, sampler] = descriptor;
if (info.type == DescriptorType::SAMPLER ||
info.type == DescriptorType::SAMPLER_EXTERNAL) {
if (info.type == DescriptorType::SAMPLER) {
assert_invariant(sampler.has_value());
printf(" {name = %s, binding = %d, type = %s, count = %d, stage = %s, flags = "
"%s, samplerType = %s}",
@@ -462,8 +456,7 @@ void GLSLPostProcessor::spirvToMsl(const SpirvBlob* spirv, std::string* outMsl,
break;
}
case DescriptorType::SAMPLER:
case DescriptorType::SAMPLER_EXTERNAL: {
case DescriptorType::SAMPLER: {
assert_invariant(sampler.has_value());
const std::string samplerName = std::string(name.c_str()) + "Smplr";
argBufferBuilder
@@ -202,11 +202,7 @@ void MaterialDescriptorBindingsChuck::flatten(Flattener& f) {
// all the material's sampler descriptors
for (auto const& entry: mSamplerInterfaceBlock.getSamplerInfoList()) {
f.writeString({ entry.uniformName.data(), entry.uniformName.size() });
if (entry.type == SamplerInterfaceBlock::Type::SAMPLER_EXTERNAL) {
f.writeUint8(uint8_t(DescriptorType::SAMPLER_EXTERNAL));
} else {
f.writeUint8(uint8_t(DescriptorType::SAMPLER));
}
f.writeUint8(uint8_t(DescriptorType::SAMPLER));
f.writeUint8(entry.binding);
}
@@ -260,11 +256,7 @@ void MaterialDescriptorSetLayoutChunk::flatten(Flattener& f) {
// all the material's sampler descriptors
for (auto const& entry: mSamplerInterfaceBlock.getSamplerInfoList()) {
if (entry.type == SamplerInterfaceBlock::Type::SAMPLER_EXTERNAL) {
f.writeUint8(uint8_t(DescriptorType::SAMPLER_EXTERNAL));
} else {
f.writeUint8(uint8_t(DescriptorType::SAMPLER));
}
f.writeUint8(uint8_t(DescriptorType::SAMPLER));
f.writeUint8(uint8_t(ShaderStageFlags::VERTEX | ShaderStageFlags::FRAGMENT));
f.writeUint8(entry.binding);
f.writeUint8(uint8_t(DescriptorFlags::NONE));
-5
View File
@@ -197,11 +197,6 @@ if (WEBGL_PTHREADS)
endif()
set(GLTFIO_WARNINGS -Wall -Werror)
if (CMAKE_CXX_STANDARD EQUAL 20)
# The following things used by AssetLoader.cpp are deprecated in c++20:
# wstring_convert and std::codecvt_utf8<char32_t>.
list(APPEND GLTFIO_WARNINGS -Wno-deprecated-declarations)
endif()
if (NOT MSVC)
target_compile_options(gltfio_core PRIVATE ${GLTFIO_WARNINGS})
endif()
@@ -26,7 +26,6 @@
#include <utils/Log.h>
#include <utils/StructureOfArrays.h>
#include <cstring>
#include <memory>
#include <unordered_map>
+1 -1
View File
@@ -40,7 +40,7 @@ inline void* aligned_alloc(size_t size, size_t align) noexcept {
#if defined(WIN32)
p = ::_aligned_malloc(size, align);
#else
(void) ::posix_memalign(&p, align, size);
::posix_memalign(&p, align, size);
#endif
return p;
}
-24
View File
@@ -134,30 +134,6 @@ void JobSystem::setThreadPriority(Priority priority) noexcept {
slog.w << "setpriority failed: " << strerror(errno) << io::endl;
}
#endif
#elif defined(__APPLE__)
qos_class_t qosClass = QOS_CLASS_DEFAULT;
switch (priority) {
case Priority::BACKGROUND:
qosClass = QOS_CLASS_BACKGROUND;
break;
case Priority::NORMAL:
qosClass = QOS_CLASS_DEFAULT;
break;
case Priority::DISPLAY:
qosClass = QOS_CLASS_USER_INTERACTIVE;
break;
case Priority::URGENT_DISPLAY:
qosClass = QOS_CLASS_USER_INTERACTIVE;
break;
}
errno = 0;
UTILS_UNUSED_IN_RELEASE int error;
error = pthread_set_qos_class_self_np(qosClass, 0);
#ifndef NDEBUG
if (UTILS_UNLIKELY(error)) {
slog.w << "pthread_set_qos_class_self_np failed: " << strerror(errno) << io::endl;
}
#endif
#endif
}
-1
View File
@@ -15,7 +15,6 @@
#ifndef DRACO_IO_FILE_UTILS_H_
#define DRACO_IO_FILE_UTILS_H_
#include <cstdint>
#include <string>
#include <vector>

Some files were not shown because too many files have changed in this diff Show More