Compare commits

..
Author SHA1 Message Date
Powei Feng 8c7a82f7b3 vk: make protected content work 2024-10-31 11:17:19 -07:00
80 changed files with 1972 additions and 2523 deletions
-1
View File
@@ -7,4 +7,3 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- vk: fix stage pool gc logic
+2 -2
View File
@@ -31,7 +31,7 @@ repositories {
}
dependencies {
implementation 'com.google.android.filament:filament-android:1.56.0'
implementation 'com.google.android.filament:filament-android:1.55.0'
}
```
@@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`:
iOS projects can use CocoaPods to install the latest release:
```shell
pod 'Filament', '~> 1.56.0'
pod 'Filament', '~> 1.55.0'
```
## Documentation
-6
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
@@ -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;
@@ -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);
}
+1 -1
View File
@@ -1,5 +1,5 @@
GROUP=com.google.android.filament
VERSION_NAME=1.56.0
VERSION_NAME=1.55.0
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
@@ -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
-5
View File
@@ -308,11 +308,6 @@ if (FILAMENT_ENABLE_MULTIVIEW)
add_definitions(-DFILAMENT_ENABLE_MULTIVIEW)
endif()
# Whether to force the profiling mode.
if (FILAMENT_FORCE_PROFILING_MODE)
add_definitions(-DFILAMENT_FORCE_PROFILING_MODE)
endif()
# ==================================================================================================
# Definitions
# ==================================================================================================
+7 -7
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
@@ -221,6 +214,9 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/VulkanSwapChain.h
src/vulkan/VulkanReadPixels.cpp
src/vulkan/VulkanReadPixels.h
src/vulkan/VulkanResourceAllocator.h
src/vulkan/VulkanResources.cpp
src/vulkan/VulkanResources.h
src/vulkan/VulkanTexture.cpp
src/vulkan/VulkanTexture.h
src/vulkan/VulkanUtility.cpp
@@ -375,6 +371,10 @@ 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)
@@ -227,7 +227,6 @@ enum class DescriptorType : uint8_t {
SHADER_STORAGE_BUFFER,
SAMPLER,
INPUT_ATTACHMENT,
SAMPLER_EXTERNAL
};
enum class DescriptorFlags : uint8_t {
@@ -21,7 +21,7 @@
#include "bluegl/BlueGL.h"
#include <osmesa.h>
#include "osmesa.h"
#include <backend/platforms/OpenGLPlatform.h>
#include <backend/DriverEnums.h>
@@ -56,7 +56,6 @@ protected:
private:
OSMesaContext mContext;
void* mOsMesaApi = nullptr;
};
} // namespace filament::backend
@@ -88,7 +88,7 @@ public:
VkFormat colorFormat = VK_FORMAT_UNDEFINED;
VkFormat depthFormat = VK_FORMAT_UNDEFINED;
VkExtent2D extent = {0, 0};
bool isProtected = false;
bool protection = false;
};
struct ImageSyncData {
-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
+16 -56
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";
}
}
@@ -797,20 +795,9 @@ void MetalDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
}
void MetalDriver::destroyProgram(Handle<HwProgram> ph) {
if (UTILS_UNLIKELY(!ph)) {
return;
if (ph) {
destruct_handle<MetalProgram>(ph);
}
// Remove any cached pipeline states that refer to these programs.
auto* metalProgram = handle_cast<MetalProgram>(ph);
const auto& functions = metalProgram->getFunctionsIfPresent();
if (UTILS_LIKELY(functions.isRaster())) { // only raster pipelines are cached
const auto& raster = functions.getRasterFunctions();
mContext->pipelineStateCache.removeIf([&](const MetalPipelineState& state) {
const auto& [fragment, vertex] = raster;
return state.fragmentFunction == fragment || state.vertexFunction == vertex;
});
}
destruct_handle<MetalProgram>(ph);
}
void MetalDriver::destroyTexture(Handle<HwTexture> th) {
@@ -832,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);
}
}
}
@@ -872,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() {
@@ -1824,16 +1794,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);
+1 -2
View File
@@ -202,7 +202,6 @@ public:
MetalProgram(MetalContext& context, Program&& program) noexcept;
const MetalShaderCompiler::MetalFunctionBundle& getFunctions();
const MetalShaderCompiler::MetalFunctionBundle& getFunctionsIfPresent() const;
private:
void initialize();
@@ -438,7 +437,7 @@ struct MetalTimerQuery : public HwTimerQuery {
struct Status {
std::atomic<bool> available {false};
std::atomic<uint64_t> elapsed {0}; // only valid if available is true
uint64_t elapsed {0}; // only valid if available is true
};
std::shared_ptr<Status> status;
+2 -8
View File
@@ -495,10 +495,6 @@ const MetalShaderCompiler::MetalFunctionBundle& MetalProgram::getFunctions() {
return mFunctionBundle;
}
const MetalShaderCompiler::MetalFunctionBundle& MetalProgram::getFunctionsIfPresent() const {
return mFunctionBundle;
}
void MetalProgram::initialize() {
if (!mToken) {
return;
@@ -1294,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;
@@ -1410,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];
@@ -83,10 +83,6 @@ public:
return std::get<Compute>(mPrograms);
}
bool isRaster() const { return std::holds_alternative<Raster>(mPrograms); }
bool isCompute() const { return std::holds_alternative<Compute>(mPrograms); }
static MetalFunctionBundle none() {
return MetalFunctionBundle(None{});
}
+1 -17
View File
@@ -28,9 +28,7 @@
#include <memory>
#include <tsl/robin_map.h>
#include <utils/Hash.h>
#include <utils/Invocable.h>
namespace filament {
namespace backend {
@@ -162,8 +160,6 @@ template<typename StateType,
typename HashFn = utils::hash::MurmurHashFn<StateType>>
class StateCache {
using MapType = tsl::robin_map<StateType, MetalType, HashFn>;
public:
StateCache() = default;
@@ -173,18 +169,6 @@ public:
void setDevice(id<MTLDevice> device) noexcept { mDevice = device; }
void removeIf(utils::Invocable<bool(const StateType&)> fn) noexcept {
typename MapType::const_iterator it = mStateCache.begin();
while (it != mStateCache.end()) {
const auto& [key, _] = *it;
if (UTILS_UNLIKELY(fn(key))) {
it = mStateCache.erase(it);
} else {
++it;
}
}
}
MetalType getOrCreateState(const StateType& state) noexcept {
assert_invariant(mDevice);
@@ -212,7 +196,7 @@ private:
StateCreator creator;
id<MTLDevice> mDevice = nil;
MapType mStateCache;
tsl::robin_map<StateType, MetalType, HashFn> mStateCache;
};
+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
+26 -55
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,47 +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
// Override the debug markers if we are forcing profiling mode
#if defined(FILAMENT_FORCE_PROFILING_MODE)
# undef DEBUG_GROUP_MARKER_LEVEL
# undef DEBUG_MARKER_LEVEL
# define DEBUG_GROUP_MARKER_LEVEL DEBUG_GROUP_MARKER_NONE
# define DEBUG_MARKER_LEVEL DEBUG_MARKER_PROFILE
#endif
#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
# ifndef NDEBUG
# error PROFILING is meaningless in DEBUG mode.
# 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;
@@ -406,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)) {
@@ -1682,7 +1661,7 @@ void OpenGLDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
// See if we need the emulated rec709 output conversion
if (UTILS_UNLIKELY(mContext.isES2())) {
sc->rec709 = ((flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) &&
sc->rec709 = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE &&
!mPlatform.isSRGBSwapChainSupported());
}
}
@@ -3469,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);
@@ -3504,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
@@ -3896,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,7 +19,6 @@
#include <utils/Log.h>
#include <utils/Panic.h>
#include <dlfcn.h>
#include <memory>
namespace filament::backend {
@@ -42,56 +41,13 @@ struct OSMesaSwapchain {
std::unique_ptr<uint8_t[]> buffer;
};
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
} // 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 = api->OSMesaCreateContext(GL_RGBA, NULL);
mContext = 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.
@@ -106,11 +62,7 @@ Driver* PlatformOSMesa::createDriver(void* const sharedGLContext,
}
void PlatformOSMesa::terminate() noexcept {
OSMesaAPI* api = (OSMesaAPI*) mOsMesaApi;
api->OSMesaDestroyContext(mContext);
delete api;
mOsMesaApi = nullptr;
OSMesaDestroyContext(mContext);
bluegl::unbind();
}
@@ -132,12 +84,11 @@ 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 = api->OSMesaMakeCurrent(mContext, (BackingType*) impl->buffer.get(),
BACKING_GL_TYPE, impl->width, impl->height);
FILAMENT_CHECK_POSTCONDITION(result == GL_TRUE) << "OSMesaMakeCurrent failed!";
auto result = OSMesaMakeCurrent(mContext, (BackingType*) impl->buffer.get(), BACKING_GL_TYPE,
impl->width, impl->height);
FILAMENT_CHECK_POSTCONDITION(result) << "OSMesaMakeCurrent failed!";
return true;
}
@@ -1,43 +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)
: 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,81 +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 "vulkan/memory/Resource.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, fvkmemory::ThreadSafeResource {
VulkanFence() {}
std::shared_ptr<VulkanCmdFence> fence;
};
struct VulkanTimerQuery : public HwTimerQuery, fvkmemory::ThreadSafeResource {
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
@@ -126,7 +126,8 @@ struct BlitterUniforms {
}// anonymous namespace
VulkanBlitter::VulkanBlitter(VkPhysicalDevice physicalDevice, VulkanCommands* commands) noexcept
: mPhysicalDevice(physicalDevice), mCommands(commands) {}
: mPhysicalDevice(physicalDevice),
mCommands(commands) {}
void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) {
@@ -150,7 +151,7 @@ void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) {
}
#endif
VulkanCommandBuffer& commands = dst.texture->getIsProtected() ?
VulkanCommandBuffer& commands = dst.texture->isProtected() ?
mCommands->getProtected() : mCommands->get();
commands.acquire(src.texture);
commands.acquire(dst.texture);
@@ -176,7 +177,7 @@ void VulkanBlitter::blit(VkFilter filter,
#endif
// src and dst should have the same aspect here
VkImageAspectFlags const aspect = src.texture->getImageAspect();
VulkanCommandBuffer& commands = dst.texture->getIsProtected() ?
VulkanCommandBuffer& commands = dst.texture->isProtected() ?
mCommands->getProtected() : mCommands->get();
commands.acquire(src.texture);
commands.acquire(dst.texture);
+98 -92
View File
@@ -58,41 +58,66 @@ VkCommandBuffer createCommandBuffer(VkDevice device, VkCommandPool pool) {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
void VulkanGroupMarkers::push(std::string const& marker, Timestamp start) noexcept {
mMarkers.push_back({marker,
start.time_since_epoch().count() > 0.0
? start
: std::chrono::high_resolution_clock::now()});
mMarkers.push_back(marker);
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
mTimestamps.push_back(start.time_since_epoch().count() > 0.0
? start
: std::chrono::high_resolution_clock::now());
#endif
}
std::pair<std::string, Timestamp> VulkanGroupMarkers::pop() noexcept {
auto ret = mMarkers.back();
auto const marker = mMarkers.back();
mMarkers.pop_back();
return ret;
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
auto const timestamp = mTimestamps.back();
mTimestamps.pop_back();
return std::make_pair(marker, timestamp);
#else
return std::make_pair(marker, Timestamp{});
#endif
}
std::pair<std::string, Timestamp> VulkanGroupMarkers::pop_bottom() noexcept {
auto ret = mMarkers.front();
auto const marker = mMarkers.front();
mMarkers.pop_front();
return ret;
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
auto const timestamp = mTimestamps.front();
mTimestamps.pop_front();
return std::make_pair(marker, timestamp);
#else
return std::make_pair(marker, Timestamp{});
#endif
}
std::pair<std::string, Timestamp> const& VulkanGroupMarkers::top() const {
std::pair<std::string, Timestamp> VulkanGroupMarkers::top() const {
assert_invariant(!empty());
return mMarkers.back();
auto const marker = mMarkers.back();
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
auto const topTimestamp = mTimestamps.front();
return std::make_pair(marker, topTimestamp);
#else
return std::make_pair(marker, Timestamp{});
#endif
}
bool VulkanGroupMarkers::empty() const noexcept {
return mMarkers.empty();
}
#endif // FVK_DEBUG_GROUP_MARKERS
VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext* context, VkDevice device, VkQueue queue,
VkCommandPool pool, bool isProtected)
VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext* context, VulkanResourceAllocator* allocator,
VkDevice device, VkQueue queue, VkCommandPool pool, bool isProtected)
: mContext(context),
mMarkerCount(0),
isProtected(isProtected),
mDevice(device),
mQueue(queue),
mResourceManager(allocator),
mBuffer(createCommandBuffer(device, pool)),
mFenceStatus(std::make_shared<VulkanCmdFence>(VK_INCOMPLETE)) {
VkSemaphoreCreateInfo sci{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
@@ -100,6 +125,7 @@ VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext* context, VkDevice device
VkFenceCreateInfo fenceCreateInfo{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &mFence);
mIsProtected = isProtected;
}
VulkanCommandBuffer::~VulkanCommandBuffer() {
@@ -109,7 +135,7 @@ VulkanCommandBuffer::~VulkanCommandBuffer() {
void VulkanCommandBuffer::reset() noexcept {
mMarkerCount = 0;
mResources.clear();
mResourceManager.clear();
mWaitSemaphores.clear();
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence
@@ -167,6 +193,8 @@ void VulkanCommandBuffer::insertEvent(char const* marker) noexcept {
}
void VulkanCommandBuffer::begin() noexcept {
FVK_LOGE <<"[" << (mIsProtected ? "protected" : "regular") <<"]=" << buffer() << " begin"
<<utils::io::endl;
// Begin writing into the command buffer.
VkCommandBufferBeginInfo const binfo{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
@@ -182,6 +210,9 @@ VkSemaphore VulkanCommandBuffer::submit() {
vkEndCommandBuffer(mBuffer);
FVK_LOGE <<"[" << (mIsProtected ? "protected" : "regular") <<"]=" << buffer() << " end"
<<utils::io::endl;
VkPipelineStageFlags const waitDestStageMasks[2] = {
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT,
@@ -219,8 +250,7 @@ VkSemaphore VulkanCommandBuffer::submit() {
#endif
mFenceStatus->setStatus(VK_NOT_READY);
UTILS_UNUSED_IN_RELEASE VkResult result =
vkQueueSubmit(mQueue, 1, &submitInfo, mFence);
VkResult result = vkQueueSubmit(mQueue, 1, &submitInfo, mFence);
#if FVK_ENABLED(FVK_DEBUG_COMMAND_BUFFER)
if (result != VK_SUCCESS) {
@@ -232,8 +262,8 @@ VkSemaphore VulkanCommandBuffer::submit() {
return mSubmission;
}
CommandBufferPool::CommandBufferPool(VulkanContext* context, VkDevice device, VkQueue queue,
uint8_t queueFamilyIndex, bool isProtected)
CommandBufferPool::CommandBufferPool(VulkanContext* context, VulkanResourceAllocator* allocator,
VkDevice device, VkQueue queue, uint8_t queueFamilyIndex, bool isProtected)
: mDevice(device),
mRecording(INVALID) {
VkCommandPoolCreateInfo createInfo = {
@@ -246,8 +276,8 @@ CommandBufferPool::CommandBufferPool(VulkanContext* context, VkDevice device, Vk
vkCreateCommandPool(device, &createInfo, VKALLOC, &mPool);
for (size_t i = 0; i < CAPACITY; ++i) {
mBuffers.emplace_back(
std::make_unique<VulkanCommandBuffer>(context, device, queue, mPool, isProtected));
mBuffers.emplace_back(std::make_unique<VulkanCommandBuffer>(context, allocator, device,
queue, mPool, isProtected));
}
}
@@ -278,19 +308,6 @@ VulkanCommandBuffer& CommandBufferPool::getRecording() {
auto& recording = *mBuffers[mRecording];
recording.begin();
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
if (mGroupMarkers) {
std::unique_ptr<VulkanGroupMarkers> markers = std::make_unique<VulkanGroupMarkers>();
while (!mGroupMarkers->empty()) {
auto [marker, timestamp] = mGroupMarkers->pop_bottom();
recording.pushMarker(marker.c_str());
markers->push(marker, timestamp);
}
std::swap(mGroupMarkers, markers);
}
#endif
return recording;
}
@@ -330,7 +347,7 @@ VkSemaphore CommandBufferPool::flush() {
void CommandBufferPool::wait() {
uint8_t count = 0;
VkFence fences[CAPACITY];
mSubmitted.forEachSetBit([this, &count, &fences] (size_t index) {
mSubmitted.forEachSetBit([this,&count,&fences] (size_t index) {
fences[count++] = mBuffers[index]->getVkFence();
});
vkWaitForFences(mDevice, count, fences, VK_TRUE, UINT64_MAX);
@@ -345,46 +362,29 @@ void CommandBufferPool::waitFor(VkSemaphore previousAction) {
recording->insertWait(previousAction);
}
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
std::string CommandBufferPool::topMarker() const {
if (!mGroupMarkers || mGroupMarkers->empty()) {
return "";
}
return std::get<0>(mGroupMarkers->top());
}
void CommandBufferPool::pushMarker(char const* marker, VulkanGroupMarkers::Timestamp timestamp) {
if (!mGroupMarkers) {
mGroupMarkers = std::make_unique<VulkanGroupMarkers>();
}
mGroupMarkers->push(marker, timestamp);
void CommandBufferPool::pushMarker(char const* marker) {
getRecording().pushMarker(marker);
}
std::pair<std::string, VulkanGroupMarkers::Timestamp> CommandBufferPool::popMarker() {
assert_invariant(mGroupMarkers && !mGroupMarkers->empty());
auto ret = mGroupMarkers->pop();
// Note that if we're popping a marker while not recording, we would just pop the conceptual
// stack of marker (i.e. mGroupMarkers) and not carry out the pop on the command buffer.
if (isRecording()) {
getRecording().popMarker();
}
return ret;
void CommandBufferPool::popMarker() {
getRecording().popMarker();
}
void CommandBufferPool::insertEvent(char const* marker) {
getRecording().insertEvent(marker);
}
#endif // FVK_DEBUG_GROUP_MARKERS
VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context)
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, device, queue, queueFamilyIndex, false)) {}
mPool(std::make_unique<CommandBufferPool>(context, allocator, device, queue, queueFamilyIndex,
false)) {
}
void VulkanCommands::terminate() {
mPool.reset();
@@ -400,19 +400,14 @@ VulkanCommandBuffer& VulkanCommands::getProtected() {
assert_invariant(mProtectedQueue != VK_NULL_HANDLE);
if (!mProtectedPool) {
mProtectedPool = std::make_unique<CommandBufferPool>(mContext, mDevice, mProtectedQueue,
mProtectedQueueFamilyIndex, true);
mProtectedPool = std::make_unique<CommandBufferPool>(mContext, mAllocator, mDevice,
mProtectedQueue, mProtectedQueueFamilyIndex, true);
}
auto& ret = mProtectedPool->getRecording();
return ret;
}
bool VulkanCommands::flush() {
// It's possible to call flush and wait at "terminate", in which case, we'll just return.
if (!mPool && !mProtectedPool) {
return false;
}
VkSemaphore dependency = mInjectedDependency;
VkSemaphore lastSubmit = mLastSubmit;
bool hasFlushed = false;
@@ -439,16 +434,11 @@ bool VulkanCommands::flush() {
mInjectedDependency = VK_NULL_HANDLE;
mLastSubmit = dependency;
}
return true;
}
void VulkanCommands::wait() {
// It's possible to call flush and wait at "terminate", in which case, we'll just return.
if (!mPool && !mProtectedPool) {
return;
}
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("commands::wait");
@@ -480,31 +470,47 @@ void VulkanCommands::updateFences() {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
void VulkanCommands::pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp) {
mPool->pushMarker(str, timestamp);
if (mProtectedPool) {
mProtectedPool->pushMarker(str, timestamp);
}
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
FVK_LOGD << "----> " << str << utils::io::endl;
// If the timestamp is not 0, then we are carrying over a marker across buffer submits.
// If it is 0, then this is a normal marker push and we should just print debug line as usual.
if (timestamp.time_since_epoch().count() == 0.0) {
FVK_LOGD << "----> " << str << utils::io::endl;
}
#endif
if (!mGroupMarkers) {
mGroupMarkers = std::make_unique<VulkanGroupMarkers>();
}
mGroupMarkers->push(str, timestamp);
mPool->pushMarker(str);
if (mProtectedPool) {
mProtectedPool->pushMarker(str);
}
}
void VulkanCommands::popGroupMarker() {
assert_invariant(mGroupMarkers);
if (!mGroupMarkers->empty()) {
VkCommandBuffer const cmdbuffer = get().buffer();
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
auto ret = mPool->popMarker();
auto const& marker = ret.first;
auto const& startTime = ret.second;
auto const endTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = endTime - startTime;
FVK_LOGD << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms"
<< utils::io::endl;
auto const [marker, startTime] = mGroupMarkers->pop();
auto const endTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = endTime - startTime;
FVK_LOGD << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms"
<< utils::io::endl;
#else
mPool->popMarker();
#endif // FVK_DEBUG_PRINT_GROUP_MARKERS
if (mProtectedPool) {
mProtectedPool->popMarker();
mGroupMarkers->pop();
#endif
mPool->popMarker();
if (mProtectedPool) {
mProtectedPool->popMarker();
}
} else if (mCarriedOverMarkers && !mCarriedOverMarkers->empty()) {
// It could be that pop is called between flush() and get() (new command buffer), in which
// case the marker is in "carried over" state, we'd just remove that. Since the
// mCarriedOverMarkers is in the opposite order, we pop the bottom instead of the top.
mCarriedOverMarkers->pop_bottom();
}
}
@@ -516,10 +522,10 @@ void VulkanCommands::insertEventMarker(char const* str, uint32_t len) {
}
std::string VulkanCommands::getTopGroupMarker() const {
if (mProtectedPool) {
return mProtectedPool->topMarker();
if (!mGroupMarkers || mGroupMarkers->empty()) {
return "";
}
return mPool->topMarker();
return std::get<0>(mGroupMarkers->top());
}
#endif // FVK_DEBUG_GROUP_MARKERS
+56 -30
View File
@@ -21,10 +21,9 @@
#include "DriverBase.h"
#include "VulkanAsyncHandles.h"
#include "VulkanConstants.h"
#include "VulkanResources.h"
#include "VulkanUtility.h"
#include "vulkan/memory/ResourcePointer.h"
#include <utils/Condition.h>
#include <utils/FixedCapacityVector.h>
@@ -39,8 +38,6 @@
namespace filament::backend {
using namespace fvkmemory;
struct VulkanContext;
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
@@ -51,20 +48,42 @@ public:
void push(std::string const& marker, Timestamp start = {}) noexcept;
std::pair<std::string, Timestamp> pop() noexcept;
std::pair<std::string, Timestamp> pop_bottom() noexcept;
std::pair<std::string, Timestamp> const& top() const;
std::pair<std::string, Timestamp> top() const;
bool empty() const noexcept;
private:
std::list<std::pair<std::string, Timestamp>> mMarkers;
std::list<std::string> mMarkers;
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
std::list<Timestamp> mTimestamps;
#endif
};
#endif // FVK_DEBUG_GROUP_MARKERS
// 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;
};
// 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,
VulkanCommandBuffer(VulkanContext* mContext, VulkanResourceAllocator* allocator,
VkDevice device, VkQueue queue, VkCommandPool pool, bool isProtected);
VulkanCommandBuffer(VulkanCommandBuffer const&) = delete;
@@ -72,10 +91,13 @@ struct VulkanCommandBuffer {
~VulkanCommandBuffer();
inline void acquire(fvkmemory::resource_ptr<fvkmemory::Resource> resource) {
mResources.push_back(resource);
inline void acquire(VulkanResource* resource) {
mResourceManager.acquire(resource);
}
inline void acquire(VulkanAcquireOnlyResourceManager* srcResources) {
mResourceManager.acquireAll(srcResources);
}
void reset() noexcept;
inline void insertWait(VkSemaphore sem) {
@@ -106,29 +128,34 @@ struct VulkanCommandBuffer {
}
VkCommandBuffer buffer() const {
FVK_LOGE <<"-- getting " << mBuffer << " -- ";
return mBuffer;
}
// debug only
bool mIsProtected;
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;
std::vector<fvkmemory::resource_ptr<Resource>> mResources;
};
struct CommandBufferPool {
using ActiveBuffers = utils::bitset64;
using ActiveBuffers = utils::bitset32;
static constexpr int8_t INVALID = -1;
CommandBufferPool(VulkanContext* context, VkDevice device, VkQueue queue,
uint8_t queueFamilyIndex, bool isProtected);
CommandBufferPool(VulkanContext* context, VulkanResourceAllocator* allocator, VkDevice device,
VkQueue queue, uint8_t queueFamilyIndex, bool isProtected);
~CommandBufferPool();
VulkanCommandBuffer& getRecording();
@@ -137,36 +164,23 @@ struct CommandBufferPool {
void update();
VkSemaphore flush();
void wait();
void waitFor(VkSemaphore previousAction);
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
std::string topMarker() const;
void pushMarker(char const* marker, VulkanGroupMarkers::Timestamp timestamp);
std::pair<std::string, VulkanGroupMarkers::Timestamp> popMarker();
void pushMarker(char const* marker);
void popMarker();
void insertEvent(char const* marker);
#endif
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);
// The number of bits in ActiveBuffers describe the usage of the buffers in the pool, so must be
// larger than the size of the pool.
static_assert(sizeof(ActiveBuffers) * 8 >= CAPACITY);
using BufferList = utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>>;
VkDevice mDevice;
VkCommandPool mPool;
ActiveBuffers mSubmitted;
std::vector<std::unique_ptr<VulkanCommandBuffer>> mBuffers;
int8_t mRecording;
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
#endif
};
// Manages a set of command buffers and semaphores, exposing an API that is significantly simpler
@@ -195,7 +209,8 @@ private:
class VulkanCommands {
public:
VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context);
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context,
VulkanResourceAllocator* allocator);
void terminate();
@@ -242,17 +257,28 @@ public:
#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;
// int8 only goes up to 127, therefore capacity must be less than that.
static_assert(CAPACITY < 128);
std::unique_ptr<CommandBufferPool> mPool;
std::unique_ptr<CommandBufferPool> mProtectedPool;
VkSemaphore mInjectedDependency = VK_NULL_HANDLE;
VkSemaphore mLastSubmit = VK_NULL_HANDLE;
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
std::unique_ptr<VulkanGroupMarkers> mCarriedOverMarkers;
#endif
};
} // namespace filament::backend
+14 -47
View File
@@ -1,7 +1,7 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* 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
*
@@ -63,7 +63,7 @@
#define FVK_DEBUG_SHADER_MODULE 0x00000800
#define FVK_DEBUG_READ_PIXELS 0x00001000
#define FVK_DEBUG_PIPELINE_CACHE 0x00002000
#define FVK_DEBUG_STAGING_ALLOCATION 0x00004000
#define FVK_DEBUG_ALLOCATION 0x00004000
// Enable the debug utils extension if it is available.
#define FVK_DEBUG_DEBUG_UTILS 0x00008000
@@ -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
@@ -93,17 +89,11 @@
#endif
#ifndef NDEBUG
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_FORWARDED_FLAG)
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PERFORMANCE | FVK_DEBUG_FORWARDED_FLAG | FVK_DEBUG_VALIDATION | FVK_DEBUG_DUMP_API)
#else
#define FVK_DEBUG_FLAGS 0
#endif
// Override the debug flags if we are forcing profiling mode
#if defined(FILAMENT_FORCE_PROFILING_MODE)
#undef FVK_DEBUG_FLAGS
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PROFILING)
#endif
#define FVK_ENABLED(flags) (((FVK_DEBUG_FLAGS) & (flags)) == (flags))
// Group marker only works only if validation or debug utils is enabled since it uses
@@ -122,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
@@ -137,34 +123,17 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
// end shorthands
#if FVK_DEBUG_FLAGS == FVK_DEBUG_PROFILING
#if FVK_ENABLED(FVK_DEBUG_SYSTRACE)
#ifndef NDEBUG
#error PROFILING is meaningless in DEBUG mode.
#endif
#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
@@ -196,16 +165,14 @@ constexpr static const int FVK_REQUIRED_VERSION_MINOR = 1;
// buffers that have been submitted but have not yet finished rendering. Note that Filament can
// issue multiple commit calls in a single frame, and that we use a triple buffered swap chain on
// some platforms.
//
// Heuristic: Triple Buffering (3) multiplied by maximum number of renderpasses (15).
constexpr static const int FVK_MAX_COMMAND_BUFFERS = 3 * 15;
constexpr static const int FVK_MAX_COMMAND_BUFFERS = 10;
// Number of command buffer submissions that should occur before an unused pipeline is removed
// from the cache.
//
// If this number is low, VkPipeline construction will occur frequently, which can
// be extremely slow. If this number is high, the memory footprint will be large.
constexpr static const int FVK_MAX_PIPELINE_AGE = FVK_MAX_COMMAND_BUFFERS;
constexpr static const int FVK_MAX_PIPELINE_AGE = 10;
// VulkanPipelineCache does not track which command buffers contain references to which pipelines,
// instead it simply waits for at least FVK_MAX_COMMAND_BUFFERS submissions to occur before
+11 -8
View File
@@ -25,11 +25,15 @@
#include <backend/PixelBufferDescriptor.h>
#include <utils/Panic.h>
#include <utils/FixedCapacityVector.h>
#include <algorithm> // for std::max
using namespace bluevk;
using utils::FixedCapacityVector;
namespace {
} // end anonymous namespace
@@ -82,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);
@@ -97,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;
@@ -109,7 +113,7 @@ void VulkanTimestamps::clearQuery(uint32_t queryIndex) {
}
void VulkanTimestamps::beginQuery(VulkanCommandBuffer const* commands,
fvkmemory::resource_ptr<VulkanTimerQuery> query) {
VulkanTimerQuery* query) {
uint32_t const index = query->getStartingQueryIndex();
auto const cmdbuffer = commands->buffer();
@@ -121,20 +125,19 @@ void VulkanTimestamps::beginQuery(VulkanCommandBuffer const* commands,
}
void VulkanTimestamps::endQuery(VulkanCommandBuffer const* commands,
fvkmemory::resource_ptr<VulkanTimerQuery> query) {
VulkanTimerQuery const* query) {
uint32_t const index = query->getStoppingQueryIndex();
vkCmdWriteTimestamp(commands->buffer(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index);
}
VulkanTimestamps::QueryResult VulkanTimestamps::getResult(
fvkmemory::resource_ptr<VulkanTimerQuery> query) {
VulkanTimestamps::QueryResult VulkanTimestamps::getResult(VulkanTimerQuery const* query) {
uint32_t const index = query->getStartingQueryIndex();
QueryResult result;
size_t const dataSize = result.size() * sizeof(uint64_t);
VkDeviceSize const stride = sizeof(uint64_t) * 2;
VkResult vkresult =
vkGetQueryPoolResults(mDevice, mPool, index, 2, dataSize, (void*) result.data(), stride,
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
vkGetQueryPoolResults(mDevice, mPool, index, 2, dataSize, (void*) result.data(),
stride, VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
FILAMENT_CHECK_POSTCONDITION(vkresult == VK_SUCCESS || vkresult == VK_NOT_READY)
<< "vkGetQueryPoolResults error: " << static_cast<int32_t>(vkresult);
if (vkresult == VK_NOT_READY) {
+42 -19
View File
@@ -21,8 +21,6 @@
#include "VulkanImageUtility.h"
#include "VulkanUtility.h"
#include "vulkan/memory/ResourcePointer.h"
#include <utils/bitset.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Mutex.h>
@@ -30,6 +28,9 @@
#include <memory>
#include <sstream>
#include <iomanip>
VK_DEFINE_HANDLE(VmaAllocator)
VK_DEFINE_HANDLE(VmaPool)
@@ -43,10 +44,10 @@ struct VulkanTimerQuery;
struct VulkanCommandBuffer;
struct VulkanAttachment {
fvkmemory::resource_ptr<VulkanTexture> texture;
VulkanTexture* texture = nullptr;
uint8_t level = 0;
uint8_t layerCount = 1;
uint8_t layer = 0;
uint16_t layer = 0;
bool isDepth() const;
VkImage getImage() const;
@@ -72,11 +73,9 @@ public:
std::tuple<uint32_t, uint32_t> getNextQuery();
void clearQuery(uint32_t queryIndex);
void beginQuery(VulkanCommandBuffer const* commands,
fvkmemory::resource_ptr<VulkanTimerQuery> query);
void endQuery(VulkanCommandBuffer const* commands,
fvkmemory::resource_ptr<VulkanTimerQuery> query);
QueryResult getResult(fvkmemory::resource_ptr<VulkanTimerQuery> query);
void beginQuery(VulkanCommandBuffer const* commands, VulkanTimerQuery* query);
void endQuery(VulkanCommandBuffer const* commands, VulkanTimerQuery const* query);
QueryResult getResult(VulkanTimerQuery const* query);
private:
VkDevice mDevice;
@@ -86,29 +85,53 @@ private:
};
struct VulkanRenderPass {
// Between the begin and end command render pass we cache the command buffer
VulkanCommandBuffer* commandBuffer;
fvkmemory::resource_ptr<VulkanRenderTarget> renderTarget;
VulkanCommandBuffer* cmdBuffer;
VulkanRenderTarget* renderTarget;
VkRenderPass renderPass;
RenderPassParams params;
int currentSubpass;
};
namespace {
template< typename T >
std::string int_to_hex( T i )
{
std::stringstream stream;
stream << "0x"
<< std::setfill ('0') << std::setw(sizeof(T)*2)
<< std::hex << i;
return stream.str();
}
bool printed = false;
}
// This is a collection of immutable data about the vulkan context. This actual handles to the
// context are stored in VulkanPlatform.
struct VulkanContext {
public:
inline uint32_t selectMemoryType(uint32_t flags, VkFlags reqs) const {
inline uint32_t selectMemoryType(uint32_t flags, VkMemoryPropertyFlags reqs) const {
if ((reqs & VK_MEMORY_PROPERTY_PROTECTED_BIT) != 0) {
assert_invariant(isProtectedMemorySupported() == true);
assert_invariant(isProtectedMemorySupported()==true);
}
for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) {
if (flags & 1) {
if (!printed) {
for (uint32_t i = 0; i < mMemoryProperties.memoryTypeCount; i++) {
utils::slog.e << "[" << int_to_hex((uint8_t) i) << "]="
<< int_to_hex(
(uint32_t) mMemoryProperties.memoryTypes[i].propertyFlags)
<< utils::io::endl;
}
printed = true;
}
for (uint32_t i = 0; i < mMemoryProperties.memoryTypeCount; i++) {
if (flags & (1ULL << i)) {
if ((mMemoryProperties.memoryTypes[i].propertyFlags & reqs) == reqs) {
return i;
}
}
flags >>= 1;
}
return (uint32_t) VK_MAX_MEMORY_TYPES;
}
@@ -164,10 +187,10 @@ public:
private:
VkPhysicalDeviceMemoryProperties mMemoryProperties = {};
VkPhysicalDeviceProperties2 mPhysicalDeviceProperties = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
};
VkPhysicalDeviceFeatures2 mPhysicalDeviceFeatures = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
};
bool mDebugMarkersSupported = false;
bool mDebugUtilsSupported = false;
File diff suppressed because it is too large Load Diff
+10 -9
View File
@@ -24,14 +24,13 @@
#include "VulkanHandles.h"
#include "VulkanPipelineCache.h"
#include "VulkanReadPixels.h"
#include "VulkanResourceAllocator.h"
#include "VulkanSamplerCache.h"
#include "VulkanStagePool.h"
#include "VulkanUtility.h"
#include "backend/DriverEnums.h"
#include "caching/VulkanDescriptorSetManager.h"
#include "caching/VulkanPipelineLayoutCache.h"
#include "memory/ResourceManager.h"
#include "memory/ResourcePointer.h"
#include "DriverBase.h"
#include "private/backend/Driver.h"
@@ -78,9 +77,6 @@ public:
#endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
private:
template<typename D>
using resource_ptr = fvkmemory::resource_ptr<D>;
static constexpr uint8_t MAX_SAMPLER_BINDING_COUNT = Program::SAMPLER_BINDING_COUNT;
void debugCommandBegin(CommandStream* cmds, bool synchronous,
@@ -117,16 +113,21 @@ private:
void collectGarbage();
VulkanPlatform* mPlatform = nullptr;
fvkmemory::ResourceManager mResourceManager;
std::unique_ptr<VulkanTimestamps> mTimestamps;
resource_ptr<VulkanSwapChain> mCurrentSwapChain;
resource_ptr<VulkanRenderTarget> mDefaultRenderTarget;
VulkanSwapChain* mCurrentSwapChain = nullptr;
VulkanRenderTarget* mDefaultRenderTarget = nullptr;
VulkanRenderPass mCurrentRenderPass = {};
VmaAllocator mAllocator = VK_NULL_HANDLE;
VkDebugReportCallbackEXT mDebugCallback = VK_NULL_HANDLE;
VulkanContext mContext = {};
VulkanResourceAllocator mResourceAllocator;
VulkanResourceManager mResourceManager;
// Used for resources that are created synchronously and used and destroyed on the backend
// thread.
VulkanThreadSafeResourceManager mThreadSafeResourceManager;
VulkanCommands mCommands;
VulkanPipelineLayoutCache mPipelineLayoutCache;
@@ -141,7 +142,7 @@ private:
// This is necessary for us to write to push constants after binding a pipeline.
struct {
resource_ptr<VulkanProgram> program;
VulkanProgram* program;
VkPipelineLayout pipelineLayout;
DescriptorSetMask descriptorSetMask;
} mBoundPipeline = {};
+97 -58
View File
@@ -22,8 +22,8 @@
#include "VulkanDriver.h"
#include "VulkanMemory.h"
#include "VulkanResourceAllocator.h"
#include "VulkanUtility.h"
#include "vulkan/memory/ResourcePointer.h"
#include "spirv/VulkanSpirvUtils.h"
#include <backend/platforms/VulkanPlatform.h>
@@ -87,8 +87,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,10 +103,9 @@ BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) {
return mask;
}
fvkmemory::resource_ptr<VulkanTexture> initMsaaTexture(
fvkmemory::resource_ptr<VulkanTexture> texture, VkDevice device,
VulkanTexture* initMsaaTexture(VulkanTexture* texture, VkDevice device,
VkPhysicalDevice physicalDevice, VulkanContext const& context, VmaAllocator allocator,
VulkanCommands* commands, fvkmemory::ResourceManager* resManager, uint8_t levels,
VulkanCommands* commands, VulkanResourceAllocator* handleAllocator, uint8_t levels,
uint8_t samples, VulkanStagePool& stagePool) {
assert_invariant(texture);
auto msTexture = texture->getSidecar();
@@ -118,9 +115,10 @@ fvkmemory::resource_ptr<VulkanTexture> initMsaaTexture(
const TextureUsage usage = texture->usage & TextureUsage::ALL_ATTACHMENTS;
assert_invariant(static_cast<uint16_t>(usage) != 0U);
msTexture = resource_ptr<VulkanTexture>::construct(resManager, device, physicalDevice,
context, allocator, resManager, commands, texture->target, levels, texture->format,
samples, texture->width, texture->height, texture->depth, usage, stagePool);
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;
@@ -128,18 +126,19 @@ fvkmemory::resource_ptr<VulkanTexture> initMsaaTexture(
} // anonymous namespace
void VulkanDescriptorSet::acquire(fvkmemory::resource_ptr<VulkanTexture> texture) {
mResources.push_back(texture);
}
void VulkanDescriptorSet::acquire(fvkmemory::resource_ptr<VulkanBufferObject> obj) {
mResources.push_back(obj);
}
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout const& layout)
: bitmask(fromBackendLayout(layout)),
: VulkanResource(VulkanResourceType::DESCRIPTOR_SET_LAYOUT),
bitmask(fromBackendLayout(layout)),
count(Count::fromLayoutBitmask(bitmask)) {}
void VulkanDescriptorSet::acquire(VulkanTexture* texture) {
mResources.acquire(texture);
}
void VulkanDescriptorSet::acquire(VulkanBufferObject* bufferObject) {
mResources.acquire(bufferObject);
}
PushConstantDescription::PushConstantDescription(backend::Program const& program) noexcept {
mRangeCount = 0;
for (auto stage : { ShaderStage::VERTEX, ShaderStage::FRAGMENT, ShaderStage::COMPUTE }) {
@@ -163,9 +162,8 @@ PushConstantDescription::PushConstantDescription(backend::Program const& program
}
}
void PushConstantDescription::write(VkCommandBuffer cmdbuf, VkPipelineLayout layout,
void PushConstantDescription::write(VulkanCommandBuffer* cmdbuf, VkPipelineLayout layout,
backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) {
uint32_t binaryValue = 0;
UTILS_UNUSED_IN_RELEASE auto const& types = mTypes[(uint8_t) stage];
if (std::holds_alternative<bool>(value)) {
@@ -181,12 +179,13 @@ void PushConstantDescription::write(VkCommandBuffer cmdbuf, VkPipelineLayout lay
int const ival = std::get<int>(value);
binaryValue = *reinterpret_cast<uint32_t const*>(&ival);
}
vkCmdPushConstants(cmdbuf, layout, getVkStage(stage), index * ENTRY_SIZE, ENTRY_SIZE,
vkCmdPushConstants(cmdbuf->buffer(), layout, getVkStage(stage), index * ENTRY_SIZE, ENTRY_SIZE,
&binaryValue);
}
VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
: HwProgram(builder.getName()),
VulkanResource(VulkanResourceType::PROGRAM),
mInfo(new(std::nothrow) PipelineInfo(builder)),
mDevice(device) {
@@ -254,24 +253,20 @@ 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() = default;
void VulkanRenderTarget::bindToSwapChain(fvkmemory::resource_ptr<VulkanSwapChain> swapchain) {
void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) {
assert_invariant(!mOffscreen);
VkExtent2D const extent = swapchain->getExtent();
VkExtent2D const extent = swapChain.getExtent();
width = extent.width;
height = extent.height;
mProtected = swapchain->isProtected();
VulkanAttachment color = {};
color.texture = swapchain->getCurrentColor();
VulkanAttachment color = {.texture = swapChain.getCurrentColor()};
mInfo->attachments = {color};
auto& fbkey = mInfo->fbkey;
@@ -283,30 +278,34 @@ void VulkanRenderTarget::bindToSwapChain(fvkmemory::resource_ptr<VulkanSwapChain
fbkey.color[0] = color.getImageView();
fbkey.resolve[0] = VK_NULL_HANDLE;
if (swapchain->getDepth()) {
VulkanAttachment depth = {};
depth.texture = swapchain->getDepth();
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();
} else {
rpkey.depthFormat = VK_FORMAT_UNDEFINED;
fbkey.depth = VK_NULL_HANDLE;
}
mInfo->colors.set(0);
mProtected = swapChain.isProtected();
}
VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice,
VulkanContext const& context, fvkmemory::ResourceManager* resourceManager,
VmaAllocator allocator, VulkanCommands* commands, uint32_t width, uint32_t height,
uint8_t samples, VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
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>()) {
auto& depth = depthStencil[0];
// Constrain the sample count according to both kinds of sample count masks obtained from
@@ -336,7 +335,7 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
continue;
}
mProtected |= texture->getIsProtected();
mProtected = mProtected || texture->isProtected();
attachments.push_back(attachment);
mInfo->colors.set(index);
@@ -345,11 +344,14 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
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, resourceManager, texture->levels, samples, stagePool);
allocator, commands, handleAllocator,
((VulkanTexture const*) texture)->levels, samples, stagePool);
if (msaaTexture && msaaTexture->isTransientAttachment()) {
rpkey.usesLazilyAllocatedMemory |= (1 << index);
}
@@ -370,6 +372,7 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
}
fbkey.color[index] = msaaAttachment.getImageView();
msaa.push_back(msaaAttachment);
mResources.acquire(msaaAttachment.texture);
}
}
@@ -382,20 +385,22 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
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, resourceManager, msLevel, samples, stagePool);
// 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);
}
}
mProtected = mProtected || depthTexture->isProtected();
}
}
@@ -484,7 +489,9 @@ void VulkanRenderTarget::emitBarriersEndRenderPass(VulkanCommandBuffer& commands
VulkanVertexBufferInfo::VulkanVertexBufferInfo(
uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
VulkanResource(VulkanResourceType::VERTEX_BUFFER_INFO),
mInfo(attributes.size()) {
auto attribDesc = mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
auto bufferDesc = mInfo.mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
auto offsets = mInfo.mSoa.data<PipelineInfo::OFFSETS>();
@@ -522,15 +529,19 @@ VulkanVertexBufferInfo::VulkanVertexBufferInfo(
}
VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
uint32_t vertexCount, fvkmemory::resource_ptr<VulkanVertexBufferInfo> vbi)
VulkanResourceAllocator* allocator,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih)
: HwVertexBuffer(vertexCount),
vbi(vbi),
// TODO: Seems a bit wasteful. can we do better here?
mBuffers(MAX_VERTEX_BUFFER_COUNT) {
VulkanResource(VulkanResourceType::VERTEX_BUFFER),
vbih(vbih),
mBuffers(MAX_VERTEX_BUFFER_COUNT), // TODO: can we do better here?
mResources(allocator) {
}
void VulkanVertexBuffer::setBuffer(fvkmemory::resource_ptr<VulkanBufferObject> bufferObject,
uint32_t index) {
void VulkanVertexBuffer::setBuffer(VulkanResourceAllocator const& allocator,
VulkanBufferObject* bufferObject, uint32_t index) {
VulkanVertexBufferInfo const* const vbi =
const_cast<VulkanResourceAllocator&>(allocator).handle_cast<VulkanVertexBufferInfo*>(vbih);
size_t const count = vbi->getAttributeCount();
VkBuffer* const vkbuffers = getVkBuffers();
int8_t const* const attribToBuffer = vbi->getAttributeToBuffer();
@@ -539,20 +550,48 @@ void VulkanVertexBuffer::setBuffer(fvkmemory::resource_ptr<VulkanBufferObject> b
vkbuffers[attribIndex] = bufferObject->buffer.getGpuBuffer();
}
}
mResources.push_back(bufferObject);
mResources.acquire(bufferObject);
}
VulkanBufferObject::VulkanBufferObject(VmaAllocator allocator, VulkanStagePool& stagePool,
uint32_t byteCount, BufferObjectBinding bindingType)
: HwBufferObject(byteCount),
VulkanResource(VulkanResourceType::BUFFER_OBJECT),
buffer(allocator, stagePool, getBufferObjectUsage(bindingType), byteCount),
bindingType(bindingType) {}
VulkanRenderPrimitive::VulkanRenderPrimitive(PrimitiveType pt,
fvkmemory::resource_ptr<VulkanVertexBuffer> vb,
fvkmemory::resource_ptr<VulkanIndexBuffer> ib)
: HwRenderPrimitive{.type = pt},
vertexBuffer(vb),
indexBuffer(ib) {}
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),
mResources(resourceAllocator) {
type = pt;
vertexBuffer = resourceAllocator->handle_cast<VulkanVertexBuffer*>(vbh);
indexBuffer = resourceAllocator->handle_cast<VulkanIndexBuffer*>(ibh);
mResources.acquire(vertexBuffer);
mResources.acquire(indexBuffer);
}
} // namespace filament::backend
+109 -41
View File
@@ -20,14 +20,14 @@
// This needs to be at the top
#include "DriverBase.h"
#include "VulkanAsyncHandles.h"
#include "VulkanBuffer.h"
#include "VulkanFboCache.h"
#include "VulkanResources.h"
#include "VulkanSwapChain.h"
#include "VulkanTexture.h"
#include "VulkanUtility.h"
#include "vulkan/memory/Resource.h"
#include <private/backend/SamplerGroup.h>
#include <backend/Program.h>
#include <utils/bitset.h>
@@ -35,8 +35,6 @@
#include <utils/Mutex.h>
#include <utils/StructureOfArrays.h>
#include <array>
namespace filament::backend {
namespace {
@@ -57,7 +55,7 @@ inline uint8_t collapsedCount(Bitmask const& mask) {
class VulkanTimestamps;
struct VulkanBufferObject;
struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Resource {
struct VulkanDescriptorSetLayout : public VulkanResource, HwDescriptorSetLayout {
static constexpr uint8_t UNIQUE_DESCRIPTOR_SET_COUNT = 4;
static constexpr uint8_t MAX_BINDINGS = 25;
@@ -131,19 +129,21 @@ private:
VkDescriptorSetLayout mVkLayout = VK_NULL_HANDLE;
};
struct VulkanDescriptorSet : public HwDescriptorSet, fvkmemory::Resource {
struct VulkanDescriptorSet : public VulkanResource, HwDescriptorSet {
public:
// Because we need to recycle descriptor sets not used, we allow for a callback that the "Pool"
// can use to repackage the vk handle.
using OnRecycle = std::function<void(VulkanDescriptorSet*)>;
VulkanDescriptorSet(VkDescriptorSet rawSet,
VulkanDescriptorSet(VulkanResourceAllocator* allocator, VkDescriptorSet rawSet,
UniformBufferBitmask const& dynamicUboMask,
uint8_t uniqueDynamicUboCount,
OnRecycle&& onRecycleFn)
: vkSet(rawSet),
: VulkanResource(VulkanResourceType::DESCRIPTOR_SET),
vkSet(rawSet),
dynamicUboMask(dynamicUboMask),
uniqueDynamicUboCount(uniqueDynamicUboCount),
mResources(allocator),
mOnRecycleFn(std::move(onRecycleFn)) {}
~VulkanDescriptorSet() {
@@ -160,8 +160,9 @@ public:
return &mOffsets;
}
void acquire(fvkmemory::resource_ptr<VulkanTexture> texture);
void acquire(fvkmemory::resource_ptr<VulkanBufferObject> buffer);
void acquire(VulkanTexture* texture);
void acquire(VulkanBufferObject* texture);
VkDescriptorSet const vkSet;
UniformBufferBitmask const dynamicUboMask;
@@ -169,7 +170,7 @@ public:
private:
backend::DescriptorSetOffsetArray mOffsets;
std::vector<fvkmemory::resource_ptr<fvkmemory::Resource>> mResources;
VulkanAcquireOnlyResourceManager mResources;
OnRecycle mOnRecycleFn;
};
@@ -177,11 +178,14 @@ using PushConstantNameArray = utils::FixedCapacityVector<char const*>;
using PushConstantNameByStage = std::array<PushConstantNameArray, Program::SHADER_TYPE_COUNT>;
struct PushConstantDescription {
explicit PushConstantDescription(backend::Program const& program) noexcept;
VkPushConstantRange const* getVkRanges() const noexcept { return mRanges; }
uint32_t getVkRangeCount() const noexcept { return mRangeCount; }
void write(VkCommandBuffer cmdbuf, VkPipelineLayout layout, backend::ShaderStage stage,
void write(VulkanCommandBuffer* cmdbuf, VkPipelineLayout layout, backend::ShaderStage stage,
uint8_t index, backend::PushConstantVariant const& value);
private:
@@ -192,10 +196,12 @@ private:
uint32_t mRangeCount;
};
struct VulkanProgram : public HwProgram, fvkmemory::Resource {
struct VulkanProgram : public HwProgram, VulkanResource {
using BindingList = CappedArray<uint16_t, MAX_SAMPLER_COUNT>;
VulkanProgram(VkDevice device, Program const& builder) noexcept;
~VulkanProgram();
inline VkShaderModule getVertexShader() const {
@@ -212,7 +218,7 @@ struct VulkanProgram : public HwProgram, fvkmemory::Resource {
return mInfo->pushConstantDescription.getVkRanges();
}
inline void writePushConstant(VkCommandBuffer cmdbuf, VkPipelineLayout layout,
inline void writePushConstant(VulkanCommandBuffer* cmdbuf, VkPipelineLayout layout,
backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) {
mInfo->pushConstantDescription.write(cmdbuf, layout, stage, index, value);
}
@@ -249,16 +255,16 @@ private:
//
// We use private inheritance to shield clients from the width / height fields in HwRenderTarget,
// which are not representative when this is the default render target.
struct VulkanRenderTarget : private HwRenderTarget, fvkmemory::Resource {
struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
// Creates an offscreen render target.
VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice,
VulkanContext const& context, fvkmemory::ResourceManager* resourceManager,
VmaAllocator allocator, VulkanCommands* commands, uint32_t width, uint32_t height,
VulkanContext const& context, VmaAllocator allocator,
VulkanCommands* commands,
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);
~VulkanRenderTarget();
// Creates a special "default" render target (i.e. associated with the swap chain)
explicit VulkanRenderTarget();
@@ -270,12 +276,12 @@ struct VulkanRenderTarget : private HwRenderTarget, fvkmemory::Resource {
return {width, height};
}
inline VulkanAttachment& getColor0() const {
inline VulkanAttachment const& getColor0() const {
assert_invariant(mInfo->colors[0]);
return mInfo->attachments[0];
}
inline VulkanAttachment& getDepth() const {
inline VulkanAttachment const& getDepth() const {
assert_invariant(hasDepth());
if (mInfo->fbkey.samples == 1) {
return mInfo->attachments[mInfo->depthIndex];
@@ -300,9 +306,10 @@ struct VulkanRenderTarget : private HwRenderTarget, fvkmemory::Resource {
inline bool hasDepth() const { return mInfo->depthIndex != Auxiliary::UNDEFINED_INDEX; }
inline bool isSwapChain() const { return !mOffscreen; }
inline bool isProtected() const { return mProtected; }
void bindToSwapChain(fvkmemory::resource_ptr<VulkanSwapChain> swapchain);
bool isProtected() const { return mProtected; }
void bindToSwapChain(VulkanSwapChain& surf);
void emitBarriersBeginRenderPass(VulkanCommandBuffer& commands);
@@ -325,12 +332,13 @@ private:
bool const mOffscreen;
bool mProtected;
VulkanAcquireOnlyResourceManager mResources;
std::unique_ptr<Auxiliary> mInfo;
};
struct VulkanBufferObject;
struct VulkanVertexBufferInfo : public HwVertexBufferInfo, fvkmemory::Resource {
struct VulkanVertexBufferInfo : public HwVertexBufferInfo, VulkanResource {
VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes);
@@ -377,24 +385,33 @@ private:
PipelineInfo mInfo;
};
struct VulkanVertexBuffer : public HwVertexBuffer, fvkmemory::Resource {
VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool, uint32_t vertexCount,
fvkmemory::resource_ptr<VulkanVertexBufferInfo> vbi);
void setBuffer(fvkmemory::resource_ptr<VulkanBufferObject> bufferObject, uint32_t index);
struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
VulkanResourceAllocator* allocator,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih);
inline VkBuffer const* getVkBuffers() const { return mBuffers.data(); }
inline VkBuffer* getVkBuffers() { return mBuffers.data(); }
fvkmemory::resource_ptr<VulkanVertexBufferInfo> vbi;
void setBuffer(VulkanResourceAllocator const& allocator,
VulkanBufferObject* bufferObject, uint32_t index);
inline VkBuffer const* getVkBuffers() const {
return mBuffers.data();
}
inline VkBuffer* getVkBuffers() {
return mBuffers.data();
}
Handle<HwVertexBufferInfo> vbih;
private:
utils::FixedCapacityVector<VkBuffer> mBuffers;
std::vector<fvkmemory::resource_ptr<VulkanBufferObject>> mResources;
FixedSizeVulkanResourceManager<MAX_VERTEX_BUFFER_COUNT> mResources;
};
struct VulkanIndexBuffer : public HwIndexBuffer, fvkmemory::Resource {
struct VulkanIndexBuffer : public HwIndexBuffer, VulkanResource {
VulkanIndexBuffer(VmaAllocator allocator, VulkanStagePool& stagePool, uint8_t elementSize,
uint32_t indexCount)
: HwIndexBuffer(elementSize, indexCount),
VulkanResource(VulkanResourceType::INDEX_BUFFER),
buffer(allocator, stagePool, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, elementSize * indexCount),
indexType(elementSize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32) {}
@@ -402,7 +419,7 @@ struct VulkanIndexBuffer : public HwIndexBuffer, fvkmemory::Resource {
const VkIndexType indexType;
};
struct VulkanBufferObject : public HwBufferObject, fvkmemory::Resource {
struct VulkanBufferObject : public HwBufferObject, VulkanResource {
VulkanBufferObject(VmaAllocator allocator, VulkanStagePool& stagePool, uint32_t byteCount,
BufferObjectBinding bindingType);
@@ -410,15 +427,66 @@ struct VulkanBufferObject : public HwBufferObject, fvkmemory::Resource {
const BufferObjectBinding bindingType;
};
struct VulkanRenderPrimitive : public HwRenderPrimitive, fvkmemory::Resource {
VulkanRenderPrimitive(PrimitiveType pt, fvkmemory::resource_ptr<VulkanVertexBuffer> vb,
fvkmemory::resource_ptr<VulkanIndexBuffer> ib);
~VulkanRenderPrimitive() = default;
fvkmemory::resource_ptr<VulkanVertexBuffer> vertexBuffer;
fvkmemory::resource_ptr<VulkanIndexBuffer> indexBuffer;
struct VulkanSamplerGroup : public HwSamplerGroup, VulkanResource {
// NOTE: we have to use out-of-line allocation here because the size of a Handle<> is limited
std::unique_ptr<SamplerGroup> sb;// FIXME: this shouldn't depend on filament::SamplerGroup
explicit VulkanSamplerGroup(size_t size) noexcept
: VulkanResource(VulkanResourceType::SAMPLER_GROUP),
sb(new SamplerGroup(size)) {}
};
struct VulkanRenderPrimitive : public HwRenderPrimitive, VulkanResource {
VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator,
PrimitiveType pt, Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh);
~VulkanRenderPrimitive() {
mResources.clear();
}
VulkanVertexBuffer* vertexBuffer = nullptr;
VulkanIndexBuffer* indexBuffer = nullptr;
private:
// Keep references to the vertex buffer and the index buffer.
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;
@@ -241,7 +241,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
return &mPipelines.emplace(mPipelineRequirements, cacheEntry).first.value();
}
void VulkanPipelineCache::bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept {
void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
mPipelineRequirements.shaders[0] = program->getVertexShader();
mPipelineRequirements.shaders[1] = program->getFragmentShader();
@@ -19,6 +19,7 @@
#include "VulkanCommands.h"
#include "VulkanMemory.h"
#include "VulkanResources.h"
#include "VulkanUtility.h"
#include <backend/DriverEnums.h>
@@ -43,6 +44,7 @@ namespace filament::backend {
struct VulkanProgram;
struct VulkanBufferObject;
struct VulkanTexture;
class VulkanResourceAllocator;
// VulkanPipelineCache manages a cache of descriptor sets and pipelines.
//
@@ -120,7 +122,7 @@ public:
void bindPipeline(VulkanCommandBuffer* commands);
// Each of the following methods are fast and do not make Vulkan calls.
void bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept;
void bindProgram(VulkanProgram* program) noexcept;
void bindRasterState(const RasterState& rasterState) noexcept;
void bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept;
void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept;
@@ -117,10 +117,9 @@ void VulkanReadPixels::terminate() noexcept {
VulkanReadPixels::VulkanReadPixels(VkDevice device)
: mDevice(device) {}
void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget, uint32_t const x,
uint32_t const y, uint32_t const width, uint32_t const height,
uint32_t const graphicsQueueFamilyIndex, PixelBufferDescriptor&& pbd,
SelecteMemoryFunction const& selectMemoryFunc,
void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint32_t const y,
uint32_t const width, uint32_t const height, uint32_t const graphicsQueueFamilyIndex,
PixelBufferDescriptor&& pbd, SelecteMemoryFunction const& selectMemoryFunc,
OnReadCompleteFunction const& readCompleteFunc) {
assert_invariant(mDevice != VK_NULL_HANDLE);
@@ -144,7 +143,7 @@ void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget
VkCommandPool& cmdpool = mCommandPool;
fvkmemory::resource_ptr<VulkanTexture> srcTexture = srcTarget->getColor0().texture;
VulkanTexture* srcTexture = srcTarget->getColor0().texture;
assert_invariant(srcTexture);
VkFormat const srcFormat = srcTexture->getVkFormat();
bool const swizzle
@@ -17,7 +17,6 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANREADPIXELS_H
#define TNT_FILAMENT_BACKEND_VULKANREADPIXELS_H
#include "vulkan/memory/ResourcePointer.h"
#include "private/backend/Driver.h"
#include <bluevk/BlueVK.h>
@@ -73,9 +72,9 @@ public:
void terminate() noexcept;
void run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget, uint32_t x, uint32_t y,
uint32_t width, uint32_t height, uint32_t graphicsQueueFamilyIndex,
PixelBufferDescriptor&& pbd, SelecteMemoryFunction const& selectMemoryFunc,
void run(VulkanRenderTarget* srcTarget, uint32_t x, uint32_t y, uint32_t width, uint32_t height,
uint32_t graphicsQueueFamilyIndex, PixelBufferDescriptor&& pbd,
SelecteMemoryFunction const& selectMemoryFunc,
OnReadCompleteFunction const& readCompleteFunc);
// This method will block until all of the in-flight requests are complete.
@@ -0,0 +1,136 @@
/*
* Copyright (C) 2023 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_VULKANRESOURCEALLOCATOR_H
#define TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H
#include "VulkanConstants.h"
#include "VulkanHandles.h"
#include <private/backend/HandleAllocator.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Log.h>
#include <type_traits>
#include <unordered_set>
namespace filament::backend {
#define RESOURCE_TYPE_COUNT (static_cast<int>(VulkanResourceType::END_TYPE))
#define DEBUG_RESOURCE_LEAKS FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
#if DEBUG_RESOURCE_LEAKS
#define TRACK_INCREMENT() \
if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \
mDebugOnlyResourceCount[static_cast<size_t>(obj->getType())]++; \
}
#define TRACK_DECREMENT() \
if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \
mDebugOnlyResourceCount[static_cast<size_t>(obj->getType())]--; \
}
#else
// No-op
#define TRACK_INCREMENT()
#define TRACK_DECREMENT()
#endif
class VulkanResourceAllocator {
public:
using AllocatorImpl = HandleAllocatorVK;
VulkanResourceAllocator(size_t arenaSize, bool disableUseAfterFreeCheck)
: mHandleAllocatorImpl("Handles", arenaSize, disableUseAfterFreeCheck)
#if DEBUG_RESOURCE_LEAKS
, mDebugOnlyResourceCount(RESOURCE_TYPE_COUNT) {
std::memset(mDebugOnlyResourceCount.data(), 0, sizeof(size_t) * RESOURCE_TYPE_COUNT);
}
#else
{}
#endif
template<typename D, typename... ARGS>
inline Handle<D> initHandle(ARGS&&... args) noexcept {
auto handle = mHandleAllocatorImpl.allocateAndConstruct<D>(std::forward<ARGS>(args)...);
auto obj = handle_cast<D*>(handle);
obj->initResource(handle.getId());
TRACK_INCREMENT();
return handle;
}
template<typename D>
inline Handle<D> allocHandle() noexcept {
return mHandleAllocatorImpl.allocate<D>();
}
template<typename D, typename B, typename... ARGS>
inline typename std::enable_if<std::is_base_of<B, D>::value, D>::type* construct(
Handle<B> const& handle, ARGS&&... args) noexcept {
auto obj = mHandleAllocatorImpl.construct<D, B>(handle, std::forward<ARGS>(args)...);
obj->initResource(handle.getId());
TRACK_INCREMENT();
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>& handle) noexcept {
return mHandleAllocatorImpl.handle_cast<Dp, B>(handle);
}
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 {
auto obj = handle_cast<D*>(handle);
TRACK_DECREMENT();
mHandleAllocatorImpl.deallocate(handle, obj);
}
inline void associateHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
mHandleAllocatorImpl.associateTagToHandle(id, std::move(tag));
}
private:
AllocatorImpl mHandleAllocatorImpl;
#if DEBUG_RESOURCE_LEAKS
public:
void print() {
FVK_LOGD << "Resource Allocator state (debug only)" << utils::io::endl;
for (size_t i = 0; i < RESOURCE_TYPE_COUNT; i++) {
FVK_LOGD << "[" << i << "]=" << mDebugOnlyResourceCount[i] << utils::io::endl;
}
FVK_LOGD << "+++++++++++++++++++++++++++++++++++++" << utils::io::endl;
}
private:
utils::FixedCapacityVector<size_t> mDebugOnlyResourceCount;
#endif
};
#undef TRACK_INCREMENT
#undef TRACK_DECREMENT
#undef DEBUG_RESOURCE_LEAKS
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H
@@ -0,0 +1,81 @@
/*
* Copyright (C) 2023 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 "VulkanResources.h"
#include "VulkanHandles.h"
#include "VulkanResourceAllocator.h"
#include "VulkanPipelineCache.h"
namespace filament::backend {
void deallocateResource(VulkanResourceAllocator* allocator, VulkanResourceType type,
HandleBase::HandleId id) {
if (IS_HEAP_ALLOC_TYPE(type)) {
return;
}
switch (type) {
case VulkanResourceType::BUFFER_OBJECT:
allocator->destruct<VulkanBufferObject>(Handle<HwBufferObject>(id));
break;
case VulkanResourceType::INDEX_BUFFER:
allocator->destruct<VulkanIndexBuffer>(Handle<HwIndexBuffer>(id));
break;
case VulkanResourceType::PROGRAM:
allocator->destruct<VulkanProgram>(Handle<HwProgram>(id));
break;
case VulkanResourceType::RENDER_TARGET:
allocator->destruct<VulkanRenderTarget>(Handle<HwRenderTarget>(id));
break;
case VulkanResourceType::SAMPLER_GROUP:
allocator->destruct<VulkanSamplerGroup>(Handle<HwSamplerGroup>(id));
break;
case VulkanResourceType::SWAP_CHAIN:
allocator->destruct<VulkanSwapChain>(Handle<HwSwapChain>(id));
break;
case VulkanResourceType::TEXTURE:
allocator->destruct<VulkanTexture>(Handle<HwTexture>(id));
break;
case VulkanResourceType::TIMER_QUERY:
allocator->destruct<VulkanTimerQuery>(Handle<HwTimerQuery>(id));
break;
case VulkanResourceType::VERTEX_BUFFER_INFO:
allocator->destruct<VulkanVertexBufferInfo>(Handle<HwVertexBufferInfo>(id));
break;
case VulkanResourceType::VERTEX_BUFFER:
allocator->destruct<VulkanVertexBuffer>(Handle<HwVertexBuffer>(id));
break;
case VulkanResourceType::RENDER_PRIMITIVE:
allocator->destruct<VulkanRenderPrimitive>(Handle<VulkanRenderPrimitive>(id));
break;
case VulkanResourceType::DESCRIPTOR_SET_LAYOUT:
allocator->destruct<VulkanDescriptorSetLayout>(Handle<VulkanDescriptorSetLayout>(id));
break;
case VulkanResourceType::DESCRIPTOR_SET:
allocator->destruct<VulkanDescriptorSet>(Handle<VulkanDescriptorSet>(id));
break;
// If the resource is heap allocated, then the resource manager just skip refcounted
// destruction.
case VulkanResourceType::FENCE:
case VulkanResourceType::HEAP_ALLOCATED:
case VulkanResourceType::END_TYPE:
break;
}
}
} // namespace filament::backend
@@ -0,0 +1,313 @@
/*
* Copyright (C) 2023 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_VULKANRESOURCES_H
#define TNT_FILAMENT_BACKEND_VULKANRESOURCES_H
#include "VulkanUtility.h"
#include <backend/Handle.h>
#include <tsl/robin_set.h>
#include <utils/Mutex.h>
#include <utils/Panic.h>
#include <mutex>
#include <unordered_set>
namespace filament::backend {
class VulkanResourceAllocator;
struct VulkanThreadSafeResource;
// Subclasses of VulkanResource must provide this enum in their construction.
enum class VulkanResourceType : uint8_t {
BUFFER_OBJECT = 0,
INDEX_BUFFER = 1,
PROGRAM = 2,
RENDER_TARGET = 3,
SAMPLER_GROUP = 4,
SWAP_CHAIN = 5,
RENDER_PRIMITIVE = 6,
TEXTURE = 7,
TIMER_QUERY = 8,
VERTEX_BUFFER = 9,
VERTEX_BUFFER_INFO = 10,
DESCRIPTOR_SET_LAYOUT = 11,
DESCRIPTOR_SET = 12,
// Below are resources that are managed manually (i.e. not ref counted).
FENCE = 13,
HEAP_ALLOCATED = 14,
END_TYPE = 15, // A placeholder
};
#define IS_HEAP_ALLOC_TYPE(f) \
(f == VulkanResourceType::FENCE || f == VulkanResourceType::HEAP_ALLOCATED)
// This is a ref-counting base class that tracks how many references of this resource exist. This
// class is paired with VulkanResourceManagerImpl which is responsible for incrementing or
// decrementing the count. Once mRefCount == 0, VulkanResourceManagerImpl will also call the
// appropriate destructor. VulkanCommandBuffer, VulkanDriver, and composite structure like
// VulkanRenderPrimitive are owners of VulkanResourceManagerImpl instances.
struct VulkanResourceBase {
protected:
explicit VulkanResourceBase(VulkanResourceType type)
: mRefCount(IS_HEAP_ALLOC_TYPE(type) ? 1 : 0),
mType(uint32_t(type)),
mHandleId(0) {
}
private:
inline VulkanResourceType getType() const noexcept {
return VulkanResourceType(mType);
}
inline HandleBase::HandleId getId() const noexcept {
return mHandleId;
}
inline void initResource(HandleBase::HandleId id) noexcept {
mHandleId = id;
}
inline void ref() noexcept {
if (IS_HEAP_ALLOC_TYPE(getType())) {
return;
}
assert_invariant(mRefCount < ((1<<24) - 1));
++mRefCount;
}
inline void deref() noexcept {
if (IS_HEAP_ALLOC_TYPE(getType())) {
return;
}
assert_invariant(mRefCount > 0);
--mRefCount;
}
inline size_t refcount() const noexcept {
return mRefCount;
}
uint32_t mRefCount : 24; // 16M is enough for the refcount
uint32_t mType : 8; // must be uint32_t or MSVC doesn't pack it. no codegen impact w/ clang.
HandleBase::HandleId mHandleId;
friend struct VulkanThreadSafeResource;
friend class VulkanResourceAllocator;
template<typename RT, typename ST>
friend class VulkanResourceManagerImpl;
};
static_assert(sizeof(VulkanResourceBase) == 8, "VulkanResourceBase should be 8 bytes");
struct VulkanThreadSafeResource {
protected:
explicit VulkanThreadSafeResource(VulkanResourceType type)
: mImpl(type) {}
private:
inline VulkanResourceType getType() {
return mImpl.getType();
}
inline HandleBase::HandleId getId() {
return mImpl.getId();
}
inline void initResource(HandleBase::HandleId id) noexcept {
std::unique_lock<utils::Mutex> lock(mMutex);
mImpl.initResource(id);
}
inline void ref() noexcept {
std::unique_lock<utils::Mutex> lock(mMutex);
mImpl.ref();
}
inline void deref() noexcept {
std::unique_lock<utils::Mutex> lock(mMutex);
mImpl.deref();
}
inline size_t refcount() noexcept {
std::unique_lock<utils::Mutex> lock(mMutex);
return mImpl.refcount();
}
utils::Mutex mMutex;
VulkanResourceBase mImpl;
friend class VulkanResourceAllocator;
template<typename RT, typename ST>
friend class VulkanResourceManagerImpl;
};
using VulkanResource = VulkanResourceBase;
namespace {
// When the size of the resource set is known to be small, (for example for VulkanRenderPrimitive),
// we just use a std::array to back the set.
template<std::size_t SIZE>
using FixedCapacityResourceSet = CappedArray<VulkanResource*, SIZE>;
// robin_set/map are useful for sets that are acquire only and the set will be iterated when the set
// is cleared.
using FastIterationResourceSet = tsl::robin_set<VulkanResource*>;
// unoredered_set is used in the general case where insert/erase can occur at will. This is useful
// for the basic object ownership count - i.e. VulkanDriver.
using ResourceSet = std::unordered_set<VulkanResource*>;
using ThreadSafeResourceSet = std::unordered_set<VulkanThreadSafeResource*>;
} // anonymous namespace
class VulkanResourceAllocator;
#define LOCK_IF_NEEDED() \
if constexpr (std::is_base_of_v<VulkanThreadSafeResource, ResourceType>) { \
mMutex->lock(); \
}
#define UNLOCK_IF_NEEDED() \
if constexpr (std::is_base_of_v<VulkanThreadSafeResource, ResourceType>) { \
mMutex->unlock(); \
}
void deallocateResource(VulkanResourceAllocator* allocator, VulkanResourceType type,
HandleBase::HandleId id);
template<typename ResourceType, typename SetType>
class VulkanResourceManagerImpl {
public:
explicit VulkanResourceManagerImpl(VulkanResourceAllocator* allocator)
: mAllocator(allocator) {
if constexpr (std::is_base_of_v<VulkanThreadSafeResource, ResourceType>) {
mMutex = std::make_unique<utils::Mutex>();
}
}
VulkanResourceManagerImpl(const VulkanResourceManagerImpl& other) = delete;
void operator=(const VulkanResourceManagerImpl& other) = delete;
VulkanResourceManagerImpl(const VulkanResourceManagerImpl&& other) = delete;
void operator=(const VulkanResourceManagerImpl&& other) = delete;
~VulkanResourceManagerImpl() {
clear();
}
inline void acquire(ResourceType* resource) {
if (IS_HEAP_ALLOC_TYPE(resource->getType())) {
return;
}
LOCK_IF_NEEDED();
if (mResources.find(resource) != mResources.end()) {
UNLOCK_IF_NEEDED();
return;
}
mResources.insert(resource);
UNLOCK_IF_NEEDED();
resource->ref();
}
// Transfers ownership from one resource set to another
template <typename tSetType>
inline void acquireAll(VulkanResourceManagerImpl<ResourceType, tSetType>* srcResources) {
copyAll(srcResources);
srcResources->clear();
}
// Transfers ownership from one resource set to another
template <typename tSetType>
inline void copyAll(VulkanResourceManagerImpl<ResourceType, tSetType>* srcResources) {
LOCK_IF_NEEDED();
for (auto iter = srcResources->mResources.begin(); iter != srcResources->mResources.end();
iter++) {
acquire(*iter);
}
UNLOCK_IF_NEEDED();
}
inline void release(ResourceType* resource) {
if (IS_HEAP_ALLOC_TYPE(resource->getType())) {
return;
}
LOCK_IF_NEEDED();
auto resItr = mResources.find(resource);
if (resItr == mResources.end()) {
UNLOCK_IF_NEEDED();
return;
}
mResources.erase(resItr);
UNLOCK_IF_NEEDED();
derefImpl(resource);
}
inline void clear() {
LOCK_IF_NEEDED();
for (auto iter = mResources.begin(); iter != mResources.end(); iter++) {
derefImpl(*iter);
}
mResources.clear();
UNLOCK_IF_NEEDED();
}
inline size_t size() {
return mResources.size();
}
private:
inline void derefImpl(ResourceType* resource) {
resource->deref();
if (resource->refcount() != 0) {
return;
}
deallocateResource(mAllocator, resource->getType(), resource->getId());
}
VulkanResourceAllocator* mAllocator;
SetType mResources;
std::unique_ptr<utils::Mutex> mMutex;
template <typename, typename> friend class VulkanResourceManagerImpl;
};
using VulkanAcquireOnlyResourceManager
= VulkanResourceManagerImpl<VulkanResource, FastIterationResourceSet>;
using VulkanResourceManager = VulkanResourceManagerImpl<VulkanResource, ResourceSet>;
template<std::size_t SIZE>
using FixedSizeVulkanResourceManager =
VulkanResourceManagerImpl<VulkanResource, FixedCapacityResourceSet<SIZE>>;
using VulkanThreadSafeResourceManager
= VulkanResourceManagerImpl<VulkanThreadSafeResource, ThreadSafeResourceSet>;
#undef LOCK_IF_NEEDED
#undef UNLOCK_IF_NEEDED
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKANRESOURCES_H
@@ -16,7 +16,6 @@
#include "VulkanStagePool.h"
#include "VulkanCommands.h"
#include "VulkanConstants.h"
#include "VulkanImageUtility.h"
#include "VulkanMemory.h"
@@ -38,7 +37,6 @@ VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
if (iter != mFreeStages.end()) {
auto stage = iter->second;
mFreeStages.erase(iter);
stage->lastAccessed = mCurrentFrame;
mUsedStages.insert(stage);
return stage;
}
@@ -61,7 +59,7 @@ VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
UTILS_UNUSED_IN_RELEASE VkResult result = vmaCreateBuffer(mAllocator, &bufferInfo,
&allocInfo, &stage->buffer, &stage->memory, nullptr);
#if FVK_ENABLED(FVK_DEBUG_STAGING_ALLOCATION)
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
if (result != VK_SUCCESS) {
FVK_LOGE << "Allocation error: " << result << utils::io::endl;
}
@@ -76,7 +74,6 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi
for (auto image : mFreeImages) {
if (image->format == vkformat && image->width == width && image->height == height) {
mFreeImages.erase(image);
image->lastAccessed = mCurrentFrame;
mUsedImages.insert(image);
return image;
}
@@ -17,7 +17,8 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANSTAGEPOOL_H
#define TNT_FILAMENT_BACKEND_VULKANSTAGEPOOL_H
#include "backend/DriverEnums.h"
#include "VulkanCommands.h"
#include "VulkanContext.h"
#include "VulkanMemory.h"
#include <map>
@@ -25,8 +26,6 @@
namespace filament::backend {
class VulkanCommands;
// Immutable POD representing a shared CPU-GPU staging area.
struct VulkanStage {
VmaAllocation memory;
+20 -25
View File
@@ -15,8 +15,6 @@
*/
#include "VulkanSwapChain.h"
#include "VulkanCommands.h"
#include "VulkanTexture.h"
#include <utils/FixedCapacityVector.h>
@@ -28,20 +26,22 @@ using namespace utils;
namespace filament::backend {
VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const& context,
fvkmemory::ResourceManager* resourceManager, VmaAllocator allocator,
VulkanCommands* commands, VulkanStagePool& stagePool, void* nativeWindow, uint64_t flags,
VkExtent2D extent)
: mPlatform(platform),
mResourceManager(resourceManager),
VmaAllocator allocator, VulkanCommands* commands, VulkanResourceAllocator* handleAllocator,
VulkanStagePool& stagePool,
void* nativeWindow, uint64_t flags, VkExtent2D extent)
: VulkanResource(VulkanResourceType::SWAP_CHAIN),
mPlatform(platform),
mCommands(commands),
mAllocator(allocator),
mHandleAllocator(handleAllocator),
mStagePool(stagePool),
mHeadless(extent.width != 0 && extent.height != 0 && !nativeWindow),
mFlushAndWaitOnResize(platform->getCustomization().flushAndWaitOnWindowResize),
mTransitionSwapChainImageLayoutForPresent(
platform->getCustomization().transitionSwapChainImageLayoutForPresent),
platform->getCustomization().transitionSwapChainImageLayoutForPresent),
mAcquired(false),
mIsFirstRenderPass(true) {
mIsFirstRenderPass(true),
mIsProtected(false) {
swapChain = mPlatform->createSwapChain(nativeWindow, flags, extent);
FILAMENT_CHECK_POSTCONDITION(swapChain) << "Unable to create swapchain";
@@ -54,9 +54,6 @@ VulkanSwapChain::~VulkanSwapChain() {
mCommands->flush();
mCommands->wait();
mColors = {};
mDepth = {};
mPlatform->destroy(swapChain);
}
@@ -67,30 +64,28 @@ void VulkanSwapChain::update() {
mColors.reserve(bundle.colors.size());
VkDevice const device = mPlatform->getDevice();
mIsProtected = bundle.protection;
TextureUsage depthUsage = TextureUsage::DEPTH_ATTACHMENT;
TextureUsage colorUsage = TextureUsage::COLOR_ATTACHMENT;
if (bundle.isProtected) {
if (bundle.protection) {
depthUsage |= TextureUsage::PROTECTED;
colorUsage |= TextureUsage::PROTECTED;
}
for (auto const color: bundle.colors) {
auto colorTexture = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager,
device, mAllocator, mResourceManager, mCommands, color, bundle.colorFormat, 1,
bundle.extent.width, bundle.extent.height, TextureUsage::COLOR_ATTACHMENT,
mStagePool);
mColors.push_back(colorTexture);
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 */));
}
mDepth = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager, device, mAllocator,
mResourceManager, mCommands, bundle.depth, bundle.depthFormat, 1, bundle.extent.width,
bundle.extent.height, TextureUsage::DEPTH_ATTACHMENT, mStagePool);
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 */);
mExtent = bundle.extent;
}
void VulkanSwapChain::present() {
if (!mHeadless && mTransitionSwapChainImageLayoutForPresent) {
VulkanCommandBuffer& commands = mCommands->get();
VulkanCommandBuffer& commands = mIsProtected ? mCommands->getProtected() : mCommands->get();
VkImageSubresourceRange const subresources{
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
.baseMipLevel = 0,
@@ -102,7 +97,7 @@ void VulkanSwapChain::present() {
}
mCommands->flush();
// call the image ready wait function
if (mExplicitImageReadyWait != nullptr) {
mExplicitImageReadyWait(swapChain);
@@ -142,7 +137,7 @@ void VulkanSwapChain::acquire(bool& resized) {
VkResult const result = mPlatform->acquire(swapChain, &imageSyncData);
mCurrentSwapIndex = imageSyncData.imageIndex;
mExplicitImageReadyWait = imageSyncData.explicitImageReadyWait;
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
<< "Cannot acquire in swapchain.";
if (imageSyncData.imageReadySemaphore != VK_NULL_HANDLE) {
mCommands->injectDependency(imageSyncData.imageReadySemaphore);
+17 -15
View File
@@ -19,9 +19,9 @@
#include "DriverBase.h"
#include "VulkanCommands.h"
#include "VulkanContext.h"
#include "VulkanTexture.h"
#include "vulkan/memory/Resource.h"
#include "VulkanResources.h"
#include <backend/platforms/VulkanPlatform.h>
@@ -36,14 +36,14 @@ namespace filament::backend {
struct VulkanHeadlessSwapChain;
struct VulkanSurfaceSwapChain;
class VulkanCommands;
class VulkanResourceAllocator;
// A wrapper around the platform implementation of swapchain.
struct VulkanSwapChain : public HwSwapChain, fvkmemory::Resource {
VulkanSwapChain(VulkanPlatform* platform, VulkanContext const& context,
fvkmemory::ResourceManager* resourceManager, VmaAllocator allocator,
VulkanCommands* commands, VulkanStagePool& stagePool, void* nativeWindow,
uint64_t flags, VkExtent2D extent = {0, 0});
struct VulkanSwapChain : public HwSwapChain, VulkanResource {
VulkanSwapChain(VulkanPlatform* platform, VulkanContext const& context, VmaAllocator allocator,
VulkanCommands* commands, VulkanResourceAllocator* handleAllocator,
VulkanStagePool& stagePool,
void* nativeWindow, uint64_t flags, VkExtent2D extent = {0, 0});
~VulkanSwapChain();
@@ -51,15 +51,15 @@ struct VulkanSwapChain : public HwSwapChain, fvkmemory::Resource {
void acquire(bool& reized);
fvkmemory::resource_ptr<VulkanTexture> getCurrentColor() const noexcept {
inline VulkanTexture* getCurrentColor() const noexcept {
uint32_t const imageIndex = mCurrentSwapIndex;
FILAMENT_CHECK_PRECONDITION(
imageIndex != VulkanPlatform::ImageSyncData::INVALID_IMAGE_INDEX);
return mColors[imageIndex];
return mColors[imageIndex].get();
}
inline fvkmemory::resource_ptr<VulkanTexture> getDepth() const noexcept {
return mDepth;
inline VulkanTexture* getDepth() const noexcept {
return mDepth.get();
}
inline bool isFirstRenderPass() const noexcept {
@@ -77,15 +77,16 @@ struct VulkanSwapChain : public HwSwapChain, fvkmemory::Resource {
inline bool isProtected() noexcept {
return mPlatform->isProtected(swapChain);
}
private:
static constexpr int IMAGE_READY_SEMAPHORE_COUNT = FVK_MAX_COMMAND_BUFFERS;
void update();
VulkanPlatform* mPlatform;
fvkmemory::ResourceManager* mResourceManager;
VulkanCommands* mCommands;
VmaAllocator mAllocator;
VulkanResourceAllocator* const mHandleAllocator;
VulkanStagePool& mStagePool;
bool const mHeadless;
bool const mFlushAndWaitOnResize;
@@ -93,13 +94,14 @@ private:
// We create VulkanTextures based on VkImages. VulkanTexture has facilities for doing layout
// transitions, which are useful here.
utils::FixedCapacityVector<fvkmemory::resource_ptr<VulkanTexture>> mColors;
fvkmemory::resource_ptr<VulkanTexture> mDepth;
utils::FixedCapacityVector<std::unique_ptr<VulkanTexture>> mColors;
std::unique_ptr<VulkanTexture> mDepth;
VkExtent2D mExtent;
uint32_t mCurrentSwapIndex;
std::function<void(Platform::SwapChain* handle)> mExplicitImageReadyWait = nullptr;
bool mAcquired;
bool mIsFirstRenderPass;
bool mIsProtected;
};
+148 -100
View File
@@ -14,11 +14,10 @@
* limitations under the License.
*/
#include "VulkanCommands.h"
#include "VulkanMemory.h"
#include "VulkanResourceAllocator.h"
#include "VulkanTexture.h"
#include "VulkanUtility.h"
#include "vulkan/memory/ResourcePointer.h"
#include <DataReshaper.h>
#include <backend/DriverEnums.h>
@@ -150,7 +149,8 @@ VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator,
VulkanCommands* commands, VulkanStagePool& stagePool, VkFormat format,
VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VulkanLayout defaultLayout,
bool isProtected)
: mVkFormat(format),
: VulkanResource(VulkanResourceType::HEAP_ALLOCATED),
mVkFormat(format),
mViewType(viewType),
mFullViewRange{filament::backend::getImageAspect(format), 0, levels, 0, layerCount},
mDefaultLayout(defaultLayout),
@@ -161,42 +161,61 @@ VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator,
mCommands(commands),
mIsTransientAttachment(false) {}
VulkanTextureState* VulkanTexture::getSharedState() {
VulkanTextureState* state = mAllocator->handle_cast<VulkanTextureState*>(mState);
return state;
}
VulkanTextureState const* VulkanTexture::getSharedState() const {
VulkanTextureState const* state = mAllocator->handle_cast<VulkanTextureState const*>(mState);
return state;
}
// Constructor for internally passed VkImage
VulkanTexture::VulkanTexture(VkDevice device, VmaAllocator allocator,
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image,
VkFormat format, uint8_t samples, uint32_t width, uint32_t height, TextureUsage tusage,
VulkanStagePool& stagePool)
: HwTexture(SamplerType::SAMPLER_2D, 1, samples, width, height, 1, TextureFormat::UNUSED,
tusage),
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, device,
allocator, commands, stagePool, format, imgutil::getViewType(SamplerType::SAMPLER_2D),
1, 1, getDefaultLayoutImpl(tusage), any(usage & TextureUsage::PROTECTED))) {
mState->mTextureImage = image;
mPrimaryViewRange = mState->mFullViewRange;
VulkanTexture::VulkanTexture(
VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
VulkanResourceAllocator* handleAllocator,
VkImage image, VkFormat format, uint8_t samples, uint32_t width, uint32_t height,
TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated)
: HwTexture(SamplerType::SAMPLER_2D, 1, samples, width, height, 1, TextureFormat::UNUSED,
tusage),
VulkanResource(
heapAllocated ? VulkanResourceType::HEAP_ALLOCATED : VulkanResourceType::TEXTURE),
mAllocator(handleAllocator),
mState(handleAllocator->initHandle<VulkanTextureState>(
device, allocator, commands, stagePool,
format, imgutil::getViewType(SamplerType::SAMPLER_2D), 1, 1,
getDefaultLayoutImpl(tusage),
any(usage& TextureUsage::PROTECTED))) {
auto* const state = getSharedState();
state->mTextureImage = image;
mPrimaryViewRange = state->mFullViewRange;
}
// Constructor for user facing texture
VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
VulkanContext const& context, VmaAllocator allocator,
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, SamplerType target,
uint8_t levels, TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h,
uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool)
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
VulkanResourceAllocator* handleAllocator, SamplerType target, uint8_t levels,
TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated)
: HwTexture(target, levels, samples, w, h, depth, tformat, tusage),
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, device,
allocator, commands, stagePool, backend::getVkFormat(tformat),
imgutil::getViewType(target), levels, getLayerCount(target, depth),
VulkanLayout::UNDEFINED, any(usage & TextureUsage::PROTECTED))) {
VulkanResource(
heapAllocated ? VulkanResourceType::HEAP_ALLOCATED : VulkanResourceType::TEXTURE),
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))) {
auto* const state = getSharedState();
// Create an appropriately-sized device-only VkImage, but do not fill it yet.
VkImageCreateInfo imageInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = target == SamplerType::SAMPLER_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D,
.format = mState->mVkFormat,
.extent = {w, h, depth},
.mipLevels = levels,
.arrayLayers = 1,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = 0,
};
VkImageCreateInfo imageInfo{.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = target == SamplerType::SAMPLER_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D,
.format = state->mVkFormat,
.extent = {w, h, depth},
.mipLevels = levels,
.arrayLayers = 1,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = 0};
if (target == SamplerType::SAMPLER_CUBEMAP) {
imageInfo.arrayLayers = 6;
imageInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
@@ -216,7 +235,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
imageInfo.arrayLayers = depth * 6;
imageInfo.extent.depth = 1;
}
if (any(usage & TextureUsage::PROTECTED)) {
if (state->mIsProtected) {
imageInfo.flags |= VK_IMAGE_CREATE_PROTECTED_BIT;
}
@@ -235,7 +254,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
none(tusage & ~TextureUsage::ALL_ATTACHMENTS) &&
// Usage contains at least one attachment flag.
any(tusage & TextureUsage::ALL_ATTACHMENTS);
mState->mIsTransientAttachment = useTransientAttachment;
state->mIsTransientAttachment = useTransientAttachment;
const VkImageUsageFlags transientFlag =
useTransientAttachment ? VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT : 0U;
@@ -280,7 +299,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
// any kind of attachment (color or depth).
const auto& limits = context.getPhysicalDeviceLimits();
if (imageInfo.usage & VK_IMAGE_USAGE_SAMPLED_BIT) {
samples = reduceSampleCount(samples, isVkDepthFormat(mState->mVkFormat)
samples = reduceSampleCount(samples, isVkDepthFormat(state->mVkFormat)
? limits.sampledImageDepthSampleCounts
: limits.sampledImageColorSampleCounts);
}
@@ -294,12 +313,12 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
this->samples = samples;
imageInfo.samples = (VkSampleCountFlagBits) samples;
VkResult error = vkCreateImage(mState->mDevice, &imageInfo, VKALLOC, &mState->mTextureImage);
VkResult error = vkCreateImage(state->mDevice, &imageInfo, VKALLOC, &state->mTextureImage);
if (error || FVK_ENABLED(FVK_DEBUG_TEXTURE)) {
FVK_LOGD << "vkCreateImage: "
<< "image = " << mState->mTextureImage << ", "
<< "image = " << state->mTextureImage << ", "
<< "result = " << error << ", "
<< "handle = " << utils::io::hex << mState->mTextureImage << utils::io::dec << ", "
<< "handle = " << utils::io::hex << state->mTextureImage << utils::io::dec << ", "
<< "extent = " << w << "x" << h << "x"<< depth << ", "
<< "mipLevels = " << int(levels) << ", "
<< "TextureUsage = " << static_cast<int>(usage) << ", "
@@ -308,18 +327,18 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
<< "type = " << imageInfo.imageType << ", "
<< "flags = " << imageInfo.flags << ", "
<< "target = " << static_cast<int>(target) <<", "
<< "format = " << mState->mVkFormat << utils::io::endl;
<< "format = " << state->mVkFormat << utils::io::endl;
}
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to create image.";
// Allocate memory for the VkImage and bind it.
VkMemoryRequirements memReqs = {};
vkGetImageMemoryRequirements(mState->mDevice, mState->mTextureImage, &memReqs);
vkGetImageMemoryRequirements(state->mDevice, state->mTextureImage, &memReqs);
const VkFlags requiredMemoryFlags =
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
(useTransientAttachment ? VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT : 0U) |
(mState->mIsProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
(state->mIsProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
uint32_t memoryTypeIndex
= context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags);
@@ -331,29 +350,39 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
.allocationSize = memReqs.size,
.memoryTypeIndex = memoryTypeIndex,
};
error = vkAllocateMemory(mState->mDevice, &allocInfo, nullptr, &mState->mTextureImageMemory);
error = vkAllocateMemory(state->mDevice, &allocInfo, nullptr, &state->mTextureImageMemory);
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to allocate image memory.";
error = vkBindImageMemory(mState->mDevice, mState->mTextureImage, mState->mTextureImageMemory,
0);
error = vkBindImageMemory(state->mDevice, state->mTextureImage, state->mTextureImageMemory, 0);
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to bind image.";
// Spec out the "primary" VkImageView that shaders use to sample from the image.
mPrimaryViewRange = mState->mFullViewRange;
mPrimaryViewRange = state->mFullViewRange;
// Go ahead and create the primary image view.
getImageView(mPrimaryViewRange, mState->mViewType, mSwizzle);
getImageView(mPrimaryViewRange, state->mViewType, mSwizzle);
mState->mDefaultLayout = getDefaultLayoutImpl(imageInfo.usage);
VulkanCommandBuffer& commandsBuf = state->mIsProtected ? state->mCommands->getProtected()
: state->mCommands->get();
commandsBuf.acquire(this);
auto const defaultLayout = state->mDefaultLayout = getDefaultLayoutImpl(imageInfo.usage);
transitionLayout(&commandsBuf, mPrimaryViewRange, defaultLayout);
}
// Constructor for creating a texture view
VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
fvkmemory::resource_ptr<VulkanTexture> src, uint8_t baseLevel,
VulkanResourceAllocator* handleAllocator, VulkanTexture const* src, uint8_t baseLevel,
uint8_t levelCount)
: HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth,
src->format, src->usage) {
src->format, src->usage),
VulkanResource(VulkanResourceType::TEXTURE),
mAllocator(handleAllocator) {
mState = src->mState;
auto* state = getSharedState();
state->refs++;
mPrimaryViewRange = src->mPrimaryViewRange;
mPrimaryViewRange.baseMipLevel = src->mPrimaryViewRange.baseMipLevel + baseLevel;
mPrimaryViewRange.levelCount = levelCount;
@@ -362,21 +391,31 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
// Constructor for creating a texture view with swizzle
VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
fvkmemory::resource_ptr<VulkanTexture> src, VkComponentMapping swizzle)
VulkanResourceAllocator* handleAllocator, VulkanTexture const* src,
VkComponentMapping swizzle)
: HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth,
src->format, src->usage) {
src->format, src->usage),
VulkanResource(VulkanResourceType::TEXTURE),
mAllocator(handleAllocator) {
mState = src->mState;
auto* state = getSharedState();
state->refs++;
mPrimaryViewRange = src->mPrimaryViewRange;
mSwizzle = composeSwizzle(src->mSwizzle, swizzle);
}
VulkanTextureState::~VulkanTextureState() {
if (mTextureImageMemory != VK_NULL_HANDLE) {
vkDestroyImage(mDevice, mTextureImage, VKALLOC);
vkFreeMemory(mDevice, mTextureImageMemory, VKALLOC);
}
for (auto entry: mCachedImageViews) {
vkDestroyImageView(mDevice, entry.second, VKALLOC);
VulkanTexture::~VulkanTexture() {
auto* const state = getSharedState();
state->refs--;
if (state->refs == 0) {
if (state->mTextureImageMemory != VK_NULL_HANDLE) {
vkDestroyImage(state->mDevice, state->mTextureImage, VKALLOC);
vkFreeMemory(state->mDevice, state->mTextureImageMemory, VKALLOC);
}
for (auto entry: state->mCachedImageViews) {
vkDestroyImageView(state->mDevice, entry.second, VKALLOC);
}
mAllocator->destruct<VulkanTextureState>(mState);
}
}
@@ -385,7 +424,8 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
assert_invariant(width <= this->width && height <= this->height);
assert_invariant(depth <= this->depth * ((target == SamplerType::SAMPLER_CUBEMAP ||
target == SamplerType::SAMPLER_CUBEMAP_ARRAY) ? 6 : 1));
assert_invariant(!mState->mIsProtected);
auto* const state = getSharedState();
assert_invariant(!state->mIsProtected);
const PixelBufferDescriptor* hostData = &data;
PixelBufferDescriptor reshapedData;
@@ -398,7 +438,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
// If format conversion is both required and supported, use vkCmdBlitImage.
const VkFormat hostFormat = backend::getVkFormat(hostData->format, hostData->type);
const VkFormat deviceFormat = getVkFormatLinear(mState->mVkFormat);
const VkFormat deviceFormat = getVkFormatLinear(state->mVkFormat);
if (hostFormat != deviceFormat && hostFormat != VK_FORMAT_UNDEFINED) {
assert_invariant(xoffset == 0 && yoffset == 0 && zoffset == 0 &&
"Offsets not yet supported when format conversion is required.");
@@ -410,16 +450,16 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
// Otherwise, use vkCmdCopyBufferToImage.
void* mapped = nullptr;
VulkanStage const* stage = mState->mStagePool.acquireStage(hostData->size);
VulkanStage const* stage = state->mStagePool.acquireStage(hostData->size);
assert_invariant(stage->memory);
vmaMapMemory(mState->mAllocator, stage->memory, &mapped);
vmaMapMemory(state->mAllocator, stage->memory, &mapped);
memcpy(mapped, hostData->buffer, hostData->size);
vmaUnmapMemory(mState->mAllocator, stage->memory);
vmaFlushAllocation(mState->mAllocator, stage->memory, 0, hostData->size);
vmaUnmapMemory(state->mAllocator, stage->memory);
vmaFlushAllocation(state->mAllocator, stage->memory, 0, hostData->size);
VulkanCommandBuffer& commands = mState->mCommands->get();
VulkanCommandBuffer& commands =state->mCommands->get();
VkCommandBuffer const cmdbuf = commands.buffer();
commands.acquire(fvkmemory::resource_ptr<VulkanTexture>::cast(this));
commands.acquire(this);
VkBufferImageCopy copyRegion = {
.bufferOffset = {},
@@ -465,24 +505,27 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
transitionLayout(&commands, transitionRange, newLayout);
vkCmdCopyBufferToImage(cmdbuf, stage->buffer, mState->mTextureImage, newVkLayout, 1, &copyRegion);
vkCmdCopyBufferToImage(cmdbuf, stage->buffer, state->mTextureImage, newVkLayout, 1, &copyRegion);
transitionLayout(&commands, transitionRange, nextLayout);
}
void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width,
uint32_t height, uint32_t depth, uint32_t miplevel) {
auto* const state = getSharedState();
assert_invariant(!state->mIsProtected);
void* mapped = nullptr;
VulkanStageImage const* stage
= mState->mStagePool.acquireImage(hostData.format, hostData.type, width, height);
vmaMapMemory(mState->mAllocator, stage->memory, &mapped);
= state->mStagePool.acquireImage(hostData.format, hostData.type, width, height);
vmaMapMemory(state->mAllocator, stage->memory, &mapped);
memcpy(mapped, hostData.buffer, hostData.size);
vmaUnmapMemory(mState->mAllocator, stage->memory);
vmaFlushAllocation(mState->mAllocator, stage->memory, 0, hostData.size);
vmaUnmapMemory(state->mAllocator, stage->memory);
vmaFlushAllocation(state->mAllocator, stage->memory, 0, hostData.size);
VulkanCommandBuffer& commands = mState->mCommands->get();
VulkanCommandBuffer& commands = state->mCommands->get();
VkCommandBuffer const cmdbuf = commands.buffer();
commands.acquire(fvkmemory::resource_ptr<VulkanTexture>::cast(this));
commands.acquire(this);
// TODO: support blit-based format conversion for 3D images and cubemaps.
const int layer = 0;
@@ -505,13 +548,14 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, u
transitionLayout(&commands, range, newLayout);
vkCmdBlitImage(cmdbuf, stage->image, imgutil::getVkLayout(VulkanLayout::TRANSFER_SRC),
mState->mTextureImage, imgutil::getVkLayout(newLayout), 1, blitRegions, VK_FILTER_NEAREST);
state->mTextureImage, imgutil::getVkLayout(newLayout), 1, blitRegions, VK_FILTER_NEAREST);
transitionLayout(&commands, range, oldLayout);
}
VulkanLayout VulkanTexture::getDefaultLayout() const {
return mState->mDefaultLayout;
auto* const state = getSharedState();
return state->mDefaultLayout;
}
VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange range) {
@@ -530,43 +574,42 @@ VkImageView VulkanTexture::getViewForType(VkImageSubresourceRange const& range,
VkImageView VulkanTexture::getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
VkComponentMapping swizzle) {
auto* const state = getSharedState();
VulkanTextureState::ImageViewKey const key{ range, viewType, swizzle };
auto iter = mState->mCachedImageViews.find(key);
if (iter != mState->mCachedImageViews.end()) {
auto iter = state->mCachedImageViews.find(key);
if (iter != state->mCachedImageViews.end()) {
return iter->second;
}
VkImageViewCreateInfo viewInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.image = mState->mTextureImage,
.image = state->mTextureImage,
.viewType = viewType,
.format = mState->mVkFormat,
.format = state->mVkFormat,
.components = swizzle,
.subresourceRange = range,
};
VkImageView imageView;
vkCreateImageView(mState->mDevice, &viewInfo, VKALLOC, &imageView);
mState->mCachedImageViews.emplace(key, imageView);
vkCreateImageView(state->mDevice, &viewInfo, VKALLOC, &imageView);
state->mCachedImageViews.emplace(key, imageView);
return imageView;
}
VkImageAspectFlags VulkanTexture::getImageAspect() const {
// Helper function in VulkanUtility
return filament::backend::getImageAspect(mState->mVkFormat);
auto* const state = getSharedState();
return filament::backend::getImageAspect(state->mVkFormat);
}
bool VulkanTexture::transitionLayout(VulkanCommandBuffer* commands,
VkImageSubresourceRange const& range, VulkanLayout newLayout) {
if (transitionLayout(commands->buffer(), range, newLayout)) {
commands->acquire(fvkmemory::resource_ptr<VulkanTexture>::cast(this));
return true;
}
return false;
return transitionLayout(commands->buffer(), range, newLayout);
}
bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceRange const& range,
VulkanLayout newLayout) {
auto* const state = getSharedState();
VulkanLayout const oldLayout = getLayout(range.baseArrayLayer, range.baseMipLevel);
uint32_t const firstLayer = range.baseArrayLayer;
@@ -597,7 +640,7 @@ bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceR
continue;
}
hasTransitions = hasTransitions || imgutil::transitionLayout(cmdbuf, {
.image = mState->mTextureImage,
.image = state->mTextureImage,
.oldLayout = layout,
.newLayout = newLayout,
.subresources = {
@@ -612,7 +655,7 @@ bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceR
}
} else if (newLayout != oldLayout) {
hasTransitions = imgutil::transitionLayout(cmdbuf, {
.image = mState->mTextureImage,
.image = state->mTextureImage,
.oldLayout = oldLayout,
.newLayout = newLayout,
.subresources = range,
@@ -645,6 +688,7 @@ bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceR
void VulkanTexture::samplerToAttachmentBarrier(VulkanCommandBuffer* commands,
VkImageSubresourceRange const& range) {
VkCommandBuffer const cmdbuf = commands->buffer();
auto* const state = getSharedState();
VkImageLayout const layout =
imgutil::getVkLayout(getLayout(range.baseArrayLayer, range.baseMipLevel));
VkImageMemoryBarrier barrier = {
@@ -656,7 +700,7 @@ void VulkanTexture::samplerToAttachmentBarrier(VulkanCommandBuffer* commands,
.newLayout = layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = mState->mTextureImage,
.image = state->mTextureImage,
.subresourceRange = range,
};
vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
@@ -668,6 +712,7 @@ void VulkanTexture::samplerToAttachmentBarrier(VulkanCommandBuffer* commands,
void VulkanTexture::attachmentToSamplerBarrier(VulkanCommandBuffer* commands,
VkImageSubresourceRange const& range) {
VkCommandBuffer const cmdbuf = commands->buffer();
auto* const state = getSharedState();
VkImageLayout const layout
= imgutil::getVkLayout(getLayout(range.baseArrayLayer, range.baseMipLevel));
VkImageMemoryBarrier barrier = {
@@ -678,7 +723,7 @@ void VulkanTexture::attachmentToSamplerBarrier(VulkanCommandBuffer* commands,
.newLayout = layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = mState->mTextureImage,
.image = state->mTextureImage,
.subresourceRange = range,
};
vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
@@ -686,6 +731,7 @@ void VulkanTexture::attachmentToSamplerBarrier(VulkanCommandBuffer* commands,
}
void VulkanTexture::setLayout(VkImageSubresourceRange const& range, VulkanLayout newLayout) {
auto* const state = getSharedState();
uint32_t const firstLayer = range.baseArrayLayer;
uint32_t const lastLayer = firstLayer + range.layerCount;
uint32_t const firstLevel = range.baseMipLevel;
@@ -698,32 +744,34 @@ void VulkanTexture::setLayout(VkImageSubresourceRange const& range, VulkanLayout
for (uint32_t layer = firstLayer; layer < lastLayer; ++layer) {
uint32_t const first = (layer << 16) | firstLevel;
uint32_t const last = (layer << 16) | lastLevel;
mState->mSubresourceLayouts.clear(first, last);
state->mSubresourceLayouts.clear(first, last);
}
} else {
for (uint32_t layer = firstLayer; layer < lastLayer; ++layer) {
uint32_t const first = (layer << 16) | firstLevel;
uint32_t const last = (layer << 16) | lastLevel;
mState->mSubresourceLayouts.add(first, last, newLayout);
state->mSubresourceLayouts.add(first, last, newLayout);
}
}
}
VulkanLayout VulkanTexture::getLayout(uint32_t layer, uint32_t level) const {
assert_invariant(level <= 0xffff && layer <= 0xffff);
auto* const state = getSharedState();
const uint32_t key = (layer << 16) | level;
if (!mState->mSubresourceLayouts.has(key)) {
if (!state->mSubresourceLayouts.has(key)) {
return VulkanLayout::UNDEFINED;
}
return mState->mSubresourceLayouts.get(key);
return state->mSubresourceLayouts.get(key);
}
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
void VulkanTexture::print() const {
auto* const state = getSharedState();
uint32_t const firstLayer = 0;
uint32_t const lastLayer = firstLayer + mState->mFullViewRange.layerCount;
uint32_t const lastLayer = firstLayer + state->mFullViewRange.layerCount;
uint32_t const firstLevel = 0;
uint32_t const lastLevel = firstLevel + mState->mFullViewRange.levelCount;
uint32_t const lastLevel = firstLevel + state->mFullViewRange.levelCount;
for (uint32_t layer = firstLayer; layer < lastLayer; ++layer) {
for (uint32_t level = firstLevel; level < lastLevel; ++level) {
@@ -732,16 +780,16 @@ void VulkanTexture::print() const {
layer < (mPrimaryViewRange.baseArrayLayer + mPrimaryViewRange.layerCount) &&
level >= mPrimaryViewRange.baseMipLevel &&
level < (mPrimaryViewRange.baseMipLevel + mPrimaryViewRange.levelCount);
FVK_LOGD << "[" << mState->mTextureImage << "]: (" << layer << "," << level
FVK_LOGD << "[" << state->mTextureImage << "]: (" << layer << "," << level
<< ")=" << getLayout(layer, level)
<< " primary=" << primary
<< utils::io::endl;
}
}
for (auto view: mState->mCachedImageViews) {
for (auto view: state->mCachedImageViews) {
auto& range = view.first.range;
FVK_LOGD << "[" << mState->mTextureImage << ", imageView=" << view.second << "]=>"
FVK_LOGD << "[" << state->mTextureImage << ", imageView=" << view.second << "]=>"
<< " (" << range.baseArrayLayer << "," << range.baseMipLevel << ")"
<< " count=(" << range.layerCount << "," << range.levelCount << ")"
<< " aspect=" << range.aspectMask << " viewType=" << view.first.type
+58 -31
View File
@@ -20,9 +20,8 @@
#include "DriverBase.h"
#include "VulkanBuffer.h"
#include "VulkanResources.h"
#include "VulkanImageUtility.h"
#include "vulkan/memory/Resource.h"
#include "vulkan/memory/ResourcePointer.h"
#include <utils/Hash.h>
#include <utils/RangeMap.h>
@@ -31,12 +30,13 @@
namespace filament::backend {
struct VulkanTextureState : public fvkmemory::Resource {
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);
~VulkanTextureState();
uint8_t layerCount, VulkanLayout defaultLayout,
bool isProtected = false);
struct ImageViewKey {
VkImageSubresourceRange range; // 4 * 5 bytes
@@ -59,8 +59,10 @@ struct VulkanTextureState : public fvkmemory::Resource {
using ImageViewHash = utils::hash::MurmurHashFn<ImageViewKey>;
uint32_t refs = 1;
// The texture with the sidecar owns the sidecar.
fvkmemory::resource_ptr<VulkanTexture> mSidecarMSAA;
std::unique_ptr<VulkanTexture> mSidecarMSAA;
VkDeviceMemory mTextureImageMemory = VK_NULL_HANDLE;
VkFormat const mVkFormat;
@@ -82,32 +84,44 @@ struct VulkanTextureState : public fvkmemory::Resource {
bool mIsTransientAttachment;
};
struct VulkanTexture : public HwTexture, fvkmemory::Resource {
struct VulkanTexture : public HwTexture, VulkanResource {
// Standard constructor for user-facing textures.
VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context,
VmaAllocator allocator, fvkmemory::ResourceManager* resourceManager,
VulkanCommands* commands, SamplerType target, uint8_t levels, TextureFormat tformat,
uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, TextureUsage tusage,
VulkanStagePool& stagePool);
VmaAllocator allocator, VulkanCommands* commands,
VulkanResourceAllocator* handleAllocator,
SamplerType target, uint8_t levels,
TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated = false);
// Specialized constructor for internally created textures (e.g. from a swap chain)
// The texture will never destroy the given VkImage, but it does manages its subresources.
VulkanTexture(VkDevice device, VmaAllocator allocator,
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image,
VulkanTexture(VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
VulkanResourceAllocator* handleAllocator,
VkImage image,
VkFormat format, uint8_t samples, uint32_t width, uint32_t height, TextureUsage tusage,
VulkanStagePool& stagePool);
VulkanStagePool& stagePool, bool heapAllocated = false);
// Constructor for creating a texture view for wrt specific mip range
VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context,
VmaAllocator allocator, VulkanCommands* commands,
fvkmemory::resource_ptr<VulkanTexture> src, uint8_t baseLevel, uint8_t levelCount);
VulkanResourceAllocator* handleAllocator,
VulkanTexture const* src, uint8_t baseLevel, uint8_t levelCount);
// Constructor for creating a texture view for swizzle.
VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context,
VmaAllocator allocator, VulkanCommands* commands,
fvkmemory::resource_ptr<VulkanTexture> src, VkComponentMapping swizzle);
VulkanResourceAllocator* handleAllocator,
VulkanTexture const* src, VkComponentMapping swizzle);
~VulkanTexture() = default;
// Specialized constructor for external images
VulkanTexture(VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
VulkanResourceAllocator* handleAllocator,
VkImage image, VkDeviceMemory memory,
VkFormat format, uint32_t width, uint32_t height, TextureUsage tusage,
VulkanStagePool& stagePool);
~VulkanTexture();
// Uploads data into a subregion of a 2D or 3D texture.
void updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height,
@@ -115,14 +129,16 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
// Returns the primary image view, which is used for shader sampling.
VkImageView getPrimaryImageView() {
return getImageView(mPrimaryViewRange, mState->mViewType, mSwizzle);
VulkanTextureState* state = getSharedState();
return getImageView(mPrimaryViewRange, state->mViewType, mSwizzle);
}
VkImageViewType getViewType() const {
return mState->mViewType;
VulkanTextureState const* state = getSharedState();
return state->mViewType;
}
VkImageSubresourceRange const& getPrimaryViewRange() const { return mPrimaryViewRange; }
VkImageSubresourceRange getPrimaryViewRange() const { return mPrimaryViewRange; }
VulkanLayout getPrimaryImageLayout() const {
return getLayout(mPrimaryViewRange.baseArrayLayer, mPrimaryViewRange.baseMipLevel);
@@ -148,28 +164,34 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
VkImageView getViewForType(VkImageSubresourceRange const& range, VkImageViewType type);
VkFormat getVkFormat() const {
return mState->mVkFormat;
VulkanTextureState const* state = getSharedState();
return state->mVkFormat;
}
VkImage getVkImage() const {
return mState->mTextureImage;
VulkanTextureState const* state = getSharedState();
return state->mTextureImage;
}
VulkanLayout getLayout(uint32_t layer, uint32_t level) const;
void setSidecar(fvkmemory::resource_ptr<VulkanTexture> sidecar) {
mState->mSidecarMSAA = sidecar;
void setSidecar(VulkanTexture* sidecar) {
VulkanTextureState* state = getSharedState();
state->mSidecarMSAA.reset(sidecar);
}
fvkmemory::resource_ptr<VulkanTexture> getSidecar() const {
return mState->mSidecarMSAA;
VulkanTexture* getSidecar() const {
VulkanTextureState const* state = getSharedState();
return state->mSidecarMSAA.get();
}
bool isTransientAttachment() const {
return mState->mIsTransientAttachment;
VulkanTextureState const* state = getSharedState();
return state->mIsTransientAttachment;
}
bool getIsProtected() const {
return mState->mIsProtected;
bool isProtected() const {
VulkanTextureState const* state = getSharedState();
return state->mIsProtected;
}
bool transitionLayout(VulkanCommandBuffer* commands, VkImageSubresourceRange const& range,
@@ -197,6 +219,9 @@ struct VulkanTexture : public HwTexture, fvkmemory::Resource {
#endif
private:
VulkanTextureState* getSharedState();
VulkanTextureState const* getSharedState() const;
// Gets or creates a cached VkImageView for a range of miplevels, array layers, viewType, and
// swizzle (or not).
VkImageView getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
@@ -205,7 +230,9 @@ private:
void updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width, uint32_t height,
uint32_t depth, uint32_t miplevel);
fvkmemory::resource_ptr<VulkanTextureState> mState;
VulkanResourceAllocator* const mAllocator;
Handle<VulkanTextureState> mState;
// Track the range of subresources that define the "primary" image view, which is the special
// image view that gets bound to an actual texture sampler.
@@ -16,11 +16,11 @@
#include "VulkanDescriptorSetManager.h"
#include <vulkan/VulkanCommands.h>
#include <vulkan/VulkanHandles.h>
#include <vulkan/VulkanUtility.h>
#include <vulkan/VulkanConstants.h>
#include <vulkan/VulkanImageUtility.h>
#include <vulkan/VulkanResources.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Panic.h>
@@ -274,7 +274,7 @@ public:
DescriptorInfinitePool(VkDevice device)
: mDevice(device) {}
VkDescriptorSet obtainSet(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
VkDescriptorSet obtainSet(VulkanDescriptorSetLayout* layout) {
auto const vklayout = layout->getVkLayout();
DescriptorPool* sameTypePool = nullptr;
for (auto& pool: mPools) {
@@ -325,12 +325,13 @@ public:
DescriptorSetLayoutManager(VkDevice device)
: mDevice(device) {}
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks) {
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout* layout) {
auto const& bitmasks = layout->bitmask;
if (auto itr = mVkLayouts.find(bitmasks); itr != mVkLayouts.end()) {
return itr->second;
}
auto vklayout = createLayout(mDevice, bitmasks);
mVkLayouts[bitmasks] = vklayout;
auto vklayout = createLayout(mDevice, layout->bitmask);
mVkLayouts[layout->bitmask] = vklayout;
return vklayout;
}
@@ -346,11 +347,10 @@ private:
mVkLayouts;
};
VulkanDescriptorSetManager::VulkanDescriptorSetManager(VkDevice device,
fvkmemory::ResourceManager* resourceManager)
VulkanResourceAllocator* resourceAllocator)
: mDevice(device),
mResourceManager(resourceManager),
mResourceAllocator(resourceAllocator),
mLayoutManager(std::make_unique<DescriptorSetLayoutManager>(device)),
mDescriptorPool(std::make_unique<DescriptorInfinitePool>(device)) {}
@@ -359,22 +359,16 @@ VulkanDescriptorSetManager::~VulkanDescriptorSetManager() = default;
void VulkanDescriptorSetManager::terminate() noexcept{
mLayoutManager.reset();
mDescriptorPool.reset();
clearHistory();
}
// bind() is not really binding the set but just stashing until we have all the info
// (pipelinelayout).
void VulkanDescriptorSetManager::bind(uint8_t setIndex,
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
void VulkanDescriptorSetManager::bind(uint8_t setIndex, VulkanDescriptorSet* set,
backend::DescriptorSetOffsetArray&& offsets) {
set->setOffsets(std::move(offsets));
mStashedSets[setIndex] = set;
}
void VulkanDescriptorSetManager::unbind(uint8_t setIndex) {
mStashedSets[setIndex] = {};
}
void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands,
VkPipelineLayout pipelineLayout, DescriptorSetMask const& setMask) {
// setMask indicates the set of descriptor sets the driver wants to bind, curMask is the
@@ -414,14 +408,14 @@ void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands,
};
}
void VulkanDescriptorSetManager::updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanBufferObject> bufferObject,
VkDeviceSize offset, VkDeviceSize size) noexcept {
void VulkanDescriptorSetManager::updateBuffer(VulkanDescriptorSet* set, uint8_t binding,
VulkanBufferObject* bufferObject, VkDeviceSize offset, VkDeviceSize size) noexcept {
VkDescriptorBufferInfo const info = {
.buffer = bufferObject->buffer.getGpuBuffer(),
.offset = offset,
.range = size,
};
VkDescriptorType type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
if (set->dynamicUboMask.test(binding)) {
@@ -440,9 +434,8 @@ void VulkanDescriptorSetManager::updateBuffer(fvkmemory::resource_ptr<VulkanDesc
set->acquire(bufferObject);
}
void VulkanDescriptorSetManager::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
VkSampler sampler) noexcept {
void VulkanDescriptorSetManager::updateSampler(VulkanDescriptorSet* set, uint8_t binding,
VulkanTexture* texture, VkSampler sampler) noexcept {
VkDescriptorImageInfo info{
.sampler = sampler,
};
@@ -472,35 +465,38 @@ void VulkanDescriptorSetManager::updateSampler(fvkmemory::resource_ptr<VulkanDes
set->acquire(texture);
}
void VulkanDescriptorSetManager::updateInputAttachment(
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
VulkanAttachment const& attachment) noexcept {
void VulkanDescriptorSetManager::updateInputAttachment(VulkanDescriptorSet* set,
VulkanAttachment attachment) noexcept {
// TOOD: fill-in this region
}
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetManager::createSet(
Handle<HwDescriptorSet> handle, fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
void VulkanDescriptorSetManager::createSet(Handle<HwDescriptorSet> handle,
VulkanDescriptorSetLayout* layout) {
auto const vkSet = mDescriptorPool->obtainSet(layout);
auto const& count = layout->count;
auto const vklayout = layout->getVkLayout();
return fvkmemory::resource_ptr<VulkanDescriptorSet>::make(mResourceManager, handle, vkSet,
mResourceAllocator->construct<VulkanDescriptorSet>(handle, mResourceAllocator, vkSet,
layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
[vkSet, count, vklayout, this](VulkanDescriptorSet*) {
// Note that mDescriptorPool could be gone due to terminate (when the backend shuts
// down).
if (mDescriptorPool) {
mDescriptorPool->recycle(count, vklayout, vkSet);
}
[vkSet, count, vklayout, this](VulkanDescriptorSet* set) {
eraseSetFromHistory(set);
mDescriptorPool->recycle(count, vklayout, vkSet);
});
}
void VulkanDescriptorSetManager::initVkLayout(
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
layout->setVkLayout(mLayoutManager->getVkLayout(layout->bitmask));
void VulkanDescriptorSetManager::destroySet(Handle<HwDescriptorSet> handle) {
}
void VulkanDescriptorSetManager::clearHistory() {
mStashedSets = {};
void VulkanDescriptorSetManager::initVkLayout(VulkanDescriptorSetLayout* layout) {
layout->setVkLayout(mLayoutManager->getVkLayout(layout));
}
void VulkanDescriptorSetManager::eraseSetFromHistory(VulkanDescriptorSet* set) {
for (uint8_t i = 0; i < mStashedSets.size(); ++i) {
if (mStashedSets[i] == set) {
mStashedSets[i] = nullptr;
}
}
}
} // namespace filament::backend
@@ -17,10 +17,9 @@
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
#include "vulkan/VulkanHandles.h"
#include "vulkan/VulkanTexture.h"
#include "vulkan/VulkanUtility.h"
#include "vulkan/memory/ResourcePointer.h"
#include <vulkan/VulkanResourceAllocator.h>
#include <vulkan/VulkanTexture.h>
#include <vulkan/VulkanUtility.h>
#include <backend/DriverEnums.h>
#include <backend/Program.h>
@@ -47,45 +46,44 @@ public:
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
VulkanDescriptorSetManager(VkDevice device, fvkmemory::ResourceManager* resourceManager);
VulkanDescriptorSetManager(VkDevice device, VulkanResourceAllocator* resourceAllocator);
~VulkanDescriptorSetManager();
void terminate() noexcept;
void updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
fvkmemory::resource_ptr<VulkanBufferObject> bufferObject, VkDeviceSize offset,
VkDeviceSize size) noexcept;
void updateBuffer(VulkanDescriptorSet* set, uint8_t binding,
VulkanBufferObject* bufferObject, VkDeviceSize offset, VkDeviceSize size) noexcept;
void updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept;
void updateSampler(VulkanDescriptorSet* set, uint8_t binding,
VulkanTexture* texture, VkSampler sampler) noexcept;
void updateInputAttachment(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
VulkanAttachment const& attachment) noexcept;
void updateInputAttachment(VulkanDescriptorSet* set, VulkanAttachment attachment) noexcept;
void bind(uint8_t setIndex, fvkmemory::resource_ptr<VulkanDescriptorSet> set,
backend::DescriptorSetOffsetArray&& offsets);
void unbind(uint8_t setIndex);
void bind(uint8_t setIndex, VulkanDescriptorSet* set, backend::DescriptorSetOffsetArray&& offsets);
void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout,
DescriptorSetMask const& setMask);
fvkmemory::resource_ptr<VulkanDescriptorSet> createSet(Handle<HwDescriptorSet> handle,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
void setPlaceHolders(VkSampler sampler, VulkanTexture* texture,
VulkanBufferObject* bufferObject) noexcept;
void initVkLayout(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
void createSet(Handle<HwDescriptorSet> handle, VulkanDescriptorSetLayout* layout);
void clearHistory();
void destroySet(Handle<HwDescriptorSet> handle);
void initVkLayout(VulkanDescriptorSetLayout* layout);
private:
class DescriptorSetLayoutManager;
class DescriptorInfinitePool;
void eraseSetFromHistory(VulkanDescriptorSet* set);
using DescriptorSetArray =
std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>, UNIQUE_DESCRIPTOR_SET_COUNT>;
std::array<VulkanDescriptorSet*, UNIQUE_DESCRIPTOR_SET_COUNT>;
VkDevice mDevice;
fvkmemory::ResourceManager* mResourceManager;
VulkanResourceAllocator* mResourceAllocator;
std::unique_ptr<DescriptorSetLayoutManager> mLayoutManager;
std::unique_ptr<DescriptorInfinitePool> mDescriptorPool;
std::pair<VulkanAttachment, VkDescriptorImageInfo> mInputAttachment;
@@ -94,7 +92,7 @@ private:
struct {
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
DescriptorSetMask setMask;
DescriptorSetArray boundSets = {};
DescriptorSetArray boundSets;
} mLastBoundInfo;
};
@@ -16,11 +16,12 @@
#include "VulkanPipelineLayoutCache.h"
#include <vulkan/VulkanResourceAllocator.h>
namespace filament::backend {
VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
DescriptorSetLayoutArray const& descriptorSetLayouts,
fvkmemory::resource_ptr<VulkanProgram> program) {
DescriptorSetLayoutArray const& descriptorSetLayouts, VulkanProgram* program) {
PipelineLayoutKey key = {};
uint8_t descSetLayoutCount = 0;
key.descSetLayouts = descriptorSetLayouts;
@@ -39,13 +40,13 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
for (uint8_t i = 0; i < pushConstantRangeCount; ++i) {
auto const& range = pushConstantRanges[i];
auto& pushConstant = key.pushConstant[i];
if (range.stageFlags & VK_SHADER_STAGE_VERTEX_BIT) {
if (range.stageFlags & VK_SHADER_STAGE_VERTEX_BIT) {
pushConstant.stage = static_cast<uint8_t>(ShaderStage::VERTEX);
}
if (range.stageFlags & VK_SHADER_STAGE_FRAGMENT_BIT) {
if (range.stageFlags & VK_SHADER_STAGE_FRAGMENT_BIT) {
pushConstant.stage = static_cast<uint8_t>(ShaderStage::FRAGMENT);
}
if (range.stageFlags & VK_SHADER_STAGE_COMPUTE_BIT) {
if (range.stageFlags & VK_SHADER_STAGE_COMPUTE_BIT) {
pushConstant.stage = static_cast<uint8_t>(ShaderStage::COMPUTE);
}
pushConstant.size = range.size;
@@ -30,7 +30,7 @@ class VulkanPipelineLayoutCache {
public:
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
VulkanPipelineLayoutCache(VkDevice device)
VulkanPipelineLayoutCache(VkDevice device, VulkanResourceAllocator* allocator)
: mDevice(device),
mTimestamp(0) {}
@@ -56,7 +56,7 @@ public:
// A pipeline layout depends on the descriptor set layout and the push constant ranges, which
// are described in the program.
VkPipelineLayout getLayout(DescriptorSetLayoutArray const& descriptorSetLayouts,
fvkmemory::resource_ptr<VulkanProgram> program);
VulkanProgram* program);
private:
using Timestamp = uint64_t;
@@ -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,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/ResourceManager.h"
#include "vulkan/VulkanHandles.h"
#include <utils/Panic.h>
namespace filament::backend::fvkmemory {
namespace {
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
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;
std::swap(gcs, mGcList);
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_RESOURCE_LEAK)
COUNTER[(size_t) type]--;
#endif
}
void ResourceManager::traceConstruction(ResourceType type, HandleId id) {
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
assert_invariant(type != ResourceType::UNDEFINED_TYPE);
COUNTER[(size_t) type]++;
#endif
}
void ResourceManager::print() const noexcept {
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
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,111 +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
@@ -159,6 +159,7 @@ ExtensionSet getInstanceExtensions(ExtensionSet const& externallyRequiredExts =
// Request all cross-platform extensions.
VK_KHR_SURFACE_EXTENSION_NAME,
VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME,
// Request these if available.
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
@@ -301,7 +302,8 @@ VkInstance createInstance(ExtensionSet const& requiredExts) {
VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
VkPhysicalDeviceFeatures2 const& features, uint32_t graphicsQueueFamilyIndex,
uint32_t protectedGraphicsQueueFamilyIndex, ExtensionSet const& deviceExtensions) {
uint32_t protectedGraphicsQueueFamilyIndex, ExtensionSet const&
deviceExtensions) {
VkDevice device;
VkDeviceQueueCreateInfo deviceQueueCreateInfo[2] = {};
const float queuePriority[] = {1.0f};
@@ -326,8 +328,7 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
deviceQueueCreateInfo[1].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 = protectedGraphicsQueueFamilyIndex == INVALID_VK_INDEX ? 1 : 2;
deviceCreateInfo.pQueueCreateInfos = deviceQueueCreateInfo;
// We could simply enable all supported features, but since that may have performance
@@ -369,17 +370,19 @@ VkDevice createLogicalDevice(VkPhysicalDevice physicalDevice,
}
VkPhysicalDeviceProtectedMemoryFeatures protectedMemory = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES,
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_FEATURES,
};
if (hasProtectedQueue) {
if (protectedGraphicsQueueFamilyIndex != INVALID_VK_INDEX) {
// Enable protected memory, if requested.
protectedMemory.protectedMemory = VK_TRUE;
protectedMemory.pNext = pNext;
pNext = &protectedMemory;
}
deviceCreateInfo.pNext = pNext;
VkResult result = vkCreateDevice(physicalDevice, &deviceCreateInfo, VKALLOC, &device);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "vkCreateDevice error=" << result << ".";
@@ -424,7 +427,7 @@ FixedCapacityVector<VkQueueFamilyProperties> getPhysicalDeviceQueueFamilyPropert
return queueFamiliesProperties;
}
uint32_t identifyGraphicsQueueFamilyIndex(VkPhysicalDevice physicalDevice, VkQueueFlags flags) {
uint32_t identifyGraphicsQueueFamilyIndex(VkPhysicalDevice physicalDevice, VkQueueFlags flags = VK_QUEUE_GRAPHICS_BIT) {
const FixedCapacityVector<VkQueueFamilyProperties> queueFamiliesProperties
= getPhysicalDeviceQueueFamilyPropertiesHelper(physicalDevice);
uint32_t graphicsQueueFamilyIndex = INVALID_VK_INDEX;
@@ -491,8 +494,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;
}
@@ -560,12 +562,16 @@ VkFormatList findAttachmentDepthStencilFormats(VkPhysicalDevice device) {
VK_FORMAT_D32_SFLOAT_S8_UINT,
VK_FORMAT_D24_UNORM_S8_UINT,
// VK_FORMAT_D16_UNORM,
// VK_FORMAT_D16_UNORM_S8_UINT,
};
std::vector<VkFormat> selectedFormats;
for (VkFormat format: formats) {
VkFormatProperties props;
vkGetPhysicalDeviceFormatProperties(device, format, &props);
if ((props.optimalTilingFeatures & features) == features) {
if ((props.optimalTilingFeatures & features) == features ||
(props.linearTilingFeatures & features) == features) {
selectedFormats.push_back(format);
}
}
@@ -714,8 +720,10 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
// 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,
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROTECTED_MEMORY_PROPERTIES,
};
protectedMemoryProperties.pNext = context.mPhysicalDeviceProperties.pNext;
context.mPhysicalDeviceProperties.pNext = &protectedMemoryProperties;
@@ -728,18 +736,17 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
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);
context.mProtectedMemorySupported = static_cast<bool>(queryProtectedMemoryFeatures.protectedMemory);
if (context.mProtectedMemorySupported) {
mImpl->mProtectedGraphicsQueueFamilyIndex
= identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice,
(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_PROTECTED_BIT));
= mImpl->mProtectedGraphicsQueueFamilyIndex == INVALID_VK_INDEX
? identifyGraphicsQueueFamilyIndex(mImpl->mPhysicalDevice, (VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_PROTECTED_BIT))
: mImpl->mProtectedGraphicsQueueFamilyIndex;
assert_invariant(mImpl->mProtectedGraphicsQueueFamilyIndex != INVALID_VK_INDEX);
}
@@ -758,8 +765,7 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
// 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;
= mImpl->mProtectedGraphicsQueueIndex == INVALID_VK_INDEX ? 0 : mImpl->mProtectedGraphicsQueueIndex;
ExtensionSet deviceExts;
// If using a shared context, we do not assume any extensions.
@@ -777,9 +783,8 @@ Driver* VulkanPlatform::createDriver(void* sharedContext,
mImpl->mDevice
= mImpl->mDevice == VK_NULL_HANDLE ? createLogicalDevice(mImpl->mPhysicalDevice,
context.mPhysicalDeviceFeatures, mImpl->mGraphicsQueueFamilyIndex,
mImpl->mProtectedGraphicsQueueFamilyIndex, deviceExts)
: mImpl->mDevice;
context.mPhysicalDeviceFeatures, mImpl->mGraphicsQueueFamilyIndex,
mImpl->mProtectedGraphicsQueueFamilyIndex, deviceExts) : mImpl->mDevice;
assert_invariant(mImpl->mDevice != VK_NULL_HANDLE);
vkGetDeviceQueue(mImpl->mDevice, mImpl->mGraphicsQueueFamilyIndex, mImpl->mGraphicsQueueIndex,
@@ -893,19 +898,22 @@ 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,
mImpl->mPhysicalDevice, mImpl->mDevice, mImpl->mGraphicsQueue, mImpl->mInstance,
surface, fallbackExtent, flags);
mImpl->mSurfaceSwapChains.insert(swapchain);
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;
}
else {
}
}
return swapchain;
}
@@ -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),
@@ -28,18 +28,99 @@ namespace filament::backend {
namespace {
// std::tuple<VkImage, VkDeviceMemory> testing(VulkanContext const& context,
// VkDevice device, VkExtent2D extent) {
// // Filament expects blit() to work with any texture, so we almost always set these usage flags
// // (see copyFrame() and readPixels()).
// // The ordering here indicates the preference of choosing depth+stencil format.
// VkFormat const formats[] = {
// VK_FORMAT_D32_SFLOAT,
// VK_FORMAT_X8_D24_UNORM_PACK32,
// VK_FORMAT_D32_SFLOAT_S8_UINT,
// VK_FORMAT_D24_UNORM_S8_UINT,
// VK_FORMAT_D16_UNORM,
// VK_FORMAT_D16_UNORM_S8_UINT,
// VK_FORMAT_R8G8B8A8_SSCALED,
// VK_FORMAT_R8G8B8A8_UINT,
// VK_FORMAT_R8G8B8A8_SINT,
// VK_FORMAT_R8G8B8A8_SRGB,
// VK_FORMAT_B8G8R8A8_UNORM,
// VK_FORMAT_B8G8R8A8_SNORM,
// };
// uint32_t memoryTypeIndex = VK_MAX_MEMORY_TYPES;
// for (auto tiling: {VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_TILING_LINEAR}) {
// for (auto format: formats) {
// VkImageCreateInfo imageInfo{
// .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
// .imageType = VK_IMAGE_TYPE_2D,
// .format = format,
// .extent = {extent.width, extent.height, 1},
// .mipLevels = 1,
// .arrayLayers = 1,
// .samples = VK_SAMPLE_COUNT_1_BIT,
// .tiling = tiling,
// .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
// };
// VkImage image;
// VkResult result = vkCreateImage(device, &imageInfo, VKALLOC, &image);
// FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
// << "Unable to create image: " << static_cast<int32_t>(result);
// // Allocate memory for the VkImage and bind it.
// // VkDeviceMemory imageMemory;
// VkMemoryRequirements memReqs;
// vkGetImageMemoryRequirements(device, image, &memReqs);
// utils::slog.e << "tiling=" << tiling << utils::io::endl;
// utils::slog.e << "choosing format=" << format << utils::io::endl;
// utils::slog.e << "required bits=" << memReqs.memoryTypeBits << utils::io::endl;
// memoryTypeIndex = context.selectMemoryType(memReqs.memoryTypeBits,
// VK_MEMORY_PROPERTY_PROTECTED_BIT);
// if (memoryTypeIndex != VK_MAX_MEMORY_TYPES) {
// utils::slog.e << "found!" << utils::io::endl;
// break;
// }
// if (memoryTypeIndex != VK_MAX_MEMORY_TYPES) {
// break;
// }
// }
// }
// // FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES)
// // << "VulkanPlatformSwapChainImpl: unable to find a memory type that meets requirements.";
// // }
// // VkMemoryAllocateInfo allocInfo = {
// // .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
// // .allocationSize = memReqs.size,
// // .memoryTypeIndex = memoryTypeIndex,
// // };
// // result = vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory);
// // FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to allocate image memory.";
// // result = vkBindImageMemory(device, image, imageMemory, 0);
// // FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to bind image.";
// return std::tuple(VK_NULL_HANDLE, VK_NULL_HANDLE);
// // return std::tuple(image, imageMemory);
// }
std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& context,
VkDevice device, VkExtent2D extent, VkFormat format,
bool isProtected) {
VkDevice device, VkExtent2D extent, VkFormat format, bool isProtected) {
bool const isDepth = isVkDepthFormat(format);
// if (isDepth) {
// return testing(context, device, extent);
// }
// Filament expects blit() to work with any texture, so we almost always set these usage flags
// (see copyFrame() and readPixels()).
VkImageUsageFlags const blittable =
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
(isProtected ? 0u
: (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT));
VkImageCreateInfo imageInfo {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.flags = isProtected ? VK_IMAGE_CREATE_PROTECTED_BIT : VkImageCreateFlags(0),
.imageType = VK_IMAGE_TYPE_2D,
.format = format,
.extent = {extent.width, extent.height, 1},
@@ -60,19 +141,19 @@ std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& co
VkMemoryRequirements memReqs;
vkGetImageMemoryRequirements(device, image, &memReqs);
const VkFlags requiredMemoryFlags =
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
(isProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
uint32_t memoryTypeIndex
= context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags);
// uint32_t const memoryTypeIndex = context.selectMemoryType(memReqs.memoryTypeBits,
// (isProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT));
uint32_t const memoryTypeIndex =
context.selectMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
// (isProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT :
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES)
<< "VulkanPlatformSwapChainImpl: unable to find a memory type that meets requirements.";
VkMemoryAllocateInfo allocInfo = {
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = memReqs.size,
.memoryTypeIndex = memoryTypeIndex,
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = memReqs.size,
.memoryTypeIndex = memoryTypeIndex,
};
result = vkAllocateMemory(device, &allocInfo, nullptr, &imageMemory);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS) << "Unable to allocate image memory.";
@@ -82,11 +163,19 @@ std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& co
}
VkFormat selectDepthFormat(VkFormatList const& depthFormats, bool hasStencil) {
for (auto f: depthFormats) {
utils::slog.e <<"formats=" << f << utils::io::endl;
}
/*
auto const formatItr = std::find_if(depthFormats.begin(), depthFormats.end(),
hasStencil ? isVkStencilFormat : isVkDepthFormat);
assert_invariant(
formatItr != depthFormats.end() && "Cannot find suitable swapchain depth format");
return *formatItr;
*/
// return VK_FORMAT_D16_UNORM_S8_UINT;
return VK_FORMAT_D16_UNORM;
// return *formatItr;
}
}// anonymous namespace
@@ -115,7 +204,7 @@ void VulkanPlatformSwapChainImpl::destroy() {
mSwapChainBundle.colors.clear();
}
VkImage VulkanPlatformSwapChainImpl::createImage(VkExtent2D extent, VkFormat format,
VkImage VulkanPlatformSwapChainImpl::createImage(VkExtent2D extent, VkFormat format,
bool isProtected) {
auto [image, memory] = createImageAndMemory(mContext, mDevice, extent, format, isProtected);
mMemory.insert({image, memory});
@@ -145,16 +234,32 @@ VulkanPlatformSurfaceSwapChain::~VulkanPlatformSurfaceSwapChain() {
VkResult VulkanPlatformSurfaceSwapChain::create() {
VkSurfaceFormatKHR surfaceFormat = {};
VkSurfaceCapabilitiesKHR caps;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(mPhysicalDevice, mSurface, &caps);
VkSurfaceProtectedCapabilitiesKHR protectedCaps = {
.sType = VK_STRUCTURE_TYPE_SURFACE_PROTECTED_CAPABILITIES_KHR,
};
VkSurfaceCapabilities2KHR caps = {
.sType = VK_STRUCTURE_TYPE_SURFACE_CAPABILITIES_2_KHR,
.pNext = &protectedCaps,
};
VkPhysicalDeviceSurfaceInfo2KHR info = {
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR,
.surface = mSurface,
};
vkGetPhysicalDeviceSurfaceCapabilities2KHR(mPhysicalDevice, &info, &caps);
FILAMENT_CHECK_PRECONDITION(!mIsProtected || protectedCaps.supportsProtected == VK_TRUE)
<< "Requesting protected swapchain but is not supported by the hardware";
utils::slog.e << "------ protected surface=" << (protectedCaps.supportsProtected == VK_TRUE)
<< utils::io::endl;
// The general advice is to require one more than the minimum swap chain length, since the
// absolute minimum could easily require waiting for a driver or presentation layer to release
// the previous frame's buffer. The only situation in which we'd ask for the minimum length is
// when using a MAILBOX presentation strategy for low-latency situations where tearing is
// acceptable.
uint32_t const maxImageCount = caps.maxImageCount;
uint32_t const minImageCount = caps.minImageCount;
uint32_t const maxImageCount = caps.surfaceCapabilities.maxImageCount;
uint32_t const minImageCount = caps.surfaceCapabilities.minImageCount;
uint32_t desiredImageCount = minImageCount + 1;
// According to section 30.5 of VK 1.1, maxImageCount of zero means "that there is no limit on
@@ -163,7 +268,7 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
if (maxImageCount != 0 && desiredImageCount > maxImageCount) {
FVK_LOGE << "Swap chain does not support " << desiredImageCount << " images."
<< utils::io::endl;
desiredImageCount = caps.minImageCount;
desiredImageCount = caps.surfaceCapabilities.minImageCount;
}
// Find a suitable surface format.
@@ -205,46 +310,50 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
<< "Desired present mode is not supported by this device.";
// Create the low-level swap chain.
if (caps.currentExtent.width == VULKAN_UNDEFINED_EXTENT
|| caps.currentExtent.height == VULKAN_UNDEFINED_EXTENT) {
auto const& currentExtent = caps.surfaceCapabilities.currentExtent;
if (currentExtent.width == VULKAN_UNDEFINED_EXTENT
|| currentExtent.height == VULKAN_UNDEFINED_EXTENT) {
mSwapChainBundle.extent = mFallbackExtent;
} else {
mSwapChainBundle.extent = caps.currentExtent;
mSwapChainBundle.extent = currentExtent;
}
VkCompositeAlphaFlagBitsKHR const compositeAlpha
= (caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
VkCompositeAlphaFlagBitsKHR const compositeAlpha =
(caps.surfaceCapabilities.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
? VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR
: VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
VkImageUsageFlags const transferBits =
(mIsProtected ? VkImageUsageFlagBits{}
: (VK_IMAGE_USAGE_TRANSFER_DST_BIT // Allows use as a blit destination
// (for copyFrame)
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT // Allows use as a blit source (for
// readPixels)
));
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,
.imageColorSpace = surfaceFormat.colorSpace,
.imageExtent = mSwapChainBundle.extent,
.imageArrayLayers = 1,
.imageUsage
= VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
| VK_IMAGE_USAGE_TRANSFER_DST_BIT // Allows use as a blit destination (for copyFrame)
| VK_IMAGE_USAGE_TRANSFER_SRC_BIT,// Allows use as a blit source (for readPixels)
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
.flags = mIsProtected ? VK_SWAPCHAIN_CREATE_PROTECTED_BIT_KHR : VkSwapchainCreateFlagsKHR{},
.surface = mSurface,
.minImageCount = desiredImageCount,
.imageFormat = surfaceFormat.format,
.imageColorSpace = surfaceFormat.colorSpace,
.imageExtent = mSwapChainBundle.extent,
.imageArrayLayers = 1,
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | transferBits,
// TODO: Setting the preTransform to IDENTITY means we are letting the Android
// Compositor handle the rotation. In some situations it might be more efficient to
// handle this ourselves by setting this field to be equal to the currentTransform mask
// in the caps, but this would involve adjusting the MVP, derivatives in GLSL, and
// possibly more.
// https://android-developers.googleblog.com/2020/02/handling-device-orientation-efficiently.html
.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
// TODO: Setting the preTransform to IDENTITY means we are letting the
// Android Compositor handle the rotation. In some situations it might be
// more efficient to handle this ourselves by setting this field to be equal
// to the currentTransform mask in the caps, but this would involve
// adjusting the MVP, derivatives in GLSL, and possibly more.
// https://android-developers.googleblog.com/2020/02/handling-device-orientation-efficiently.html
.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
.compositeAlpha = compositeAlpha,
.presentMode = desiredPresentMode,
.clipped = VK_TRUE,
.compositeAlpha = compositeAlpha,
.presentMode = desiredPresentMode,
.clipped = VK_TRUE,
.oldSwapchain = mSwapchain,
.oldSwapchain = mSwapchain,
};
VkResult result = vkCreateSwapchainKHR(mDevice, &createInfo, VKALLOC, &mSwapchain);
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
@@ -254,17 +363,16 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
mSwapChainBundle.colorFormat = surfaceFormat.format;
mSwapChainBundle.depthFormat =
selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), mHasStencil);
mSwapChainBundle.depth = createImage(mSwapChainBundle.extent,
mSwapChainBundle.depthFormat, mIsProtected);
mSwapChainBundle.isProtected = mIsProtected;
mSwapChainBundle.depth =
createImage(mSwapChainBundle.extent, mSwapChainBundle.depthFormat, mIsProtected);
mSwapChainBundle.protection = 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
<< "identity-transform=" << (caps.surfaceCapabilities.currentTransform == 1) << ", "
<< "depth=" << mSwapChainBundle.depthFormat
<< io::endl;
VkSemaphoreCreateInfo const semaphoreCreateInfo = {
@@ -354,6 +462,10 @@ VulkanPlatformHeadlessSwapChain::VulkanPlatformHeadlessSwapChain(VulkanContext c
VkDevice device, VkQueue queue, VkExtent2D extent, uint64_t flags)
: VulkanPlatformSwapChainImpl(context, device, queue),
mCurrentIndex(0) {
assert_invariant(!(flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) &&
"Protected headless swapchain not supported");
mSwapChainBundle.extent = extent;
mSwapChainBundle.colorFormat = (flags & backend::SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) != 0
? VK_FORMAT_R8G8B8A8_SRGB
-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;
-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();
+7 -86
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);
@@ -1355,32 +1316,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 +1355,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 {
+2 -35
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>
@@ -675,36 +672,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 -19
View File
@@ -347,12 +347,6 @@ FMaterial::~FMaterial() noexcept = default;
void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantValue) noexcept {
DriverApi& driverApi = mEngine.getDriverApi();
FMaterial const* const pDefaultMaterial = mEngine.getDefaultMaterial();
auto hasDefaultDepthVariant = [pDefaultMaterial](Variant k) {
auto const& end = pDefaultMaterial->mDepthVariants.end();
return end != std::find(pDefaultMaterial->mDepthVariants.begin(), end, k);
};
if (mMaterialDomain == MaterialDomain::SURFACE) {
auto& cachedPrograms = mCachedPrograms;
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
@@ -361,9 +355,8 @@ void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantV
if (UTILS_LIKELY(!mIsDefaultMaterial)) {
// The depth variants may be shared with the default material, in which case
// we should not free it now.
bool const isSharedVariant = Variant::isValidDepthVariant(variant) &&
!mHasCustomDepthShader &&
hasDefaultDepthVariant(variant);
bool const isSharedVariant =
Variant::isValidDepthVariant(variant) && !mHasCustomDepthShader;
if (isSharedVariant) {
// we don't own this variant, skip.
continue;
@@ -374,7 +367,8 @@ void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantV
}
}
if (UTILS_UNLIKELY(!mIsDefaultMaterial && !mHasCustomDepthShader)) {
if (UTILS_UNLIKELY(!mIsDefaultMaterial && !mHasCustomDepthShader)) {
FMaterial const* const pDefaultMaterial = mEngine.getDefaultMaterial();
for (Variant const variant: pDefaultMaterial->mDepthVariants) {
pDefaultMaterial->prepareProgram(variant);
if (!cachedPrograms[variant.key]) {
@@ -632,7 +626,6 @@ Program FMaterial::getProgramWithVariants(
}
void FMaterial::createAndCacheProgram(Program&& p, Variant variant) const noexcept {
assert_invariant(!mCachedPrograms[variant.key]);
auto program = mEngine.getDriverApi().createProgram(std::move(p));
mEngine.getDriverApi().setDebugTag(program.getId(), mName);
assert_invariant(program);
@@ -744,19 +737,12 @@ void FMaterial::onQueryCallback(void* userdata, VariantList* pVariants) {
void FMaterial::destroyPrograms(FEngine& engine) {
DriverApi& driverApi = engine.getDriverApi();
auto& cachedPrograms = mCachedPrograms;
auto hasDefaultDepthVariant = [pDefaultMaterial = engine.getDefaultMaterial()](Variant k) {
auto const& end = pDefaultMaterial->mDepthVariants.end();
return end != std::find(pDefaultMaterial->mDepthVariants.begin(), end, k);
};
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
const Variant variant(k);
if (!mIsDefaultMaterial) {
// The depth variants may be shared with the default material, in which case
// we should not free it now.
bool const isSharedVariant = Variant::isValidDepthVariant(variant) &&
!mHasCustomDepthShader && hasDefaultDepthVariant(variant);
bool const isSharedVariant = Variant::isValidDepthVariant(variant) && !mHasCustomDepthShader;
if (isSharedVariant) {
// we don't own this variant, skip.
continue;
+7 -10
View File
@@ -280,13 +280,14 @@ bool FRenderer::beginFrame(FSwapChain* swapChain, uint64_t vsyncSteadyClockTimeN
SYSTRACE_CALL();
#if 0 && defined(__ANDROID__)
char scratch[PROP_VALUE_MAX + 1];
int length = __system_property_get("debug.filament.protected", scratch);
if (swapChain && length > 0) {
#if 1 && defined(__ANDROID__)
// char scratch[PROP_VALUE_MAX + 1];
// int length = __system_property_get("debug.filament.protected", scratch);
// if (swapChain && length > 0) {
if (swapChain) {
uint64_t flags = swapChain->getFlags();
bool value = bool(atoi(scratch));
if (value) {
// bool value = bool(atoi(scratch));
if (true) {
flags |= SwapChain::CONFIG_PROTECTED_CONTENT;
} else {
flags &= ~SwapChain::CONFIG_PROTECTED_CONTENT;
@@ -584,10 +585,6 @@ void FRenderer::render(FView const* view) {
void FRenderer::renderInternal(FView const* view) {
FEngine& engine = mEngine;
FILAMENT_CHECK_PRECONDITION(!view->hasPostProcessPass() ||
engine.hasFeatureLevel(FeatureLevel::FEATURE_LEVEL_1))
<< "post-processing is not supported at FEATURE_LEVEL_0";
// per-renderpass data
RootArenaScope rootArenaScope(engine.getPerRenderPassArena());
+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,
+2 -2
View File
@@ -1,12 +1,12 @@
Pod::Spec.new do |spec|
spec.name = "Filament"
spec.version = "1.56.0"
spec.version = "1.55.0"
spec.license = { :type => "Apache 2.0", :file => "LICENSE" }
spec.homepage = "https://google.github.io/filament"
spec.authors = "Google LLC."
spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL."
spec.platform = :ios, "11.0"
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.56.0/filament-v1.56.0-ios.tgz" }
spec.source = { :http => "https://github.com/google/filament/releases/download/v1.55.0/filament-v1.55.0-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,8 +36,6 @@ 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
@@ -1,59 +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 <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
+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));
+16 -20
View File
@@ -278,23 +278,18 @@ public:
void* pop() noexcept {
Node* const pStorage = mStorage;
HeadPtr currentHead = mHead.load(std::memory_order_relaxed);
HeadPtr currentHead = mHead.load();
while (currentHead.offset >= 0) {
// The value of "pNext" we load here might already contain application data if another
// thread raced ahead of us. But in that case, the computed "newHead" will be discarded
// since compare_exchange_weak() fails. Then this thread will loop with the updated
// since compare_exchange_weak fails. Then this thread will loop with the updated
// value of currentHead, and try again.
// TSAN complains if we don't use a local variable here.
Node const node = pStorage[currentHead.offset];
Node const* const pNext = node.next;
Node* const pNext = pStorage[currentHead.offset].next.load(std::memory_order_relaxed);
const HeadPtr newHead{ pNext ? int32_t(pNext - pStorage) : -1, currentHead.tag + 1 };
// In the rare case that the other thread that raced ahead of us already returned the
// same mHead we just loaded, but it now has a different "next" value, the tag field
// will not match, and compare_exchange_weak() will fail and prevent that particular
// race condition.
// acquire: no read/write can be reordered before this
if (mHead.compare_exchange_weak(currentHead, newHead,
std::memory_order_acquire, std::memory_order_relaxed)) {
// In the rare case that the other thread that raced ahead of us already returned the
// same mHead we just loaded, but it now has a different "next" value, the tag field will not
// match, and compare_exchange_weak will fail and prevent that particular race condition.
if (mHead.compare_exchange_weak(currentHead, newHead)) {
// This assert needs to occur after we have validated that there was no race condition
// Otherwise, next might already contain application data, if another thread
// raced ahead of us after we loaded mHead, but before we loaded mHead->next.
@@ -311,15 +306,13 @@ public:
Node* const storage = mStorage;
assert_invariant(p && p >= storage);
Node* const node = static_cast<Node*>(p);
HeadPtr currentHead = mHead.load(std::memory_order_relaxed);
HeadPtr currentHead = mHead.load();
HeadPtr newHead = { int32_t(node - storage), currentHead.tag + 1 };
do {
newHead.tag = currentHead.tag + 1;
Node* const pNext = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr;
node->next = pNext; // could be a race with pop, corrected by CAS
} while(!mHead.compare_exchange_weak(currentHead, newHead,
std::memory_order_release, std::memory_order_relaxed));
// release: no read/write can be reordered after this
Node* const n = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr;
node->next.store(n, std::memory_order_relaxed);
} while(!mHead.compare_exchange_weak(currentHead, newHead));
}
void* getFirst() noexcept {
@@ -327,7 +320,10 @@ public:
}
struct Node {
// There is a benign data race when a pop() is interrupted by a
// This should be a regular (non-atomic) pointer, but this causes TSAN to complain
// about a data-race that exists but is benin. We always use this atomic<> in
// relaxed mode.
// The data race TSAN complains about is when a pop() is interrupted by a
// pop() + push() just after mHead->next is read -- it appears as though it is written
// without synchronization (by the push), however in that case, the pop's CAS will fail
// and things will auto-correct.
@@ -350,7 +346,7 @@ public:
// |
// CAS, tag++
//
Node* next = nullptr;
std::atomic<Node*> next;
};
private:
+1 -1
View File
@@ -446,7 +446,7 @@ private:
Condition mWaiterCondition;
std::atomic<int32_t> mActiveJobs = { 0 };
utils::Arena<utils::ObjectPoolAllocator<Job>, LockingPolicy::Mutex> mJobPool;
utils::Arena<utils::ThreadSafeObjectPoolAllocator<Job>, LockingPolicy::NoLock> mJobPool;
template <typename T>
using aligned_vector = std::vector<T, utils::STLAlignedAllocator<T>>;
+2 -2
View File
@@ -95,11 +95,11 @@ protected:
size_t length() const noexcept;
private:
void reserve(size_t newCapacity) noexcept;
void reserve(size_t newSize) noexcept;
char* buffer = nullptr; // buffer address
char* curr = nullptr; // current pointer
size_t sizeRemaining = 0; // size remaining
size_t size = 0; // size remaining
size_t capacity = 0; // total capacity of the buffer
};
+13 -14
View File
@@ -237,23 +237,23 @@ ostream::Buffer::~Buffer() noexcept {
void ostream::Buffer::advance(ssize_t n) noexcept {
if (n > 0) {
size_t const written = n < sizeRemaining ? size_t(n) : sizeRemaining;
size_t const written = n < size ? size_t(n) : size;
curr += written;
sizeRemaining -= written;
size -= written;
}
}
void ostream::Buffer::reserve(size_t newCapacity) noexcept {
void ostream::Buffer::reserve(size_t newSize) noexcept {
size_t const offset = curr - buffer;
if (buffer == nullptr) {
buffer = (char*)malloc(newCapacity);
buffer = (char*)malloc(newSize);
} else {
buffer = (char*)realloc(buffer, newCapacity);
buffer = (char*)realloc(buffer, newSize);
}
assert(buffer);
capacity = newCapacity;
capacity = newSize;
curr = buffer + offset;
sizeRemaining = capacity - offset;
size = capacity - offset;
}
void ostream::Buffer::reset() noexcept {
@@ -264,7 +264,7 @@ void ostream::Buffer::reset() noexcept {
capacity = 1024;
}
curr = buffer;
sizeRemaining = capacity;
size = capacity;
}
size_t ostream::Buffer::length() const noexcept {
@@ -272,14 +272,13 @@ size_t ostream::Buffer::length() const noexcept {
}
std::pair<char*, size_t> ostream::Buffer::grow(size_t s) noexcept {
if (UTILS_UNLIKELY(sizeRemaining < s)) {
size_t const usedSize = curr - buffer;
size_t const neededCapacity = usedSize + s;
size_t const newCapacity = std::max(size_t(32), (neededCapacity * 3 + 1) / 2); // 32 bytes minimum
if (UTILS_UNLIKELY(size < s)) {
size_t const used = curr - buffer;
size_t const newCapacity = std::max(size_t(32), used + (s * 3 + 1) / 2); // 32 bytes minimum
reserve(newCapacity);
assert(sizeRemaining >= s);
assert(size >= s);
}
return { curr, sizeRemaining };
return { curr, size };
}
} // namespace utils::io
+2 -2
View File
@@ -19,7 +19,7 @@
float sampleDepth(const mediump sampler2DArrayShadow map,
const highp vec4 scissorNormalized,
const uint layer, highp vec2 uv, highp float depth) {
const uint layer, highp vec2 uv, float depth) {
// clamp needed for directional lights and/or large kernels
uv = clamp(uv, scissorNormalized.xy, scissorNormalized.zw);
@@ -51,7 +51,7 @@ float ShadowSample_PCF_Low(const mediump sampler2DArrayShadow map,
highp vec2 texelSize = vec2(1.0) / size;
// Castaño, 2013, "Shadow Mapping Summary Part 1"
highp float depth = position.z;
float depth = position.z;
// clamp position to avoid overflows below, which cause some GPUs to abort
position.xy = clamp(position.xy, vec2(-1.0), vec2(2.0));
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "filament",
"version": "1.56.0",
"version": "1.55.0",
"description": "Real-time physically based rendering engine",
"main": "filament.js",
"module": "filament.js",