Skip to content

Commit

Permalink
Use FixedCircularBuffer
Browse files Browse the repository at this point in the history
  • Loading branch information
bejado committed Oct 16, 2023
1 parent 8eb51dc commit 943655f
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 14 deletions.
15 changes: 8 additions & 7 deletions filament/backend/src/metal/MetalContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@
#include <Metal/Metal.h>
#include <QuartzCore/QuartzCore.h>

#include <utils/FixedCircularBuffer.h>

#include <array>
#include <atomic>
#include <stack>
#include <deque>

#if defined(FILAMENT_METAL_PROFILING)
#include <os/log.h>
Expand Down Expand Up @@ -117,12 +118,12 @@ struct MetalContext {
tsl::robin_set<MetalSamplerGroup*> samplerGroups;
tsl::robin_set<MetalTexture*> textures;

// This deque implements delayed destruction for Metal texture handles. It keeps a handle to
// the most recent kMetalFreedTextureListSize texture handles. When we're asked to destroy a
// texture handle, we free its texture memory, but keep the MetalTexture object alive, marking
// it as "terminated". If we later are asked to use that texture, we can check its terminated
// status and throw an error instead of crashing.
std::deque<Handle<HwTexture>> texturesToDestroy;
// This circle buffer implements delayed destruction for Metal texture handles. It keeps a
// handle to the most recent kMetalFreedTextureListSize texture handles. When we're asked to
// destroy a texture handle, we free its texture memory, but keep the MetalTexture object alive,
// marking it as "terminated". If we later are asked to use that texture, we can check its
// terminated status and throw an error instead of crashing.
utils::FixedCircularBuffer<Handle<HwTexture>, kMetalFreedTextureListSize> texturesToDestroy;

MetalBufferPool* bufferPool;

Expand Down
14 changes: 7 additions & 7 deletions filament/backend/src/metal/MetalDriver.mm
Original file line number Diff line number Diff line change
Expand Up @@ -537,12 +537,12 @@
metalTexture->terminate();

// Delay the destruction of this texture handle.
mContext->texturesToDestroy.push_back(th);
if (mContext->texturesToDestroy.size() > kMetalFreedTextureListSize) {
Handle<HwTexture> handleToFree = mContext->texturesToDestroy.front();
mContext->texturesToDestroy.pop_front();
if (mContext->texturesToDestroy.full()) {
Handle<HwTexture> handleToFree = mContext->texturesToDestroy.pop();
destruct_handle<MetalTexture>(handleToFree);
}
assert_invariant(!mContext->texturesToDestroy.full());
mContext->texturesToDestroy.push(th);
}

void MetalDriver::destroyRenderTarget(Handle<HwRenderTarget> rth) {
Expand All @@ -569,10 +569,10 @@

void MetalDriver::terminate() {
// Terminate any oustanding MetalTextures.
for (auto th : mContext->texturesToDestroy) {
destruct_handle<MetalTexture>(th);
while (mContext->texturesToDestroy.size() > 0) {
Handle<HwTexture> toDestroy = mContext->texturesToDestroy.pop();
destruct_handle<MetalTexture>(toDestroy);
}
mContext->texturesToDestroy.clear();

// finish() will flush the pending command buffer and will ensure all GPU work has finished.
// This must be done before calling bufferPool->reset() to ensure no buffers are in flight.
Expand Down
1 change: 1 addition & 0 deletions libs/utils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ set(TEST_SRCS
test/test_CyclicBarrier.cpp
test/test_Entity.cpp
test/test_FixedCapacityVector.cpp
test/test_FixedCircularBuffer.cpp
test/test_Hash.cpp
test/test_JobSystem.cpp
test/test_QuadTreeArray.cpp
Expand Down
60 changes: 60 additions & 0 deletions libs/utils/include/utils/FixedCircularBuffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* 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_UTILS_FIXEDCIRCULARBUFFER_H
#define TNT_UTILS_FIXEDCIRCULARBUFFER_H

#include <utils/debug.h>

#include <stddef.h>
#include <type_traits>

namespace utils {

template<typename T, size_t N>
class FixedCircularBuffer {
public:

size_t size() const noexcept { return mSize; }
bool full() const noexcept { return mSize == N; }

void push(T v) noexcept {
assert_invariant(!full());
mData[mEnd] = v;
mEnd = (mEnd + 1) % N;
mSize++;
}

T pop() noexcept {
assert_invariant(mSize > 0);
T result = mData[mBegin];
mBegin = (mBegin + 1) % N;
mSize--;
return result;
}

private:
T mData[N];

size_t mBegin = 0;
size_t mEnd = 0;
size_t mSize = 0;
};

} // namespace utils

#endif // TNT_UTILS_FIXEDCIRCULARBUFFER_H
65 changes: 65 additions & 0 deletions libs/utils/test/test_FixedCircularBuffer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* 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 <gtest/gtest.h>

#include <utils/FixedCircularBuffer.h>

using namespace utils;

TEST(FixedCircularBufferTest, Simple) {
FixedCircularBuffer<int, 4> circularBuffer;
EXPECT_EQ(circularBuffer.size(), 0);

circularBuffer.push(1);
circularBuffer.push(2);
circularBuffer.push(3);
EXPECT_EQ(circularBuffer.size(), 3);
EXPECT_EQ(circularBuffer.pop(), 1);
EXPECT_EQ(circularBuffer.pop(), 2);
EXPECT_EQ(circularBuffer.pop(), 3);
EXPECT_EQ(circularBuffer.size(), 0);

circularBuffer.push(4);
circularBuffer.push(5);
circularBuffer.push(6);
circularBuffer.push(7);
EXPECT_EQ(circularBuffer.size(), 4);
EXPECT_TRUE(circularBuffer.full());
EXPECT_EQ(circularBuffer.pop(), 4);
EXPECT_EQ(circularBuffer.pop(), 5);
EXPECT_EQ(circularBuffer.pop(), 6);
EXPECT_EQ(circularBuffer.pop(), 7);
}

TEST(FixedCircularBufferTest, Exceptions) {
#if !defined(NDEBUG) && defined(GTEST_HAS_DEATH_TEST)
FixedCircularBuffer<int, 4> circularBuffer;

EXPECT_DEATH({
circularBuffer.pop(); // should assert
}, "failed assertion");

EXPECT_DEATH({
circularBuffer.push(1);
circularBuffer.push(2);
circularBuffer.push(3);
circularBuffer.push(4);
circularBuffer.push(5); // should assert
}, "failed assertion");
#endif
}

0 comments on commit 943655f

Please sign in to comment.