Compare commits

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