Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c7a82f7b3 |
@@ -31,7 +31,7 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.google.android.filament:filament-android:1.56.1'
|
||||
implementation 'com.google.android.filament:filament-android:1.55.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.1'
|
||||
pod 'Filament', '~> 1.55.0'
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -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,5 +1,5 @@
|
||||
GROUP=com.google.android.filament
|
||||
VERSION_NAME=1.56.1
|
||||
VERSION_NAME=1.55.0
|
||||
|
||||
POM_DESCRIPTION=Real-time physically based rendering engine for Android.
|
||||
|
||||
|
||||
+2
-10
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -227,7 +227,6 @@ enum class DescriptorType : uint8_t {
|
||||
SHADER_STORAGE_BUFFER,
|
||||
SAMPLER,
|
||||
INPUT_ATTACHMENT,
|
||||
SAMPLER_EXTERNAL
|
||||
};
|
||||
|
||||
enum class DescriptorFlags : uint8_t {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -623,8 +623,6 @@ const char* toString(DescriptorType type) {
|
||||
return "SAMPLER";
|
||||
case DescriptorType::INPUT_ATTACHMENT:
|
||||
return "INPUT_ATTACHMENT";
|
||||
case DescriptorType::SAMPLER_EXTERNAL:
|
||||
return "SAMPLER_EXTERNAL";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -821,24 +819,17 @@ void MetalDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
|
||||
}
|
||||
|
||||
void MetalDriver::destroySwapChain(Handle<HwSwapChain> sch) {
|
||||
if (UTILS_UNLIKELY(!sch)) {
|
||||
return;
|
||||
}
|
||||
auto* swapChain = handle_cast<MetalSwapChain>(sch);
|
||||
if (mContext->currentDrawSwapChain == swapChain) {
|
||||
mContext->currentDrawSwapChain = nullptr;
|
||||
}
|
||||
if (mContext->currentReadSwapChain == swapChain) {
|
||||
mContext->currentReadSwapChain = nullptr;
|
||||
}
|
||||
// If the SwapChain is a pixel buffer, we need to wait for the current command buffer to
|
||||
// complete before destroying it. This is because pixel buffer SwapChains hold a
|
||||
// MetalExternalImage that could still being rendered into.
|
||||
if (UTILS_UNLIKELY(swapChain->isPixelBuffer())) {
|
||||
executeAfterCurrentCommandBufferCompletes(
|
||||
[this, sch]() mutable { destruct_handle<MetalSwapChain>(sch); });
|
||||
} else {
|
||||
destruct_handle<MetalSwapChain>(sch);
|
||||
if (sch) {
|
||||
auto* swapChain = handle_cast<MetalSwapChain>(sch);
|
||||
// If the SwapChain is a pixel buffer, we need to wait for the current command buffer to
|
||||
// complete before destroying it. This is because pixel buffer SwapChains hold a
|
||||
// MetalExternalImage that could still being rendered into.
|
||||
if (UTILS_UNLIKELY(swapChain->isPixelBuffer())) {
|
||||
executeAfterCurrentCommandBufferCompletes(
|
||||
[this, sch]() mutable { destruct_handle<MetalSwapChain>(sch); });
|
||||
} else {
|
||||
destruct_handle<MetalSwapChain>(sch);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -861,20 +852,10 @@ void MetalDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> dslh)
|
||||
|
||||
void MetalDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
|
||||
DEBUG_LOG("destroyDescriptorSet(dsh = %d)\n", dsh.getId());
|
||||
if (!dsh) {
|
||||
return;
|
||||
if (dsh) {
|
||||
executeAfterCurrentCommandBufferCompletes(
|
||||
[this, dsh]() mutable { destruct_handle<MetalDescriptorSet>(dsh); });
|
||||
}
|
||||
|
||||
// Unbind this descriptor set.
|
||||
auto* descriptorSet = handle_cast<MetalDescriptorSet>(dsh);
|
||||
for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) {
|
||||
if (UTILS_UNLIKELY(mContext->currentDescriptorSets[i] == descriptorSet)) {
|
||||
mContext->currentDescriptorSets[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
executeAfterCurrentCommandBufferCompletes(
|
||||
[this, dsh]() mutable { destruct_handle<MetalDescriptorSet>(dsh); });
|
||||
}
|
||||
|
||||
void MetalDriver::terminate() {
|
||||
@@ -1813,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);
|
||||
|
||||
@@ -1290,8 +1290,7 @@ id<MTLArgumentEncoder> MetalDescriptorSetLayout::getArgumentEncoderSlow(id<MTLDe
|
||||
[arguments addObject:bufferArgument];
|
||||
break;
|
||||
}
|
||||
case DescriptorType::SAMPLER:
|
||||
case DescriptorType::SAMPLER_EXTERNAL: {
|
||||
case DescriptorType::SAMPLER: {
|
||||
MTLArgumentDescriptor* textureArgument = [MTLArgumentDescriptor argumentDescriptor];
|
||||
textureArgument.index = binding.binding * 2;
|
||||
textureArgument.dataType = MTLDataTypeTexture;
|
||||
@@ -1406,8 +1405,7 @@ id<MTLBuffer> MetalDescriptorSet::finalizeAndGetBuffer(MetalDriver* driver, Shad
|
||||
atIndex:binding.binding * 2];
|
||||
break;
|
||||
}
|
||||
case DescriptorType::SAMPLER:
|
||||
case DescriptorType::SAMPLER_EXTERNAL: {
|
||||
case DescriptorType::SAMPLER: {
|
||||
auto found = textures.find(binding.binding);
|
||||
if (found == textures.end()) {
|
||||
[encoder setTexture:driver->mContext->emptyTexture atIndex:binding.binding * 2];
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
#include "OpenGLDriverFactory.h"
|
||||
#include "OpenGLProgram.h"
|
||||
#include "OpenGLTimerQuery.h"
|
||||
#include "SystraceProfile.h"
|
||||
#include "gl_headers.h"
|
||||
|
||||
#include <backend/platforms/OpenGLPlatform.h>
|
||||
@@ -92,35 +91,24 @@
|
||||
#define DEBUG_GROUP_MARKER_NONE 0x00 // no debug marker
|
||||
#define DEBUG_GROUP_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
|
||||
#define DEBUG_GROUP_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
|
||||
#define DEBUG_GROUP_MARKER_ALL 0xFF // all markers
|
||||
#define DEBUG_GROUP_MARKER_ALL 0x03 // all markers
|
||||
|
||||
#define DEBUG_MARKER_NONE 0x00 // no debug marker
|
||||
#define DEBUG_MARKER_OPENGL 0x01 // markers in the gl command queue (req. driver support)
|
||||
#define DEBUG_MARKER_BACKEND 0x02 // markers on the backend side (systrace)
|
||||
#define DEBUG_MARKER_PROFILE 0x04 // profiling on the backend side (systrace)
|
||||
#define DEBUG_MARKER_ALL (0xFF & ~DEBUG_MARKER_PROFILE) // all markers
|
||||
#define DEBUG_MARKER_ALL 0x03 // all markers
|
||||
|
||||
// set to the desired debug marker level (for user markers [default: All])
|
||||
#define DEBUG_GROUP_MARKER_LEVEL DEBUG_GROUP_MARKER_ALL
|
||||
|
||||
// set to the desired debug level (for internal debugging [Default: None])
|
||||
#define DEBUG_MARKER_LEVEL DEBUG_MARKER_NONE
|
||||
#define DEBUG_MARKER_LEVEL DEBUG_MARKER_NONE
|
||||
|
||||
#if DEBUG_MARKER_LEVEL == DEBUG_MARKER_PROFILE
|
||||
# define DEBUG_MARKER()
|
||||
# define PROFILE_MARKER(marker) PROFILE_SCOPE(marker);
|
||||
# if DEBUG_GROUP_MARKER_LEVEL != DEBUG_GROUP_MARKER_NONE
|
||||
# error PROFILING is exclusive; group markers must be disabled.
|
||||
# endif
|
||||
#elif DEBUG_MARKER_LEVEL > DEBUG_MARKER_NONE
|
||||
# define DEBUG_MARKER() DebugMarker _debug_marker(*this, __func__);
|
||||
# define PROFILE_MARKER(marker) DEBUG_MARKER()
|
||||
# if DEBUG_MARKER_LEVEL & DEBUG_MARKER_PROFILE
|
||||
# error PROFILING is exclusive; all other debug features must be disabled.
|
||||
# endif
|
||||
#if DEBUG_MARKER_LEVEL > DEBUG_MARKER_NONE
|
||||
# define DEBUG_MARKER() \
|
||||
DebugMarker _debug_marker(*this, __func__);
|
||||
#else
|
||||
# define DEBUG_MARKER()
|
||||
# define PROFILE_MARKER(marker)
|
||||
#endif
|
||||
|
||||
using namespace filament::math;
|
||||
@@ -394,21 +382,24 @@ void OpenGLDriver::bindTexture(GLuint unit, GLTexture const* t) noexcept {
|
||||
}
|
||||
|
||||
bool OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
|
||||
bool success = true;
|
||||
if (mBoundProgram != p) {
|
||||
// compile/link the program if needed and call glUseProgram
|
||||
success = p->use(this, mContext);
|
||||
assert_invariant(success == p->isValid());
|
||||
if (success) {
|
||||
// TODO: we could even improve this if the program could tell us which of the descriptors
|
||||
// bindings actually changed. In practice, it is likely that set 0 or 1 might not
|
||||
// change often.
|
||||
decltype(mInvalidDescriptorSetBindings) changed;
|
||||
changed.setValue((1 << MAX_DESCRIPTOR_SET_COUNT) - 1);
|
||||
mInvalidDescriptorSetBindings |= changed;
|
||||
if (UTILS_UNLIKELY(mBoundProgram == p)) {
|
||||
// program didn't change, don't do anything.
|
||||
return true;
|
||||
}
|
||||
|
||||
mBoundProgram = p;
|
||||
}
|
||||
// compile/link the program if needed and call glUseProgram
|
||||
bool const success = p->use(this, mContext);
|
||||
assert_invariant(success == p->isValid());
|
||||
|
||||
if (success) {
|
||||
// TODO: we could even improve this if the program could tell us which of the descriptors
|
||||
// bindings actually changed. In practice, it is likely that set 0 or 1 might not
|
||||
// change often.
|
||||
decltype(mInvalidDescriptorSetBindings) changed;
|
||||
changed.setValue((1 << MAX_DESCRIPTOR_SET_COUNT) - 1);
|
||||
mInvalidDescriptorSetBindings |= changed;
|
||||
|
||||
mBoundProgram = p;
|
||||
}
|
||||
|
||||
if (UTILS_UNLIKELY(mContext.isES2() && success)) {
|
||||
@@ -3457,7 +3448,7 @@ void OpenGLDriver::beginFrame(
|
||||
UTILS_UNUSED int64_t monotonic_clock_ns,
|
||||
UTILS_UNUSED int64_t refreshIntervalNs,
|
||||
UTILS_UNUSED uint32_t frameId) {
|
||||
PROFILE_MARKER(PROFILE_NAME_BEGINFRAME)
|
||||
DEBUG_MARKER()
|
||||
auto& gl = mContext;
|
||||
insertEventMarker("beginFrame");
|
||||
mPlatform.beginFrame(monotonic_clock_ns, refreshIntervalNs, frameId);
|
||||
@@ -3492,7 +3483,7 @@ void OpenGLDriver::setPresentationTime(int64_t monotonic_clock_ns) {
|
||||
}
|
||||
|
||||
void OpenGLDriver::endFrame(UTILS_UNUSED uint32_t frameId) {
|
||||
PROFILE_MARKER(PROFILE_NAME_ENDFRAME)
|
||||
DEBUG_MARKER()
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
// WebGL builds are single-threaded so users might manipulate various GL state after we're
|
||||
// done with the frame. We do NOT officially support using Filament in this way, but we can
|
||||
@@ -3884,14 +3875,6 @@ void OpenGLDriver::bindDescriptorSet(
|
||||
backend::DescriptorSetHandle dsh,
|
||||
backend::descriptor_set_t set,
|
||||
backend::DescriptorSetOffsetArray&& offsets) {
|
||||
|
||||
if (UTILS_UNLIKELY(!dsh)) {
|
||||
mBoundDescriptorSets[set].dsh = dsh;
|
||||
mInvalidDescriptorSetBindings.set(set, true);
|
||||
mInvalidDescriptorSetBindingOffsets.set(set, true);
|
||||
return;
|
||||
}
|
||||
|
||||
// handle_cast<> here also serves to validate the handle (it actually cannot return nullptr)
|
||||
GLDescriptorSet const* const ds = handle_cast<GLDescriptorSet*>(dsh);
|
||||
if (ds) {
|
||||
|
||||
@@ -174,8 +174,7 @@ void OpenGLProgram::initializeProgramState(OpenGLContext& context, GLuint progra
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DescriptorType::SAMPLER:
|
||||
case DescriptorType::SAMPLER_EXTERNAL: {
|
||||
case DescriptorType::SAMPLER: {
|
||||
if (!entry.name.empty()) {
|
||||
GLint const loc = glGetUniformLocation(program, entry.name.c_str());
|
||||
if (loc >= 0) {
|
||||
@@ -255,9 +254,7 @@ void OpenGLProgram::updateUniforms(
|
||||
for (size_t i = 0, c = records.uniforms.size(); i < c; i++) {
|
||||
Program::Uniform const& u = records.uniforms[i];
|
||||
GLint const loc = records.locations[i];
|
||||
// mRec709Location is special, it is handled by setRec709ColorSpace() and the corresponding
|
||||
// entry in `buffer` is typically not initialized, so we skip it.
|
||||
if (loc < 0 || loc == mRec709Location) {
|
||||
if (loc < 0) {
|
||||
continue;
|
||||
}
|
||||
// u.offset is in 'uint32_t' units
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "VulkanAsyncHandles.h"
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
VulkanTimerQuery::VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices)
|
||||
: VulkanThreadSafeResource(VulkanResourceType::TIMER_QUERY),
|
||||
mStartingQueryIndex(std::get<0>(indices)),
|
||||
mStoppingQueryIndex(std::get<1>(indices)) {}
|
||||
|
||||
void VulkanTimerQuery::setFence(std::shared_ptr<VulkanCmdFence> fence) noexcept {
|
||||
std::unique_lock<utils::Mutex> lock(mFenceMutex);
|
||||
mFence = fence;
|
||||
}
|
||||
|
||||
bool VulkanTimerQuery::isCompleted() noexcept {
|
||||
std::unique_lock<utils::Mutex> lock(mFenceMutex);
|
||||
// QueryValue is a synchronous call and might occur before beginTimerQuery has written anything
|
||||
// into the command buffer, which is an error according to the validation layer that ships in
|
||||
// the Android NDK. Even when AVAILABILITY_BIT is set, validation seems to require that the
|
||||
// timestamp has at least been written into a processed command buffer.
|
||||
|
||||
// This fence indicates that the corresponding buffer has been completed.
|
||||
return mFence && mFence->getStatus() == VK_SUCCESS;
|
||||
}
|
||||
|
||||
VulkanTimerQuery::~VulkanTimerQuery() = default;
|
||||
|
||||
} // namespace filament::backend
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKANASYNCHANDLES_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKANASYNCHANDLES_H
|
||||
|
||||
#include <bluevk/BlueVK.h>
|
||||
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include "VulkanResources.h"
|
||||
|
||||
#include <utils/Mutex.h>
|
||||
#include <utils/Condition.h>
|
||||
|
||||
namespace filament::backend {
|
||||
|
||||
// Wrapper to enable use of shared_ptr for implementing shared ownership of low-level Vulkan fences.
|
||||
struct VulkanCmdFence {
|
||||
VulkanCmdFence(VkResult initialStatus) {
|
||||
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence
|
||||
// gets submitted, its status changes to VK_NOT_READY. Finally, when the GPU actually
|
||||
// finishes executing the command buffer, the status changes to VK_SUCCESS.
|
||||
status.store(initialStatus);
|
||||
}
|
||||
|
||||
~VulkanCmdFence() = default;
|
||||
|
||||
void setStatus(VkResult value) { status.store(value); }
|
||||
|
||||
VkResult getStatus() { return status.load(std::memory_order_acquire); }
|
||||
|
||||
private:
|
||||
std::atomic<VkResult> status;
|
||||
};
|
||||
|
||||
struct VulkanFence : public HwFence, VulkanResource {
|
||||
VulkanFence() : VulkanResource(VulkanResourceType::FENCE) {}
|
||||
|
||||
explicit VulkanFence(std::shared_ptr<VulkanCmdFence> fence)
|
||||
: VulkanResource(VulkanResourceType::FENCE),
|
||||
fence(fence) {}
|
||||
|
||||
std::shared_ptr<VulkanCmdFence> fence;
|
||||
};
|
||||
|
||||
struct VulkanTimerQuery : public HwTimerQuery, VulkanThreadSafeResource {
|
||||
explicit VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices);
|
||||
~VulkanTimerQuery();
|
||||
|
||||
void setFence(std::shared_ptr<VulkanCmdFence> fence) noexcept;
|
||||
|
||||
bool isCompleted() noexcept;
|
||||
|
||||
uint32_t getStartingQueryIndex() const {
|
||||
return mStartingQueryIndex;
|
||||
}
|
||||
|
||||
uint32_t getStoppingQueryIndex() const {
|
||||
return mStoppingQueryIndex;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t mStartingQueryIndex;
|
||||
uint32_t mStoppingQueryIndex;
|
||||
|
||||
std::shared_ptr<VulkanCmdFence> mFence;
|
||||
utils::Mutex mFenceMutex;
|
||||
};
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKANHASYNCANDLES_H
|
||||
@@ -151,7 +151,8 @@ void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) {
|
||||
}
|
||||
#endif
|
||||
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VulkanCommandBuffer& commands = dst.texture->isProtected() ?
|
||||
mCommands->getProtected() : mCommands->get();
|
||||
commands.acquire(src.texture);
|
||||
commands.acquire(dst.texture);
|
||||
resolveFast(&commands, aspect, src, dst);
|
||||
@@ -176,7 +177,8 @@ void VulkanBlitter::blit(VkFilter filter,
|
||||
#endif
|
||||
// src and dst should have the same aspect here
|
||||
VkImageAspectFlags const aspect = src.texture->getImageAspect();
|
||||
VulkanCommandBuffer& commands = mCommands->get();
|
||||
VulkanCommandBuffer& commands = dst.texture->isProtected() ?
|
||||
mCommands->getProtected() : mCommands->get();
|
||||
commands.acquire(src.texture);
|
||||
commands.acquire(dst.texture);
|
||||
blitFast(&commands, aspect, filter, src, dst, srcRectPair, dstRectPair);
|
||||
|
||||
@@ -125,6 +125,7 @@ VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext* context, VulkanResourceA
|
||||
|
||||
VkFenceCreateInfo fenceCreateInfo{.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO};
|
||||
vkCreateFence(device, &fenceCreateInfo, VKALLOC, &mFence);
|
||||
mIsProtected = isProtected;
|
||||
}
|
||||
|
||||
VulkanCommandBuffer::~VulkanCommandBuffer() {
|
||||
@@ -192,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,
|
||||
@@ -207,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,
|
||||
@@ -244,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) {
|
||||
@@ -342,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);
|
||||
@@ -429,7 +434,7 @@ bool VulkanCommands::flush() {
|
||||
mInjectedDependency = VK_NULL_HANDLE;
|
||||
mLastSubmit = dependency;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
#include "DriverBase.h"
|
||||
|
||||
#include "VulkanAsyncHandles.h"
|
||||
#include "VulkanConstants.h"
|
||||
#include "VulkanResources.h"
|
||||
#include "VulkanUtility.h"
|
||||
@@ -61,6 +60,25 @@ private:
|
||||
|
||||
#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.
|
||||
@@ -110,9 +128,13 @@ struct VulkanCommandBuffer {
|
||||
}
|
||||
|
||||
VkCommandBuffer buffer() const {
|
||||
FVK_LOGE <<"-- getting " << mBuffer << " -- ";
|
||||
return mBuffer;
|
||||
}
|
||||
|
||||
// debug only
|
||||
bool mIsProtected;
|
||||
|
||||
private:
|
||||
VulkanContext* mContext;
|
||||
uint8_t mMarkerCount;
|
||||
@@ -153,8 +175,6 @@ struct CommandBufferPool {
|
||||
|
||||
private:
|
||||
static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS;
|
||||
// int8 only goes up to 127, therefore capacity must be less than that.
|
||||
static_assert(CAPACITY < 128);
|
||||
using BufferList = utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>>;
|
||||
VkDevice mDevice;
|
||||
VkCommandPool mPool;
|
||||
@@ -237,6 +257,8 @@ 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
|
||||
@@ -244,6 +266,9 @@ private:
|
||||
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;
|
||||
|
||||
|
||||
@@ -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,7 +89,7 @@
|
||||
#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
|
||||
@@ -116,10 +112,6 @@ static_assert(FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS));
|
||||
static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
|
||||
#endif
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_PROFILING) && FVK_DEBUG_FLAGS != FVK_DEBUG_PROFILING
|
||||
#error PROFILING is exclusive; all other debug features must be disabled.
|
||||
#endif
|
||||
|
||||
// end dependcy checks
|
||||
|
||||
// Shorthand for combination of enabled debug flags
|
||||
@@ -131,30 +123,17 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
|
||||
|
||||
// end shorthands
|
||||
|
||||
#if FVK_DEBUG_FLAGS == FVK_DEBUG_PROFILING
|
||||
#if FVK_ENABLED(FVK_DEBUG_SYSTRACE)
|
||||
|
||||
#define FVK_SYSTRACE_CONTEXT()
|
||||
#define FVK_SYSTRACE_START(marker)
|
||||
#define FVK_SYSTRACE_END()
|
||||
#define FVK_SYSTRACE_SCOPE()
|
||||
#define FVK_PROFILE_MARKER(marker) PROFILE_SCOPE(marker)
|
||||
|
||||
#elif FVK_ENABLED(FVK_DEBUG_SYSTRACE)
|
||||
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#define FVK_SYSTRACE_CONTEXT() SYSTRACE_CONTEXT()
|
||||
#define FVK_SYSTRACE_START(marker) SYSTRACE_NAME_BEGIN(marker)
|
||||
#define FVK_SYSTRACE_END() SYSTRACE_NAME_END()
|
||||
#define FVK_SYSTRACE_SCOPE() SYSTRACE_NAME(__func__)
|
||||
#define FVK_PROFILE_MARKER(marker) FVK_SYSTRACE_SCOPE()
|
||||
#include <utils/Systrace.h>
|
||||
|
||||
#define FVK_SYSTRACE_CONTEXT() SYSTRACE_CONTEXT()
|
||||
#define FVK_SYSTRACE_START(marker) SYSTRACE_NAME_BEGIN(marker)
|
||||
#define FVK_SYSTRACE_END() SYSTRACE_NAME_END()
|
||||
#else
|
||||
#define FVK_SYSTRACE_CONTEXT()
|
||||
#define FVK_SYSTRACE_START(marker)
|
||||
#define FVK_SYSTRACE_END()
|
||||
#define FVK_SYSTRACE_SCOPE()
|
||||
#define FVK_PROFILE_MARKER(marker)
|
||||
#define FVK_SYSTRACE_CONTEXT()
|
||||
#define FVK_SYSTRACE_START(marker)
|
||||
#define FVK_SYSTRACE_END()
|
||||
#endif
|
||||
|
||||
#ifndef FVK_HANDLE_ARENA_SIZE_IN_MB
|
||||
|
||||
@@ -86,7 +86,7 @@ VulkanTimestamps::VulkanTimestamps(VkDevice device) : mDevice(device) {
|
||||
VkQueryPoolCreateInfo tqpCreateInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
|
||||
.queryType = VK_QUERY_TYPE_TIMESTAMP,
|
||||
};
|
||||
};
|
||||
std::unique_lock<utils::Mutex> lock(mMutex);
|
||||
tqpCreateInfo.queryCount = mUsed.size() * 2;
|
||||
VkResult result = vkCreateQueryPool(mDevice, &tqpCreateInfo, VKALLOC, &mPool);
|
||||
@@ -101,7 +101,7 @@ std::tuple<uint32_t, uint32_t> VulkanTimestamps::getNextQuery() {
|
||||
for (size_t timerIndex = 0; timerIndex < maxTimers; ++timerIndex) {
|
||||
if (!mUsed.test(timerIndex)) {
|
||||
mUsed.set(timerIndex);
|
||||
return std::make_tuple(timerIndex * 2, timerIndex * 2 + 1);
|
||||
return std::make_tuple(timerIndex * 2, timerIndex * 2 + 1);
|
||||
}
|
||||
}
|
||||
FVK_LOGE << "More than " << maxTimers << " timers are not supported." << utils::io::endl;
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
VK_DEFINE_HANDLE(VmaAllocator)
|
||||
VK_DEFINE_HANDLE(VmaPool)
|
||||
|
||||
@@ -82,27 +85,53 @@ private:
|
||||
};
|
||||
|
||||
struct VulkanRenderPass {
|
||||
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;
|
||||
}
|
||||
@@ -158,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;
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
|
||||
#include "CommandStreamDispatcher.h"
|
||||
#include "DataReshaper.h"
|
||||
#include "SystraceProfile.h"
|
||||
#include "VulkanAsyncHandles.h"
|
||||
#include "VulkanBuffer.h"
|
||||
#include "VulkanCommands.h"
|
||||
#include "VulkanDriverFactory.h"
|
||||
@@ -106,7 +104,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugReportCallback(VkDebugReportFlagsEXT flags,
|
||||
FVK_LOGE << utils::io::endl;
|
||||
return VK_FALSE;
|
||||
}
|
||||
#endif // FVK_ENABLED(FVK_DEBUG_VALIDATION)
|
||||
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION)
|
||||
|
||||
#if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
|
||||
@@ -127,7 +125,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL debugUtilsCallback(VkDebugUtilsMessageSeverityFla
|
||||
FVK_LOGE << utils::io::endl;
|
||||
return VK_FALSE;
|
||||
}
|
||||
#endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
#endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
|
||||
|
||||
}// anonymous namespace
|
||||
@@ -157,7 +155,7 @@ DebugUtils::DebugUtils(VkInstance instance, VkDevice device, VulkanContext const
|
||||
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS)
|
||||
<< "Unable to create Vulkan debug messenger.";
|
||||
}
|
||||
#endif // FVK_ENABLED(FVK_DEBUG_VALIDATION)
|
||||
#endif // FVK_EANBLED(FVK_DEBUG_VALIDATION)
|
||||
}
|
||||
|
||||
DebugUtils* DebugUtils::get() {
|
||||
@@ -185,7 +183,7 @@ void DebugUtils::setName(VkObjectType type, uint64_t handle, char const* name) {
|
||||
};
|
||||
vkSetDebugUtilsObjectNameEXT(impl->mDevice, &info);
|
||||
}
|
||||
#endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
#endif // FVK_EANBLED(FVK_DEBUG_DEBUG_UTILS)
|
||||
|
||||
Dispatcher VulkanDriver::getDispatcher() const noexcept {
|
||||
return ConcreteDispatcher<VulkanDriver>::make();
|
||||
@@ -200,9 +198,9 @@ VulkanDriver::VulkanDriver(VulkanPlatform* platform, VulkanContext const& contex
|
||||
mResourceAllocator(driverConfig.handleArenaSize, driverConfig.disableHandleUseAfterFreeCheck),
|
||||
mResourceManager(&mResourceAllocator),
|
||||
mThreadSafeResourceManager(&mResourceAllocator),
|
||||
mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(),
|
||||
mPlatform->getGraphicsQueueFamilyIndex(),
|
||||
nullptr, 0, &mContext, &mResourceAllocator),
|
||||
mCommands(mPlatform->getDevice(), mPlatform->getGraphicsQueue(), mPlatform->getGraphicsQueueFamilyIndex(),
|
||||
mPlatform->getProtectedGraphicsQueue(), mPlatform->getProtectedGraphicsQueueFamilyIndex(),
|
||||
&mContext, &mResourceAllocator),
|
||||
mPipelineLayoutCache(mPlatform->getDevice(), &mResourceAllocator),
|
||||
mPipelineCache(mPlatform->getDevice(), mAllocator),
|
||||
mStagePool(mAllocator, &mCommands),
|
||||
@@ -347,7 +345,8 @@ void VulkanDriver::tick(int) {
|
||||
// rather than the wall clock, because we must wait 3 frames after a DriverAPI-level resource has
|
||||
// been destroyed for safe destruction, due to outstanding command buffers and triple buffering.
|
||||
void VulkanDriver::collectGarbage() {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("gc");
|
||||
// Command buffers need to be submitted and completed before other resources can be gc'd. And
|
||||
// its gc() function carrys out the *wait*.
|
||||
mCommands.gc();
|
||||
@@ -358,11 +357,16 @@ void VulkanDriver::collectGarbage() {
|
||||
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
|
||||
mResourceAllocator.print();
|
||||
#endif
|
||||
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
void VulkanDriver::beginFrame(int64_t monotonic_clock_ns,
|
||||
int64_t refreshIntervalNs, uint32_t frameId) {
|
||||
FVK_PROFILE_MARKER(PROFILE_NAME_BEGINFRAME);
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("beginFrame");
|
||||
// Do nothing.
|
||||
FVK_SYSTRACE_END();
|
||||
FVK_LOGE <<"-----begin frame ---" << utils::io::endl;
|
||||
}
|
||||
|
||||
void VulkanDriver::setFrameScheduledCallback(Handle<HwSwapChain> sch, CallbackHandler* handler,
|
||||
@@ -377,9 +381,12 @@ void VulkanDriver::setPresentationTime(int64_t monotonic_clock_ns) {
|
||||
}
|
||||
|
||||
void VulkanDriver::endFrame(uint32_t frameId) {
|
||||
FVK_PROFILE_MARKER(PROFILE_NAME_ENDFRAME);
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("endframe");
|
||||
mCommands.flush();
|
||||
collectGarbage();
|
||||
FVK_SYSTRACE_END();
|
||||
FVK_LOGE <<"-----end frame ---" << utils::io::endl;
|
||||
}
|
||||
|
||||
void VulkanDriver::updateDescriptorSetBuffer(
|
||||
@@ -412,17 +419,21 @@ void VulkanDriver::updateDescriptorSetTexture(
|
||||
}
|
||||
|
||||
void VulkanDriver::flush(int) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("flush");
|
||||
mCommands.flush();
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::finish(int dummy) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("finish");
|
||||
|
||||
mCommands.flush();
|
||||
mCommands.wait();
|
||||
|
||||
mReadPixels.runUntilComplete();
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::createRenderPrimitiveR(Handle<HwRenderPrimitive> rph,
|
||||
@@ -518,19 +529,6 @@ void VulkanDriver::createTextureR(Handle<HwTexture> th, SamplerType target, uint
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
//void VulkanDriver::createTextureSwizzledR(Handle<HwTexture> th, SamplerType target, uint8_t levels,
|
||||
// TextureFormat format, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
|
||||
// TextureUsage usage,
|
||||
// TextureSwizzle r, TextureSwizzle g, TextureSwizzle b, TextureSwizzle a) {
|
||||
// TextureSwizzle swizzleArray[] = {r, g, b, a};
|
||||
// const VkComponentMapping swizzleMap = getSwizzleMap(swizzleArray);
|
||||
// auto vktexture = mResourceAllocator.construct<VulkanTexture>(th, mPlatform->getDevice(),
|
||||
// mPlatform->getPhysicalDevice(), mContext, mAllocator, &mCommands, &mResourceAllocator,
|
||||
// target, levels, format, samples, w, h, depth, usage, mStagePool,
|
||||
// false /*heap allocated */, swizzleMap);
|
||||
// mResourceManager.acquire(vktexture);
|
||||
//}
|
||||
|
||||
void VulkanDriver::createTextureViewR(Handle<HwTexture> th, Handle<HwTexture> srch,
|
||||
uint8_t baseLevel, uint8_t levelCount) {
|
||||
VulkanTexture const* src = mResourceAllocator.handle_cast<VulkanTexture const*>(srch);
|
||||
@@ -682,7 +680,12 @@ void VulkanDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
|
||||
}
|
||||
|
||||
void VulkanDriver::createFenceR(Handle<HwFence> fh, int) {
|
||||
VulkanCommandBuffer* cmdbuf = &mCommands.get();
|
||||
VulkanCommandBuffer* cmdbuf = nullptr;
|
||||
if (mCurrentRenderPass.cmdBuffer) {
|
||||
cmdbuf = mCurrentRenderPass.cmdBuffer;
|
||||
} else {
|
||||
cmdbuf = &mCommands.get();
|
||||
}
|
||||
mResourceAllocator.construct<VulkanFence>(fh, cmdbuf->getFenceStatus());
|
||||
}
|
||||
|
||||
@@ -695,6 +698,15 @@ void VulkanDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
|
||||
auto swapChain = mResourceAllocator.construct<VulkanSwapChain>(sch, mPlatform, mContext,
|
||||
mAllocator, &mCommands, &mResourceAllocator, mStagePool, nativeWindow, flags);
|
||||
mResourceManager.acquire(swapChain);
|
||||
|
||||
if (flags & backend::SWAP_CHAIN_CONFIG_PROTECTED_CONTENT) {
|
||||
if (!isProtectedContentSupported()) {
|
||||
utils::slog.w << "protected swapchain requested, but Vulkan does not support it"
|
||||
<< utils::io::endl;
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VulkanDriver::createSwapChainHeadlessR(Handle<HwSwapChain> sch, uint32_t width,
|
||||
@@ -1230,7 +1242,10 @@ void VulkanDriver::compilePrograms(CompilerPriorityQueue priority,
|
||||
}
|
||||
|
||||
void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassParams& params) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("beginRenderPass");
|
||||
|
||||
FVK_LOGE <<"begin render pass" << utils::io::endl;
|
||||
|
||||
VulkanRenderTarget* const rt = mResourceAllocator.handle_cast<VulkanRenderTarget*>(rth);
|
||||
VkExtent2D const& extent = rt->getExtent();
|
||||
@@ -1240,6 +1255,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
// first render pass. Note however that its contents are often preserved on subsequent render
|
||||
// passes, due to multiple views.
|
||||
TargetBufferFlags discardStart = params.flags.discardStart;
|
||||
|
||||
if (rt->isSwapChain()) {
|
||||
VulkanSwapChain* sc = mCurrentSwapChain;
|
||||
assert_invariant(sc);
|
||||
@@ -1260,7 +1276,7 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
// If that's the case, we need to change the layout of the texture to DEPTH_SAMPLER, which is a
|
||||
// more general layout. Otherwise, we prefer the DEPTH_ATTACHMENT layout, which is optimal for
|
||||
// the non-sampling case.
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
VulkanCommandBuffer& commands = rt->isProtected() ? mCommands.getProtected() : mCommands.get();
|
||||
VkCommandBuffer const cmdbuffer = commands.buffer();
|
||||
|
||||
// Scissor is reset with each render pass
|
||||
@@ -1369,18 +1385,23 @@ void VulkanDriver::beginRenderPass(Handle<HwRenderTarget> rth, const RenderPassP
|
||||
vkCmdSetViewport(cmdbuffer, 0, 1, &viewport);
|
||||
|
||||
mCurrentRenderPass = {
|
||||
.cmdBuffer = &commands,
|
||||
.renderTarget = rt,
|
||||
.renderPass = renderPassInfo.renderPass,
|
||||
.params = params,
|
||||
.currentSubpass = 0,
|
||||
};
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::endRenderPass(int) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("endRenderPass");
|
||||
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
VkCommandBuffer cmdbuffer = commands.buffer();
|
||||
FVK_LOGE <<"end render pass" << utils::io::endl;
|
||||
|
||||
VulkanCommandBuffer* cmdbuf = mCurrentRenderPass.cmdBuffer;
|
||||
VkCommandBuffer cmdbuffer = cmdbuf->buffer();
|
||||
vkCmdEndRenderPass(cmdbuffer);
|
||||
|
||||
VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
|
||||
@@ -1388,22 +1409,24 @@ void VulkanDriver::endRenderPass(int) {
|
||||
|
||||
// Since we might soon be sampling from the render target that we just wrote to, we need a
|
||||
// pipeline barrier between framebuffer writes and shader reads.
|
||||
rt->emitBarriersEndRenderPass(commands);
|
||||
rt->emitBarriersEndRenderPass(*cmdbuf);
|
||||
|
||||
mRenderPassFboInfo = {};
|
||||
mCurrentRenderPass.renderTarget = nullptr;
|
||||
mCurrentRenderPass.renderPass = VK_NULL_HANDLE;
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::nextSubpass(int) {
|
||||
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.currentSubpass == 0)
|
||||
<< "Only two subpasses are currently supported.";
|
||||
|
||||
VulkanCommandBuffer* cmdbuf = mCurrentRenderPass.cmdBuffer;
|
||||
VulkanRenderTarget* renderTarget = mCurrentRenderPass.renderTarget;
|
||||
assert_invariant(renderTarget);
|
||||
assert_invariant(mCurrentRenderPass.params.subpassMask);
|
||||
|
||||
vkCmdNextSubpass(mCommands.get().buffer(), VK_SUBPASS_CONTENTS_INLINE);
|
||||
vkCmdNextSubpass(cmdbuf->buffer(), VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
mPipelineCache.bindRenderPass(mCurrentRenderPass.renderPass,
|
||||
++mCurrentRenderPass.currentSubpass);
|
||||
@@ -1415,7 +1438,8 @@ void VulkanDriver::nextSubpass(int) {
|
||||
}
|
||||
|
||||
void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain> readSch) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("makeCurrent");
|
||||
|
||||
ASSERT_PRECONDITION_NON_FATAL(drawSch == readSch,
|
||||
"Vulkan driver does not support distinct draw/read swap chains.");
|
||||
@@ -1432,22 +1456,26 @@ void VulkanDriver::makeCurrent(Handle<HwSwapChain> drawSch, Handle<HwSwapChain>
|
||||
if (UTILS_LIKELY(mDefaultRenderTarget)) {
|
||||
mDefaultRenderTarget->bindToSwapChain(*swapChain);
|
||||
}
|
||||
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::commit(Handle<HwSwapChain> sch) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("commit");
|
||||
|
||||
VulkanSwapChain* swapChain = mResourceAllocator.handle_cast<VulkanSwapChain*>(sch);
|
||||
|
||||
// Present the backbuffer after the most recent command buffer submission has finished.
|
||||
swapChain->present();
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::setPushConstant(backend::ShaderStage stage, uint8_t index,
|
||||
backend::PushConstantVariant value) {
|
||||
assert_invariant(mBoundPipeline.program && "Expect a program when writing to push constants");
|
||||
VulkanCommands* commands = &mCommands;
|
||||
mBoundPipeline.program->writePushConstant(commands, mBoundPipeline.pipelineLayout, stage, index,
|
||||
VulkanCommandBuffer* cmdbuf = mCurrentRenderPass.cmdBuffer;
|
||||
mBoundPipeline.program->writePushConstant(cmdbuf, mBoundPipeline.pipelineLayout, stage, index,
|
||||
value);
|
||||
}
|
||||
|
||||
@@ -1458,6 +1486,7 @@ void VulkanDriver::insertEventMarker(char const* string) {
|
||||
}
|
||||
|
||||
void VulkanDriver::pushGroupMarker(char const* string) {
|
||||
FVK_LOGE <<"---> " << string << utils::io::endl;
|
||||
// Turns out all the markers are 0-terminated, so we can just pass it without len.
|
||||
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
|
||||
mCommands.pushGroupMarker(string);
|
||||
@@ -1502,7 +1531,8 @@ void VulkanDriver::readBufferSubData(backend::BufferObjectHandle boh,
|
||||
void VulkanDriver::resolve(
|
||||
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer,
|
||||
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("resolve");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
|
||||
<< "resolve() cannot be invoked inside a render pass.";
|
||||
@@ -1538,13 +1568,16 @@ void VulkanDriver::resolve(
|
||||
mBlitter.resolve(
|
||||
{ .texture = dstTexture, .level = dstLevel, .layer = dstLayer },
|
||||
{ .texture = srcTexture, .level = srcLevel, .layer = srcLayer });
|
||||
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::blit(
|
||||
Handle<HwTexture> dst, uint8_t srcLevel, uint8_t srcLayer, math::uint2 dstOrigin,
|
||||
Handle<HwTexture> src, uint8_t dstLevel, uint8_t dstLayer, math::uint2 srcOrigin,
|
||||
math::uint2 size) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("blit");
|
||||
|
||||
FILAMENT_CHECK_PRECONDITION(mCurrentRenderPass.renderPass == VK_NULL_HANDLE)
|
||||
<< "blit() cannot be invoked inside a render pass.";
|
||||
@@ -1578,13 +1611,16 @@ void VulkanDriver::blit(
|
||||
mBlitter.blit(VK_FILTER_NEAREST,
|
||||
{ .texture = dstTexture, .level = dstLevel, .layer = dstLayer }, dstOffsets,
|
||||
{ .texture = srcTexture, .level = srcLevel, .layer = srcLayer }, srcOffsets);
|
||||
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
Handle<HwRenderTarget> dst, Viewport dstRect,
|
||||
Handle<HwRenderTarget> src, Viewport srcRect,
|
||||
SamplerMagFilter filter) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("blitDEPRECATED");
|
||||
|
||||
// Note: blitDEPRECATED is only used for Renderer::copyFrame()
|
||||
|
||||
@@ -1623,12 +1659,15 @@ void VulkanDriver::blitDEPRECATED(TargetBufferFlags buffers,
|
||||
mBlitter.blit(vkfilter,
|
||||
dstTarget->getColor0(), dstOffsets,
|
||||
srcTarget->getColor0(), srcOffsets);
|
||||
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("bindPipeline");
|
||||
|
||||
VulkanCommandBuffer* commands = &mCommands.get();
|
||||
VulkanCommandBuffer* commands = mCurrentRenderPass.cmdBuffer;
|
||||
const VulkanVertexBufferInfo& vbi =
|
||||
*mResourceAllocator.handle_cast<VulkanVertexBufferInfo*>(pipelineState.vertexBufferInfo);
|
||||
|
||||
@@ -1639,6 +1678,8 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
auto* program = mResourceAllocator.handle_cast<VulkanProgram*>(programHandle);
|
||||
commands->acquire(program);
|
||||
|
||||
FVK_LOGE <<"bindpipeline with cmdbuf=" << commands->buffer() << utils::io::endl;
|
||||
|
||||
// Update the VK raster state.
|
||||
const VulkanRenderTarget* rt = mCurrentRenderPass.renderTarget;
|
||||
|
||||
@@ -1702,12 +1743,15 @@ void VulkanDriver::bindPipeline(PipelineState const& pipelineState) {
|
||||
|
||||
mPipelineCache.bindLayout(pipelineLayout);
|
||||
mPipelineCache.bindPipeline(commands);
|
||||
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("bindRenderPrimitive");
|
||||
|
||||
VulkanCommandBuffer* commands = &mCommands.get();
|
||||
VulkanCommandBuffer* commands = mCurrentRenderPass.cmdBuffer;
|
||||
VkCommandBuffer cmdbuffer = commands->buffer();
|
||||
const VulkanRenderPrimitive& prim = *mResourceAllocator.handle_cast<VulkanRenderPrimitive*>(rph);
|
||||
commands->acquire(prim.indexBuffer);
|
||||
@@ -1728,27 +1772,28 @@ void VulkanDriver::bindRenderPrimitive(Handle<HwRenderPrimitive> rph) {
|
||||
vkCmdBindVertexBuffers(cmdbuffer, 0, bufferCount, buffers, offsets);
|
||||
vkCmdBindIndexBuffer(cmdbuffer, prim.indexBuffer->buffer.getGpuBuffer(), 0,
|
||||
prim.indexBuffer->indexType);
|
||||
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::bindDescriptorSet(
|
||||
backend::DescriptorSetHandle dsh,
|
||||
backend::descriptor_set_t setIndex,
|
||||
backend::DescriptorSetOffsetArray&& offsets) {
|
||||
if (dsh) {
|
||||
VulkanDescriptorSet* set = mResourceAllocator.handle_cast<VulkanDescriptorSet*>(dsh);
|
||||
mDescriptorSetManager.bind(setIndex, set, std::move(offsets));
|
||||
} else {
|
||||
mDescriptorSetManager.unbind(setIndex);
|
||||
}
|
||||
VulkanDescriptorSet* set = mResourceAllocator.handle_cast<VulkanDescriptorSet*>(dsh);
|
||||
mDescriptorSetManager.bind(setIndex, set, std::move(offsets));
|
||||
}
|
||||
|
||||
void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t instanceCount) {
|
||||
FVK_SYSTRACE_SCOPE();
|
||||
FVK_SYSTRACE_CONTEXT();
|
||||
FVK_SYSTRACE_START("draw2");
|
||||
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
VkCommandBuffer cmdbuffer = commands.buffer();
|
||||
VulkanCommandBuffer* commands = mCurrentRenderPass.cmdBuffer;
|
||||
VkCommandBuffer cmdbuffer = commands->buffer();
|
||||
|
||||
mDescriptorSetManager.commit(&commands, mBoundPipeline.pipelineLayout,
|
||||
FVK_LOGE <<"draw2 with cmdbuf=" << cmdbuffer << utils::io::endl;
|
||||
|
||||
mDescriptorSetManager.commit(commands, mBoundPipeline.pipelineLayout,
|
||||
mBoundPipeline.descriptorSetMask);
|
||||
|
||||
// Finally, make the actual draw call. TODO: support subranges
|
||||
@@ -1757,6 +1802,8 @@ void VulkanDriver::draw2(uint32_t indexOffset, uint32_t indexCount, uint32_t ins
|
||||
const uint32_t firstInstId = 0;
|
||||
|
||||
vkCmdDrawIndexed(cmdbuffer, indexCount, instanceCount, firstIndex, vertexOffset, firstInstId);
|
||||
|
||||
FVK_SYSTRACE_END();
|
||||
}
|
||||
|
||||
void VulkanDriver::draw(PipelineState state, Handle<HwRenderPrimitive> rph,
|
||||
@@ -1774,8 +1821,8 @@ void VulkanDriver::dispatchCompute(Handle<HwProgram> program, math::uint3 workGr
|
||||
}
|
||||
|
||||
void VulkanDriver::scissor(Viewport scissorBox) {
|
||||
VulkanCommandBuffer& commands = mCommands.get();
|
||||
VkCommandBuffer cmdbuffer = commands.buffer();
|
||||
VulkanCommandBuffer* commands = mCurrentRenderPass.cmdBuffer;
|
||||
VkCommandBuffer cmdbuffer = commands->buffer();
|
||||
|
||||
// TODO: it's a common case that scissor() is called with (0, 0, maxint, maxint)
|
||||
// we should maybe have a fast path for this and avoid vkCmdSetScissor() if possible
|
||||
|
||||
@@ -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;
|
||||
@@ -164,9 +162,8 @@ PushConstantDescription::PushConstantDescription(backend::Program const& program
|
||||
}
|
||||
}
|
||||
|
||||
void PushConstantDescription::write(VulkanCommands* commands, VkPipelineLayout layout,
|
||||
void PushConstantDescription::write(VulkanCommandBuffer* cmdbuf, VkPipelineLayout layout,
|
||||
backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) {
|
||||
VulkanCommandBuffer* cmdbuf = &(commands->get());
|
||||
uint32_t binaryValue = 0;
|
||||
UTILS_UNUSED_IN_RELEASE auto const& types = mTypes[(uint8_t) stage];
|
||||
if (std::holds_alternative<bool>(value)) {
|
||||
@@ -258,7 +255,6 @@ 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;
|
||||
@@ -270,8 +266,6 @@ void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) {
|
||||
VkExtent2D const extent = swapChain.getExtent();
|
||||
width = extent.width;
|
||||
height = extent.height;
|
||||
mProtected = swapChain.isProtected();
|
||||
|
||||
VulkanAttachment color = {.texture = swapChain.getCurrentColor()};
|
||||
mInfo->attachments = {color};
|
||||
|
||||
@@ -297,6 +291,7 @@ void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) {
|
||||
fbkey.depth = depth.getImageView();
|
||||
}
|
||||
mInfo->colors.set(0);
|
||||
mProtected = swapChain.isProtected();
|
||||
}
|
||||
|
||||
VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
@@ -340,6 +335,8 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
|
||||
continue;
|
||||
}
|
||||
|
||||
mProtected = mProtected || texture->isProtected();
|
||||
|
||||
attachments.push_back(attachment);
|
||||
mInfo->colors.set(index);
|
||||
|
||||
@@ -403,6 +400,7 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
|
||||
mResources.acquire(msaa);
|
||||
}
|
||||
}
|
||||
mProtected = mProtected || depthTexture->isProtected();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,6 +560,29 @@ VulkanBufferObject::VulkanBufferObject(VmaAllocator allocator, VulkanStagePool&
|
||||
buffer(allocator, stagePool, getBufferObjectUsage(bindingType), byteCount),
|
||||
bindingType(bindingType) {}
|
||||
|
||||
VulkanTimerQuery::VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices)
|
||||
: VulkanThreadSafeResource(VulkanResourceType::TIMER_QUERY),
|
||||
mStartingQueryIndex(std::get<0>(indices)),
|
||||
mStoppingQueryIndex(std::get<1>(indices)) {}
|
||||
|
||||
void VulkanTimerQuery::setFence(std::shared_ptr<VulkanCmdFence> fence) noexcept {
|
||||
std::unique_lock<utils::Mutex> lock(mFenceMutex);
|
||||
mFence = fence;
|
||||
}
|
||||
|
||||
bool VulkanTimerQuery::isCompleted() noexcept {
|
||||
std::unique_lock<utils::Mutex> lock(mFenceMutex);
|
||||
// QueryValue is a synchronous call and might occur before beginTimerQuery has written anything
|
||||
// into the command buffer, which is an error according to the validation layer that ships in
|
||||
// the Android NDK. Even when AVAILABILITY_BIT is set, validation seems to require that the
|
||||
// timestamp has at least been written into a processed command buffer.
|
||||
|
||||
// This fence indicates that the corresponding buffer has been completed.
|
||||
return mFence && mFence->getStatus() == VK_SUCCESS;
|
||||
}
|
||||
|
||||
VulkanTimerQuery::~VulkanTimerQuery() = default;
|
||||
|
||||
VulkanRenderPrimitive::VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator,
|
||||
PrimitiveType pt, Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh)
|
||||
: VulkanResource(VulkanResourceType::RENDER_PRIMITIVE),
|
||||
|
||||
@@ -185,7 +185,7 @@ struct PushConstantDescription {
|
||||
|
||||
uint32_t getVkRangeCount() const noexcept { return mRangeCount; }
|
||||
|
||||
void write(VulkanCommands* commands, VkPipelineLayout layout, backend::ShaderStage stage,
|
||||
void write(VulkanCommandBuffer* cmdbuf, VkPipelineLayout layout, backend::ShaderStage stage,
|
||||
uint8_t index, backend::PushConstantVariant const& value);
|
||||
|
||||
private:
|
||||
@@ -218,9 +218,9 @@ struct VulkanProgram : public HwProgram, VulkanResource {
|
||||
return mInfo->pushConstantDescription.getVkRanges();
|
||||
}
|
||||
|
||||
inline void writePushConstant(VulkanCommands* commands, VkPipelineLayout layout,
|
||||
inline void writePushConstant(VulkanCommandBuffer* cmdbuf, VkPipelineLayout layout,
|
||||
backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) {
|
||||
mInfo->pushConstantDescription.write(commands, layout, stage, index, value);
|
||||
mInfo->pushConstantDescription.write(cmdbuf, layout, stage, index, value);
|
||||
}
|
||||
|
||||
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
|
||||
@@ -306,7 +306,8 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
|
||||
inline bool hasDepth() const { return mInfo->depthIndex != Auxiliary::UNDEFINED_INDEX; }
|
||||
|
||||
inline bool isSwapChain() const { return !mOffscreen; }
|
||||
inline bool isProtected() const { return mProtected; }
|
||||
|
||||
bool isProtected() const { return mProtected; }
|
||||
|
||||
void bindToSwapChain(VulkanSwapChain& surf);
|
||||
|
||||
@@ -450,6 +451,42 @@ private:
|
||||
FixedSizeVulkanResourceManager<2> mResources;
|
||||
};
|
||||
|
||||
struct VulkanFence : public HwFence, VulkanResource {
|
||||
VulkanFence()
|
||||
: VulkanResource(VulkanResourceType::FENCE) {}
|
||||
|
||||
explicit VulkanFence(std::shared_ptr<VulkanCmdFence> fence)
|
||||
: VulkanResource(VulkanResourceType::FENCE),
|
||||
fence(fence) {}
|
||||
|
||||
std::shared_ptr<VulkanCmdFence> fence;
|
||||
};
|
||||
|
||||
struct VulkanTimerQuery : public HwTimerQuery, VulkanThreadSafeResource {
|
||||
explicit VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices);
|
||||
~VulkanTimerQuery();
|
||||
|
||||
void setFence(std::shared_ptr<VulkanCmdFence> fence) noexcept;
|
||||
|
||||
bool isCompleted() noexcept;
|
||||
|
||||
uint32_t getStartingQueryIndex() const {
|
||||
return mStartingQueryIndex;
|
||||
}
|
||||
|
||||
uint32_t getStoppingQueryIndex() const {
|
||||
return mStoppingQueryIndex;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t mStartingQueryIndex;
|
||||
uint32_t mStoppingQueryIndex;
|
||||
|
||||
std::shared_ptr<VulkanCmdFence> mFence;
|
||||
utils::Mutex mFenceMutex;
|
||||
};
|
||||
|
||||
|
||||
inline constexpr VkBufferUsageFlagBits getBufferObjectUsage(
|
||||
BufferObjectBinding bindingType) noexcept {
|
||||
switch(bindingType) {
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
#pragma clang diagnostic ignored "-Wnullability-completeness"
|
||||
#pragma clang diagnostic ignored "-Wextra-semi"
|
||||
#pragma clang diagnostic ignored "-Wc++98-compat-extra-semi"
|
||||
#pragma clang diagnostic ignored "-Wthread-safety-analysis"
|
||||
|
||||
static const PFN_vkGetInstanceProcAddr& vkGetInstanceProcAddr = bluevk::vkGetInstanceProcAddr;
|
||||
static const PFN_vkGetDeviceProcAddr& vkGetDeviceProcAddr = bluevk::vkGetDeviceProcAddr;
|
||||
|
||||
@@ -40,7 +40,8 @@ VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const&
|
||||
mTransitionSwapChainImageLayoutForPresent(
|
||||
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";
|
||||
|
||||
@@ -63,9 +64,10 @@ 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;
|
||||
}
|
||||
@@ -83,7 +85,7 @@ void VulkanSwapChain::update() {
|
||||
|
||||
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,
|
||||
|
||||
@@ -77,6 +77,7 @@ struct VulkanSwapChain : public HwSwapChain, VulkanResource {
|
||||
inline bool isProtected() noexcept {
|
||||
return mPlatform->isProtected(swapChain);
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr int IMAGE_READY_SEMAPHORE_COUNT = FVK_MAX_COMMAND_BUFFERS;
|
||||
|
||||
@@ -100,6 +101,7 @@ private:
|
||||
std::function<void(Platform::SwapChain* handle)> mExplicitImageReadyWait = nullptr;
|
||||
bool mAcquired;
|
||||
bool mIsFirstRenderPass;
|
||||
bool mIsProtected;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
mAllocator(handleAllocator),
|
||||
mState(handleAllocator->initHandle<VulkanTextureState>(device, allocator, commands, stagePool,
|
||||
backend::getVkFormat(tformat), imgutil::getViewType(target), levels,
|
||||
getLayerCount(target, depth), VulkanLayout::UNDEFINED, any(usage& TextureUsage::PROTECTED))) {
|
||||
getLayerCount(target, depth), VulkanLayout::UNDEFINED, any(usage & TextureUsage::PROTECTED))) {
|
||||
auto* const state = getSharedState();
|
||||
|
||||
// Create an appropriately-sized device-only VkImage, but do not fill it yet.
|
||||
@@ -235,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;
|
||||
}
|
||||
|
||||
@@ -361,7 +361,9 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
// Go ahead and create the primary image view.
|
||||
getImageView(mPrimaryViewRange, state->mViewType, mSwizzle);
|
||||
|
||||
VulkanCommandBuffer& commandsBuf = state->mCommands->get();
|
||||
VulkanCommandBuffer& commandsBuf = state->mIsProtected ? state->mCommands->getProtected()
|
||||
: state->mCommands->get();
|
||||
|
||||
commandsBuf.acquire(this);
|
||||
|
||||
auto const defaultLayout = state->mDefaultLayout = getDefaultLayoutImpl(imageInfo.usage);
|
||||
@@ -423,7 +425,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
|
||||
assert_invariant(depth <= this->depth * ((target == SamplerType::SAMPLER_CUBEMAP ||
|
||||
target == SamplerType::SAMPLER_CUBEMAP_ARRAY) ? 6 : 1));
|
||||
auto* const state = getSharedState();
|
||||
assert_invariant(state->mIsProtected == false);
|
||||
assert_invariant(!state->mIsProtected);
|
||||
const PixelBufferDescriptor* hostData = &data;
|
||||
PixelBufferDescriptor reshapedData;
|
||||
|
||||
@@ -455,7 +457,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
|
||||
vmaUnmapMemory(state->mAllocator, stage->memory);
|
||||
vmaFlushAllocation(state->mAllocator, stage->memory, 0, hostData->size);
|
||||
|
||||
VulkanCommandBuffer& commands = state->mCommands->get();
|
||||
VulkanCommandBuffer& commands =state->mCommands->get();
|
||||
VkCommandBuffer const cmdbuf = commands.buffer();
|
||||
commands.acquire(this);
|
||||
|
||||
@@ -511,6 +513,8 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
|
||||
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
|
||||
= state->mStagePool.acquireImage(hostData.format, hostData.type, width, height);
|
||||
|
||||
@@ -35,7 +35,8 @@ class VulkanResourceAllocator;
|
||||
struct VulkanTextureState : public VulkanResource {
|
||||
VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
|
||||
VulkanStagePool& stagePool, VkFormat format, VkImageViewType viewType, uint8_t levels,
|
||||
uint8_t layerCount, VulkanLayout defaultLayout, bool isProtected);
|
||||
uint8_t layerCount, VulkanLayout defaultLayout,
|
||||
bool isProtected = false);
|
||||
|
||||
struct ImageViewKey {
|
||||
VkImageSubresourceRange range; // 4 * 5 bytes
|
||||
@@ -113,6 +114,13 @@ struct VulkanTexture : public HwTexture, VulkanResource {
|
||||
VulkanResourceAllocator* handleAllocator,
|
||||
VulkanTexture const* src, VkComponentMapping swizzle);
|
||||
|
||||
// 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.
|
||||
@@ -181,7 +189,7 @@ struct VulkanTexture : public HwTexture, VulkanResource {
|
||||
return state->mIsTransientAttachment;
|
||||
}
|
||||
|
||||
bool getIsProtected() const {
|
||||
bool isProtected() const {
|
||||
VulkanTextureState const* state = getSharedState();
|
||||
return state->mIsProtected;
|
||||
}
|
||||
|
||||
@@ -369,10 +369,6 @@ void VulkanDescriptorSetManager::bind(uint8_t setIndex, VulkanDescriptorSet* set
|
||||
mStashedSets[setIndex] = set;
|
||||
}
|
||||
|
||||
void VulkanDescriptorSetManager::unbind(uint8_t setIndex) {
|
||||
mStashedSets[setIndex] = nullptr;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -61,8 +61,6 @@ public:
|
||||
|
||||
void bind(uint8_t setIndex, VulkanDescriptorSet* set, backend::DescriptorSetOffsetArray&& offsets);
|
||||
|
||||
void unbind(uint8_t setIndex);
|
||||
|
||||
void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout,
|
||||
DescriptorSetMask const& setMask);
|
||||
|
||||
@@ -94,7 +92,7 @@ private:
|
||||
struct {
|
||||
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
|
||||
DescriptorSetMask setMask;
|
||||
DescriptorSetArray boundSets = {};
|
||||
DescriptorSetArray boundSets;
|
||||
} mLastBoundInfo;
|
||||
};
|
||||
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "vulkan/memory/Resource.h"
|
||||
#include "vulkan/memory/ResourceManager.h"
|
||||
|
||||
#include "vulkan/VulkanHandles.h"
|
||||
|
||||
namespace filament::backend::fvkmemory {
|
||||
|
||||
template ResourceType getTypeEnum<VulkanBufferObject>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanIndexBuffer>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanProgram>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanRenderTarget>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanSwapChain>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanRenderPrimitive>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanTexture>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanTextureState>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanTimerQuery>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanVertexBuffer>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanVertexBufferInfo>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanDescriptorSetLayout>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanDescriptorSet>() noexcept;
|
||||
template ResourceType getTypeEnum<VulkanFence>() noexcept;
|
||||
|
||||
template<typename D>
|
||||
ResourceType getTypeEnum() noexcept {
|
||||
if constexpr (std::is_same_v<D, VulkanBufferObject>) {
|
||||
return ResourceType::BUFFER_OBJECT;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanIndexBuffer>) {
|
||||
return ResourceType::INDEX_BUFFER;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanProgram>) {
|
||||
return ResourceType::PROGRAM;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanRenderTarget>) {
|
||||
return ResourceType::RENDER_TARGET;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanSwapChain>) {
|
||||
return ResourceType::SWAP_CHAIN;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanRenderPrimitive>) {
|
||||
return ResourceType::RENDER_PRIMITIVE;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanTexture>) {
|
||||
return ResourceType::TEXTURE;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanTextureState>) {
|
||||
return ResourceType::TEXTURE_STATE;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanTimerQuery>) {
|
||||
return ResourceType::TIMER_QUERY;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanVertexBuffer>) {
|
||||
return ResourceType::VERTEX_BUFFER;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanVertexBufferInfo>) {
|
||||
return ResourceType::VERTEX_BUFFER_INFO;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanDescriptorSetLayout>) {
|
||||
return ResourceType::DESCRIPTOR_SET_LAYOUT;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanDescriptorSet>) {
|
||||
return ResourceType::DESCRIPTOR_SET;
|
||||
}
|
||||
if constexpr (std::is_same_v<D, VulkanFence>) {
|
||||
return ResourceType::FENCE;
|
||||
}
|
||||
return ResourceType::UNDEFINED_TYPE;
|
||||
}
|
||||
|
||||
std::string getTypeStr(ResourceType type) {
|
||||
switch (type) {
|
||||
case ResourceType::BUFFER_OBJECT:
|
||||
return "BufferObject";
|
||||
case ResourceType::INDEX_BUFFER:
|
||||
return "IndexBuffer";
|
||||
case ResourceType::PROGRAM:
|
||||
return "Program";
|
||||
case ResourceType::RENDER_TARGET:
|
||||
return "RenderTarget";
|
||||
case ResourceType::SWAP_CHAIN:
|
||||
return "SwapChain";
|
||||
case ResourceType::RENDER_PRIMITIVE:
|
||||
return "RenderPrimitive";
|
||||
case ResourceType::TEXTURE:
|
||||
return "Texture";
|
||||
case ResourceType::TEXTURE_STATE:
|
||||
return "TextureState";
|
||||
case ResourceType::TIMER_QUERY:
|
||||
return "TimerQuery";
|
||||
case ResourceType::VERTEX_BUFFER:
|
||||
return "VertexBuffer";
|
||||
case ResourceType::VERTEX_BUFFER_INFO:
|
||||
return "VertexBufferInfo";
|
||||
case ResourceType::DESCRIPTOR_SET_LAYOUT:
|
||||
return "DescriptorSetLayout";
|
||||
case ResourceType::DESCRIPTOR_SET:
|
||||
return "DescriptorSet";
|
||||
case ResourceType::FENCE:
|
||||
return "Fence";
|
||||
case ResourceType::UNDEFINED_TYPE:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void Resource::destroy(ResourceType type, HandleId id) {
|
||||
resManager->destructLaterWithType(type, id);
|
||||
|
||||
}
|
||||
void ThreadSafeResource::destroy(ResourceType type, HandleId id) {
|
||||
resManager->destructLaterWithType(type, id);
|
||||
}
|
||||
|
||||
} // namespace filament::backend::fvkmemory
|
||||
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCE_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCE_H
|
||||
|
||||
#include <private/backend/HandleAllocator.h>
|
||||
#include <utils/Mutex.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
namespace filament::backend::fvkmemory {
|
||||
|
||||
using CounterIndex = int32_t;
|
||||
using HandleId = HandleBase::HandleId;
|
||||
|
||||
class ResourceManager;
|
||||
|
||||
template <typename D>
|
||||
struct resource_ptr;
|
||||
|
||||
// Subclasses of VulkanResource must provide this enum in their construction.
|
||||
enum class ResourceType : uint8_t {
|
||||
BUFFER_OBJECT = 0,
|
||||
INDEX_BUFFER = 1,
|
||||
PROGRAM = 2,
|
||||
RENDER_TARGET = 3,
|
||||
SWAP_CHAIN = 4,
|
||||
RENDER_PRIMITIVE = 5,
|
||||
TEXTURE = 6,
|
||||
TEXTURE_STATE = 7,
|
||||
TIMER_QUERY = 8,
|
||||
VERTEX_BUFFER = 9,
|
||||
VERTEX_BUFFER_INFO = 10,
|
||||
DESCRIPTOR_SET_LAYOUT = 11,
|
||||
DESCRIPTOR_SET = 12,
|
||||
FENCE = 13,
|
||||
UNDEFINED_TYPE = 14, // Must be the last enum because we use it for iterating over the enums.
|
||||
};
|
||||
|
||||
template<typename D>
|
||||
ResourceType getTypeEnum() noexcept;
|
||||
|
||||
std::string getTypeStr(ResourceType type);
|
||||
|
||||
inline bool isThreadSafeType(ResourceType type) {
|
||||
return type == ResourceType::FENCE || type == ResourceType::TIMER_QUERY;
|
||||
}
|
||||
|
||||
struct Resource {
|
||||
Resource()
|
||||
: resManager(nullptr),
|
||||
id(HandleBase::nullid),
|
||||
mCount(0),
|
||||
restype(ResourceType::UNDEFINED_TYPE) {}
|
||||
|
||||
private:
|
||||
inline void inc() noexcept {
|
||||
mCount++;
|
||||
}
|
||||
|
||||
inline void dec() noexcept {
|
||||
assert_invariant(mCount > 0);
|
||||
if (--mCount == 0) {
|
||||
destroy(restype, id);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void init(HandleId id, ResourceManager* resManager) {
|
||||
this->id = id;
|
||||
this->resManager = resManager;
|
||||
this->restype = getTypeEnum<T>();
|
||||
}
|
||||
|
||||
void destroy(ResourceType type, HandleId id);
|
||||
|
||||
ResourceManager* resManager; // 8
|
||||
HandleId id; // 4
|
||||
uint32_t mCount : 24;
|
||||
ResourceType restype : 6; // restype + mCount is 4 bytes.
|
||||
|
||||
friend class ResourceManager;
|
||||
|
||||
template <typename D>
|
||||
friend struct resource_ptr;
|
||||
};
|
||||
|
||||
struct ThreadSafeResource {
|
||||
ThreadSafeResource()
|
||||
: resManager(nullptr),
|
||||
id(HandleBase::nullid),
|
||||
mCount(0),
|
||||
restype(ResourceType::UNDEFINED_TYPE) {}
|
||||
|
||||
private:
|
||||
inline void inc() noexcept {
|
||||
mCount.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
inline void dec() noexcept {
|
||||
if (mCount.fetch_sub(1, std::memory_order_acq_rel) == 1) {
|
||||
destroy(restype, id);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void init(HandleId id, ResourceManager* resManager) {
|
||||
this->id = id;
|
||||
this->resManager = resManager;
|
||||
this->restype = getTypeEnum<T>();
|
||||
}
|
||||
|
||||
void destroy(ResourceType type, HandleId id);
|
||||
|
||||
ResourceManager* resManager; // 8
|
||||
HandleId id; // 4
|
||||
std::atomic<uint32_t> mCount; // 4
|
||||
ResourceType restype; // 1
|
||||
|
||||
friend class ResourceManager;
|
||||
|
||||
template <typename D>
|
||||
friend struct resource_ptr;
|
||||
};
|
||||
|
||||
} // namespace filament::backend::fvkmemory
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCE_H
|
||||
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "vulkan/memory/ResourceManager.h"
|
||||
#include "vulkan/VulkanHandles.h"
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
namespace filament::backend::fvkmemory {
|
||||
|
||||
namespace {
|
||||
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
|
||||
uint32_t COUNTER[(size_t) ResourceType::UNDEFINED_TYPE] = {};
|
||||
#endif
|
||||
}
|
||||
|
||||
ResourceManager::ResourceManager(size_t arenaSize, bool disableUseAfterFreeCheck)
|
||||
: mHandleAllocatorImpl("Handles", arenaSize, disableUseAfterFreeCheck) {}
|
||||
|
||||
void ResourceManager::gc() noexcept {
|
||||
auto destroyAll = [this](GcList& list) {
|
||||
for (auto const& [type, id]: list) {
|
||||
destroyWithType(type, id);
|
||||
}
|
||||
list.clear();
|
||||
};
|
||||
|
||||
{
|
||||
// Note that we're not copying mThreadSafeGcList because the objects here do not have
|
||||
// resource_ptrs to other handle objects, so their desctruction would not add more elements
|
||||
// to mThreadSafeGcList.
|
||||
std::unique_lock<utils::Mutex> lock(mThreadSafeGcListMutex);
|
||||
destroyAll(mThreadSafeGcList);
|
||||
}
|
||||
|
||||
GcList gcs;
|
||||
gcs.insert(gcs.end(), mGcList.begin(), mGcList.end());
|
||||
mGcList.clear();
|
||||
destroyAll(gcs);
|
||||
}
|
||||
|
||||
void ResourceManager::terminate() noexcept {
|
||||
while (!mThreadSafeGcList.empty() || !mGcList.empty()) {
|
||||
gc();
|
||||
}
|
||||
}
|
||||
|
||||
void ResourceManager::destroyWithType(ResourceType type, HandleId id) {
|
||||
switch (type) {
|
||||
case ResourceType::BUFFER_OBJECT:
|
||||
destruct<VulkanBufferObject>(Handle<VulkanBufferObject>(id));
|
||||
break;
|
||||
case ResourceType::INDEX_BUFFER:
|
||||
destruct<VulkanIndexBuffer>(Handle<VulkanIndexBuffer>(id));
|
||||
break;
|
||||
case ResourceType::PROGRAM:
|
||||
destruct<VulkanProgram>(Handle<VulkanProgram>(id));
|
||||
break;
|
||||
case ResourceType::RENDER_TARGET:
|
||||
destruct<VulkanRenderTarget>(Handle<VulkanRenderTarget>(id));
|
||||
break;
|
||||
case ResourceType::SWAP_CHAIN:
|
||||
destruct<VulkanSwapChain>(Handle<VulkanSwapChain>(id));
|
||||
break;
|
||||
case ResourceType::RENDER_PRIMITIVE:
|
||||
destruct<VulkanRenderPrimitive>(Handle<VulkanRenderPrimitive>(id));
|
||||
break;
|
||||
case ResourceType::TEXTURE:
|
||||
destruct<VulkanTexture>(Handle<VulkanTexture>(id));
|
||||
break;
|
||||
case ResourceType::TEXTURE_STATE:
|
||||
destruct<VulkanTextureState>(Handle<VulkanTextureState>(id));
|
||||
break;
|
||||
case ResourceType::TIMER_QUERY:
|
||||
destruct<VulkanTimerQuery>(Handle<VulkanTimerQuery>(id));
|
||||
break;
|
||||
case ResourceType::VERTEX_BUFFER:
|
||||
destruct<VulkanVertexBuffer>(Handle<VulkanVertexBuffer>(id));
|
||||
break;
|
||||
case ResourceType::VERTEX_BUFFER_INFO:
|
||||
destruct<VulkanVertexBufferInfo>(Handle<VulkanVertexBufferInfo>(id));
|
||||
break;
|
||||
case ResourceType::DESCRIPTOR_SET_LAYOUT:
|
||||
destruct<VulkanDescriptorSetLayout>(Handle<VulkanDescriptorSetLayout>(id));
|
||||
break;
|
||||
case ResourceType::DESCRIPTOR_SET:
|
||||
destruct<VulkanDescriptorSet>(Handle<VulkanDescriptorSet>(id));
|
||||
break;
|
||||
case ResourceType::FENCE:
|
||||
destruct<VulkanFence>(Handle<VulkanFence>(id));
|
||||
break;
|
||||
case ResourceType::UNDEFINED_TYPE:
|
||||
break;
|
||||
}
|
||||
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
|
||||
COUNTER[(size_t) type]--;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ResourceManager::traceConstruction(ResourceType type, HandleId id) {
|
||||
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
|
||||
assert_invariant(type != ResourceType::UNDEFINED_TYPE);
|
||||
COUNTER[(size_t) type]++;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ResourceManager::print() const noexcept {
|
||||
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
|
||||
utils::slog.e << "-------------------" << utils::io::endl;
|
||||
for (size_t i = 0; i < (size_t) ResourceType::UNDEFINED_TYPE; ++i) {
|
||||
utils::slog.e <<" " << getTypeStr((ResourceType) i) << "=" << COUNTER[i] << utils::io::endl;
|
||||
}
|
||||
utils::slog.e << "+++++++++++++++++++" << utils::io::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCEMANAGER_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCEMANAGER_H
|
||||
|
||||
#include "vulkan/memory/Resource.h"
|
||||
|
||||
#include "vulkan/VulkanAsyncHandles.h"
|
||||
|
||||
#include <private/backend/HandleAllocator.h>
|
||||
|
||||
#include <utils/Panic.h>
|
||||
|
||||
namespace filament::backend::fvkmemory {
|
||||
|
||||
class ResourceManager {
|
||||
public:
|
||||
ResourceManager(size_t arenaSize, bool disableUseAfterFreeCheck);
|
||||
|
||||
template<typename D>
|
||||
inline Handle<D> allocHandle() noexcept {
|
||||
return mHandleAllocatorImpl.allocate<D>();
|
||||
}
|
||||
|
||||
inline void associateHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
|
||||
mHandleAllocatorImpl.associateTagToHandle(id, std::move(tag));
|
||||
}
|
||||
|
||||
void gc() noexcept;
|
||||
void print() const noexcept;
|
||||
void terminate() noexcept;
|
||||
|
||||
private:
|
||||
using AllocatorImpl = HandleAllocatorVK;
|
||||
|
||||
template<typename D>
|
||||
using requires_thread_safety = typename std::disjunction<std::is_same<D, VulkanFence>,
|
||||
std::is_same<D, VulkanTimerQuery>>;
|
||||
|
||||
template<typename D, typename B, typename... ARGS>
|
||||
inline D* construct(Handle<B> const& handle, ARGS&&... args) noexcept {
|
||||
constexpr bool THREAD_SAFETY = requires_thread_safety<D>::value;
|
||||
D* obj = mHandleAllocatorImpl.construct<D, B>(handle, std::forward<ARGS>(args)...);
|
||||
if constexpr (THREAD_SAFETY) {
|
||||
((ThreadSafeResource*) obj)->template init<D>(handle.getId(), this);
|
||||
} else {
|
||||
((Resource*) obj)->template init<D>(handle.getId(), this);
|
||||
}
|
||||
traceConstruction(getTypeEnum<D>(), handle.getId());
|
||||
return obj;
|
||||
}
|
||||
|
||||
template<typename Dp, typename B>
|
||||
inline typename std::enable_if_t<
|
||||
std::is_pointer_v<Dp> && std::is_base_of_v<B, typename std::remove_pointer_t<Dp>>, Dp>
|
||||
handle_cast(Handle<B> const& handle) noexcept {
|
||||
return mHandleAllocatorImpl.handle_cast<Dp, B>(handle);
|
||||
}
|
||||
|
||||
template<typename D, typename B>
|
||||
inline void destruct(Handle<B> handle) noexcept {
|
||||
D* obj = handle_cast<D*>(handle);
|
||||
mHandleAllocatorImpl.deallocate(handle, obj);
|
||||
}
|
||||
|
||||
// This will post the destruction to a list that will be processed when gc is called from the
|
||||
// backend thread.
|
||||
inline void destructLaterWithType(ResourceType type, HandleId id) {
|
||||
if (isThreadSafeType(type)) {
|
||||
std::unique_lock<utils::Mutex> lock(mThreadSafeGcListMutex);
|
||||
mThreadSafeGcList.push_back({type, id});
|
||||
} else {
|
||||
mGcList.push_back({type, id});
|
||||
}
|
||||
}
|
||||
|
||||
void destroyWithType(ResourceType type, HandleId id);
|
||||
|
||||
void traceConstruction(ResourceType type, HandleId id);
|
||||
|
||||
AllocatorImpl mHandleAllocatorImpl;
|
||||
|
||||
using GcList = std::vector<std::pair<ResourceType, HandleId>>;
|
||||
|
||||
utils::Mutex mThreadSafeGcListMutex;
|
||||
GcList mThreadSafeGcList;
|
||||
GcList mGcList;
|
||||
|
||||
template<typename D>
|
||||
friend struct resource_ptr;
|
||||
|
||||
friend struct Resource;
|
||||
friend struct ThreadSafeResource;
|
||||
};
|
||||
|
||||
} // namespace filament::backend::fvkmemory
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCEMANAGER_H
|
||||
@@ -1,194 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCEPOINTER_H
|
||||
#define TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCEPOINTER_H
|
||||
|
||||
#include "vulkan/memory/Resource.h"
|
||||
#include "vulkan/memory/ResourceManager.h"
|
||||
|
||||
#include <backend/Handle.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace filament::backend {
|
||||
class VulkanDriver;
|
||||
|
||||
namespace fvkmemory {
|
||||
|
||||
// This is a ref-counting, reference holder class (similar to std::shared_ptr) that serves as the
|
||||
// primary interface between the vk backend (VulkanDriver) and the handle allocator. All objects
|
||||
// that are allocated from the handle allocator are expected to be contained within a
|
||||
template<typename D>
|
||||
struct resource_ptr {
|
||||
|
||||
private:
|
||||
template<typename B>
|
||||
using enabled_resource_ptr =
|
||||
resource_ptr<typename std::enable_if<std::is_base_of<B, D>::value, D>::type>;
|
||||
public:
|
||||
template<typename B, typename... ARGS>
|
||||
static enabled_resource_ptr<B> make(ResourceManager* resManager, Handle<B> const& handle,
|
||||
ARGS&&... args) noexcept {
|
||||
D* ptr = resManager->construct<D, B>(handle, std::forward<ARGS>(args)...);
|
||||
return {ptr};
|
||||
}
|
||||
|
||||
// This will alloc a handle and then construct the object.
|
||||
template<typename... ARGS>
|
||||
static resource_ptr<D> construct(ResourceManager* resManager, ARGS&&... args) noexcept {
|
||||
auto handle = resManager->allocHandle<D>();
|
||||
D* ptr = resManager->construct<D, D>(handle, std::forward<ARGS>(args)...);
|
||||
return {ptr};
|
||||
}
|
||||
|
||||
template<typename B>
|
||||
static enabled_resource_ptr<B> cast(ResourceManager* resManager,
|
||||
Handle<B> const& handle) noexcept {
|
||||
D* ptr = resManager->handle_cast<D*, B>(handle);
|
||||
return {ptr};
|
||||
}
|
||||
|
||||
template<typename B>
|
||||
static enabled_resource_ptr<B> cast(B* ptr) noexcept {
|
||||
return {ptr};
|
||||
}
|
||||
|
||||
~resource_ptr() {
|
||||
if (mRef) {
|
||||
mRef->dec();
|
||||
}
|
||||
}
|
||||
|
||||
inline resource_ptr() = default;
|
||||
|
||||
// move constructor operator
|
||||
inline resource_ptr(resource_ptr<D>&& rhs) {
|
||||
(*this) = std::move(rhs);
|
||||
}
|
||||
|
||||
inline resource_ptr(resource_ptr<D> const& rhs) {
|
||||
(*this) = rhs;
|
||||
}
|
||||
|
||||
template<typename E>
|
||||
using is_supported_t =
|
||||
typename std::enable_if<std::is_base_of<D, E>::value>::type;
|
||||
template<typename E, typename = is_supported_t<E>>
|
||||
inline resource_ptr(resource_ptr<E> const& rhs) {
|
||||
(*this) = rhs;
|
||||
}
|
||||
|
||||
// move operator
|
||||
inline resource_ptr<D>& operator=(resource_ptr<D> && rhs) {
|
||||
std::swap(mRef, rhs.mRef);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline resource_ptr<D>& operator=(resource_ptr<D> const& rhs) {
|
||||
if (mRef == rhs.mRef) {
|
||||
return *this;
|
||||
}
|
||||
if (mRef) {
|
||||
mRef->dec();
|
||||
}
|
||||
mRef = rhs.mRef;
|
||||
mRef->inc();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename E, typename = is_supported_t<E>>
|
||||
inline resource_ptr<D>& operator=(resource_ptr<E> const& rhs) {
|
||||
if (mRef == rhs.mRef) {
|
||||
return *this;
|
||||
}
|
||||
if (mRef) {
|
||||
mRef->dec();
|
||||
}
|
||||
mRef = rhs.mRef;
|
||||
mRef->inc();
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline bool operator==(resource_ptr<D> const& other) const {
|
||||
return id() == other.id() && type() == other.type();
|
||||
}
|
||||
|
||||
inline explicit operator bool() const {
|
||||
return bool(mRef);
|
||||
}
|
||||
|
||||
inline D* operator->() {
|
||||
return get();
|
||||
}
|
||||
|
||||
inline D const* operator->() const {
|
||||
return get();
|
||||
}
|
||||
|
||||
inline HandleId id() const {
|
||||
if (mRef) {
|
||||
return mRef->id;
|
||||
}
|
||||
return HandleBase::nullid;
|
||||
}
|
||||
|
||||
inline D* get() const {
|
||||
assert_invariant(mRef);
|
||||
return mRef;
|
||||
}
|
||||
|
||||
private:
|
||||
// inc() and dec() For tracking ref-count with respect to create/destroy backend APIs. They can
|
||||
// only be used from VulkanDriver.
|
||||
inline void dec() {
|
||||
assert_invariant(mRef);
|
||||
mRef->dec();
|
||||
}
|
||||
|
||||
inline void inc() {
|
||||
assert_invariant(mRef);
|
||||
mRef->inc();
|
||||
}
|
||||
|
||||
inline ResourceType type() const {
|
||||
if (mRef) {
|
||||
return mRef->restype;
|
||||
}
|
||||
return ResourceType::UNDEFINED_TYPE;
|
||||
}
|
||||
|
||||
resource_ptr(D* ref)
|
||||
: mRef(ref) {
|
||||
mRef->inc();
|
||||
}
|
||||
|
||||
D* mRef = nullptr;
|
||||
|
||||
// Allow generic structures to hold resources of different types. For example,
|
||||
// std::vector<fvkmemory::Resource> resources;
|
||||
friend struct resource_ptr<Resource>;
|
||||
friend struct resource_ptr<ThreadSafeResource>;
|
||||
|
||||
// This enables access to resource_ptr's private inc() and dec() methods.
|
||||
friend class filament::backend::VulkanDriver;
|
||||
};
|
||||
|
||||
} // namespace fvkmemory
|
||||
|
||||
} // namespace filament::backend
|
||||
|
||||
#endif // TNT_FILAMENT_BACKEND_VULKAN_MEMORY_RESOURCEPOINTER_H
|
||||
@@ -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,13 +28,96 @@ 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) {
|
||||
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,
|
||||
@@ -58,16 +141,19 @@ std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& co
|
||||
VkMemoryRequirements memReqs;
|
||||
vkGetImageMemoryRequirements(device, image, &memReqs);
|
||||
|
||||
uint32_t memoryTypeIndex
|
||||
= context.selectMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
|
||||
// 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.";
|
||||
@@ -77,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
|
||||
@@ -110,8 +204,9 @@ void VulkanPlatformSwapChainImpl::destroy() {
|
||||
mSwapChainBundle.colors.clear();
|
||||
}
|
||||
|
||||
VkImage VulkanPlatformSwapChainImpl::createImage(VkExtent2D extent, VkFormat format) {
|
||||
auto [image, memory] = createImageAndMemory(mContext, mDevice, extent, format);
|
||||
VkImage VulkanPlatformSwapChainImpl::createImage(VkExtent2D extent, VkFormat format,
|
||||
bool isProtected) {
|
||||
auto [image, memory] = createImageAndMemory(mContext, mDevice, extent, format, isProtected);
|
||||
mMemory.insert({image, memory});
|
||||
return image;
|
||||
}
|
||||
@@ -139,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
|
||||
@@ -157,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.
|
||||
@@ -199,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)
|
||||
@@ -248,16 +363,16 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
|
||||
mSwapChainBundle.colorFormat = surfaceFormat.format;
|
||||
mSwapChainBundle.depthFormat =
|
||||
selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), mHasStencil);
|
||||
mSwapChainBundle.depth = createImage(mSwapChainBundle.extent, mSwapChainBundle.depthFormat);
|
||||
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 = {
|
||||
@@ -347,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
|
||||
@@ -356,13 +475,13 @@ VulkanPlatformHeadlessSwapChain::VulkanPlatformHeadlessSwapChain(VulkanContext c
|
||||
images.reserve(HEADLESS_SWAPCHAIN_SIZE);
|
||||
images.resize(HEADLESS_SWAPCHAIN_SIZE);
|
||||
for (size_t i = 0; i < HEADLESS_SWAPCHAIN_SIZE; ++i) {
|
||||
images[i] = createImage(extent, mSwapChainBundle.colorFormat);
|
||||
images[i] = createImage(extent, mSwapChainBundle.colorFormat, false);
|
||||
}
|
||||
|
||||
bool const hasStencil = (flags & backend::SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0;
|
||||
mSwapChainBundle.depthFormat =
|
||||
selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), hasStencil);
|
||||
mSwapChainBundle.depth = createImage(extent, mSwapChainBundle.depthFormat);
|
||||
mSwapChainBundle.depth = createImage(extent, mSwapChainBundle.depthFormat, false);
|
||||
}
|
||||
|
||||
VulkanPlatformHeadlessSwapChain::~VulkanPlatformHeadlessSwapChain() {
|
||||
|
||||
@@ -73,7 +73,7 @@ protected:
|
||||
// Non-virtual override-able method
|
||||
void destroy();
|
||||
|
||||
VkImage createImage(VkExtent2D extent, VkFormat format);
|
||||
VkImage createImage(VkExtent2D extent, VkFormat format, bool isProtected);
|
||||
|
||||
VulkanContext const& mContext;
|
||||
VkDevice mDevice;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = "Filament"
|
||||
spec.version = "1.56.1"
|
||||
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.1/filament-v1.56.1-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 = {
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
namespace filament {
|
||||
|
||||
// update this when a new version of filament wouldn't work with older materials
|
||||
static constexpr size_t MATERIAL_VERSION = 56;
|
||||
static constexpr size_t MATERIAL_VERSION = 55;
|
||||
|
||||
/**
|
||||
* Supported shading models
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "filament",
|
||||
"version": "1.56.1",
|
||||
"version": "1.55.0",
|
||||
"description": "Real-time physically based rendering engine",
|
||||
"main": "filament.js",
|
||||
"module": "filament.js",
|
||||
|
||||
Reference in New Issue
Block a user