Compare commits

...

18 Commits

Author SHA1 Message Date
Powei Feng
36c76daf34 material: fix depth variant leak
The default material does not cover all of the depth variants,
and so for the client material's depth variants (with no custom
depth shader), we need to check if the program is allocated for
the material or if it is actually part of the default material.
2024-11-14 15:13:48 -08:00
Powei Feng
43b9c06f79 vk: increase number of command buffers (#8271)
Small number of pre-allocated command buffers can cause
unnecessary blocking on the CPU.  We increase this number.
2024-11-14 19:24:29 +00:00
Powei Feng
05ffc76032 vk: clean up group marker logic (#8272)
The old logic was unnecessarily complex. We simplify it and also
properly store the marker stack on the CommandPool instead of
VulkanCommands.
2024-11-14 19:11:17 +00:00
Ben Doherty
8e5ba51718 Fix TSAN warning in MetalHandles (#8262) 2024-11-14 10:12:53 -08:00
Mathias Agopian
8aa9c4d004 Don't use AtomicFreeList to squash TSAN complains
The previous fix attempt didn't work on some test. There are no known
bugs with AtomicFreeList other than tripping TSAN, and it's unclear
that TSAN isn't at fault.

However, switching to using a mutex works fine and doesn't appear to
be slower (it's actually faster with synthetic benchmarks on macOS)

BUGS=[377369108]
2024-11-14 10:00:00 -08:00
Mathias Agopian
d075a877f2 Fix TSAN warnings with AtomicFreeList
I do not think there was an actual error with AtomicFreeList, however
TSAN detected a data race when concurrent pop() happened. In that case,
there is indeed a race, where we can end-up reading data that is
already corrupted by the concurrent pop. However, that situation is
corrected by the following CAS. Somehow TSAN didn't see that.
The fix is strange and consists in replacing:
```
auto pNext = storage[offset].next;
```

with

```
auto s = storage[offset];
auto pNext = s.next;
```

In this PR we also adjust the memory ordering to be less strong. i.e.
we do not need `memory_order_seq_cst`, only the appropriate acquire or
release semantic.

In addition we also make `Node* next` a non-atomic variable again. It
should have been, but was change to placate an older version of TSAN.

BUGS=[377369108]
2024-11-13 21:50:05 -08:00
Mathias Agopian
5dfd285105 ostream buffer growth was broken
It only grew by 3/2 of the needed size instead of the needed capacity,
which could be extremely slow when growing by a constant amount
many times.
2024-11-12 16:03:35 -08:00
Ben Doherty
51392c77aa Metal: unbind descriptor sets upon destruction (#8268) 2024-11-12 14:08:15 -08:00
mdagois
e78691b12b Forcing profile mode (#8256) 2024-11-12 20:05:08 +00:00
Powei Feng
bc65135c51 vk: refactor resource ref-counting (#8254)
Continuing from PR #8220.

Here we change all of the references from the old ref-counting ways
to the new ref-counting structs and mechanisms. There should be no
functional change.
2024-11-12 10:33:00 -08:00
Serge Metral
0108d66aa1 vk: Implement protected content
- Cache the render buffer in the render pass strucutre
- Enable the proteded path in the Render Target
- Make sure we build the protected version of the depth/color in the swap chaing
- Minor cleanup/restrucuring of the code
2024-11-12 09:10:03 -08:00
1558287830
96443fa1fd vk: fix stage pool gc logic (#8260)
* vk: fix stage pool gc logic

* Update NEW_RELEASE_NOTES.md

---------

Co-authored-by: linkunhai <linkunhai@bytedance.com>
2024-11-12 06:49:12 +00:00
Mathias Agopian
61c4df9503 fix uninitialized variable on ES2 devices
On ES2 devices (or in forceES2 mode), we emulate the sRGB swapchain
in the shader if the h/w doesn't support it. In that case, the emulation
is controlled by a uniform that technically lives in the frameUniforms
block. However, the frameUniforms buffer is not updated, instead,
the uniform is manually set. Unfortunately, the UBO emulation
overrides it with the uninitialized variable.

BUGS=[377913730]
2024-11-11 16:30:24 -08:00
Mathias Agopian
b990652702 assert if post-processing is used at Feature Level 0
We assert in Renderer::render(View*) as early as possible. The
current behavior can result in a SEG FAULT which is harder to debug.
2024-11-11 16:30:24 -08:00
Mathias Agopian
7f7d4d89ea fix some missing highp qualifiers
BUGS=[377751005]
2024-11-08 14:37:34 -08:00
Powei Feng
2b7f325cc7 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 21:55:20 +00:00
Ben Doherty
b9768394a1 Metal: remove cached pipeline states with deleted programs (#8243) 2024-11-07 11:23:30 -08:00
Powei Feng
3e02d5016e vk: fix uninitialized param (#8253) 2024-11-06 22:04:57 +00:00
56 changed files with 1090 additions and 1505 deletions

View File

@@ -7,3 +7,4 @@ for next branch cut* header.
appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md).
## Release notes for next branch cut
- vk: fix stage pool gc logic

View File

@@ -112,7 +112,13 @@ class MainActivity : Activity() {
}
private fun setupFilament() {
engine = Engine.Builder().featureLevel(Engine.FeatureLevel.FEATURE_LEVEL_0).build()
val config = Engine.Config()
//config.forceGLES2Context = true
engine = Engine.Builder()
.config(config)
.featureLevel(Engine.FeatureLevel.FEATURE_LEVEL_0)
.build()
renderer = engine.createRenderer()
scene = engine.createScene()
view = engine.createView()
@@ -123,7 +129,9 @@ class MainActivity : Activity() {
scene.skybox = Skybox.Builder().color(0.035f, 0.035f, 0.035f, 1.0f).build(engine)
// post-processing is not supported at feature level 0
view.isPostProcessingEnabled = false
if (engine.activeFeatureLevel == Engine.FeatureLevel.FEATURE_LEVEL_0) {
view.isPostProcessingEnabled = false
}
// Tell the view which camera we want to use
view.camera = camera

View File

@@ -308,6 +308,11 @@ if (FILAMENT_ENABLE_MULTIVIEW)
add_definitions(-DFILAMENT_ENABLE_MULTIVIEW)
endif()
# Whether to force the profiling mode.
if (FILAMENT_FORCE_PROFILING_MODE)
add_definitions(-DFILAMENT_FORCE_PROFILING_MODE)
endif()
# ==================================================================================================
# Definitions
# ==================================================================================================

View File

@@ -221,9 +221,6 @@ if (FILAMENT_SUPPORTS_VULKAN)
src/vulkan/VulkanSwapChain.h
src/vulkan/VulkanReadPixels.cpp
src/vulkan/VulkanReadPixels.h
src/vulkan/VulkanResourceAllocator.h
src/vulkan/VulkanResources.cpp
src/vulkan/VulkanResources.h
src/vulkan/VulkanTexture.cpp
src/vulkan/VulkanTexture.h
src/vulkan/VulkanUtility.cpp
@@ -378,10 +375,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)

View File

@@ -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

View File

@@ -797,9 +797,20 @@ void MetalDriver::destroyRenderPrimitive(Handle<HwRenderPrimitive> rph) {
}
void MetalDriver::destroyProgram(Handle<HwProgram> ph) {
if (ph) {
destruct_handle<MetalProgram>(ph);
if (UTILS_UNLIKELY(!ph)) {
return;
}
// Remove any cached pipeline states that refer to these programs.
auto* metalProgram = handle_cast<MetalProgram>(ph);
const auto& functions = metalProgram->getFunctionsIfPresent();
if (UTILS_LIKELY(functions.isRaster())) { // only raster pipelines are cached
const auto& raster = functions.getRasterFunctions();
mContext->pipelineStateCache.removeIf([&](const MetalPipelineState& state) {
const auto& [fragment, vertex] = raster;
return state.fragmentFunction == fragment || state.vertexFunction == vertex;
});
}
destruct_handle<MetalProgram>(ph);
}
void MetalDriver::destroyTexture(Handle<HwTexture> th) {
@@ -861,10 +872,20 @@ void MetalDriver::destroyDescriptorSetLayout(Handle<HwDescriptorSetLayout> dslh)
void MetalDriver::destroyDescriptorSet(Handle<HwDescriptorSet> dsh) {
DEBUG_LOG("destroyDescriptorSet(dsh = %d)\n", dsh.getId());
if (dsh) {
executeAfterCurrentCommandBufferCompletes(
[this, dsh]() mutable { destruct_handle<MetalDescriptorSet>(dsh); });
if (!dsh) {
return;
}
// Unbind this descriptor set.
auto* descriptorSet = handle_cast<MetalDescriptorSet>(dsh);
for (size_t i = 0; i < MAX_DESCRIPTOR_SET_COUNT; i++) {
if (UTILS_UNLIKELY(mContext->currentDescriptorSets[i] == descriptorSet)) {
mContext->currentDescriptorSets[i] = nullptr;
}
}
executeAfterCurrentCommandBufferCompletes(
[this, dsh]() mutable { destruct_handle<MetalDescriptorSet>(dsh); });
}
void MetalDriver::terminate() {

View File

@@ -202,6 +202,7 @@ public:
MetalProgram(MetalContext& context, Program&& program) noexcept;
const MetalShaderCompiler::MetalFunctionBundle& getFunctions();
const MetalShaderCompiler::MetalFunctionBundle& getFunctionsIfPresent() const;
private:
void initialize();
@@ -437,7 +438,7 @@ struct MetalTimerQuery : public HwTimerQuery {
struct Status {
std::atomic<bool> available {false};
uint64_t elapsed {0}; // only valid if available is true
std::atomic<uint64_t> elapsed {0}; // only valid if available is true
};
std::shared_ptr<Status> status;

View File

@@ -495,6 +495,10 @@ const MetalShaderCompiler::MetalFunctionBundle& MetalProgram::getFunctions() {
return mFunctionBundle;
}
const MetalShaderCompiler::MetalFunctionBundle& MetalProgram::getFunctionsIfPresent() const {
return mFunctionBundle;
}
void MetalProgram::initialize() {
if (!mToken) {
return;

View File

@@ -83,6 +83,10 @@ public:
return std::get<Compute>(mPrograms);
}
bool isRaster() const { return std::holds_alternative<Raster>(mPrograms); }
bool isCompute() const { return std::holds_alternative<Compute>(mPrograms); }
static MetalFunctionBundle none() {
return MetalFunctionBundle(None{});
}

View File

@@ -28,7 +28,9 @@
#include <memory>
#include <tsl/robin_map.h>
#include <utils/Hash.h>
#include <utils/Invocable.h>
namespace filament {
namespace backend {
@@ -160,6 +162,8 @@ template<typename StateType,
typename HashFn = utils::hash::MurmurHashFn<StateType>>
class StateCache {
using MapType = tsl::robin_map<StateType, MetalType, HashFn>;
public:
StateCache() = default;
@@ -169,6 +173,18 @@ public:
void setDevice(id<MTLDevice> device) noexcept { mDevice = device; }
void removeIf(utils::Invocable<bool(const StateType&)> fn) noexcept {
typename MapType::const_iterator it = mStateCache.begin();
while (it != mStateCache.end()) {
const auto& [key, _] = *it;
if (UTILS_UNLIKELY(fn(key))) {
it = mStateCache.erase(it);
} else {
++it;
}
}
}
MetalType getOrCreateState(const StateType& state) noexcept {
assert_invariant(mDevice);
@@ -196,7 +212,7 @@ private:
StateCreator creator;
id<MTLDevice> mDevice = nil;
tsl::robin_map<StateType, MetalType, HashFn> mStateCache;
MapType mStateCache;
};

View File

@@ -614,7 +614,7 @@ FeatureLevel OpenGLContext::resolveFeatureLevel(GLint major, GLint minor,
featureLevel = FeatureLevel::FEATURE_LEVEL_2;
if (gets.max_texture_image_units >= MAX_FRAGMENT_SAMPLER_COUNT &&
gets.max_combined_texture_image_units >=
(MAX_FRAGMENT_SAMPLER_COUNT + MAX_VERTEX_SAMPLER_COUNT)) {
(MAX_FRAGMENT_SAMPLER_COUNT + MAX_VERTEX_SAMPLER_COUNT)) {
featureLevel = FeatureLevel::FEATURE_LEVEL_3;
}
}
@@ -623,15 +623,13 @@ FeatureLevel OpenGLContext::resolveFeatureLevel(GLint major, GLint minor,
# ifndef IOS // IOS is guaranteed to have ES3.x
else if (UTILS_UNLIKELY(major == 2)) {
// Runtime OpenGL version is ES 2.x
# if defined(BACKEND_OPENGL_LEVEL_GLES30)
// mandatory extensions (all supported by Mali-400 and Adreno 304)
assert_invariant(exts.OES_depth_texture);
assert_invariant(exts.OES_depth24);
assert_invariant(exts.OES_packed_depth_stencil);
assert_invariant(exts.OES_rgb8_rgba8);
assert_invariant(exts.OES_standard_derivatives);
assert_invariant(exts.OES_texture_npot);
# endif
// note: mandatory extensions (all supported by Mali-400 and Adreno 304)
// OES_depth_texture
// OES_depth24
// OES_packed_depth_stencil
// OES_rgb8_rgba8
// OES_standard_derivatives
// OES_texture_npot
featureLevel = FeatureLevel::FEATURE_LEVEL_0;
}
# endif // IOS

View File

@@ -106,17 +106,29 @@
// set to the desired debug level (for internal debugging [Default: None])
#define DEBUG_MARKER_LEVEL DEBUG_MARKER_NONE
// Override the debug markers if we are forcing profiling mode
#if defined(FILAMENT_FORCE_PROFILING_MODE)
# undef DEBUG_GROUP_MARKER_LEVEL
# undef DEBUG_MARKER_LEVEL
# define DEBUG_GROUP_MARKER_LEVEL DEBUG_GROUP_MARKER_NONE
# define DEBUG_MARKER_LEVEL DEBUG_MARKER_PROFILE
#endif
#if DEBUG_MARKER_LEVEL == DEBUG_MARKER_PROFILE
# define DEBUG_MARKER()
# define PROFILE_MARKER(marker) PROFILE_SCOPE(marker);
# if DEBUG_GROUP_MARKER_LEVEL != DEBUG_GROUP_MARKER_NONE
# error PROFILING is exclusive; group markers must be disabled.
# error PROFILING is exclusive; group markers must be disabled.
# endif
# ifndef NDEBUG
# error PROFILING is meaningless in DEBUG mode.
# endif
#elif DEBUG_MARKER_LEVEL > DEBUG_MARKER_NONE
# define DEBUG_MARKER() DebugMarker _debug_marker(*this, __func__);
# define PROFILE_MARKER(marker) DEBUG_MARKER()
# if DEBUG_MARKER_LEVEL & DEBUG_MARKER_PROFILE
# error PROFILING is exclusive; all other debug features must be disabled.
# error PROFILING is exclusive; all other debug features must be disabled.
# endif
#else
# define DEBUG_MARKER()
@@ -394,24 +406,21 @@ void OpenGLDriver::bindTexture(GLuint unit, GLTexture const* t) noexcept {
}
bool OpenGLDriver::useProgram(OpenGLProgram* p) noexcept {
if (UTILS_UNLIKELY(mBoundProgram == p)) {
// program didn't change, don't do anything.
return true;
}
bool success = true;
if (mBoundProgram != p) {
// compile/link the program if needed and call glUseProgram
success = p->use(this, mContext);
assert_invariant(success == p->isValid());
if (success) {
// TODO: we could even improve this if the program could tell us which of the descriptors
// bindings actually changed. In practice, it is likely that set 0 or 1 might not
// change often.
decltype(mInvalidDescriptorSetBindings) changed;
changed.setValue((1 << MAX_DESCRIPTOR_SET_COUNT) - 1);
mInvalidDescriptorSetBindings |= changed;
// compile/link the program if needed and call glUseProgram
bool const success = p->use(this, mContext);
assert_invariant(success == p->isValid());
if (success) {
// TODO: we could even improve this if the program could tell us which of the descriptors
// bindings actually changed. In practice, it is likely that set 0 or 1 might not
// change often.
decltype(mInvalidDescriptorSetBindings) changed;
changed.setValue((1 << MAX_DESCRIPTOR_SET_COUNT) - 1);
mInvalidDescriptorSetBindings |= changed;
mBoundProgram = p;
mBoundProgram = p;
}
}
if (UTILS_UNLIKELY(mContext.isES2() && success)) {
@@ -1673,7 +1682,7 @@ void OpenGLDriver::createSwapChainR(Handle<HwSwapChain> sch, void* nativeWindow,
// See if we need the emulated rec709 output conversion
if (UTILS_UNLIKELY(mContext.isES2())) {
sc->rec709 = (flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE &&
sc->rec709 = ((flags & SWAP_CHAIN_CONFIG_SRGB_COLORSPACE) &&
!mPlatform.isSRGBSwapChainSupported());
}
}

View File

@@ -255,7 +255,9 @@ void OpenGLProgram::updateUniforms(
for (size_t i = 0, c = records.uniforms.size(); i < c; i++) {
Program::Uniform const& u = records.uniforms[i];
GLint const loc = records.locations[i];
if (loc < 0) {
// mRec709Location is special, it is handled by setRec709ColorSpace() and the corresponding
// entry in `buffer` is typically not initialized, so we skip it.
if (loc < 0 || loc == mRec709Location) {
continue;
}
// u.offset is in 'uint32_t' units

View File

@@ -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;
}

View File

@@ -19,8 +19,7 @@
namespace filament::backend {
VulkanTimerQuery::VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices)
: VulkanThreadSafeResource(VulkanResourceType::TIMER_QUERY),
mStartingQueryIndex(std::get<0>(indices)),
: mStartingQueryIndex(std::get<0>(indices)),
mStoppingQueryIndex(std::get<1>(indices)) {}
void VulkanTimerQuery::setFence(std::shared_ptr<VulkanCmdFence> fence) noexcept {

View File

@@ -21,7 +21,7 @@
#include "DriverBase.h"
#include "VulkanResources.h"
#include "vulkan/memory/Resource.h"
#include <utils/Mutex.h>
#include <utils/Condition.h>
@@ -47,17 +47,12 @@ private:
std::atomic<VkResult> status;
};
struct VulkanFence : public HwFence, VulkanResource {
VulkanFence() : VulkanResource(VulkanResourceType::FENCE) {}
explicit VulkanFence(std::shared_ptr<VulkanCmdFence> fence)
: VulkanResource(VulkanResourceType::FENCE),
fence(fence) {}
struct VulkanFence : public HwFence, fvkmemory::ThreadSafeResource {
VulkanFence() {}
std::shared_ptr<VulkanCmdFence> fence;
};
struct VulkanTimerQuery : public HwTimerQuery, VulkanThreadSafeResource {
struct VulkanTimerQuery : public HwTimerQuery, fvkmemory::ThreadSafeResource {
explicit VulkanTimerQuery(std::tuple<uint32_t, uint32_t> indices);
~VulkanTimerQuery();

View File

@@ -126,8 +126,7 @@ struct BlitterUniforms {
}// anonymous namespace
VulkanBlitter::VulkanBlitter(VkPhysicalDevice physicalDevice, VulkanCommands* commands) noexcept
: mPhysicalDevice(physicalDevice),
mCommands(commands) {}
: mPhysicalDevice(physicalDevice), mCommands(commands) {}
void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) {
@@ -151,7 +150,8 @@ void VulkanBlitter::resolve(VulkanAttachment dst, VulkanAttachment src) {
}
#endif
VulkanCommandBuffer& commands = mCommands->get();
VulkanCommandBuffer& commands = dst.texture->getIsProtected() ?
mCommands->getProtected() : mCommands->get();
commands.acquire(src.texture);
commands.acquire(dst.texture);
resolveFast(&commands, aspect, src, dst);
@@ -176,7 +176,8 @@ void VulkanBlitter::blit(VkFilter filter,
#endif
// src and dst should have the same aspect here
VkImageAspectFlags const aspect = src.texture->getImageAspect();
VulkanCommandBuffer& commands = mCommands->get();
VulkanCommandBuffer& commands = dst.texture->getIsProtected() ?
mCommands->getProtected() : mCommands->get();
commands.acquire(src.texture);
commands.acquire(dst.texture);
blitFast(&commands, aspect, filter, src, dst, srcRectPair, dstRectPair);

View File

@@ -58,66 +58,41 @@ VkCommandBuffer createCommandBuffer(VkDevice device, VkCommandPool pool) {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
void VulkanGroupMarkers::push(std::string const& marker, Timestamp start) noexcept {
mMarkers.push_back(marker);
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
mTimestamps.push_back(start.time_since_epoch().count() > 0.0
? start
: std::chrono::high_resolution_clock::now());
#endif
mMarkers.push_back({marker,
start.time_since_epoch().count() > 0.0
? start
: std::chrono::high_resolution_clock::now()});
}
std::pair<std::string, Timestamp> VulkanGroupMarkers::pop() noexcept {
auto const marker = mMarkers.back();
auto ret = mMarkers.back();
mMarkers.pop_back();
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
auto const timestamp = mTimestamps.back();
mTimestamps.pop_back();
return std::make_pair(marker, timestamp);
#else
return std::make_pair(marker, Timestamp{});
#endif
return ret;
}
std::pair<std::string, Timestamp> VulkanGroupMarkers::pop_bottom() noexcept {
auto const marker = mMarkers.front();
auto ret = mMarkers.front();
mMarkers.pop_front();
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
auto const timestamp = mTimestamps.front();
mTimestamps.pop_front();
return std::make_pair(marker, timestamp);
#else
return std::make_pair(marker, Timestamp{});
#endif
return ret;
}
std::pair<std::string, Timestamp> VulkanGroupMarkers::top() const {
std::pair<std::string, Timestamp> const& VulkanGroupMarkers::top() const {
assert_invariant(!empty());
auto const marker = mMarkers.back();
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
auto const topTimestamp = mTimestamps.front();
return std::make_pair(marker, topTimestamp);
#else
return std::make_pair(marker, Timestamp{});
#endif
return mMarkers.back();
}
bool VulkanGroupMarkers::empty() const noexcept {
return mMarkers.empty();
}
#endif // FVK_DEBUG_GROUP_MARKERS
VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext* context, VulkanResourceAllocator* allocator,
VkDevice device, VkQueue queue, VkCommandPool pool, bool isProtected)
VulkanCommandBuffer::VulkanCommandBuffer(VulkanContext* context, VkDevice device, VkQueue queue,
VkCommandPool pool, bool isProtected)
: mContext(context),
mMarkerCount(0),
isProtected(isProtected),
mDevice(device),
mQueue(queue),
mResourceManager(allocator),
mBuffer(createCommandBuffer(device, pool)),
mFenceStatus(std::make_shared<VulkanCmdFence>(VK_INCOMPLETE)) {
VkSemaphoreCreateInfo sci{.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO};
@@ -134,7 +109,7 @@ VulkanCommandBuffer::~VulkanCommandBuffer() {
void VulkanCommandBuffer::reset() noexcept {
mMarkerCount = 0;
mResourceManager.clear();
mResources.clear();
mWaitSemaphores.clear();
// Internally we use the VK_INCOMPLETE status to mean "not yet submitted". When this fence
@@ -257,8 +232,8 @@ VkSemaphore VulkanCommandBuffer::submit() {
return mSubmission;
}
CommandBufferPool::CommandBufferPool(VulkanContext* context, VulkanResourceAllocator* allocator,
VkDevice device, VkQueue queue, uint8_t queueFamilyIndex, bool isProtected)
CommandBufferPool::CommandBufferPool(VulkanContext* context, VkDevice device, VkQueue queue,
uint8_t queueFamilyIndex, bool isProtected)
: mDevice(device),
mRecording(INVALID) {
VkCommandPoolCreateInfo createInfo = {
@@ -271,8 +246,8 @@ CommandBufferPool::CommandBufferPool(VulkanContext* context, VulkanResourceAlloc
vkCreateCommandPool(device, &createInfo, VKALLOC, &mPool);
for (size_t i = 0; i < CAPACITY; ++i) {
mBuffers.emplace_back(std::make_unique<VulkanCommandBuffer>(context, allocator, device,
queue, mPool, isProtected));
mBuffers.emplace_back(
std::make_unique<VulkanCommandBuffer>(context, device, queue, mPool, isProtected));
}
}
@@ -303,6 +278,19 @@ VulkanCommandBuffer& CommandBufferPool::getRecording() {
auto& recording = *mBuffers[mRecording];
recording.begin();
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
if (mGroupMarkers) {
std::unique_ptr<VulkanGroupMarkers> markers = std::make_unique<VulkanGroupMarkers>();
while (!mGroupMarkers->empty()) {
auto [marker, timestamp] = mGroupMarkers->pop_bottom();
recording.pushMarker(marker.c_str());
markers->push(marker, timestamp);
}
std::swap(mGroupMarkers, markers);
}
#endif
return recording;
}
@@ -357,29 +345,46 @@ void CommandBufferPool::waitFor(VkSemaphore previousAction) {
recording->insertWait(previousAction);
}
void CommandBufferPool::pushMarker(char const* marker) {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
std::string CommandBufferPool::topMarker() const {
if (!mGroupMarkers || mGroupMarkers->empty()) {
return "";
}
return std::get<0>(mGroupMarkers->top());
}
void CommandBufferPool::pushMarker(char const* marker, VulkanGroupMarkers::Timestamp timestamp) {
if (!mGroupMarkers) {
mGroupMarkers = std::make_unique<VulkanGroupMarkers>();
}
mGroupMarkers->push(marker, timestamp);
getRecording().pushMarker(marker);
}
void CommandBufferPool::popMarker() {
getRecording().popMarker();
std::pair<std::string, VulkanGroupMarkers::Timestamp> CommandBufferPool::popMarker() {
assert_invariant(mGroupMarkers && !mGroupMarkers->empty());
auto ret = mGroupMarkers->pop();
// Note that if we're popping a marker while not recording, we would just pop the conceptual
// stack of marker (i.e. mGroupMarkers) and not carry out the pop on the command buffer.
if (isRecording()) {
getRecording().popMarker();
}
return ret;
}
void CommandBufferPool::insertEvent(char const* marker) {
getRecording().insertEvent(marker);
}
#endif // FVK_DEBUG_GROUP_MARKERS
VulkanCommands::VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context,
VulkanResourceAllocator* allocator)
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context)
: mDevice(device),
mProtectedQueue(protectedQueue),
mProtectedQueueFamilyIndex(protectedQueueFamilyIndex),
mAllocator(allocator),
mContext(context),
mPool(std::make_unique<CommandBufferPool>(context, allocator, device, queue, queueFamilyIndex,
false)) {
}
mPool(std::make_unique<CommandBufferPool>(context, device, queue, queueFamilyIndex, false)) {}
void VulkanCommands::terminate() {
mPool.reset();
@@ -395,14 +400,19 @@ VulkanCommandBuffer& VulkanCommands::getProtected() {
assert_invariant(mProtectedQueue != VK_NULL_HANDLE);
if (!mProtectedPool) {
mProtectedPool = std::make_unique<CommandBufferPool>(mContext, mAllocator, mDevice,
mProtectedQueue, mProtectedQueueFamilyIndex, true);
mProtectedPool = std::make_unique<CommandBufferPool>(mContext, mDevice, mProtectedQueue,
mProtectedQueueFamilyIndex, true);
}
auto& ret = mProtectedPool->getRecording();
return ret;
}
bool VulkanCommands::flush() {
// It's possible to call flush and wait at "terminate", in which case, we'll just return.
if (!mPool && !mProtectedPool) {
return false;
}
VkSemaphore dependency = mInjectedDependency;
VkSemaphore lastSubmit = mLastSubmit;
bool hasFlushed = false;
@@ -434,6 +444,11 @@ bool VulkanCommands::flush() {
}
void VulkanCommands::wait() {
// It's possible to call flush and wait at "terminate", in which case, we'll just return.
if (!mPool && !mProtectedPool) {
return;
}
FVK_SYSTRACE_CONTEXT();
FVK_SYSTRACE_START("commands::wait");
@@ -465,47 +480,31 @@ void VulkanCommands::updateFences() {
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
void VulkanCommands::pushGroupMarker(char const* str, VulkanGroupMarkers::Timestamp timestamp) {
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
// If the timestamp is not 0, then we are carrying over a marker across buffer submits.
// If it is 0, then this is a normal marker push and we should just print debug line as usual.
if (timestamp.time_since_epoch().count() == 0.0) {
FVK_LOGD << "----> " << str << utils::io::endl;
}
#endif
if (!mGroupMarkers) {
mGroupMarkers = std::make_unique<VulkanGroupMarkers>();
}
mGroupMarkers->push(str, timestamp);
mPool->pushMarker(str);
mPool->pushMarker(str, timestamp);
if (mProtectedPool) {
mProtectedPool->pushMarker(str);
mProtectedPool->pushMarker(str, timestamp);
}
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
FVK_LOGD << "----> " << str << utils::io::endl;
#endif
}
void VulkanCommands::popGroupMarker() {
assert_invariant(mGroupMarkers);
if (!mGroupMarkers->empty()) {
VkCommandBuffer const cmdbuffer = get().buffer();
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
auto const [marker, startTime] = mGroupMarkers->pop();
auto const endTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = endTime - startTime;
FVK_LOGD << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms"
<< utils::io::endl;
auto ret = mPool->popMarker();
auto const& marker = ret.first;
auto const& startTime = ret.second;
auto const endTime = std::chrono::high_resolution_clock::now();
std::chrono::duration<double> diff = endTime - startTime;
FVK_LOGD << "<---- " << marker << " elapsed: " << (diff.count() * 1000) << " ms"
<< utils::io::endl;
#else
mGroupMarkers->pop();
#endif
mPool->popMarker();
if (mProtectedPool) {
mProtectedPool->popMarker();
}
} else if (mCarriedOverMarkers && !mCarriedOverMarkers->empty()) {
// It could be that pop is called between flush() and get() (new command buffer), in which
// case the marker is in "carried over" state, we'd just remove that. Since the
// mCarriedOverMarkers is in the opposite order, we pop the bottom instead of the top.
mCarriedOverMarkers->pop_bottom();
mPool->popMarker();
#endif // FVK_DEBUG_PRINT_GROUP_MARKERS
if (mProtectedPool) {
mProtectedPool->popMarker();
}
}
@@ -517,10 +516,10 @@ void VulkanCommands::insertEventMarker(char const* str, uint32_t len) {
}
std::string VulkanCommands::getTopGroupMarker() const {
if (!mGroupMarkers || mGroupMarkers->empty()) {
return "";
if (mProtectedPool) {
return mProtectedPool->topMarker();
}
return std::get<0>(mGroupMarkers->top());
return mPool->topMarker();
}
#endif // FVK_DEBUG_GROUP_MARKERS

View File

@@ -23,8 +23,8 @@
#include "VulkanAsyncHandles.h"
#include "VulkanConstants.h"
#include "VulkanResources.h"
#include "VulkanUtility.h"
#include "vulkan/memory/ResourcePointer.h"
#include <utils/Condition.h>
#include <utils/FixedCapacityVector.h>
@@ -39,6 +39,8 @@
namespace filament::backend {
using namespace fvkmemory;
struct VulkanContext;
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
@@ -49,14 +51,11 @@ public:
void push(std::string const& marker, Timestamp start = {}) noexcept;
std::pair<std::string, Timestamp> pop() noexcept;
std::pair<std::string, Timestamp> pop_bottom() noexcept;
std::pair<std::string, Timestamp> top() const;
std::pair<std::string, Timestamp> const& top() const;
bool empty() const noexcept;
private:
std::list<std::string> mMarkers;
#if FVK_ENABLED(FVK_DEBUG_PRINT_GROUP_MARKERS)
std::list<Timestamp> mTimestamps;
#endif
std::list<std::pair<std::string, Timestamp>> mMarkers;
};
#endif // FVK_DEBUG_GROUP_MARKERS
@@ -65,7 +64,7 @@ private:
// DriverApi fence object and should not be destroyed until both the DriverApi object is freed and
// we're done waiting on the most recent submission of the given command buffer.
struct VulkanCommandBuffer {
VulkanCommandBuffer(VulkanContext* mContext, VulkanResourceAllocator* allocator,
VulkanCommandBuffer(VulkanContext* mContext,
VkDevice device, VkQueue queue, VkCommandPool pool, bool isProtected);
VulkanCommandBuffer(VulkanCommandBuffer const&) = delete;
@@ -73,13 +72,10 @@ struct VulkanCommandBuffer {
~VulkanCommandBuffer();
inline void acquire(VulkanResource* resource) {
mResourceManager.acquire(resource);
inline void acquire(fvkmemory::resource_ptr<fvkmemory::Resource> resource) {
mResources.push_back(resource);
}
inline void acquire(VulkanAcquireOnlyResourceManager* srcResources) {
mResourceManager.acquireAll(srcResources);
}
void reset() noexcept;
inline void insertWait(VkSemaphore sem) {
@@ -119,21 +115,20 @@ private:
bool const isProtected;
VkDevice mDevice;
VkQueue mQueue;
VulkanAcquireOnlyResourceManager mResourceManager;
CappedArray<VkSemaphore, 2> mWaitSemaphores;
VkCommandBuffer mBuffer;
VkSemaphore mSubmission;
VkFence mFence;
std::shared_ptr<VulkanCmdFence> mFenceStatus;
std::vector<fvkmemory::resource_ptr<Resource>> mResources;
};
struct CommandBufferPool {
using ActiveBuffers = utils::bitset32;
using ActiveBuffers = utils::bitset64;
static constexpr int8_t INVALID = -1;
CommandBufferPool(VulkanContext* context, VulkanResourceAllocator* allocator, VkDevice device,
VkQueue queue, uint8_t queueFamilyIndex, bool isProtected);
CommandBufferPool(VulkanContext* context, VkDevice device, VkQueue queue,
uint8_t queueFamilyIndex, bool isProtected);
~CommandBufferPool();
VulkanCommandBuffer& getRecording();
@@ -142,12 +137,14 @@ struct CommandBufferPool {
void update();
VkSemaphore flush();
void wait();
void waitFor(VkSemaphore previousAction);
void pushMarker(char const* marker);
void popMarker();
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
std::string topMarker() const;
void pushMarker(char const* marker, VulkanGroupMarkers::Timestamp timestamp);
std::pair<std::string, VulkanGroupMarkers::Timestamp> popMarker();
void insertEvent(char const* marker);
#endif
inline bool isRecording() const { return mRecording != INVALID; }
@@ -155,12 +152,21 @@ private:
static constexpr int CAPACITY = FVK_MAX_COMMAND_BUFFERS;
// int8 only goes up to 127, therefore capacity must be less than that.
static_assert(CAPACITY < 128);
// The number of bits in ActiveBuffers describe the usage of the buffers in the pool, so must be
// larger than the size of the pool.
static_assert(sizeof(ActiveBuffers) * 8 >= CAPACITY);
using BufferList = utils::FixedCapacityVector<std::unique_ptr<VulkanCommandBuffer>>;
VkDevice mDevice;
VkCommandPool mPool;
ActiveBuffers mSubmitted;
std::vector<std::unique_ptr<VulkanCommandBuffer>> mBuffers;
int8_t mRecording;
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
#endif
};
// Manages a set of command buffers and semaphores, exposing an API that is significantly simpler
@@ -189,8 +195,7 @@ private:
class VulkanCommands {
public:
VulkanCommands(VkDevice device, VkQueue queue, uint32_t queueFamilyIndex,
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context,
VulkanResourceAllocator* allocator);
VkQueue protectedQueue, uint32_t protectedQueueFamilyIndex, VulkanContext* context);
void terminate();
@@ -241,7 +246,6 @@ private:
VkQueue const mProtectedQueue;
// For defered initialization if/when we need protected content
uint32_t const mProtectedQueueFamilyIndex;
VulkanResourceAllocator* mAllocator;
VulkanContext* mContext;
std::unique_ptr<CommandBufferPool> mPool;
@@ -249,11 +253,6 @@ private:
VkSemaphore mInjectedDependency = VK_NULL_HANDLE;
VkSemaphore mLastSubmit = VK_NULL_HANDLE;
#if FVK_ENABLED(FVK_DEBUG_GROUP_MARKERS)
std::unique_ptr<VulkanGroupMarkers> mGroupMarkers;
std::unique_ptr<VulkanGroupMarkers> mCarriedOverMarkers;
#endif
};
} // namespace filament::backend

View File

@@ -1,7 +1,7 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
@@ -63,7 +63,7 @@
#define FVK_DEBUG_SHADER_MODULE 0x00000800
#define FVK_DEBUG_READ_PIXELS 0x00001000
#define FVK_DEBUG_PIPELINE_CACHE 0x00002000
#define FVK_DEBUG_ALLOCATION 0x00004000
#define FVK_DEBUG_STAGING_ALLOCATION 0x00004000
// Enable the debug utils extension if it is available.
#define FVK_DEBUG_DEBUG_UTILS 0x00008000
@@ -98,6 +98,12 @@
#define FVK_DEBUG_FLAGS 0
#endif
// Override the debug flags if we are forcing profiling mode
#if defined(FILAMENT_FORCE_PROFILING_MODE)
#undef FVK_DEBUG_FLAGS
#define FVK_DEBUG_FLAGS (FVK_DEBUG_PROFILING)
#endif
#define FVK_ENABLED(flags) (((FVK_DEBUG_FLAGS) & (flags)) == (flags))
// Group marker only works only if validation or debug utils is enabled since it uses
@@ -133,6 +139,10 @@ static_assert(FVK_ENABLED(FVK_DEBUG_VALIDATION));
#if FVK_DEBUG_FLAGS == FVK_DEBUG_PROFILING
#ifndef NDEBUG
#error PROFILING is meaningless in DEBUG mode.
#endif
#define FVK_SYSTRACE_CONTEXT()
#define FVK_SYSTRACE_START(marker)
#define FVK_SYSTRACE_END()
@@ -186,14 +196,16 @@ constexpr static const int FVK_REQUIRED_VERSION_MINOR = 1;
// buffers that have been submitted but have not yet finished rendering. Note that Filament can
// issue multiple commit calls in a single frame, and that we use a triple buffered swap chain on
// some platforms.
constexpr static const int FVK_MAX_COMMAND_BUFFERS = 10;
//
// Heuristic: Triple Buffering (3) multiplied by maximum number of renderpasses (15).
constexpr static const int FVK_MAX_COMMAND_BUFFERS = 3 * 15;
// Number of command buffer submissions that should occur before an unused pipeline is removed
// from the cache.
//
// If this number is low, VkPipeline construction will occur frequently, which can
// be extremely slow. If this number is high, the memory footprint will be large.
constexpr static const int FVK_MAX_PIPELINE_AGE = 10;
constexpr static const int FVK_MAX_PIPELINE_AGE = FVK_MAX_COMMAND_BUFFERS;
// VulkanPipelineCache does not track which command buffers contain references to which pipelines,
// instead it simply waits for at least FVK_MAX_COMMAND_BUFFERS submissions to occur before

View File

@@ -25,15 +25,11 @@
#include <backend/PixelBufferDescriptor.h>
#include <utils/Panic.h>
#include <utils/FixedCapacityVector.h>
#include <algorithm> // for std::max
using namespace bluevk;
using utils::FixedCapacityVector;
namespace {
} // end anonymous namespace
@@ -113,7 +109,7 @@ void VulkanTimestamps::clearQuery(uint32_t queryIndex) {
}
void VulkanTimestamps::beginQuery(VulkanCommandBuffer const* commands,
VulkanTimerQuery* query) {
fvkmemory::resource_ptr<VulkanTimerQuery> query) {
uint32_t const index = query->getStartingQueryIndex();
auto const cmdbuffer = commands->buffer();
@@ -125,19 +121,20 @@ void VulkanTimestamps::beginQuery(VulkanCommandBuffer const* commands,
}
void VulkanTimestamps::endQuery(VulkanCommandBuffer const* commands,
VulkanTimerQuery const* query) {
fvkmemory::resource_ptr<VulkanTimerQuery> query) {
uint32_t const index = query->getStoppingQueryIndex();
vkCmdWriteTimestamp(commands->buffer(), VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, mPool, index);
}
VulkanTimestamps::QueryResult VulkanTimestamps::getResult(VulkanTimerQuery const* query) {
VulkanTimestamps::QueryResult VulkanTimestamps::getResult(
fvkmemory::resource_ptr<VulkanTimerQuery> query) {
uint32_t const index = query->getStartingQueryIndex();
QueryResult result;
size_t const dataSize = result.size() * sizeof(uint64_t);
VkDeviceSize const stride = sizeof(uint64_t) * 2;
VkResult vkresult =
vkGetQueryPoolResults(mDevice, mPool, index, 2, dataSize, (void*) result.data(),
stride, VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
vkGetQueryPoolResults(mDevice, mPool, index, 2, dataSize, (void*) result.data(), stride,
VK_QUERY_RESULT_64_BIT | VK_QUERY_RESULT_WITH_AVAILABILITY_BIT);
FILAMENT_CHECK_POSTCONDITION(vkresult == VK_SUCCESS || vkresult == VK_NOT_READY)
<< "vkGetQueryPoolResults error: " << static_cast<int32_t>(vkresult);
if (vkresult == VK_NOT_READY) {

View File

@@ -21,6 +21,8 @@
#include "VulkanImageUtility.h"
#include "VulkanUtility.h"
#include "vulkan/memory/ResourcePointer.h"
#include <utils/bitset.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Mutex.h>
@@ -41,10 +43,10 @@ struct VulkanTimerQuery;
struct VulkanCommandBuffer;
struct VulkanAttachment {
VulkanTexture* texture = nullptr;
fvkmemory::resource_ptr<VulkanTexture> texture;
uint8_t level = 0;
uint8_t layerCount = 1;
uint16_t layer = 0;
uint8_t layer = 0;
bool isDepth() const;
VkImage getImage() const;
@@ -70,9 +72,11 @@ public:
std::tuple<uint32_t, uint32_t> getNextQuery();
void clearQuery(uint32_t queryIndex);
void beginQuery(VulkanCommandBuffer const* commands, VulkanTimerQuery* query);
void endQuery(VulkanCommandBuffer const* commands, VulkanTimerQuery const* query);
QueryResult getResult(VulkanTimerQuery const* query);
void beginQuery(VulkanCommandBuffer const* commands,
fvkmemory::resource_ptr<VulkanTimerQuery> query);
void endQuery(VulkanCommandBuffer const* commands,
fvkmemory::resource_ptr<VulkanTimerQuery> query);
QueryResult getResult(fvkmemory::resource_ptr<VulkanTimerQuery> query);
private:
VkDevice mDevice;
@@ -82,7 +86,9 @@ private:
};
struct VulkanRenderPass {
VulkanRenderTarget* renderTarget;
// Between the begin and end command render pass we cache the command buffer
VulkanCommandBuffer* commandBuffer;
fvkmemory::resource_ptr<VulkanRenderTarget> renderTarget;
VkRenderPass renderPass;
RenderPassParams params;
int currentSubpass;

File diff suppressed because it is too large Load Diff

View File

@@ -24,13 +24,14 @@
#include "VulkanHandles.h"
#include "VulkanPipelineCache.h"
#include "VulkanReadPixels.h"
#include "VulkanResourceAllocator.h"
#include "VulkanSamplerCache.h"
#include "VulkanStagePool.h"
#include "VulkanUtility.h"
#include "backend/DriverEnums.h"
#include "caching/VulkanDescriptorSetManager.h"
#include "caching/VulkanPipelineLayoutCache.h"
#include "memory/ResourceManager.h"
#include "memory/ResourcePointer.h"
#include "DriverBase.h"
#include "private/backend/Driver.h"
@@ -77,6 +78,9 @@ public:
#endif // FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
private:
template<typename D>
using resource_ptr = fvkmemory::resource_ptr<D>;
static constexpr uint8_t MAX_SAMPLER_BINDING_COUNT = Program::SAMPLER_BINDING_COUNT;
void debugCommandBegin(CommandStream* cmds, bool synchronous,
@@ -113,21 +117,16 @@ private:
void collectGarbage();
VulkanPlatform* mPlatform = nullptr;
fvkmemory::ResourceManager mResourceManager;
std::unique_ptr<VulkanTimestamps> mTimestamps;
VulkanSwapChain* mCurrentSwapChain = nullptr;
VulkanRenderTarget* mDefaultRenderTarget = nullptr;
resource_ptr<VulkanSwapChain> mCurrentSwapChain;
resource_ptr<VulkanRenderTarget> mDefaultRenderTarget;
VulkanRenderPass mCurrentRenderPass = {};
VmaAllocator mAllocator = VK_NULL_HANDLE;
VkDebugReportCallbackEXT mDebugCallback = VK_NULL_HANDLE;
VulkanContext mContext = {};
VulkanResourceAllocator mResourceAllocator;
VulkanResourceManager mResourceManager;
// Used for resources that are created synchronously and used and destroyed on the backend
// thread.
VulkanThreadSafeResourceManager mThreadSafeResourceManager;
VulkanCommands mCommands;
VulkanPipelineLayoutCache mPipelineLayoutCache;
@@ -142,7 +141,7 @@ private:
// This is necessary for us to write to push constants after binding a pipeline.
struct {
VulkanProgram* program;
resource_ptr<VulkanProgram> program;
VkPipelineLayout pipelineLayout;
DescriptorSetMask descriptorSetMask;
} mBoundPipeline = {};

View File

@@ -22,8 +22,8 @@
#include "VulkanDriver.h"
#include "VulkanMemory.h"
#include "VulkanResourceAllocator.h"
#include "VulkanUtility.h"
#include "vulkan/memory/ResourcePointer.h"
#include "spirv/VulkanSpirvUtils.h"
#include <backend/platforms/VulkanPlatform.h>
@@ -105,9 +105,10 @@ BitmaskGroup fromBackendLayout(DescriptorSetLayout const& layout) {
return mask;
}
VulkanTexture* initMsaaTexture(VulkanTexture* texture, VkDevice device,
fvkmemory::resource_ptr<VulkanTexture> initMsaaTexture(
fvkmemory::resource_ptr<VulkanTexture> texture, VkDevice device,
VkPhysicalDevice physicalDevice, VulkanContext const& context, VmaAllocator allocator,
VulkanCommands* commands, VulkanResourceAllocator* handleAllocator, uint8_t levels,
VulkanCommands* commands, fvkmemory::ResourceManager* resManager, uint8_t levels,
uint8_t samples, VulkanStagePool& stagePool) {
assert_invariant(texture);
auto msTexture = texture->getSidecar();
@@ -117,10 +118,9 @@ VulkanTexture* initMsaaTexture(VulkanTexture* texture, VkDevice device,
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 */);
msTexture = resource_ptr<VulkanTexture>::construct(resManager, device, physicalDevice,
context, allocator, resManager, commands, texture->target, levels, texture->format,
samples, texture->width, texture->height, texture->depth, usage, stagePool);
texture->setSidecar(msTexture);
}
return msTexture;
@@ -128,19 +128,18 @@ VulkanTexture* initMsaaTexture(VulkanTexture* texture, VkDevice device,
} // anonymous namespace
void VulkanDescriptorSet::acquire(fvkmemory::resource_ptr<VulkanTexture> texture) {
mResources.push_back(texture);
}
void VulkanDescriptorSet::acquire(fvkmemory::resource_ptr<VulkanBufferObject> obj) {
mResources.push_back(obj);
}
VulkanDescriptorSetLayout::VulkanDescriptorSetLayout(DescriptorSetLayout const& layout)
: VulkanResource(VulkanResourceType::DESCRIPTOR_SET_LAYOUT),
bitmask(fromBackendLayout(layout)),
: bitmask(fromBackendLayout(layout)),
count(Count::fromLayoutBitmask(bitmask)) {}
void VulkanDescriptorSet::acquire(VulkanTexture* texture) {
mResources.acquire(texture);
}
void VulkanDescriptorSet::acquire(VulkanBufferObject* bufferObject) {
mResources.acquire(bufferObject);
}
PushConstantDescription::PushConstantDescription(backend::Program const& program) noexcept {
mRangeCount = 0;
for (auto stage : { ShaderStage::VERTEX, ShaderStage::FRAGMENT, ShaderStage::COMPUTE }) {
@@ -164,9 +163,9 @@ PushConstantDescription::PushConstantDescription(backend::Program const& program
}
}
void PushConstantDescription::write(VulkanCommands* commands, VkPipelineLayout layout,
void PushConstantDescription::write(VkCommandBuffer cmdbuf, VkPipelineLayout layout,
backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) {
VulkanCommandBuffer* cmdbuf = &(commands->get());
uint32_t binaryValue = 0;
UTILS_UNUSED_IN_RELEASE auto const& types = mTypes[(uint8_t) stage];
if (std::holds_alternative<bool>(value)) {
@@ -182,13 +181,12 @@ void PushConstantDescription::write(VulkanCommands* commands, VkPipelineLayout l
int const ival = std::get<int>(value);
binaryValue = *reinterpret_cast<uint32_t const*>(&ival);
}
vkCmdPushConstants(cmdbuf->buffer(), layout, getVkStage(stage), index * ENTRY_SIZE, ENTRY_SIZE,
vkCmdPushConstants(cmdbuf, layout, getVkStage(stage), index * ENTRY_SIZE, ENTRY_SIZE,
&binaryValue);
}
VulkanProgram::VulkanProgram(VkDevice device, Program const& builder) noexcept
: HwProgram(builder.getName()),
VulkanResource(VulkanResourceType::PROGRAM),
mInfo(new(std::nothrow) PipelineInfo(builder)),
mDevice(device) {
@@ -256,23 +254,24 @@ 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;
}
void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) {
VulkanRenderTarget::~VulkanRenderTarget() = default;
void VulkanRenderTarget::bindToSwapChain(fvkmemory::resource_ptr<VulkanSwapChain> swapchain) {
assert_invariant(!mOffscreen);
VkExtent2D const extent = swapChain.getExtent();
VkExtent2D const extent = swapchain->getExtent();
width = extent.width;
height = extent.height;
mProtected = swapChain.isProtected();
mProtected = swapchain->isProtected();
VulkanAttachment color = {.texture = swapChain.getCurrentColor()};
VulkanAttachment color = {};
color.texture = swapchain->getCurrentColor();
mInfo->attachments = {color};
auto& fbkey = mInfo->fbkey;
@@ -284,33 +283,30 @@ void VulkanRenderTarget::bindToSwapChain(VulkanSwapChain& swapChain) {
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()};
if (swapchain->getDepth()) {
VulkanAttachment depth = {};
depth.texture = swapchain->getDepth();
mInfo->attachments.push_back(depth);
mInfo->depthIndex = 1;
rpkey.depthFormat = depth.getFormat();
fbkey.depth = depth.getImageView();
} else {
rpkey.depthFormat = VK_FORMAT_UNDEFINED;
fbkey.depth = VK_NULL_HANDLE;
}
mInfo->colors.set(0);
}
VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice,
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
VulkanResourceAllocator* handleAllocator, uint32_t width, uint32_t height, uint8_t samples,
VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
VulkanContext const& context, fvkmemory::ResourceManager* resourceManager,
VmaAllocator allocator, VulkanCommands* commands, uint32_t width, uint32_t height,
uint8_t samples, VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool, uint8_t layerCount)
: HwRenderTarget(width, height),
VulkanResource(VulkanResourceType::RENDER_TARGET),
mOffscreen(true),
mProtected(false),
mResources(handleAllocator),
mInfo(std::make_unique<Auxiliary>()) {
auto& depth = depthStencil[0];
// Constrain the sample count according to both kinds of sample count masks obtained from
@@ -340,6 +336,8 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
continue;
}
mProtected |= texture->getIsProtected();
attachments.push_back(attachment);
mInfo->colors.set(index);
@@ -347,14 +345,11 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
fbkey.color[index] = attachment.getImageView();
fbkey.resolve[index] = VK_NULL_HANDLE;
mResources.acquire(attachment.texture);
if (samples > 1) {
VulkanAttachment msaaAttachment = {};
if (texture->samples == 1) {
auto msaaTexture = initMsaaTexture(texture, device, physicalDevice, context,
allocator, commands, handleAllocator,
((VulkanTexture const*) texture)->levels, samples, stagePool);
allocator, commands, resourceManager, texture->levels, samples, stagePool);
if (msaaTexture && msaaTexture->isTransientAttachment()) {
rpkey.usesLazilyAllocatedMemory |= (1 << index);
}
@@ -375,7 +370,6 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
}
fbkey.color[index] = msaaAttachment.getImageView();
msaa.push_back(msaaAttachment);
mResources.acquire(msaaAttachment.texture);
}
}
@@ -388,19 +382,18 @@ VulkanRenderTarget::VulkanRenderTarget(VkDevice device, VkPhysicalDevice physica
auto depthTexture = depth.texture;
mInfo->depthIndex = (uint8_t) attachments.size();
attachments.push_back(depth);
mResources.acquire(depthTexture);
fbkey.depth = depth.getImageView();
if (samples > 1) {
mInfo->msaaDepthIndex = mInfo->depthIndex;
if (depthTexture->samples == 1) {
// MSAA depth texture must have the mipmap count of 1
uint8_t const msLevel = 1;
// Create sidecar MSAA texture for the depth attachment if it does not already exist.
auto msaa = initMsaaTexture(depthTexture, device, physicalDevice, context, allocator,
commands, handleAllocator, msLevel, samples, stagePool);
// Create sidecar MSAA texture for the depth attachment if it does not already
// exist.
auto msaa = initMsaaTexture(depthTexture, device, physicalDevice, context,
allocator, commands, resourceManager, msLevel, samples, stagePool);
mInfo->msaaDepthIndex = (uint8_t) attachments.size();
attachments.push_back({ .texture = msaa, .layerCount = layerCount });
mResources.acquire(msaa);
}
}
}
@@ -491,9 +484,7 @@ void VulkanRenderTarget::emitBarriersEndRenderPass(VulkanCommandBuffer& commands
VulkanVertexBufferInfo::VulkanVertexBufferInfo(
uint8_t bufferCount, uint8_t attributeCount, AttributeArray const& attributes)
: HwVertexBufferInfo(bufferCount, attributeCount),
VulkanResource(VulkanResourceType::VERTEX_BUFFER_INFO),
mInfo(attributes.size()) {
auto attribDesc = mInfo.mSoa.data<PipelineInfo::ATTRIBUTE_DESCRIPTION>();
auto bufferDesc = mInfo.mSoa.data<PipelineInfo::BUFFER_DESCRIPTION>();
auto offsets = mInfo.mSoa.data<PipelineInfo::OFFSETS>();
@@ -531,19 +522,15 @@ VulkanVertexBufferInfo::VulkanVertexBufferInfo(
}
VulkanVertexBuffer::VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
VulkanResourceAllocator* allocator,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih)
uint32_t vertexCount, fvkmemory::resource_ptr<VulkanVertexBufferInfo> vbi)
: HwVertexBuffer(vertexCount),
VulkanResource(VulkanResourceType::VERTEX_BUFFER),
vbih(vbih),
mBuffers(MAX_VERTEX_BUFFER_COUNT), // TODO: can we do better here?
mResources(allocator) {
vbi(vbi),
// TODO: Seems a bit wasteful. can we do better here?
mBuffers(MAX_VERTEX_BUFFER_COUNT) {
}
void VulkanVertexBuffer::setBuffer(VulkanResourceAllocator const& allocator,
VulkanBufferObject* bufferObject, uint32_t index) {
VulkanVertexBufferInfo const* const vbi =
const_cast<VulkanResourceAllocator&>(allocator).handle_cast<VulkanVertexBufferInfo*>(vbih);
void VulkanVertexBuffer::setBuffer(fvkmemory::resource_ptr<VulkanBufferObject> bufferObject,
uint32_t index) {
size_t const count = vbi->getAttributeCount();
VkBuffer* const vkbuffers = getVkBuffers();
int8_t const* const attribToBuffer = vbi->getAttributeToBuffer();
@@ -552,25 +539,20 @@ void VulkanVertexBuffer::setBuffer(VulkanResourceAllocator const& allocator,
vkbuffers[attribIndex] = bufferObject->buffer.getGpuBuffer();
}
}
mResources.acquire(bufferObject);
mResources.push_back(bufferObject);
}
VulkanBufferObject::VulkanBufferObject(VmaAllocator allocator, VulkanStagePool& stagePool,
uint32_t byteCount, BufferObjectBinding bindingType)
: HwBufferObject(byteCount),
VulkanResource(VulkanResourceType::BUFFER_OBJECT),
buffer(allocator, stagePool, getBufferObjectUsage(bindingType), byteCount),
bindingType(bindingType) {}
VulkanRenderPrimitive::VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator,
PrimitiveType pt, Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh)
: VulkanResource(VulkanResourceType::RENDER_PRIMITIVE),
mResources(resourceAllocator) {
type = pt;
vertexBuffer = resourceAllocator->handle_cast<VulkanVertexBuffer*>(vbh);
indexBuffer = resourceAllocator->handle_cast<VulkanIndexBuffer*>(ibh);
mResources.acquire(vertexBuffer);
mResources.acquire(indexBuffer);
}
VulkanRenderPrimitive::VulkanRenderPrimitive(PrimitiveType pt,
fvkmemory::resource_ptr<VulkanVertexBuffer> vb,
fvkmemory::resource_ptr<VulkanIndexBuffer> ib)
: HwRenderPrimitive{.type = pt},
vertexBuffer(vb),
indexBuffer(ib) {}
} // namespace filament::backend

View File

@@ -20,14 +20,14 @@
// This needs to be at the top
#include "DriverBase.h"
#include "VulkanAsyncHandles.h"
#include "VulkanBuffer.h"
#include "VulkanFboCache.h"
#include "VulkanResources.h"
#include "VulkanSwapChain.h"
#include "VulkanTexture.h"
#include "VulkanUtility.h"
#include "vulkan/memory/Resource.h"
#include <private/backend/SamplerGroup.h>
#include <backend/Program.h>
#include <utils/bitset.h>
@@ -35,6 +35,8 @@
#include <utils/Mutex.h>
#include <utils/StructureOfArrays.h>
#include <array>
namespace filament::backend {
namespace {
@@ -55,7 +57,7 @@ inline uint8_t collapsedCount(Bitmask const& mask) {
class VulkanTimestamps;
struct VulkanBufferObject;
struct VulkanDescriptorSetLayout : public VulkanResource, HwDescriptorSetLayout {
struct VulkanDescriptorSetLayout : public HwDescriptorSetLayout, fvkmemory::Resource {
static constexpr uint8_t UNIQUE_DESCRIPTOR_SET_COUNT = 4;
static constexpr uint8_t MAX_BINDINGS = 25;
@@ -129,21 +131,19 @@ private:
VkDescriptorSetLayout mVkLayout = VK_NULL_HANDLE;
};
struct VulkanDescriptorSet : public VulkanResource, HwDescriptorSet {
struct VulkanDescriptorSet : public HwDescriptorSet, fvkmemory::Resource {
public:
// Because we need to recycle descriptor sets not used, we allow for a callback that the "Pool"
// can use to repackage the vk handle.
using OnRecycle = std::function<void(VulkanDescriptorSet*)>;
VulkanDescriptorSet(VulkanResourceAllocator* allocator, VkDescriptorSet rawSet,
VulkanDescriptorSet(VkDescriptorSet rawSet,
UniformBufferBitmask const& dynamicUboMask,
uint8_t uniqueDynamicUboCount,
OnRecycle&& onRecycleFn)
: VulkanResource(VulkanResourceType::DESCRIPTOR_SET),
vkSet(rawSet),
: vkSet(rawSet),
dynamicUboMask(dynamicUboMask),
uniqueDynamicUboCount(uniqueDynamicUboCount),
mResources(allocator),
mOnRecycleFn(std::move(onRecycleFn)) {}
~VulkanDescriptorSet() {
@@ -160,9 +160,8 @@ public:
return &mOffsets;
}
void acquire(VulkanTexture* texture);
void acquire(VulkanBufferObject* texture);
void acquire(fvkmemory::resource_ptr<VulkanTexture> texture);
void acquire(fvkmemory::resource_ptr<VulkanBufferObject> buffer);
VkDescriptorSet const vkSet;
UniformBufferBitmask const dynamicUboMask;
@@ -170,7 +169,7 @@ public:
private:
backend::DescriptorSetOffsetArray mOffsets;
VulkanAcquireOnlyResourceManager mResources;
std::vector<fvkmemory::resource_ptr<fvkmemory::Resource>> mResources;
OnRecycle mOnRecycleFn;
};
@@ -178,14 +177,11 @@ using PushConstantNameArray = utils::FixedCapacityVector<char const*>;
using PushConstantNameByStage = std::array<PushConstantNameArray, Program::SHADER_TYPE_COUNT>;
struct PushConstantDescription {
explicit PushConstantDescription(backend::Program const& program) noexcept;
VkPushConstantRange const* getVkRanges() const noexcept { return mRanges; }
uint32_t getVkRangeCount() const noexcept { return mRangeCount; }
void write(VulkanCommands* commands, VkPipelineLayout layout, backend::ShaderStage stage,
void write(VkCommandBuffer cmdbuf, VkPipelineLayout layout, backend::ShaderStage stage,
uint8_t index, backend::PushConstantVariant const& value);
private:
@@ -196,12 +192,10 @@ private:
uint32_t mRangeCount;
};
struct VulkanProgram : public HwProgram, VulkanResource {
struct VulkanProgram : public HwProgram, fvkmemory::Resource {
using BindingList = CappedArray<uint16_t, MAX_SAMPLER_COUNT>;
VulkanProgram(VkDevice device, Program const& builder) noexcept;
~VulkanProgram();
inline VkShaderModule getVertexShader() const {
@@ -218,9 +212,9 @@ struct VulkanProgram : public HwProgram, VulkanResource {
return mInfo->pushConstantDescription.getVkRanges();
}
inline void writePushConstant(VulkanCommands* commands, VkPipelineLayout layout,
inline void writePushConstant(VkCommandBuffer cmdbuf, VkPipelineLayout layout,
backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const& value) {
mInfo->pushConstantDescription.write(commands, layout, stage, index, value);
mInfo->pushConstantDescription.write(cmdbuf, layout, stage, index, value);
}
#if FVK_ENABLED_DEBUG_SAMPLER_NAME
@@ -255,16 +249,16 @@ private:
//
// We use private inheritance to shield clients from the width / height fields in HwRenderTarget,
// which are not representative when this is the default render target.
struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
struct VulkanRenderTarget : private HwRenderTarget, fvkmemory::Resource {
// Creates an offscreen render target.
VulkanRenderTarget(VkDevice device, VkPhysicalDevice physicalDevice,
VulkanContext const& context, VmaAllocator allocator,
VulkanCommands* commands,
VulkanResourceAllocator* handleAllocator,
uint32_t width, uint32_t height,
VulkanContext const& context, fvkmemory::ResourceManager* resourceManager,
VmaAllocator allocator, VulkanCommands* commands, uint32_t width, uint32_t height,
uint8_t samples, VulkanAttachment color[MRT::MAX_SUPPORTED_RENDER_TARGET_COUNT],
VulkanAttachment depthStencil[2], VulkanStagePool& stagePool, uint8_t layerCount);
~VulkanRenderTarget();
// Creates a special "default" render target (i.e. associated with the swap chain)
explicit VulkanRenderTarget();
@@ -276,12 +270,12 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
return {width, height};
}
inline VulkanAttachment const& getColor0() const {
inline VulkanAttachment& getColor0() const {
assert_invariant(mInfo->colors[0]);
return mInfo->attachments[0];
}
inline VulkanAttachment const& getDepth() const {
inline VulkanAttachment& getDepth() const {
assert_invariant(hasDepth());
if (mInfo->fbkey.samples == 1) {
return mInfo->attachments[mInfo->depthIndex];
@@ -308,7 +302,7 @@ struct VulkanRenderTarget : private HwRenderTarget, VulkanResource {
inline bool isSwapChain() const { return !mOffscreen; }
inline bool isProtected() const { return mProtected; }
void bindToSwapChain(VulkanSwapChain& surf);
void bindToSwapChain(fvkmemory::resource_ptr<VulkanSwapChain> swapchain);
void emitBarriersBeginRenderPass(VulkanCommandBuffer& commands);
@@ -331,13 +325,12 @@ private:
bool const mOffscreen;
bool mProtected;
VulkanAcquireOnlyResourceManager mResources;
std::unique_ptr<Auxiliary> mInfo;
};
struct VulkanBufferObject;
struct VulkanVertexBufferInfo : public HwVertexBufferInfo, VulkanResource {
struct VulkanVertexBufferInfo : public HwVertexBufferInfo, fvkmemory::Resource {
VulkanVertexBufferInfo(uint8_t bufferCount, uint8_t attributeCount,
AttributeArray const& attributes);
@@ -384,33 +377,24 @@ private:
PipelineInfo mInfo;
};
struct VulkanVertexBuffer : public HwVertexBuffer, VulkanResource {
VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool,
VulkanResourceAllocator* allocator,
uint32_t vertexCount, Handle<HwVertexBufferInfo> vbih);
struct VulkanVertexBuffer : public HwVertexBuffer, fvkmemory::Resource {
VulkanVertexBuffer(VulkanContext& context, VulkanStagePool& stagePool, uint32_t vertexCount,
fvkmemory::resource_ptr<VulkanVertexBufferInfo> vbi);
void setBuffer(fvkmemory::resource_ptr<VulkanBufferObject> bufferObject, uint32_t index);
void setBuffer(VulkanResourceAllocator const& allocator,
VulkanBufferObject* bufferObject, uint32_t index);
inline VkBuffer const* getVkBuffers() const { return mBuffers.data(); }
inline VkBuffer* getVkBuffers() { return mBuffers.data(); }
fvkmemory::resource_ptr<VulkanVertexBufferInfo> vbi;
inline VkBuffer const* getVkBuffers() const {
return mBuffers.data();
}
inline VkBuffer* getVkBuffers() {
return mBuffers.data();
}
Handle<HwVertexBufferInfo> vbih;
private:
utils::FixedCapacityVector<VkBuffer> mBuffers;
FixedSizeVulkanResourceManager<MAX_VERTEX_BUFFER_COUNT> mResources;
std::vector<fvkmemory::resource_ptr<VulkanBufferObject>> mResources;
};
struct VulkanIndexBuffer : public HwIndexBuffer, VulkanResource {
struct VulkanIndexBuffer : public HwIndexBuffer, fvkmemory::Resource {
VulkanIndexBuffer(VmaAllocator allocator, VulkanStagePool& stagePool, uint8_t elementSize,
uint32_t indexCount)
: HwIndexBuffer(elementSize, indexCount),
VulkanResource(VulkanResourceType::INDEX_BUFFER),
buffer(allocator, stagePool, VK_BUFFER_USAGE_INDEX_BUFFER_BIT, elementSize * indexCount),
indexType(elementSize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32) {}
@@ -418,7 +402,7 @@ struct VulkanIndexBuffer : public HwIndexBuffer, VulkanResource {
const VkIndexType indexType;
};
struct VulkanBufferObject : public HwBufferObject, VulkanResource {
struct VulkanBufferObject : public HwBufferObject, fvkmemory::Resource {
VulkanBufferObject(VmaAllocator allocator, VulkanStagePool& stagePool, uint32_t byteCount,
BufferObjectBinding bindingType);
@@ -426,28 +410,13 @@ struct VulkanBufferObject : public HwBufferObject, VulkanResource {
const BufferObjectBinding bindingType;
};
struct VulkanSamplerGroup : public HwSamplerGroup, VulkanResource {
// NOTE: we have to use out-of-line allocation here because the size of a Handle<> is limited
std::unique_ptr<SamplerGroup> sb;// FIXME: this shouldn't depend on filament::SamplerGroup
explicit VulkanSamplerGroup(size_t size) noexcept
: VulkanResource(VulkanResourceType::SAMPLER_GROUP),
sb(new SamplerGroup(size)) {}
};
struct VulkanRenderPrimitive : public HwRenderPrimitive, fvkmemory::Resource {
VulkanRenderPrimitive(PrimitiveType pt, fvkmemory::resource_ptr<VulkanVertexBuffer> vb,
fvkmemory::resource_ptr<VulkanIndexBuffer> ib);
~VulkanRenderPrimitive() = default;
struct VulkanRenderPrimitive : public HwRenderPrimitive, VulkanResource {
VulkanRenderPrimitive(VulkanResourceAllocator* resourceAllocator,
PrimitiveType pt, Handle<HwVertexBuffer> vbh, Handle<HwIndexBuffer> ibh);
~VulkanRenderPrimitive() {
mResources.clear();
}
VulkanVertexBuffer* vertexBuffer = nullptr;
VulkanIndexBuffer* indexBuffer = nullptr;
private:
// Keep references to the vertex buffer and the index buffer.
FixedSizeVulkanResourceManager<2> mResources;
fvkmemory::resource_ptr<VulkanVertexBuffer> vertexBuffer;
fvkmemory::resource_ptr<VulkanIndexBuffer> indexBuffer;
};
inline constexpr VkBufferUsageFlagBits getBufferObjectUsage(

View File

@@ -241,7 +241,7 @@ VulkanPipelineCache::PipelineCacheEntry* VulkanPipelineCache::createPipeline() n
return &mPipelines.emplace(mPipelineRequirements, cacheEntry).first.value();
}
void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept {
void VulkanPipelineCache::bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept {
mPipelineRequirements.shaders[0] = program->getVertexShader();
mPipelineRequirements.shaders[1] = program->getFragmentShader();

View File

@@ -19,7 +19,6 @@
#include "VulkanCommands.h"
#include "VulkanMemory.h"
#include "VulkanResources.h"
#include "VulkanUtility.h"
#include <backend/DriverEnums.h>
@@ -44,7 +43,6 @@ namespace filament::backend {
struct VulkanProgram;
struct VulkanBufferObject;
struct VulkanTexture;
class VulkanResourceAllocator;
// VulkanPipelineCache manages a cache of descriptor sets and pipelines.
//
@@ -122,7 +120,7 @@ public:
void bindPipeline(VulkanCommandBuffer* commands);
// Each of the following methods are fast and do not make Vulkan calls.
void bindProgram(VulkanProgram* program) noexcept;
void bindProgram(fvkmemory::resource_ptr<VulkanProgram> program) noexcept;
void bindRasterState(const RasterState& rasterState) noexcept;
void bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept;
void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept;

View File

@@ -117,9 +117,10 @@ void VulkanReadPixels::terminate() noexcept {
VulkanReadPixels::VulkanReadPixels(VkDevice device)
: mDevice(device) {}
void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint32_t const y,
uint32_t const width, uint32_t const height, uint32_t const graphicsQueueFamilyIndex,
PixelBufferDescriptor&& pbd, SelecteMemoryFunction const& selectMemoryFunc,
void VulkanReadPixels::run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget, uint32_t const x,
uint32_t const y, uint32_t const width, uint32_t const height,
uint32_t const graphicsQueueFamilyIndex, PixelBufferDescriptor&& pbd,
SelecteMemoryFunction const& selectMemoryFunc,
OnReadCompleteFunction const& readCompleteFunc) {
assert_invariant(mDevice != VK_NULL_HANDLE);
@@ -143,7 +144,7 @@ void VulkanReadPixels::run(VulkanRenderTarget* srcTarget, uint32_t const x, uint
VkCommandPool& cmdpool = mCommandPool;
VulkanTexture* srcTexture = srcTarget->getColor0().texture;
fvkmemory::resource_ptr<VulkanTexture> srcTexture = srcTarget->getColor0().texture;
assert_invariant(srcTexture);
VkFormat const srcFormat = srcTexture->getVkFormat();
bool const swizzle

View File

@@ -17,6 +17,7 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANREADPIXELS_H
#define TNT_FILAMENT_BACKEND_VULKANREADPIXELS_H
#include "vulkan/memory/ResourcePointer.h"
#include "private/backend/Driver.h"
#include <bluevk/BlueVK.h>
@@ -72,9 +73,9 @@ public:
void terminate() noexcept;
void run(VulkanRenderTarget* srcTarget, uint32_t x, uint32_t y, uint32_t width, uint32_t height,
uint32_t graphicsQueueFamilyIndex, PixelBufferDescriptor&& pbd,
SelecteMemoryFunction const& selectMemoryFunc,
void run(fvkmemory::resource_ptr<VulkanRenderTarget> srcTarget, uint32_t x, uint32_t y,
uint32_t width, uint32_t height, uint32_t graphicsQueueFamilyIndex,
PixelBufferDescriptor&& pbd, SelecteMemoryFunction const& selectMemoryFunc,
OnReadCompleteFunction const& readCompleteFunc);
// This method will block until all of the in-flight requests are complete.

View File

@@ -1,136 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H
#define TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H
#include "VulkanConstants.h"
#include "VulkanHandles.h"
#include <private/backend/HandleAllocator.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Log.h>
#include <type_traits>
#include <unordered_set>
namespace filament::backend {
#define RESOURCE_TYPE_COUNT (static_cast<int>(VulkanResourceType::END_TYPE))
#define DEBUG_RESOURCE_LEAKS FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
#if DEBUG_RESOURCE_LEAKS
#define TRACK_INCREMENT() \
if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \
mDebugOnlyResourceCount[static_cast<size_t>(obj->getType())]++; \
}
#define TRACK_DECREMENT() \
if (!IS_HEAP_ALLOC_TYPE(obj->getType())) { \
mDebugOnlyResourceCount[static_cast<size_t>(obj->getType())]--; \
}
#else
// No-op
#define TRACK_INCREMENT()
#define TRACK_DECREMENT()
#endif
class VulkanResourceAllocator {
public:
using AllocatorImpl = HandleAllocatorVK;
VulkanResourceAllocator(size_t arenaSize, bool disableUseAfterFreeCheck)
: mHandleAllocatorImpl("Handles", arenaSize, disableUseAfterFreeCheck)
#if DEBUG_RESOURCE_LEAKS
, mDebugOnlyResourceCount(RESOURCE_TYPE_COUNT) {
std::memset(mDebugOnlyResourceCount.data(), 0, sizeof(size_t) * RESOURCE_TYPE_COUNT);
}
#else
{}
#endif
template<typename D, typename... ARGS>
inline Handle<D> initHandle(ARGS&&... args) noexcept {
auto handle = mHandleAllocatorImpl.allocateAndConstruct<D>(std::forward<ARGS>(args)...);
auto obj = handle_cast<D*>(handle);
obj->initResource(handle.getId());
TRACK_INCREMENT();
return handle;
}
template<typename D>
inline Handle<D> allocHandle() noexcept {
return mHandleAllocatorImpl.allocate<D>();
}
template<typename D, typename B, typename... ARGS>
inline typename std::enable_if<std::is_base_of<B, D>::value, D>::type* construct(
Handle<B> const& handle, ARGS&&... args) noexcept {
auto obj = mHandleAllocatorImpl.construct<D, B>(handle, std::forward<ARGS>(args)...);
obj->initResource(handle.getId());
TRACK_INCREMENT();
return obj;
}
template<typename Dp, typename B>
inline typename std::enable_if_t<
std::is_pointer_v<Dp> && std::is_base_of_v<B, typename std::remove_pointer_t<Dp>>, Dp>
handle_cast(Handle<B>& handle) noexcept {
return mHandleAllocatorImpl.handle_cast<Dp, B>(handle);
}
template<typename Dp, typename B>
inline typename std::enable_if_t<
std::is_pointer_v<Dp> && std::is_base_of_v<B, typename std::remove_pointer_t<Dp>>, Dp>
handle_cast(Handle<B> const& handle) noexcept {
return mHandleAllocatorImpl.handle_cast<Dp, B>(handle);
}
template<typename D, typename B>
inline void destruct(Handle<B> handle) noexcept {
auto obj = handle_cast<D*>(handle);
TRACK_DECREMENT();
mHandleAllocatorImpl.deallocate(handle, obj);
}
inline void associateHandle(HandleBase::HandleId id, utils::CString&& tag) noexcept {
mHandleAllocatorImpl.associateTagToHandle(id, std::move(tag));
}
private:
AllocatorImpl mHandleAllocatorImpl;
#if DEBUG_RESOURCE_LEAKS
public:
void print() {
FVK_LOGD << "Resource Allocator state (debug only)" << utils::io::endl;
for (size_t i = 0; i < RESOURCE_TYPE_COUNT; i++) {
FVK_LOGD << "[" << i << "]=" << mDebugOnlyResourceCount[i] << utils::io::endl;
}
FVK_LOGD << "+++++++++++++++++++++++++++++++++++++" << utils::io::endl;
}
private:
utils::FixedCapacityVector<size_t> mDebugOnlyResourceCount;
#endif
};
#undef TRACK_INCREMENT
#undef TRACK_DECREMENT
#undef DEBUG_RESOURCE_LEAKS
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKANRESOURCEALLOCATOR_H

View File

@@ -1,81 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "VulkanResources.h"
#include "VulkanHandles.h"
#include "VulkanResourceAllocator.h"
#include "VulkanPipelineCache.h"
namespace filament::backend {
void deallocateResource(VulkanResourceAllocator* allocator, VulkanResourceType type,
HandleBase::HandleId id) {
if (IS_HEAP_ALLOC_TYPE(type)) {
return;
}
switch (type) {
case VulkanResourceType::BUFFER_OBJECT:
allocator->destruct<VulkanBufferObject>(Handle<HwBufferObject>(id));
break;
case VulkanResourceType::INDEX_BUFFER:
allocator->destruct<VulkanIndexBuffer>(Handle<HwIndexBuffer>(id));
break;
case VulkanResourceType::PROGRAM:
allocator->destruct<VulkanProgram>(Handle<HwProgram>(id));
break;
case VulkanResourceType::RENDER_TARGET:
allocator->destruct<VulkanRenderTarget>(Handle<HwRenderTarget>(id));
break;
case VulkanResourceType::SAMPLER_GROUP:
allocator->destruct<VulkanSamplerGroup>(Handle<HwSamplerGroup>(id));
break;
case VulkanResourceType::SWAP_CHAIN:
allocator->destruct<VulkanSwapChain>(Handle<HwSwapChain>(id));
break;
case VulkanResourceType::TEXTURE:
allocator->destruct<VulkanTexture>(Handle<HwTexture>(id));
break;
case VulkanResourceType::TIMER_QUERY:
allocator->destruct<VulkanTimerQuery>(Handle<HwTimerQuery>(id));
break;
case VulkanResourceType::VERTEX_BUFFER_INFO:
allocator->destruct<VulkanVertexBufferInfo>(Handle<HwVertexBufferInfo>(id));
break;
case VulkanResourceType::VERTEX_BUFFER:
allocator->destruct<VulkanVertexBuffer>(Handle<HwVertexBuffer>(id));
break;
case VulkanResourceType::RENDER_PRIMITIVE:
allocator->destruct<VulkanRenderPrimitive>(Handle<VulkanRenderPrimitive>(id));
break;
case VulkanResourceType::DESCRIPTOR_SET_LAYOUT:
allocator->destruct<VulkanDescriptorSetLayout>(Handle<VulkanDescriptorSetLayout>(id));
break;
case VulkanResourceType::DESCRIPTOR_SET:
allocator->destruct<VulkanDescriptorSet>(Handle<VulkanDescriptorSet>(id));
break;
// If the resource is heap allocated, then the resource manager just skip refcounted
// destruction.
case VulkanResourceType::FENCE:
case VulkanResourceType::HEAP_ALLOCATED:
case VulkanResourceType::END_TYPE:
break;
}
}
} // namespace filament::backend

View File

@@ -1,313 +0,0 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef TNT_FILAMENT_BACKEND_VULKANRESOURCES_H
#define TNT_FILAMENT_BACKEND_VULKANRESOURCES_H
#include "VulkanUtility.h"
#include <backend/Handle.h>
#include <tsl/robin_set.h>
#include <utils/Mutex.h>
#include <utils/Panic.h>
#include <mutex>
#include <unordered_set>
namespace filament::backend {
class VulkanResourceAllocator;
struct VulkanThreadSafeResource;
// Subclasses of VulkanResource must provide this enum in their construction.
enum class VulkanResourceType : uint8_t {
BUFFER_OBJECT = 0,
INDEX_BUFFER = 1,
PROGRAM = 2,
RENDER_TARGET = 3,
SAMPLER_GROUP = 4,
SWAP_CHAIN = 5,
RENDER_PRIMITIVE = 6,
TEXTURE = 7,
TIMER_QUERY = 8,
VERTEX_BUFFER = 9,
VERTEX_BUFFER_INFO = 10,
DESCRIPTOR_SET_LAYOUT = 11,
DESCRIPTOR_SET = 12,
// Below are resources that are managed manually (i.e. not ref counted).
FENCE = 13,
HEAP_ALLOCATED = 14,
END_TYPE = 15, // A placeholder
};
#define IS_HEAP_ALLOC_TYPE(f) \
(f == VulkanResourceType::FENCE || f == VulkanResourceType::HEAP_ALLOCATED)
// This is a ref-counting base class that tracks how many references of this resource exist. This
// class is paired with VulkanResourceManagerImpl which is responsible for incrementing or
// decrementing the count. Once mRefCount == 0, VulkanResourceManagerImpl will also call the
// appropriate destructor. VulkanCommandBuffer, VulkanDriver, and composite structure like
// VulkanRenderPrimitive are owners of VulkanResourceManagerImpl instances.
struct VulkanResourceBase {
protected:
explicit VulkanResourceBase(VulkanResourceType type)
: mRefCount(IS_HEAP_ALLOC_TYPE(type) ? 1 : 0),
mType(uint32_t(type)),
mHandleId(0) {
}
private:
inline VulkanResourceType getType() const noexcept {
return VulkanResourceType(mType);
}
inline HandleBase::HandleId getId() const noexcept {
return mHandleId;
}
inline void initResource(HandleBase::HandleId id) noexcept {
mHandleId = id;
}
inline void ref() noexcept {
if (IS_HEAP_ALLOC_TYPE(getType())) {
return;
}
assert_invariant(mRefCount < ((1<<24) - 1));
++mRefCount;
}
inline void deref() noexcept {
if (IS_HEAP_ALLOC_TYPE(getType())) {
return;
}
assert_invariant(mRefCount > 0);
--mRefCount;
}
inline size_t refcount() const noexcept {
return mRefCount;
}
uint32_t mRefCount : 24; // 16M is enough for the refcount
uint32_t mType : 8; // must be uint32_t or MSVC doesn't pack it. no codegen impact w/ clang.
HandleBase::HandleId mHandleId;
friend struct VulkanThreadSafeResource;
friend class VulkanResourceAllocator;
template<typename RT, typename ST>
friend class VulkanResourceManagerImpl;
};
static_assert(sizeof(VulkanResourceBase) == 8, "VulkanResourceBase should be 8 bytes");
struct VulkanThreadSafeResource {
protected:
explicit VulkanThreadSafeResource(VulkanResourceType type)
: mImpl(type) {}
private:
inline VulkanResourceType getType() {
return mImpl.getType();
}
inline HandleBase::HandleId getId() {
return mImpl.getId();
}
inline void initResource(HandleBase::HandleId id) noexcept {
std::unique_lock<utils::Mutex> lock(mMutex);
mImpl.initResource(id);
}
inline void ref() noexcept {
std::unique_lock<utils::Mutex> lock(mMutex);
mImpl.ref();
}
inline void deref() noexcept {
std::unique_lock<utils::Mutex> lock(mMutex);
mImpl.deref();
}
inline size_t refcount() noexcept {
std::unique_lock<utils::Mutex> lock(mMutex);
return mImpl.refcount();
}
utils::Mutex mMutex;
VulkanResourceBase mImpl;
friend class VulkanResourceAllocator;
template<typename RT, typename ST>
friend class VulkanResourceManagerImpl;
};
using VulkanResource = VulkanResourceBase;
namespace {
// When the size of the resource set is known to be small, (for example for VulkanRenderPrimitive),
// we just use a std::array to back the set.
template<std::size_t SIZE>
using FixedCapacityResourceSet = CappedArray<VulkanResource*, SIZE>;
// robin_set/map are useful for sets that are acquire only and the set will be iterated when the set
// is cleared.
using FastIterationResourceSet = tsl::robin_set<VulkanResource*>;
// unoredered_set is used in the general case where insert/erase can occur at will. This is useful
// for the basic object ownership count - i.e. VulkanDriver.
using ResourceSet = std::unordered_set<VulkanResource*>;
using ThreadSafeResourceSet = std::unordered_set<VulkanThreadSafeResource*>;
} // anonymous namespace
class VulkanResourceAllocator;
#define LOCK_IF_NEEDED() \
if constexpr (std::is_base_of_v<VulkanThreadSafeResource, ResourceType>) { \
mMutex->lock(); \
}
#define UNLOCK_IF_NEEDED() \
if constexpr (std::is_base_of_v<VulkanThreadSafeResource, ResourceType>) { \
mMutex->unlock(); \
}
void deallocateResource(VulkanResourceAllocator* allocator, VulkanResourceType type,
HandleBase::HandleId id);
template<typename ResourceType, typename SetType>
class VulkanResourceManagerImpl {
public:
explicit VulkanResourceManagerImpl(VulkanResourceAllocator* allocator)
: mAllocator(allocator) {
if constexpr (std::is_base_of_v<VulkanThreadSafeResource, ResourceType>) {
mMutex = std::make_unique<utils::Mutex>();
}
}
VulkanResourceManagerImpl(const VulkanResourceManagerImpl& other) = delete;
void operator=(const VulkanResourceManagerImpl& other) = delete;
VulkanResourceManagerImpl(const VulkanResourceManagerImpl&& other) = delete;
void operator=(const VulkanResourceManagerImpl&& other) = delete;
~VulkanResourceManagerImpl() {
clear();
}
inline void acquire(ResourceType* resource) {
if (IS_HEAP_ALLOC_TYPE(resource->getType())) {
return;
}
LOCK_IF_NEEDED();
if (mResources.find(resource) != mResources.end()) {
UNLOCK_IF_NEEDED();
return;
}
mResources.insert(resource);
UNLOCK_IF_NEEDED();
resource->ref();
}
// Transfers ownership from one resource set to another
template <typename tSetType>
inline void acquireAll(VulkanResourceManagerImpl<ResourceType, tSetType>* srcResources) {
copyAll(srcResources);
srcResources->clear();
}
// Transfers ownership from one resource set to another
template <typename tSetType>
inline void copyAll(VulkanResourceManagerImpl<ResourceType, tSetType>* srcResources) {
LOCK_IF_NEEDED();
for (auto iter = srcResources->mResources.begin(); iter != srcResources->mResources.end();
iter++) {
acquire(*iter);
}
UNLOCK_IF_NEEDED();
}
inline void release(ResourceType* resource) {
if (IS_HEAP_ALLOC_TYPE(resource->getType())) {
return;
}
LOCK_IF_NEEDED();
auto resItr = mResources.find(resource);
if (resItr == mResources.end()) {
UNLOCK_IF_NEEDED();
return;
}
mResources.erase(resItr);
UNLOCK_IF_NEEDED();
derefImpl(resource);
}
inline void clear() {
LOCK_IF_NEEDED();
for (auto iter = mResources.begin(); iter != mResources.end(); iter++) {
derefImpl(*iter);
}
mResources.clear();
UNLOCK_IF_NEEDED();
}
inline size_t size() {
return mResources.size();
}
private:
inline void derefImpl(ResourceType* resource) {
resource->deref();
if (resource->refcount() != 0) {
return;
}
deallocateResource(mAllocator, resource->getType(), resource->getId());
}
VulkanResourceAllocator* mAllocator;
SetType mResources;
std::unique_ptr<utils::Mutex> mMutex;
template <typename, typename> friend class VulkanResourceManagerImpl;
};
using VulkanAcquireOnlyResourceManager
= VulkanResourceManagerImpl<VulkanResource, FastIterationResourceSet>;
using VulkanResourceManager = VulkanResourceManagerImpl<VulkanResource, ResourceSet>;
template<std::size_t SIZE>
using FixedSizeVulkanResourceManager =
VulkanResourceManagerImpl<VulkanResource, FixedCapacityResourceSet<SIZE>>;
using VulkanThreadSafeResourceManager
= VulkanResourceManagerImpl<VulkanThreadSafeResource, ThreadSafeResourceSet>;
#undef LOCK_IF_NEEDED
#undef UNLOCK_IF_NEEDED
} // namespace filament::backend
#endif // TNT_FILAMENT_BACKEND_VULKANRESOURCES_H

View File

@@ -16,6 +16,7 @@
#include "VulkanStagePool.h"
#include "VulkanCommands.h"
#include "VulkanConstants.h"
#include "VulkanImageUtility.h"
#include "VulkanMemory.h"
@@ -37,6 +38,7 @@ VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
if (iter != mFreeStages.end()) {
auto stage = iter->second;
mFreeStages.erase(iter);
stage->lastAccessed = mCurrentFrame;
mUsedStages.insert(stage);
return stage;
}
@@ -59,7 +61,7 @@ VulkanStage const* VulkanStagePool::acquireStage(uint32_t numBytes) {
UTILS_UNUSED_IN_RELEASE VkResult result = vmaCreateBuffer(mAllocator, &bufferInfo,
&allocInfo, &stage->buffer, &stage->memory, nullptr);
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
#if FVK_ENABLED(FVK_DEBUG_STAGING_ALLOCATION)
if (result != VK_SUCCESS) {
FVK_LOGE << "Allocation error: " << result << utils::io::endl;
}
@@ -74,6 +76,7 @@ VulkanStageImage const* VulkanStagePool::acquireImage(PixelDataFormat format, Pi
for (auto image : mFreeImages) {
if (image->format == vkformat && image->width == width && image->height == height) {
mFreeImages.erase(image);
image->lastAccessed = mCurrentFrame;
mUsedImages.insert(image);
return image;
}

View File

@@ -17,8 +17,7 @@
#ifndef TNT_FILAMENT_BACKEND_VULKANSTAGEPOOL_H
#define TNT_FILAMENT_BACKEND_VULKANSTAGEPOOL_H
#include "VulkanCommands.h"
#include "VulkanContext.h"
#include "backend/DriverEnums.h"
#include "VulkanMemory.h"
#include <map>
@@ -26,6 +25,8 @@
namespace filament::backend {
class VulkanCommands;
// Immutable POD representing a shared CPU-GPU staging area.
struct VulkanStage {
VmaAllocation memory;

View File

@@ -15,6 +15,8 @@
*/
#include "VulkanSwapChain.h"
#include "VulkanCommands.h"
#include "VulkanTexture.h"
#include <utils/FixedCapacityVector.h>
@@ -26,19 +28,18 @@ using namespace utils;
namespace filament::backend {
VulkanSwapChain::VulkanSwapChain(VulkanPlatform* platform, VulkanContext const& context,
VmaAllocator allocator, VulkanCommands* commands, VulkanResourceAllocator* handleAllocator,
VulkanStagePool& stagePool,
void* nativeWindow, uint64_t flags, VkExtent2D extent)
: VulkanResource(VulkanResourceType::SWAP_CHAIN),
mPlatform(platform),
fvkmemory::ResourceManager* resourceManager, VmaAllocator allocator,
VulkanCommands* commands, VulkanStagePool& stagePool, void* nativeWindow, uint64_t flags,
VkExtent2D extent)
: mPlatform(platform),
mResourceManager(resourceManager),
mCommands(commands),
mAllocator(allocator),
mHandleAllocator(handleAllocator),
mStagePool(stagePool),
mHeadless(extent.width != 0 && extent.height != 0 && !nativeWindow),
mFlushAndWaitOnResize(platform->getCustomization().flushAndWaitOnWindowResize),
mTransitionSwapChainImageLayoutForPresent(
platform->getCustomization().transitionSwapChainImageLayoutForPresent),
platform->getCustomization().transitionSwapChainImageLayoutForPresent),
mAcquired(false),
mIsFirstRenderPass(true) {
swapChain = mPlatform->createSwapChain(nativeWindow, flags, extent);
@@ -53,6 +54,9 @@ VulkanSwapChain::~VulkanSwapChain() {
mCommands->flush();
mCommands->wait();
mColors = {};
mDepth = {};
mPlatform->destroy(swapChain);
}
@@ -70,13 +74,16 @@ void VulkanSwapChain::update() {
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 */));
auto colorTexture = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager,
device, mAllocator, mResourceManager, mCommands, color, bundle.colorFormat, 1,
bundle.extent.width, bundle.extent.height, TextureUsage::COLOR_ATTACHMENT,
mStagePool);
mColors.push_back(colorTexture);
}
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 */);
mDepth = fvkmemory::resource_ptr<VulkanTexture>::construct(mResourceManager, device, mAllocator,
mResourceManager, mCommands, bundle.depth, bundle.depthFormat, 1, bundle.extent.width,
bundle.extent.height, TextureUsage::DEPTH_ATTACHMENT, mStagePool);
mExtent = bundle.extent;
}
@@ -95,7 +102,7 @@ void VulkanSwapChain::present() {
}
mCommands->flush();
// call the image ready wait function
if (mExplicitImageReadyWait != nullptr) {
mExplicitImageReadyWait(swapChain);
@@ -135,7 +142,7 @@ void VulkanSwapChain::acquire(bool& resized) {
VkResult const result = mPlatform->acquire(swapChain, &imageSyncData);
mCurrentSwapIndex = imageSyncData.imageIndex;
mExplicitImageReadyWait = imageSyncData.explicitImageReadyWait;
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
FILAMENT_CHECK_POSTCONDITION(result == VK_SUCCESS || result == VK_SUBOPTIMAL_KHR)
<< "Cannot acquire in swapchain.";
if (imageSyncData.imageReadySemaphore != VK_NULL_HANDLE) {
mCommands->injectDependency(imageSyncData.imageReadySemaphore);

View File

@@ -19,9 +19,9 @@
#include "DriverBase.h"
#include "VulkanCommands.h"
#include "VulkanContext.h"
#include "VulkanResources.h"
#include "VulkanTexture.h"
#include "vulkan/memory/Resource.h"
#include <backend/platforms/VulkanPlatform.h>
@@ -36,14 +36,14 @@ namespace filament::backend {
struct VulkanHeadlessSwapChain;
struct VulkanSurfaceSwapChain;
class VulkanResourceAllocator;
class VulkanCommands;
// A wrapper around the platform implementation of swapchain.
struct VulkanSwapChain : public HwSwapChain, VulkanResource {
VulkanSwapChain(VulkanPlatform* platform, VulkanContext const& context, VmaAllocator allocator,
VulkanCommands* commands, VulkanResourceAllocator* handleAllocator,
VulkanStagePool& stagePool,
void* nativeWindow, uint64_t flags, VkExtent2D extent = {0, 0});
struct VulkanSwapChain : public HwSwapChain, fvkmemory::Resource {
VulkanSwapChain(VulkanPlatform* platform, VulkanContext const& context,
fvkmemory::ResourceManager* resourceManager, VmaAllocator allocator,
VulkanCommands* commands, VulkanStagePool& stagePool, void* nativeWindow,
uint64_t flags, VkExtent2D extent = {0, 0});
~VulkanSwapChain();
@@ -51,15 +51,15 @@ struct VulkanSwapChain : public HwSwapChain, VulkanResource {
void acquire(bool& reized);
inline VulkanTexture* getCurrentColor() const noexcept {
fvkmemory::resource_ptr<VulkanTexture> getCurrentColor() const noexcept {
uint32_t const imageIndex = mCurrentSwapIndex;
FILAMENT_CHECK_PRECONDITION(
imageIndex != VulkanPlatform::ImageSyncData::INVALID_IMAGE_INDEX);
return mColors[imageIndex].get();
return mColors[imageIndex];
}
inline VulkanTexture* getDepth() const noexcept {
return mDepth.get();
inline fvkmemory::resource_ptr<VulkanTexture> getDepth() const noexcept {
return mDepth;
}
inline bool isFirstRenderPass() const noexcept {
@@ -83,9 +83,9 @@ private:
void update();
VulkanPlatform* mPlatform;
fvkmemory::ResourceManager* mResourceManager;
VulkanCommands* mCommands;
VmaAllocator mAllocator;
VulkanResourceAllocator* const mHandleAllocator;
VulkanStagePool& mStagePool;
bool const mHeadless;
bool const mFlushAndWaitOnResize;
@@ -93,8 +93,8 @@ private:
// We create VulkanTextures based on VkImages. VulkanTexture has facilities for doing layout
// transitions, which are useful here.
utils::FixedCapacityVector<std::unique_ptr<VulkanTexture>> mColors;
std::unique_ptr<VulkanTexture> mDepth;
utils::FixedCapacityVector<fvkmemory::resource_ptr<VulkanTexture>> mColors;
fvkmemory::resource_ptr<VulkanTexture> mDepth;
VkExtent2D mExtent;
uint32_t mCurrentSwapIndex;
std::function<void(Platform::SwapChain* handle)> mExplicitImageReadyWait = nullptr;

View File

@@ -14,10 +14,11 @@
* limitations under the License.
*/
#include "VulkanCommands.h"
#include "VulkanMemory.h"
#include "VulkanResourceAllocator.h"
#include "VulkanTexture.h"
#include "VulkanUtility.h"
#include "vulkan/memory/ResourcePointer.h"
#include <DataReshaper.h>
#include <backend/DriverEnums.h>
@@ -149,8 +150,7 @@ VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator,
VulkanCommands* commands, VulkanStagePool& stagePool, VkFormat format,
VkImageViewType viewType, uint8_t levels, uint8_t layerCount, VulkanLayout defaultLayout,
bool isProtected)
: VulkanResource(VulkanResourceType::HEAP_ALLOCATED),
mVkFormat(format),
: mVkFormat(format),
mViewType(viewType),
mFullViewRange{filament::backend::getImageAspect(format), 0, levels, 0, layerCount},
mDefaultLayout(defaultLayout),
@@ -161,61 +161,42 @@ VulkanTextureState::VulkanTextureState(VkDevice device, VmaAllocator allocator,
mCommands(commands),
mIsTransientAttachment(false) {}
VulkanTextureState* VulkanTexture::getSharedState() {
VulkanTextureState* state = mAllocator->handle_cast<VulkanTextureState*>(mState);
return state;
}
VulkanTextureState const* VulkanTexture::getSharedState() const {
VulkanTextureState const* state = mAllocator->handle_cast<VulkanTextureState const*>(mState);
return state;
}
// Constructor for internally passed VkImage
VulkanTexture::VulkanTexture(
VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
VulkanResourceAllocator* handleAllocator,
VkImage image, VkFormat format, uint8_t samples, uint32_t width, uint32_t height,
TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated)
: HwTexture(SamplerType::SAMPLER_2D, 1, samples, width, height, 1, TextureFormat::UNUSED,
tusage),
VulkanResource(
heapAllocated ? VulkanResourceType::HEAP_ALLOCATED : VulkanResourceType::TEXTURE),
mAllocator(handleAllocator),
mState(handleAllocator->initHandle<VulkanTextureState>(
device, allocator, commands, stagePool,
format, imgutil::getViewType(SamplerType::SAMPLER_2D), 1, 1,
getDefaultLayoutImpl(tusage),
any(usage& TextureUsage::PROTECTED))) {
auto* const state = getSharedState();
state->mTextureImage = image;
mPrimaryViewRange = state->mFullViewRange;
VulkanTexture::VulkanTexture(VkDevice device, VmaAllocator allocator,
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image,
VkFormat format, uint8_t samples, uint32_t width, uint32_t height, TextureUsage tusage,
VulkanStagePool& stagePool)
: HwTexture(SamplerType::SAMPLER_2D, 1, samples, width, height, 1, TextureFormat::UNUSED,
tusage),
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, device,
allocator, commands, stagePool, format, imgutil::getViewType(SamplerType::SAMPLER_2D),
1, 1, getDefaultLayoutImpl(tusage), any(usage & TextureUsage::PROTECTED))) {
mState->mTextureImage = image;
mPrimaryViewRange = mState->mFullViewRange;
}
// Constructor for user facing texture
VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
VulkanResourceAllocator* handleAllocator, SamplerType target, uint8_t levels,
TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated)
VulkanContext const& context, VmaAllocator allocator,
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, SamplerType target,
uint8_t levels, TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h,
uint32_t depth, TextureUsage tusage, VulkanStagePool& stagePool)
: HwTexture(target, levels, samples, w, h, depth, tformat, tusage),
VulkanResource(
heapAllocated ? VulkanResourceType::HEAP_ALLOCATED : VulkanResourceType::TEXTURE),
mAllocator(handleAllocator),
mState(handleAllocator->initHandle<VulkanTextureState>(device, allocator, commands, stagePool,
backend::getVkFormat(tformat), imgutil::getViewType(target), levels,
getLayerCount(target, depth), VulkanLayout::UNDEFINED, any(usage& TextureUsage::PROTECTED))) {
auto* const state = getSharedState();
mState(fvkmemory::resource_ptr<VulkanTextureState>::construct(resourceManager, device,
allocator, commands, stagePool, backend::getVkFormat(tformat),
imgutil::getViewType(target), levels, getLayerCount(target, depth),
VulkanLayout::UNDEFINED, any(usage & TextureUsage::PROTECTED))) {
// Create an appropriately-sized device-only VkImage, but do not fill it yet.
VkImageCreateInfo imageInfo{.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = target == SamplerType::SAMPLER_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D,
.format = state->mVkFormat,
.extent = {w, h, depth},
.mipLevels = levels,
.arrayLayers = 1,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = 0};
VkImageCreateInfo imageInfo{
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.imageType = target == SamplerType::SAMPLER_3D ? VK_IMAGE_TYPE_3D : VK_IMAGE_TYPE_2D,
.format = mState->mVkFormat,
.extent = {w, h, depth},
.mipLevels = levels,
.arrayLayers = 1,
.tiling = VK_IMAGE_TILING_OPTIMAL,
.usage = 0,
};
if (target == SamplerType::SAMPLER_CUBEMAP) {
imageInfo.arrayLayers = 6;
imageInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
@@ -254,7 +235,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
none(tusage & ~TextureUsage::ALL_ATTACHMENTS) &&
// Usage contains at least one attachment flag.
any(tusage & TextureUsage::ALL_ATTACHMENTS);
state->mIsTransientAttachment = useTransientAttachment;
mState->mIsTransientAttachment = useTransientAttachment;
const VkImageUsageFlags transientFlag =
useTransientAttachment ? VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT : 0U;
@@ -299,7 +280,7 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
// any kind of attachment (color or depth).
const auto& limits = context.getPhysicalDeviceLimits();
if (imageInfo.usage & VK_IMAGE_USAGE_SAMPLED_BIT) {
samples = reduceSampleCount(samples, isVkDepthFormat(state->mVkFormat)
samples = reduceSampleCount(samples, isVkDepthFormat(mState->mVkFormat)
? limits.sampledImageDepthSampleCounts
: limits.sampledImageColorSampleCounts);
}
@@ -313,12 +294,12 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
this->samples = samples;
imageInfo.samples = (VkSampleCountFlagBits) samples;
VkResult error = vkCreateImage(state->mDevice, &imageInfo, VKALLOC, &state->mTextureImage);
VkResult error = vkCreateImage(mState->mDevice, &imageInfo, VKALLOC, &mState->mTextureImage);
if (error || FVK_ENABLED(FVK_DEBUG_TEXTURE)) {
FVK_LOGD << "vkCreateImage: "
<< "image = " << state->mTextureImage << ", "
<< "image = " << mState->mTextureImage << ", "
<< "result = " << error << ", "
<< "handle = " << utils::io::hex << state->mTextureImage << utils::io::dec << ", "
<< "handle = " << utils::io::hex << mState->mTextureImage << utils::io::dec << ", "
<< "extent = " << w << "x" << h << "x"<< depth << ", "
<< "mipLevels = " << int(levels) << ", "
<< "TextureUsage = " << static_cast<int>(usage) << ", "
@@ -327,18 +308,18 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
<< "type = " << imageInfo.imageType << ", "
<< "flags = " << imageInfo.flags << ", "
<< "target = " << static_cast<int>(target) <<", "
<< "format = " << state->mVkFormat << utils::io::endl;
<< "format = " << mState->mVkFormat << utils::io::endl;
}
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to create image.";
// Allocate memory for the VkImage and bind it.
VkMemoryRequirements memReqs = {};
vkGetImageMemoryRequirements(state->mDevice, state->mTextureImage, &memReqs);
vkGetImageMemoryRequirements(mState->mDevice, mState->mTextureImage, &memReqs);
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);
(mState->mIsProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
uint32_t memoryTypeIndex
= context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags);
@@ -350,37 +331,29 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
.allocationSize = memReqs.size,
.memoryTypeIndex = memoryTypeIndex,
};
error = vkAllocateMemory(state->mDevice, &allocInfo, nullptr, &state->mTextureImageMemory);
error = vkAllocateMemory(mState->mDevice, &allocInfo, nullptr, &mState->mTextureImageMemory);
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to allocate image memory.";
error = vkBindImageMemory(state->mDevice, state->mTextureImage, state->mTextureImageMemory, 0);
error = vkBindImageMemory(mState->mDevice, mState->mTextureImage, mState->mTextureImageMemory,
0);
FILAMENT_CHECK_POSTCONDITION(!error) << "Unable to bind image.";
// Spec out the "primary" VkImageView that shaders use to sample from the image.
mPrimaryViewRange = state->mFullViewRange;
mPrimaryViewRange = mState->mFullViewRange;
// Go ahead and create the primary image view.
getImageView(mPrimaryViewRange, state->mViewType, mSwizzle);
getImageView(mPrimaryViewRange, mState->mViewType, mSwizzle);
VulkanCommandBuffer& commandsBuf = state->mCommands->get();
commandsBuf.acquire(this);
auto const defaultLayout = state->mDefaultLayout = getDefaultLayoutImpl(imageInfo.usage);
transitionLayout(&commandsBuf, mPrimaryViewRange, defaultLayout);
mState->mDefaultLayout = getDefaultLayoutImpl(imageInfo.usage);
}
// Constructor for creating a texture view
VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
VulkanResourceAllocator* handleAllocator, VulkanTexture const* src, uint8_t baseLevel,
fvkmemory::resource_ptr<VulkanTexture> src, uint8_t baseLevel,
uint8_t levelCount)
: HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth,
src->format, src->usage),
VulkanResource(VulkanResourceType::TEXTURE),
mAllocator(handleAllocator) {
src->format, src->usage) {
mState = src->mState;
auto* state = getSharedState();
state->refs++;
mPrimaryViewRange = src->mPrimaryViewRange;
mPrimaryViewRange.baseMipLevel = src->mPrimaryViewRange.baseMipLevel + baseLevel;
mPrimaryViewRange.levelCount = levelCount;
@@ -389,31 +362,21 @@ VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
// Constructor for creating a texture view with swizzle
VulkanTexture::VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice,
VulkanContext const& context, VmaAllocator allocator, VulkanCommands* commands,
VulkanResourceAllocator* handleAllocator, VulkanTexture const* src,
VkComponentMapping swizzle)
fvkmemory::resource_ptr<VulkanTexture> src, VkComponentMapping swizzle)
: HwTexture(src->target, src->levels, src->samples, src->width, src->height, src->depth,
src->format, src->usage),
VulkanResource(VulkanResourceType::TEXTURE),
mAllocator(handleAllocator) {
src->format, src->usage) {
mState = src->mState;
auto* state = getSharedState();
state->refs++;
mPrimaryViewRange = src->mPrimaryViewRange;
mSwizzle = composeSwizzle(src->mSwizzle, swizzle);
}
VulkanTexture::~VulkanTexture() {
auto* const state = getSharedState();
state->refs--;
if (state->refs == 0) {
if (state->mTextureImageMemory != VK_NULL_HANDLE) {
vkDestroyImage(state->mDevice, state->mTextureImage, VKALLOC);
vkFreeMemory(state->mDevice, state->mTextureImageMemory, VKALLOC);
}
for (auto entry: state->mCachedImageViews) {
vkDestroyImageView(state->mDevice, entry.second, VKALLOC);
}
mAllocator->destruct<VulkanTextureState>(mState);
VulkanTextureState::~VulkanTextureState() {
if (mTextureImageMemory != VK_NULL_HANDLE) {
vkDestroyImage(mDevice, mTextureImage, VKALLOC);
vkFreeMemory(mDevice, mTextureImageMemory, VKALLOC);
}
for (auto entry: mCachedImageViews) {
vkDestroyImageView(mDevice, entry.second, VKALLOC);
}
}
@@ -422,8 +385,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
assert_invariant(width <= this->width && height <= this->height);
assert_invariant(depth <= this->depth * ((target == SamplerType::SAMPLER_CUBEMAP ||
target == SamplerType::SAMPLER_CUBEMAP_ARRAY) ? 6 : 1));
auto* const state = getSharedState();
assert_invariant(state->mIsProtected == false);
assert_invariant(!mState->mIsProtected);
const PixelBufferDescriptor* hostData = &data;
PixelBufferDescriptor reshapedData;
@@ -436,7 +398,7 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
// If format conversion is both required and supported, use vkCmdBlitImage.
const VkFormat hostFormat = backend::getVkFormat(hostData->format, hostData->type);
const VkFormat deviceFormat = getVkFormatLinear(state->mVkFormat);
const VkFormat deviceFormat = getVkFormatLinear(mState->mVkFormat);
if (hostFormat != deviceFormat && hostFormat != VK_FORMAT_UNDEFINED) {
assert_invariant(xoffset == 0 && yoffset == 0 && zoffset == 0 &&
"Offsets not yet supported when format conversion is required.");
@@ -448,16 +410,16 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
// Otherwise, use vkCmdCopyBufferToImage.
void* mapped = nullptr;
VulkanStage const* stage = state->mStagePool.acquireStage(hostData->size);
VulkanStage const* stage = mState->mStagePool.acquireStage(hostData->size);
assert_invariant(stage->memory);
vmaMapMemory(state->mAllocator, stage->memory, &mapped);
vmaMapMemory(mState->mAllocator, stage->memory, &mapped);
memcpy(mapped, hostData->buffer, hostData->size);
vmaUnmapMemory(state->mAllocator, stage->memory);
vmaFlushAllocation(state->mAllocator, stage->memory, 0, hostData->size);
vmaUnmapMemory(mState->mAllocator, stage->memory);
vmaFlushAllocation(mState->mAllocator, stage->memory, 0, hostData->size);
VulkanCommandBuffer& commands = state->mCommands->get();
VulkanCommandBuffer& commands = mState->mCommands->get();
VkCommandBuffer const cmdbuf = commands.buffer();
commands.acquire(this);
commands.acquire(fvkmemory::resource_ptr<VulkanTexture>::cast(this));
VkBufferImageCopy copyRegion = {
.bufferOffset = {},
@@ -503,25 +465,24 @@ void VulkanTexture::updateImage(const PixelBufferDescriptor& data, uint32_t widt
transitionLayout(&commands, transitionRange, newLayout);
vkCmdCopyBufferToImage(cmdbuf, stage->buffer, state->mTextureImage, newVkLayout, 1, &copyRegion);
vkCmdCopyBufferToImage(cmdbuf, stage->buffer, mState->mTextureImage, newVkLayout, 1, &copyRegion);
transitionLayout(&commands, transitionRange, nextLayout);
}
void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width,
uint32_t height, uint32_t depth, uint32_t miplevel) {
auto* const state = getSharedState();
void* mapped = nullptr;
VulkanStageImage const* stage
= state->mStagePool.acquireImage(hostData.format, hostData.type, width, height);
vmaMapMemory(state->mAllocator, stage->memory, &mapped);
= mState->mStagePool.acquireImage(hostData.format, hostData.type, width, height);
vmaMapMemory(mState->mAllocator, stage->memory, &mapped);
memcpy(mapped, hostData.buffer, hostData.size);
vmaUnmapMemory(state->mAllocator, stage->memory);
vmaFlushAllocation(state->mAllocator, stage->memory, 0, hostData.size);
vmaUnmapMemory(mState->mAllocator, stage->memory);
vmaFlushAllocation(mState->mAllocator, stage->memory, 0, hostData.size);
VulkanCommandBuffer& commands = state->mCommands->get();
VulkanCommandBuffer& commands = mState->mCommands->get();
VkCommandBuffer const cmdbuf = commands.buffer();
commands.acquire(this);
commands.acquire(fvkmemory::resource_ptr<VulkanTexture>::cast(this));
// TODO: support blit-based format conversion for 3D images and cubemaps.
const int layer = 0;
@@ -544,14 +505,13 @@ void VulkanTexture::updateImageWithBlit(const PixelBufferDescriptor& hostData, u
transitionLayout(&commands, range, newLayout);
vkCmdBlitImage(cmdbuf, stage->image, imgutil::getVkLayout(VulkanLayout::TRANSFER_SRC),
state->mTextureImage, imgutil::getVkLayout(newLayout), 1, blitRegions, VK_FILTER_NEAREST);
mState->mTextureImage, imgutil::getVkLayout(newLayout), 1, blitRegions, VK_FILTER_NEAREST);
transitionLayout(&commands, range, oldLayout);
}
VulkanLayout VulkanTexture::getDefaultLayout() const {
auto* const state = getSharedState();
return state->mDefaultLayout;
return mState->mDefaultLayout;
}
VkImageView VulkanTexture::getAttachmentView(VkImageSubresourceRange range) {
@@ -570,42 +530,43 @@ VkImageView VulkanTexture::getViewForType(VkImageSubresourceRange const& range,
VkImageView VulkanTexture::getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
VkComponentMapping swizzle) {
auto* const state = getSharedState();
VulkanTextureState::ImageViewKey const key{ range, viewType, swizzle };
auto iter = state->mCachedImageViews.find(key);
if (iter != state->mCachedImageViews.end()) {
auto iter = mState->mCachedImageViews.find(key);
if (iter != mState->mCachedImageViews.end()) {
return iter->second;
}
VkImageViewCreateInfo viewInfo = {
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
.image = state->mTextureImage,
.image = mState->mTextureImage,
.viewType = viewType,
.format = state->mVkFormat,
.format = mState->mVkFormat,
.components = swizzle,
.subresourceRange = range,
};
VkImageView imageView;
vkCreateImageView(state->mDevice, &viewInfo, VKALLOC, &imageView);
state->mCachedImageViews.emplace(key, imageView);
vkCreateImageView(mState->mDevice, &viewInfo, VKALLOC, &imageView);
mState->mCachedImageViews.emplace(key, imageView);
return imageView;
}
VkImageAspectFlags VulkanTexture::getImageAspect() const {
// Helper function in VulkanUtility
auto* const state = getSharedState();
return filament::backend::getImageAspect(state->mVkFormat);
return filament::backend::getImageAspect(mState->mVkFormat);
}
bool VulkanTexture::transitionLayout(VulkanCommandBuffer* commands,
VkImageSubresourceRange const& range, VulkanLayout newLayout) {
return transitionLayout(commands->buffer(), range, newLayout);
if (transitionLayout(commands->buffer(), range, newLayout)) {
commands->acquire(fvkmemory::resource_ptr<VulkanTexture>::cast(this));
return true;
}
return false;
}
bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceRange const& range,
VulkanLayout newLayout) {
auto* const state = getSharedState();
VulkanLayout const oldLayout = getLayout(range.baseArrayLayer, range.baseMipLevel);
uint32_t const firstLayer = range.baseArrayLayer;
@@ -636,7 +597,7 @@ bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceR
continue;
}
hasTransitions = hasTransitions || imgutil::transitionLayout(cmdbuf, {
.image = state->mTextureImage,
.image = mState->mTextureImage,
.oldLayout = layout,
.newLayout = newLayout,
.subresources = {
@@ -651,7 +612,7 @@ bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceR
}
} else if (newLayout != oldLayout) {
hasTransitions = imgutil::transitionLayout(cmdbuf, {
.image = state->mTextureImage,
.image = mState->mTextureImage,
.oldLayout = oldLayout,
.newLayout = newLayout,
.subresources = range,
@@ -684,7 +645,6 @@ bool VulkanTexture::transitionLayout(VkCommandBuffer cmdbuf, VkImageSubresourceR
void VulkanTexture::samplerToAttachmentBarrier(VulkanCommandBuffer* commands,
VkImageSubresourceRange const& range) {
VkCommandBuffer const cmdbuf = commands->buffer();
auto* const state = getSharedState();
VkImageLayout const layout =
imgutil::getVkLayout(getLayout(range.baseArrayLayer, range.baseMipLevel));
VkImageMemoryBarrier barrier = {
@@ -696,7 +656,7 @@ void VulkanTexture::samplerToAttachmentBarrier(VulkanCommandBuffer* commands,
.newLayout = layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = state->mTextureImage,
.image = mState->mTextureImage,
.subresourceRange = range,
};
vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
@@ -708,7 +668,6 @@ void VulkanTexture::samplerToAttachmentBarrier(VulkanCommandBuffer* commands,
void VulkanTexture::attachmentToSamplerBarrier(VulkanCommandBuffer* commands,
VkImageSubresourceRange const& range) {
VkCommandBuffer const cmdbuf = commands->buffer();
auto* const state = getSharedState();
VkImageLayout const layout
= imgutil::getVkLayout(getLayout(range.baseArrayLayer, range.baseMipLevel));
VkImageMemoryBarrier barrier = {
@@ -719,7 +678,7 @@ void VulkanTexture::attachmentToSamplerBarrier(VulkanCommandBuffer* commands,
.newLayout = layout,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = state->mTextureImage,
.image = mState->mTextureImage,
.subresourceRange = range,
};
vkCmdPipelineBarrier(cmdbuf, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
@@ -727,7 +686,6 @@ void VulkanTexture::attachmentToSamplerBarrier(VulkanCommandBuffer* commands,
}
void VulkanTexture::setLayout(VkImageSubresourceRange const& range, VulkanLayout newLayout) {
auto* const state = getSharedState();
uint32_t const firstLayer = range.baseArrayLayer;
uint32_t const lastLayer = firstLayer + range.layerCount;
uint32_t const firstLevel = range.baseMipLevel;
@@ -740,34 +698,32 @@ void VulkanTexture::setLayout(VkImageSubresourceRange const& range, VulkanLayout
for (uint32_t layer = firstLayer; layer < lastLayer; ++layer) {
uint32_t const first = (layer << 16) | firstLevel;
uint32_t const last = (layer << 16) | lastLevel;
state->mSubresourceLayouts.clear(first, last);
mState->mSubresourceLayouts.clear(first, last);
}
} else {
for (uint32_t layer = firstLayer; layer < lastLayer; ++layer) {
uint32_t const first = (layer << 16) | firstLevel;
uint32_t const last = (layer << 16) | lastLevel;
state->mSubresourceLayouts.add(first, last, newLayout);
mState->mSubresourceLayouts.add(first, last, newLayout);
}
}
}
VulkanLayout VulkanTexture::getLayout(uint32_t layer, uint32_t level) const {
assert_invariant(level <= 0xffff && layer <= 0xffff);
auto* const state = getSharedState();
const uint32_t key = (layer << 16) | level;
if (!state->mSubresourceLayouts.has(key)) {
if (!mState->mSubresourceLayouts.has(key)) {
return VulkanLayout::UNDEFINED;
}
return state->mSubresourceLayouts.get(key);
return mState->mSubresourceLayouts.get(key);
}
#if FVK_ENABLED(FVK_DEBUG_TEXTURE)
void VulkanTexture::print() const {
auto* const state = getSharedState();
uint32_t const firstLayer = 0;
uint32_t const lastLayer = firstLayer + state->mFullViewRange.layerCount;
uint32_t const lastLayer = firstLayer + mState->mFullViewRange.layerCount;
uint32_t const firstLevel = 0;
uint32_t const lastLevel = firstLevel + state->mFullViewRange.levelCount;
uint32_t const lastLevel = firstLevel + mState->mFullViewRange.levelCount;
for (uint32_t layer = firstLayer; layer < lastLayer; ++layer) {
for (uint32_t level = firstLevel; level < lastLevel; ++level) {
@@ -776,16 +732,16 @@ void VulkanTexture::print() const {
layer < (mPrimaryViewRange.baseArrayLayer + mPrimaryViewRange.layerCount) &&
level >= mPrimaryViewRange.baseMipLevel &&
level < (mPrimaryViewRange.baseMipLevel + mPrimaryViewRange.levelCount);
FVK_LOGD << "[" << state->mTextureImage << "]: (" << layer << "," << level
FVK_LOGD << "[" << mState->mTextureImage << "]: (" << layer << "," << level
<< ")=" << getLayout(layer, level)
<< " primary=" << primary
<< utils::io::endl;
}
}
for (auto view: state->mCachedImageViews) {
for (auto view: mState->mCachedImageViews) {
auto& range = view.first.range;
FVK_LOGD << "[" << state->mTextureImage << ", imageView=" << view.second << "]=>"
FVK_LOGD << "[" << mState->mTextureImage << ", imageView=" << view.second << "]=>"
<< " (" << range.baseArrayLayer << "," << range.baseMipLevel << ")"
<< " count=(" << range.layerCount << "," << range.levelCount << ")"
<< " aspect=" << range.aspectMask << " viewType=" << view.first.type

View File

@@ -20,8 +20,9 @@
#include "DriverBase.h"
#include "VulkanBuffer.h"
#include "VulkanResources.h"
#include "VulkanImageUtility.h"
#include "vulkan/memory/Resource.h"
#include "vulkan/memory/ResourcePointer.h"
#include <utils/Hash.h>
#include <utils/RangeMap.h>
@@ -30,13 +31,13 @@
namespace filament::backend {
class VulkanResourceAllocator;
struct VulkanTextureState : public VulkanResource {
struct VulkanTextureState : public fvkmemory::Resource {
VulkanTextureState(VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
VulkanStagePool& stagePool, VkFormat format, VkImageViewType viewType, uint8_t levels,
uint8_t layerCount, VulkanLayout defaultLayout, bool isProtected);
~VulkanTextureState();
struct ImageViewKey {
VkImageSubresourceRange range; // 4 * 5 bytes
VkImageViewType type; // 4 bytes
@@ -58,10 +59,8 @@ struct VulkanTextureState : public VulkanResource {
using ImageViewHash = utils::hash::MurmurHashFn<ImageViewKey>;
uint32_t refs = 1;
// The texture with the sidecar owns the sidecar.
std::unique_ptr<VulkanTexture> mSidecarMSAA;
fvkmemory::resource_ptr<VulkanTexture> mSidecarMSAA;
VkDeviceMemory mTextureImageMemory = VK_NULL_HANDLE;
VkFormat const mVkFormat;
@@ -83,37 +82,32 @@ struct VulkanTextureState : public VulkanResource {
bool mIsTransientAttachment;
};
struct VulkanTexture : public HwTexture, VulkanResource {
struct VulkanTexture : public HwTexture, fvkmemory::Resource {
// Standard constructor for user-facing textures.
VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context,
VmaAllocator allocator, VulkanCommands* commands,
VulkanResourceAllocator* handleAllocator,
SamplerType target, uint8_t levels,
TextureFormat tformat, uint8_t samples, uint32_t w, uint32_t h, uint32_t depth,
TextureUsage tusage, VulkanStagePool& stagePool, bool heapAllocated = false);
VmaAllocator allocator, fvkmemory::ResourceManager* resourceManager,
VulkanCommands* commands, SamplerType target, uint8_t levels, TextureFormat tformat,
uint8_t samples, uint32_t w, uint32_t h, uint32_t depth, TextureUsage tusage,
VulkanStagePool& stagePool);
// Specialized constructor for internally created textures (e.g. from a swap chain)
// The texture will never destroy the given VkImage, but it does manages its subresources.
VulkanTexture(VkDevice device, VmaAllocator allocator, VulkanCommands* commands,
VulkanResourceAllocator* handleAllocator,
VkImage image,
VulkanTexture(VkDevice device, VmaAllocator allocator,
fvkmemory::ResourceManager* resourceManager, VulkanCommands* commands, VkImage image,
VkFormat format, uint8_t samples, uint32_t width, uint32_t height, TextureUsage tusage,
VulkanStagePool& stagePool, bool heapAllocated = false);
VulkanStagePool& stagePool);
// Constructor for creating a texture view for wrt specific mip range
VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context,
VmaAllocator allocator, VulkanCommands* commands,
VulkanResourceAllocator* handleAllocator,
VulkanTexture const* src, uint8_t baseLevel, uint8_t levelCount);
fvkmemory::resource_ptr<VulkanTexture> src, uint8_t baseLevel, uint8_t levelCount);
// Constructor for creating a texture view for swizzle.
VulkanTexture(VkDevice device, VkPhysicalDevice physicalDevice, VulkanContext const& context,
VmaAllocator allocator, VulkanCommands* commands,
VulkanResourceAllocator* handleAllocator,
VulkanTexture const* src, VkComponentMapping swizzle);
fvkmemory::resource_ptr<VulkanTexture> src, VkComponentMapping swizzle);
~VulkanTexture();
~VulkanTexture() = default;
// Uploads data into a subregion of a 2D or 3D texture.
void updateImage(const PixelBufferDescriptor& data, uint32_t width, uint32_t height,
@@ -121,16 +115,14 @@ struct VulkanTexture : public HwTexture, VulkanResource {
// Returns the primary image view, which is used for shader sampling.
VkImageView getPrimaryImageView() {
VulkanTextureState* state = getSharedState();
return getImageView(mPrimaryViewRange, state->mViewType, mSwizzle);
return getImageView(mPrimaryViewRange, mState->mViewType, mSwizzle);
}
VkImageViewType getViewType() const {
VulkanTextureState const* state = getSharedState();
return state->mViewType;
return mState->mViewType;
}
VkImageSubresourceRange getPrimaryViewRange() const { return mPrimaryViewRange; }
VkImageSubresourceRange const& getPrimaryViewRange() const { return mPrimaryViewRange; }
VulkanLayout getPrimaryImageLayout() const {
return getLayout(mPrimaryViewRange.baseArrayLayer, mPrimaryViewRange.baseMipLevel);
@@ -156,34 +148,28 @@ struct VulkanTexture : public HwTexture, VulkanResource {
VkImageView getViewForType(VkImageSubresourceRange const& range, VkImageViewType type);
VkFormat getVkFormat() const {
VulkanTextureState const* state = getSharedState();
return state->mVkFormat;
return mState->mVkFormat;
}
VkImage getVkImage() const {
VulkanTextureState const* state = getSharedState();
return state->mTextureImage;
return mState->mTextureImage;
}
VulkanLayout getLayout(uint32_t layer, uint32_t level) const;
void setSidecar(VulkanTexture* sidecar) {
VulkanTextureState* state = getSharedState();
state->mSidecarMSAA.reset(sidecar);
void setSidecar(fvkmemory::resource_ptr<VulkanTexture> sidecar) {
mState->mSidecarMSAA = sidecar;
}
VulkanTexture* getSidecar() const {
VulkanTextureState const* state = getSharedState();
return state->mSidecarMSAA.get();
fvkmemory::resource_ptr<VulkanTexture> getSidecar() const {
return mState->mSidecarMSAA;
}
bool isTransientAttachment() const {
VulkanTextureState const* state = getSharedState();
return state->mIsTransientAttachment;
return mState->mIsTransientAttachment;
}
bool getIsProtected() const {
VulkanTextureState const* state = getSharedState();
return state->mIsProtected;
return mState->mIsProtected;
}
bool transitionLayout(VulkanCommandBuffer* commands, VkImageSubresourceRange const& range,
@@ -211,9 +197,6 @@ struct VulkanTexture : public HwTexture, VulkanResource {
#endif
private:
VulkanTextureState* getSharedState();
VulkanTextureState const* getSharedState() const;
// Gets or creates a cached VkImageView for a range of miplevels, array layers, viewType, and
// swizzle (or not).
VkImageView getImageView(VkImageSubresourceRange range, VkImageViewType viewType,
@@ -222,9 +205,7 @@ private:
void updateImageWithBlit(const PixelBufferDescriptor& hostData, uint32_t width, uint32_t height,
uint32_t depth, uint32_t miplevel);
VulkanResourceAllocator* const mAllocator;
Handle<VulkanTextureState> mState;
fvkmemory::resource_ptr<VulkanTextureState> mState;
// Track the range of subresources that define the "primary" image view, which is the special
// image view that gets bound to an actual texture sampler.

View File

@@ -16,11 +16,11 @@
#include "VulkanDescriptorSetManager.h"
#include <vulkan/VulkanCommands.h>
#include <vulkan/VulkanHandles.h>
#include <vulkan/VulkanUtility.h>
#include <vulkan/VulkanConstants.h>
#include <vulkan/VulkanImageUtility.h>
#include <vulkan/VulkanResources.h>
#include <utils/FixedCapacityVector.h>
#include <utils/Panic.h>
@@ -274,7 +274,7 @@ public:
DescriptorInfinitePool(VkDevice device)
: mDevice(device) {}
VkDescriptorSet obtainSet(VulkanDescriptorSetLayout* layout) {
VkDescriptorSet obtainSet(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
auto const vklayout = layout->getVkLayout();
DescriptorPool* sameTypePool = nullptr;
for (auto& pool: mPools) {
@@ -325,13 +325,12 @@ public:
DescriptorSetLayoutManager(VkDevice device)
: mDevice(device) {}
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout* layout) {
auto const& bitmasks = layout->bitmask;
VkDescriptorSetLayout getVkLayout(VulkanDescriptorSetLayout::Bitmask const& bitmasks) {
if (auto itr = mVkLayouts.find(bitmasks); itr != mVkLayouts.end()) {
return itr->second;
}
auto vklayout = createLayout(mDevice, layout->bitmask);
mVkLayouts[layout->bitmask] = vklayout;
auto vklayout = createLayout(mDevice, bitmasks);
mVkLayouts[bitmasks] = vklayout;
return vklayout;
}
@@ -347,10 +346,11 @@ private:
mVkLayouts;
};
VulkanDescriptorSetManager::VulkanDescriptorSetManager(VkDevice device,
VulkanResourceAllocator* resourceAllocator)
fvkmemory::ResourceManager* resourceManager)
: mDevice(device),
mResourceAllocator(resourceAllocator),
mResourceManager(resourceManager),
mLayoutManager(std::make_unique<DescriptorSetLayoutManager>(device)),
mDescriptorPool(std::make_unique<DescriptorInfinitePool>(device)) {}
@@ -359,18 +359,20 @@ VulkanDescriptorSetManager::~VulkanDescriptorSetManager() = default;
void VulkanDescriptorSetManager::terminate() noexcept{
mLayoutManager.reset();
mDescriptorPool.reset();
clearHistory();
}
// bind() is not really binding the set but just stashing until we have all the info
// (pipelinelayout).
void VulkanDescriptorSetManager::bind(uint8_t setIndex, VulkanDescriptorSet* set,
void VulkanDescriptorSetManager::bind(uint8_t setIndex,
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
backend::DescriptorSetOffsetArray&& offsets) {
set->setOffsets(std::move(offsets));
mStashedSets[setIndex] = set;
}
void VulkanDescriptorSetManager::unbind(uint8_t setIndex) {
mStashedSets[setIndex] = nullptr;
mStashedSets[setIndex] = {};
}
void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands,
@@ -412,14 +414,14 @@ void VulkanDescriptorSetManager::commit(VulkanCommandBuffer* commands,
};
}
void VulkanDescriptorSetManager::updateBuffer(VulkanDescriptorSet* set, uint8_t binding,
VulkanBufferObject* bufferObject, VkDeviceSize offset, VkDeviceSize size) noexcept {
void VulkanDescriptorSetManager::updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanBufferObject> bufferObject,
VkDeviceSize offset, VkDeviceSize size) noexcept {
VkDescriptorBufferInfo const info = {
.buffer = bufferObject->buffer.getGpuBuffer(),
.offset = offset,
.range = size,
};
VkDescriptorType type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
if (set->dynamicUboMask.test(binding)) {
@@ -438,8 +440,9 @@ void VulkanDescriptorSetManager::updateBuffer(VulkanDescriptorSet* set, uint8_t
set->acquire(bufferObject);
}
void VulkanDescriptorSetManager::updateSampler(VulkanDescriptorSet* set, uint8_t binding,
VulkanTexture* texture, VkSampler sampler) noexcept {
void VulkanDescriptorSetManager::updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
uint8_t binding, fvkmemory::resource_ptr<VulkanTexture> texture,
VkSampler sampler) noexcept {
VkDescriptorImageInfo info{
.sampler = sampler,
};
@@ -469,38 +472,35 @@ void VulkanDescriptorSetManager::updateSampler(VulkanDescriptorSet* set, uint8_t
set->acquire(texture);
}
void VulkanDescriptorSetManager::updateInputAttachment(VulkanDescriptorSet* set,
VulkanAttachment attachment) noexcept {
void VulkanDescriptorSetManager::updateInputAttachment(
fvkmemory::resource_ptr<VulkanDescriptorSet> set,
VulkanAttachment const& attachment) noexcept {
// TOOD: fill-in this region
}
void VulkanDescriptorSetManager::createSet(Handle<HwDescriptorSet> handle,
VulkanDescriptorSetLayout* layout) {
fvkmemory::resource_ptr<VulkanDescriptorSet> VulkanDescriptorSetManager::createSet(
Handle<HwDescriptorSet> handle, fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
auto const vkSet = mDescriptorPool->obtainSet(layout);
auto const& count = layout->count;
auto const vklayout = layout->getVkLayout();
mResourceAllocator->construct<VulkanDescriptorSet>(handle, mResourceAllocator, vkSet,
return fvkmemory::resource_ptr<VulkanDescriptorSet>::make(mResourceManager, handle, vkSet,
layout->bitmask.dynamicUbo, layout->count.dynamicUbo,
[vkSet, count, vklayout, this](VulkanDescriptorSet* set) {
eraseSetFromHistory(set);
mDescriptorPool->recycle(count, vklayout, vkSet);
[vkSet, count, vklayout, this](VulkanDescriptorSet*) {
// Note that mDescriptorPool could be gone due to terminate (when the backend shuts
// down).
if (mDescriptorPool) {
mDescriptorPool->recycle(count, vklayout, vkSet);
}
});
}
void VulkanDescriptorSetManager::destroySet(Handle<HwDescriptorSet> handle) {
void VulkanDescriptorSetManager::initVkLayout(
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout) {
layout->setVkLayout(mLayoutManager->getVkLayout(layout->bitmask));
}
void VulkanDescriptorSetManager::initVkLayout(VulkanDescriptorSetLayout* layout) {
layout->setVkLayout(mLayoutManager->getVkLayout(layout));
void VulkanDescriptorSetManager::clearHistory() {
mStashedSets = {};
}
void VulkanDescriptorSetManager::eraseSetFromHistory(VulkanDescriptorSet* set) {
for (uint8_t i = 0; i < mStashedSets.size(); ++i) {
if (mStashedSets[i] == set) {
mStashedSets[i] = nullptr;
}
}
}
} // namespace filament::backend

View File

@@ -17,9 +17,10 @@
#ifndef TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
#define TNT_FILAMENT_BACKEND_CACHING_VULKANDESCRIPTORSETMANAGER_H
#include <vulkan/VulkanResourceAllocator.h>
#include <vulkan/VulkanTexture.h>
#include <vulkan/VulkanUtility.h>
#include "vulkan/VulkanHandles.h"
#include "vulkan/VulkanTexture.h"
#include "vulkan/VulkanUtility.h"
#include "vulkan/memory/ResourcePointer.h"
#include <backend/DriverEnums.h>
#include <backend/Program.h>
@@ -46,46 +47,45 @@ public:
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
VulkanDescriptorSetManager(VkDevice device, VulkanResourceAllocator* resourceAllocator);
VulkanDescriptorSetManager(VkDevice device, fvkmemory::ResourceManager* resourceManager);
~VulkanDescriptorSetManager();
void terminate() noexcept;
void updateBuffer(VulkanDescriptorSet* set, uint8_t binding,
VulkanBufferObject* bufferObject, VkDeviceSize offset, VkDeviceSize size) noexcept;
void updateBuffer(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
fvkmemory::resource_ptr<VulkanBufferObject> bufferObject, VkDeviceSize offset,
VkDeviceSize size) noexcept;
void updateSampler(VulkanDescriptorSet* set, uint8_t binding,
VulkanTexture* texture, VkSampler sampler) noexcept;
void updateSampler(fvkmemory::resource_ptr<VulkanDescriptorSet> set, uint8_t binding,
fvkmemory::resource_ptr<VulkanTexture> texture, VkSampler sampler) noexcept;
void updateInputAttachment(VulkanDescriptorSet* set, VulkanAttachment attachment) noexcept;
void updateInputAttachment(fvkmemory::resource_ptr<VulkanDescriptorSet> set,
VulkanAttachment const& attachment) noexcept;
void bind(uint8_t setIndex, VulkanDescriptorSet* set, backend::DescriptorSetOffsetArray&& offsets);
void bind(uint8_t setIndex, fvkmemory::resource_ptr<VulkanDescriptorSet> set,
backend::DescriptorSetOffsetArray&& offsets);
void unbind(uint8_t setIndex);
void commit(VulkanCommandBuffer* commands, VkPipelineLayout pipelineLayout,
DescriptorSetMask const& setMask);
void setPlaceHolders(VkSampler sampler, VulkanTexture* texture,
VulkanBufferObject* bufferObject) noexcept;
fvkmemory::resource_ptr<VulkanDescriptorSet> createSet(Handle<HwDescriptorSet> handle,
fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
void createSet(Handle<HwDescriptorSet> handle, VulkanDescriptorSetLayout* layout);
void initVkLayout(fvkmemory::resource_ptr<VulkanDescriptorSetLayout> layout);
void destroySet(Handle<HwDescriptorSet> handle);
void initVkLayout(VulkanDescriptorSetLayout* layout);
void clearHistory();
private:
class DescriptorSetLayoutManager;
class DescriptorInfinitePool;
void eraseSetFromHistory(VulkanDescriptorSet* set);
using DescriptorSetArray =
std::array<VulkanDescriptorSet*, UNIQUE_DESCRIPTOR_SET_COUNT>;
std::array<fvkmemory::resource_ptr<VulkanDescriptorSet>, UNIQUE_DESCRIPTOR_SET_COUNT>;
VkDevice mDevice;
VulkanResourceAllocator* mResourceAllocator;
fvkmemory::ResourceManager* mResourceManager;
std::unique_ptr<DescriptorSetLayoutManager> mLayoutManager;
std::unique_ptr<DescriptorInfinitePool> mDescriptorPool;
std::pair<VulkanAttachment, VkDescriptorImageInfo> mInputAttachment;
@@ -94,7 +94,7 @@ private:
struct {
VkPipelineLayout pipelineLayout = VK_NULL_HANDLE;
DescriptorSetMask setMask;
DescriptorSetArray boundSets;
DescriptorSetArray boundSets = {};
} mLastBoundInfo;
};

View File

@@ -16,12 +16,11 @@
#include "VulkanPipelineLayoutCache.h"
#include <vulkan/VulkanResourceAllocator.h>
namespace filament::backend {
VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
DescriptorSetLayoutArray const& descriptorSetLayouts, VulkanProgram* program) {
DescriptorSetLayoutArray const& descriptorSetLayouts,
fvkmemory::resource_ptr<VulkanProgram> program) {
PipelineLayoutKey key = {};
uint8_t descSetLayoutCount = 0;
key.descSetLayouts = descriptorSetLayouts;
@@ -40,13 +39,13 @@ VkPipelineLayout VulkanPipelineLayoutCache::getLayout(
for (uint8_t i = 0; i < pushConstantRangeCount; ++i) {
auto const& range = pushConstantRanges[i];
auto& pushConstant = key.pushConstant[i];
if (range.stageFlags & VK_SHADER_STAGE_VERTEX_BIT) {
if (range.stageFlags & VK_SHADER_STAGE_VERTEX_BIT) {
pushConstant.stage = static_cast<uint8_t>(ShaderStage::VERTEX);
}
if (range.stageFlags & VK_SHADER_STAGE_FRAGMENT_BIT) {
if (range.stageFlags & VK_SHADER_STAGE_FRAGMENT_BIT) {
pushConstant.stage = static_cast<uint8_t>(ShaderStage::FRAGMENT);
}
if (range.stageFlags & VK_SHADER_STAGE_COMPUTE_BIT) {
if (range.stageFlags & VK_SHADER_STAGE_COMPUTE_BIT) {
pushConstant.stage = static_cast<uint8_t>(ShaderStage::COMPUTE);
}
pushConstant.size = range.size;

View File

@@ -30,7 +30,7 @@ class VulkanPipelineLayoutCache {
public:
using DescriptorSetLayoutArray = VulkanDescriptorSetLayout::DescriptorSetLayoutArray;
VulkanPipelineLayoutCache(VkDevice device, VulkanResourceAllocator* allocator)
VulkanPipelineLayoutCache(VkDevice device)
: mDevice(device),
mTimestamp(0) {}
@@ -56,7 +56,7 @@ public:
// A pipeline layout depends on the descriptor set layout and the push constant ranges, which
// are described in the program.
VkPipelineLayout getLayout(DescriptorSetLayoutArray const& descriptorSetLayouts,
VulkanProgram* program);
fvkmemory::resource_ptr<VulkanProgram> program);
private:
using Timestamp = uint64_t;

View File

@@ -22,7 +22,7 @@
namespace filament::backend::fvkmemory {
namespace {
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
uint32_t COUNTER[(size_t) ResourceType::UNDEFINED_TYPE] = {};
#endif
}
@@ -47,8 +47,7 @@ void ResourceManager::gc() noexcept {
}
GcList gcs;
gcs.insert(gcs.end(), mGcList.begin(), mGcList.end());
mGcList.clear();
std::swap(gcs, mGcList);
destroyAll(gcs);
}
@@ -105,20 +104,20 @@ void ResourceManager::destroyWithType(ResourceType type, HandleId id) {
case ResourceType::UNDEFINED_TYPE:
break;
}
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
COUNTER[(size_t) type]--;
#endif
}
void ResourceManager::traceConstruction(ResourceType type, HandleId id) {
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
assert_invariant(type != ResourceType::UNDEFINED_TYPE);
COUNTER[(size_t) type]++;
#endif
}
void ResourceManager::print() const noexcept {
#if FVK_ENABLED(FVK_DEBUG_ALLOCATION)
#if FVK_ENABLED(FVK_DEBUG_RESOURCE_LEAK)
utils::slog.e << "-------------------" << utils::io::endl;
for (size_t i = 0; i < (size_t) ResourceType::UNDEFINED_TYPE; ++i) {
utils::slog.e <<" " << getTypeStr((ResourceType) i) << "=" << COUNTER[i] << utils::io::endl;

View File

@@ -95,7 +95,6 @@ private:
AllocatorImpl mHandleAllocatorImpl;
using GcList = std::vector<std::pair<ResourceType, HandleId>>;
utils::Mutex mThreadSafeGcListMutex;
GcList mThreadSafeGcList;
GcList mGcList;

View File

@@ -29,7 +29,8 @@ namespace filament::backend {
namespace {
std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& context,
VkDevice device, VkExtent2D extent, VkFormat format) {
VkDevice device, VkExtent2D extent, VkFormat format,
bool isProtected) {
bool const isDepth = isVkDepthFormat(format);
// Filament expects blit() to work with any texture, so we almost always set these usage flags
// (see copyFrame() and readPixels()).
@@ -38,6 +39,7 @@ std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& co
VkImageCreateInfo imageInfo {
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
.flags = isProtected ? VK_IMAGE_CREATE_PROTECTED_BIT : VkImageCreateFlags(0),
.imageType = VK_IMAGE_TYPE_2D,
.format = format,
.extent = {extent.width, extent.height, 1},
@@ -58,8 +60,11 @@ std::tuple<VkImage, VkDeviceMemory> createImageAndMemory(VulkanContext const& co
VkMemoryRequirements memReqs;
vkGetImageMemoryRequirements(device, image, &memReqs);
const VkFlags requiredMemoryFlags =
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
(isProtected ? VK_MEMORY_PROPERTY_PROTECTED_BIT : 0U);
uint32_t memoryTypeIndex
= context.selectMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
= context.selectMemoryType(memReqs.memoryTypeBits, requiredMemoryFlags);
FILAMENT_CHECK_POSTCONDITION(memoryTypeIndex < VK_MAX_MEMORY_TYPES)
<< "VulkanPlatformSwapChainImpl: unable to find a memory type that meets requirements.";
@@ -110,8 +115,9 @@ void VulkanPlatformSwapChainImpl::destroy() {
mSwapChainBundle.colors.clear();
}
VkImage VulkanPlatformSwapChainImpl::createImage(VkExtent2D extent, VkFormat format) {
auto [image, memory] = createImageAndMemory(mContext, mDevice, extent, format);
VkImage VulkanPlatformSwapChainImpl::createImage(VkExtent2D extent, VkFormat format,
bool isProtected) {
auto [image, memory] = createImageAndMemory(mContext, mDevice, extent, format, isProtected);
mMemory.insert({image, memory});
return image;
}
@@ -248,7 +254,8 @@ VkResult VulkanPlatformSurfaceSwapChain::create() {
mSwapChainBundle.colorFormat = surfaceFormat.format;
mSwapChainBundle.depthFormat =
selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), mHasStencil);
mSwapChainBundle.depth = createImage(mSwapChainBundle.extent, mSwapChainBundle.depthFormat);
mSwapChainBundle.depth = createImage(mSwapChainBundle.extent,
mSwapChainBundle.depthFormat, mIsProtected);
mSwapChainBundle.isProtected = mIsProtected;
FVK_LOGI << "vkCreateSwapchain"
@@ -356,13 +363,13 @@ VulkanPlatformHeadlessSwapChain::VulkanPlatformHeadlessSwapChain(VulkanContext c
images.reserve(HEADLESS_SWAPCHAIN_SIZE);
images.resize(HEADLESS_SWAPCHAIN_SIZE);
for (size_t i = 0; i < HEADLESS_SWAPCHAIN_SIZE; ++i) {
images[i] = createImage(extent, mSwapChainBundle.colorFormat);
images[i] = createImage(extent, mSwapChainBundle.colorFormat, false);
}
bool const hasStencil = (flags & backend::SWAP_CHAIN_HAS_STENCIL_BUFFER) != 0;
mSwapChainBundle.depthFormat =
selectDepthFormat(mContext.getAttachmentDepthStencilFormats(), hasStencil);
mSwapChainBundle.depth = createImage(extent, mSwapChainBundle.depthFormat);
mSwapChainBundle.depth = createImage(extent, mSwapChainBundle.depthFormat, false);
}
VulkanPlatformHeadlessSwapChain::~VulkanPlatformHeadlessSwapChain() {

View File

@@ -73,7 +73,7 @@ protected:
// Non-virtual override-able method
void destroy();
VkImage createImage(VkExtent2D extent, VkFormat format);
VkImage createImage(VkExtent2D extent, VkFormat format, bool isProtected);
VulkanContext const& mContext;
VkDevice mDevice;

View File

@@ -347,6 +347,12 @@ FMaterial::~FMaterial() noexcept = default;
void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantValue) noexcept {
DriverApi& driverApi = mEngine.getDriverApi();
FMaterial const* const pDefaultMaterial = mEngine.getDefaultMaterial();
auto hasDefaultDepthVariant = [pDefaultMaterial](Variant k) {
auto const& end = pDefaultMaterial->mDepthVariants.end();
return end != std::find(pDefaultMaterial->mDepthVariants.begin(), end, k);
};
if (mMaterialDomain == MaterialDomain::SURFACE) {
auto& cachedPrograms = mCachedPrograms;
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
@@ -355,8 +361,9 @@ void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantV
if (UTILS_LIKELY(!mIsDefaultMaterial)) {
// The depth variants may be shared with the default material, in which case
// we should not free it now.
bool const isSharedVariant =
Variant::isValidDepthVariant(variant) && !mHasCustomDepthShader;
bool const isSharedVariant = Variant::isValidDepthVariant(variant) &&
!mHasCustomDepthShader &&
hasDefaultDepthVariant(variant);
if (isSharedVariant) {
// we don't own this variant, skip.
continue;
@@ -367,8 +374,7 @@ void FMaterial::invalidate(Variant::type_t variantMask, Variant::type_t variantV
}
}
if (UTILS_UNLIKELY(!mIsDefaultMaterial && !mHasCustomDepthShader)) {
FMaterial const* const pDefaultMaterial = mEngine.getDefaultMaterial();
if (UTILS_UNLIKELY(!mIsDefaultMaterial && !mHasCustomDepthShader)) {
for (Variant const variant: pDefaultMaterial->mDepthVariants) {
pDefaultMaterial->prepareProgram(variant);
if (!cachedPrograms[variant.key]) {
@@ -626,6 +632,7 @@ Program FMaterial::getProgramWithVariants(
}
void FMaterial::createAndCacheProgram(Program&& p, Variant variant) const noexcept {
assert_invariant(!mCachedPrograms[variant.key]);
auto program = mEngine.getDriverApi().createProgram(std::move(p));
mEngine.getDriverApi().setDebugTag(program.getId(), mName);
assert_invariant(program);
@@ -737,12 +744,19 @@ void FMaterial::onQueryCallback(void* userdata, VariantList* pVariants) {
void FMaterial::destroyPrograms(FEngine& engine) {
DriverApi& driverApi = engine.getDriverApi();
auto& cachedPrograms = mCachedPrograms;
auto hasDefaultDepthVariant = [pDefaultMaterial = engine.getDefaultMaterial()](Variant k) {
auto const& end = pDefaultMaterial->mDepthVariants.end();
return end != std::find(pDefaultMaterial->mDepthVariants.begin(), end, k);
};
for (size_t k = 0, n = VARIANT_COUNT; k < n; ++k) {
const Variant variant(k);
if (!mIsDefaultMaterial) {
// The depth variants may be shared with the default material, in which case
// we should not free it now.
bool const isSharedVariant = Variant::isValidDepthVariant(variant) && !mHasCustomDepthShader;
bool const isSharedVariant = Variant::isValidDepthVariant(variant) &&
!mHasCustomDepthShader && hasDefaultDepthVariant(variant);
if (isSharedVariant) {
// we don't own this variant, skip.
continue;

View File

@@ -584,6 +584,10 @@ void FRenderer::render(FView const* view) {
void FRenderer::renderInternal(FView const* view) {
FEngine& engine = mEngine;
FILAMENT_CHECK_PRECONDITION(!view->hasPostProcessPass() ||
engine.hasFeatureLevel(FeatureLevel::FEATURE_LEVEL_1))
<< "post-processing is not supported at FEATURE_LEVEL_0";
// per-renderpass data
RootArenaScope rootArenaScope(engine.getPerRenderPassArena());

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()

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

View File

@@ -278,18 +278,23 @@ public:
void* pop() noexcept {
Node* const pStorage = mStorage;
HeadPtr currentHead = mHead.load();
HeadPtr currentHead = mHead.load(std::memory_order_relaxed);
while (currentHead.offset >= 0) {
// The value of "pNext" we load here might already contain application data if another
// thread raced ahead of us. But in that case, the computed "newHead" will be discarded
// since compare_exchange_weak fails. Then this thread will loop with the updated
// since compare_exchange_weak() fails. Then this thread will loop with the updated
// value of currentHead, and try again.
Node* const pNext = pStorage[currentHead.offset].next.load(std::memory_order_relaxed);
// TSAN complains if we don't use a local variable here.
Node const node = pStorage[currentHead.offset];
Node const* const pNext = node.next;
const HeadPtr newHead{ pNext ? int32_t(pNext - pStorage) : -1, currentHead.tag + 1 };
// In the rare case that the other thread that raced ahead of us already returned the
// same mHead we just loaded, but it now has a different "next" value, the tag field will not
// match, and compare_exchange_weak will fail and prevent that particular race condition.
if (mHead.compare_exchange_weak(currentHead, newHead)) {
// In the rare case that the other thread that raced ahead of us already returned the
// same mHead we just loaded, but it now has a different "next" value, the tag field
// will not match, and compare_exchange_weak() will fail and prevent that particular
// race condition.
// acquire: no read/write can be reordered before this
if (mHead.compare_exchange_weak(currentHead, newHead,
std::memory_order_acquire, std::memory_order_relaxed)) {
// This assert needs to occur after we have validated that there was no race condition
// Otherwise, next might already contain application data, if another thread
// raced ahead of us after we loaded mHead, but before we loaded mHead->next.
@@ -306,13 +311,15 @@ public:
Node* const storage = mStorage;
assert_invariant(p && p >= storage);
Node* const node = static_cast<Node*>(p);
HeadPtr currentHead = mHead.load();
HeadPtr currentHead = mHead.load(std::memory_order_relaxed);
HeadPtr newHead = { int32_t(node - storage), currentHead.tag + 1 };
do {
newHead.tag = currentHead.tag + 1;
Node* const n = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr;
node->next.store(n, std::memory_order_relaxed);
} while(!mHead.compare_exchange_weak(currentHead, newHead));
Node* const pNext = (currentHead.offset >= 0) ? (storage + currentHead.offset) : nullptr;
node->next = pNext; // could be a race with pop, corrected by CAS
} while(!mHead.compare_exchange_weak(currentHead, newHead,
std::memory_order_release, std::memory_order_relaxed));
// release: no read/write can be reordered after this
}
void* getFirst() noexcept {
@@ -320,10 +327,7 @@ public:
}
struct Node {
// This should be a regular (non-atomic) pointer, but this causes TSAN to complain
// about a data-race that exists but is benin. We always use this atomic<> in
// relaxed mode.
// The data race TSAN complains about is when a pop() is interrupted by a
// There is a benign data race when a pop() is interrupted by a
// pop() + push() just after mHead->next is read -- it appears as though it is written
// without synchronization (by the push), however in that case, the pop's CAS will fail
// and things will auto-correct.
@@ -346,7 +350,7 @@ public:
// |
// CAS, tag++
//
std::atomic<Node*> next;
Node* next = nullptr;
};
private:

View File

@@ -446,7 +446,7 @@ private:
Condition mWaiterCondition;
std::atomic<int32_t> mActiveJobs = { 0 };
utils::Arena<utils::ThreadSafeObjectPoolAllocator<Job>, LockingPolicy::NoLock> mJobPool;
utils::Arena<utils::ObjectPoolAllocator<Job>, LockingPolicy::Mutex> mJobPool;
template <typename T>
using aligned_vector = std::vector<T, utils::STLAlignedAllocator<T>>;

View File

@@ -95,11 +95,11 @@ protected:
size_t length() const noexcept;
private:
void reserve(size_t newSize) noexcept;
void reserve(size_t newCapacity) noexcept;
char* buffer = nullptr; // buffer address
char* curr = nullptr; // current pointer
size_t size = 0; // size remaining
size_t sizeRemaining = 0; // size remaining
size_t capacity = 0; // total capacity of the buffer
};

View File

@@ -237,23 +237,23 @@ ostream::Buffer::~Buffer() noexcept {
void ostream::Buffer::advance(ssize_t n) noexcept {
if (n > 0) {
size_t const written = n < size ? size_t(n) : size;
size_t const written = n < sizeRemaining ? size_t(n) : sizeRemaining;
curr += written;
size -= written;
sizeRemaining -= written;
}
}
void ostream::Buffer::reserve(size_t newSize) noexcept {
void ostream::Buffer::reserve(size_t newCapacity) noexcept {
size_t const offset = curr - buffer;
if (buffer == nullptr) {
buffer = (char*)malloc(newSize);
buffer = (char*)malloc(newCapacity);
} else {
buffer = (char*)realloc(buffer, newSize);
buffer = (char*)realloc(buffer, newCapacity);
}
assert(buffer);
capacity = newSize;
capacity = newCapacity;
curr = buffer + offset;
size = capacity - offset;
sizeRemaining = capacity - offset;
}
void ostream::Buffer::reset() noexcept {
@@ -264,7 +264,7 @@ void ostream::Buffer::reset() noexcept {
capacity = 1024;
}
curr = buffer;
size = capacity;
sizeRemaining = capacity;
}
size_t ostream::Buffer::length() const noexcept {
@@ -272,13 +272,14 @@ size_t ostream::Buffer::length() const noexcept {
}
std::pair<char*, size_t> ostream::Buffer::grow(size_t s) noexcept {
if (UTILS_UNLIKELY(size < s)) {
size_t const used = curr - buffer;
size_t const newCapacity = std::max(size_t(32), used + (s * 3 + 1) / 2); // 32 bytes minimum
if (UTILS_UNLIKELY(sizeRemaining < s)) {
size_t const usedSize = curr - buffer;
size_t const neededCapacity = usedSize + s;
size_t const newCapacity = std::max(size_t(32), (neededCapacity * 3 + 1) / 2); // 32 bytes minimum
reserve(newCapacity);
assert(size >= s);
assert(sizeRemaining >= s);
}
return { curr, size };
return { curr, sizeRemaining };
}
} // namespace utils::io

View File

@@ -19,7 +19,7 @@
float sampleDepth(const mediump sampler2DArrayShadow map,
const highp vec4 scissorNormalized,
const uint layer, highp vec2 uv, float depth) {
const uint layer, highp vec2 uv, highp float depth) {
// clamp needed for directional lights and/or large kernels
uv = clamp(uv, scissorNormalized.xy, scissorNormalized.zw);
@@ -51,7 +51,7 @@ float ShadowSample_PCF_Low(const mediump sampler2DArrayShadow map,
highp vec2 texelSize = vec2(1.0) / size;
// Castaño, 2013, "Shadow Mapping Summary Part 1"
float depth = position.z;
highp float depth = position.z;
// clamp position to avoid overflows below, which cause some GPUs to abort
position.xy = clamp(position.xy, vec2(-1.0), vec2(2.0));