Correct and revise OpenGL texture pooling (#2709)
This commit is contained in:
@@ -404,6 +404,7 @@ list(APPEND INCLUDE_FILES
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/util/instrumentation.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/util/interpolate.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/util/logging.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/util/lru_cache.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/util/noncopyable.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/util/platform.hpp
|
||||
${PROJECT_SOURCE_DIR}/include/mbgl/util/premultiply.hpp
|
||||
@@ -1094,6 +1095,8 @@ if(MLN_WITH_OPENGL)
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/gl/renderbuffer_resource.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/gl/renderbuffer_resource.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/gl/renderer_backend.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/gl/resource_pool.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/gl/resource_pool.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/gl/state.hpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/gl/texture.cpp
|
||||
${PROJECT_SOURCE_DIR}/src/mbgl/gl/texture.hpp
|
||||
|
||||
@@ -830,6 +830,7 @@ MLN_CORE_HEADERS = [
|
||||
"include/mbgl/util/instrumentation.hpp",
|
||||
"include/mbgl/util/interpolate.hpp",
|
||||
"include/mbgl/util/logging.hpp",
|
||||
"include/mbgl/util/lru_cache.hpp",
|
||||
"include/mbgl/util/monotonic_timer.hpp",
|
||||
"include/mbgl/util/noncopyable.hpp",
|
||||
"include/mbgl/util/platform.hpp",
|
||||
@@ -886,6 +887,8 @@ MLN_OPENGL_SOURCE = [
|
||||
"src/mbgl/gl/renderbuffer_resource.cpp",
|
||||
"src/mbgl/gl/renderbuffer_resource.hpp",
|
||||
"src/mbgl/gl/renderer_backend.cpp",
|
||||
"src/mbgl/gl/resource_pool.cpp",
|
||||
"src/mbgl/gl/resource_pool.hpp",
|
||||
"src/mbgl/gl/state.hpp",
|
||||
"src/mbgl/gl/texture.cpp",
|
||||
"src/mbgl/gl/texture.hpp",
|
||||
|
||||
@@ -59,8 +59,8 @@ public:
|
||||
void unbind() noexcept;
|
||||
|
||||
private:
|
||||
void createObject() noexcept;
|
||||
void createStorage(const void* data = nullptr) noexcept;
|
||||
void allocateTexture() noexcept;
|
||||
void updateTextureData(const void* data = nullptr) noexcept;
|
||||
|
||||
private:
|
||||
gl::Context& context;
|
||||
|
||||
@@ -93,7 +93,7 @@ constexpr const char* tracyConstMemoryLabel = "Constant Buffer Memory";
|
||||
#define MLN_TRACE_GL_CONTEXT() ((void)0)
|
||||
#define MLN_TRACE_GL_ZONE(label) ((void)0)
|
||||
#define MLN_TRACE_FUNC_GL() ((void)0)
|
||||
#define MLN_END_FRAME() FrameMark;
|
||||
#define MLN_END_FRAME() FrameMark
|
||||
|
||||
#endif // MLN_RENDER_BACKEND_OPENGL
|
||||
|
||||
|
||||
60
include/mbgl/util/lru_cache.hpp
Normal file
60
include/mbgl/util/lru_cache.hpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
// Simple non-thread-safe LRU cache
|
||||
template <typename Item, typename Hash = std::hash<Item>>
|
||||
class LRU {
|
||||
public:
|
||||
// Number of cached items
|
||||
size_t size() const { return list.size(); }
|
||||
|
||||
// Check if cache is empty
|
||||
bool empty() const { return list.empty(); }
|
||||
|
||||
// Access x
|
||||
// If x is already cached, move it to the front (most recently used)
|
||||
void touch(Item x) {
|
||||
auto it = map.find(x);
|
||||
if (it != map.end()) {
|
||||
list.splice(list.begin(), list, it->second);
|
||||
} else {
|
||||
list.push_front(x);
|
||||
map[std::move(x)] = list.begin();
|
||||
}
|
||||
}
|
||||
|
||||
// Evict the least recently used item
|
||||
// If the cache is empty, throw an exception
|
||||
Item evict() {
|
||||
if (list.empty()) {
|
||||
throw std::runtime_error("LRU::evict: empty cache");
|
||||
}
|
||||
Item x = std::move(list.back());
|
||||
map.erase(x);
|
||||
list.pop_back();
|
||||
return x;
|
||||
}
|
||||
|
||||
// Check if item exists in cache without changing its order
|
||||
bool isHit(const Item& x) const { return map.find(std::move(x)) != map.end(); }
|
||||
|
||||
// remove item from cache if it exists
|
||||
void remove(const Item& x) {
|
||||
auto it = map.find(x);
|
||||
if (it != map.end()) {
|
||||
list.erase(it->second);
|
||||
map.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::list<Item> list;
|
||||
std::unordered_map<Item, typename std::list<Item>::iterator, Hash> map;
|
||||
};
|
||||
|
||||
} // namespace mbgl
|
||||
@@ -19,8 +19,8 @@
|
||||
15,
|
||||
1,
|
||||
[
|
||||
147712,
|
||||
147712
|
||||
164096,
|
||||
164096
|
||||
],
|
||||
[
|
||||
130,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
51,
|
||||
1,
|
||||
[
|
||||
53024,
|
||||
53024
|
||||
53032,
|
||||
53032
|
||||
],
|
||||
[
|
||||
43734,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
17,
|
||||
1,
|
||||
[
|
||||
125574,
|
||||
125574
|
||||
191110,
|
||||
191110
|
||||
],
|
||||
[
|
||||
454,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
7,
|
||||
1,
|
||||
[
|
||||
32768,
|
||||
32768
|
||||
49152,
|
||||
49152
|
||||
],
|
||||
[
|
||||
46,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
8,
|
||||
1,
|
||||
[
|
||||
32768,
|
||||
32768
|
||||
49152,
|
||||
49152
|
||||
],
|
||||
[
|
||||
46,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
9,
|
||||
1,
|
||||
[
|
||||
22784,
|
||||
22784
|
||||
29184,
|
||||
29184
|
||||
],
|
||||
[
|
||||
34,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
5,
|
||||
1,
|
||||
[
|
||||
22784,
|
||||
22784
|
||||
25088,
|
||||
25088
|
||||
],
|
||||
[
|
||||
22,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
7,
|
||||
1,
|
||||
[
|
||||
32768,
|
||||
32768
|
||||
49152,
|
||||
49152
|
||||
],
|
||||
[
|
||||
94,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
9,
|
||||
1,
|
||||
[
|
||||
33768,
|
||||
33768
|
||||
50702,
|
||||
50702
|
||||
],
|
||||
[
|
||||
34,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
9,
|
||||
1,
|
||||
[
|
||||
22080,
|
||||
22080
|
||||
27776,
|
||||
27776
|
||||
],
|
||||
[
|
||||
34,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
7,
|
||||
1,
|
||||
[
|
||||
32768,
|
||||
32768
|
||||
49152,
|
||||
49152
|
||||
],
|
||||
[
|
||||
34,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
5,
|
||||
1,
|
||||
[
|
||||
32768,
|
||||
32768
|
||||
114688,
|
||||
114688
|
||||
],
|
||||
[
|
||||
22,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
7,
|
||||
1,
|
||||
[
|
||||
32768,
|
||||
32768
|
||||
49152,
|
||||
49152
|
||||
],
|
||||
[
|
||||
34,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
5,
|
||||
1,
|
||||
[
|
||||
65536,
|
||||
65536
|
||||
81920,
|
||||
81920
|
||||
],
|
||||
[
|
||||
22,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
9,
|
||||
1,
|
||||
[
|
||||
21264,
|
||||
21264
|
||||
42528,
|
||||
42528
|
||||
],
|
||||
[
|
||||
34,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
17,
|
||||
1,
|
||||
[
|
||||
163840,
|
||||
163840
|
||||
196608,
|
||||
196608
|
||||
],
|
||||
[
|
||||
118,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
7,
|
||||
1,
|
||||
[
|
||||
32768,
|
||||
32768
|
||||
65536,
|
||||
65536
|
||||
],
|
||||
[
|
||||
82,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
9,
|
||||
1,
|
||||
[
|
||||
24576,
|
||||
24576
|
||||
46656,
|
||||
46656
|
||||
],
|
||||
[
|
||||
34,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
7,
|
||||
1,
|
||||
[
|
||||
32768,
|
||||
32768
|
||||
49152,
|
||||
49152
|
||||
],
|
||||
[
|
||||
46,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
7,
|
||||
1,
|
||||
[
|
||||
32768,
|
||||
32768
|
||||
49152,
|
||||
49152
|
||||
],
|
||||
[
|
||||
46,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
32,
|
||||
4,
|
||||
[
|
||||
158509,
|
||||
158509
|
||||
321286,
|
||||
321286
|
||||
],
|
||||
[
|
||||
712,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
60,
|
||||
4,
|
||||
[
|
||||
131780,
|
||||
131780
|
||||
230615,
|
||||
230615
|
||||
],
|
||||
[
|
||||
102328,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
9,
|
||||
1,
|
||||
[
|
||||
215688,
|
||||
215688
|
||||
281224,
|
||||
281224
|
||||
],
|
||||
[
|
||||
58,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
36,
|
||||
4,
|
||||
[
|
||||
143344,
|
||||
143344
|
||||
250852,
|
||||
250852
|
||||
],
|
||||
[
|
||||
194056,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
52,
|
||||
4,
|
||||
[
|
||||
296872,
|
||||
296872
|
||||
685542,
|
||||
685542
|
||||
],
|
||||
[
|
||||
79084,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
27,
|
||||
1,
|
||||
[
|
||||
150144,
|
||||
150144
|
||||
215680,
|
||||
215680
|
||||
],
|
||||
[
|
||||
406,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
15,
|
||||
1,
|
||||
[
|
||||
98816,
|
||||
98816
|
||||
164352,
|
||||
164352
|
||||
],
|
||||
[
|
||||
82,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
15,
|
||||
1,
|
||||
[
|
||||
98816,
|
||||
98816
|
||||
164352,
|
||||
164352
|
||||
],
|
||||
[
|
||||
82,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
21,
|
||||
1,
|
||||
[
|
||||
196608,
|
||||
196608
|
||||
262144,
|
||||
262144
|
||||
],
|
||||
[
|
||||
118,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
13,
|
||||
1,
|
||||
[
|
||||
131072,
|
||||
131072
|
||||
196608,
|
||||
196608
|
||||
],
|
||||
[
|
||||
70,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
15,
|
||||
1,
|
||||
[
|
||||
147712,
|
||||
147712
|
||||
164096,
|
||||
164096
|
||||
],
|
||||
[
|
||||
130,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
51,
|
||||
1,
|
||||
[
|
||||
53024,
|
||||
53024
|
||||
53032,
|
||||
53032
|
||||
],
|
||||
[
|
||||
43734,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
17,
|
||||
1,
|
||||
[
|
||||
125574,
|
||||
125574
|
||||
191110,
|
||||
191110
|
||||
],
|
||||
[
|
||||
454,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
7,
|
||||
1,
|
||||
[
|
||||
32768,
|
||||
32768
|
||||
49152,
|
||||
49152
|
||||
],
|
||||
[
|
||||
46,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
8,
|
||||
1,
|
||||
[
|
||||
32768,
|
||||
32768
|
||||
49152,
|
||||
49152
|
||||
],
|
||||
[
|
||||
46,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
9,
|
||||
1,
|
||||
[
|
||||
22784,
|
||||
22784
|
||||
29184,
|
||||
29184
|
||||
],
|
||||
[
|
||||
34,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
5,
|
||||
1,
|
||||
[
|
||||
22784,
|
||||
22784
|
||||
25088,
|
||||
25088
|
||||
],
|
||||
[
|
||||
22,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
7,
|
||||
1,
|
||||
[
|
||||
32768,
|
||||
32768
|
||||
49152,
|
||||
49152
|
||||
],
|
||||
[
|
||||
94,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
9,
|
||||
1,
|
||||
[
|
||||
33768,
|
||||
33768
|
||||
50702,
|
||||
50702
|
||||
],
|
||||
[
|
||||
34,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
9,
|
||||
1,
|
||||
[
|
||||
22080,
|
||||
22080
|
||||
27776,
|
||||
27776
|
||||
],
|
||||
[
|
||||
34,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
7,
|
||||
1,
|
||||
[
|
||||
32768,
|
||||
32768
|
||||
49152,
|
||||
49152
|
||||
],
|
||||
[
|
||||
34,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
5,
|
||||
1,
|
||||
[
|
||||
32768,
|
||||
32768
|
||||
114688,
|
||||
114688
|
||||
],
|
||||
[
|
||||
22,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
7,
|
||||
1,
|
||||
[
|
||||
32768,
|
||||
32768
|
||||
49152,
|
||||
49152
|
||||
],
|
||||
[
|
||||
34,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
5,
|
||||
1,
|
||||
[
|
||||
65536,
|
||||
65536
|
||||
81920,
|
||||
81920
|
||||
],
|
||||
[
|
||||
22,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
9,
|
||||
1,
|
||||
[
|
||||
21264,
|
||||
21264
|
||||
42528,
|
||||
42528
|
||||
],
|
||||
[
|
||||
34,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
17,
|
||||
1,
|
||||
[
|
||||
163840,
|
||||
163840
|
||||
196608,
|
||||
196608
|
||||
],
|
||||
[
|
||||
118,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
7,
|
||||
1,
|
||||
[
|
||||
32768,
|
||||
32768
|
||||
65536,
|
||||
65536
|
||||
],
|
||||
[
|
||||
82,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
9,
|
||||
1,
|
||||
[
|
||||
24576,
|
||||
24576
|
||||
46656,
|
||||
46656
|
||||
],
|
||||
[
|
||||
34,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
7,
|
||||
1,
|
||||
[
|
||||
32768,
|
||||
32768
|
||||
49152,
|
||||
49152
|
||||
],
|
||||
[
|
||||
46,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
7,
|
||||
1,
|
||||
[
|
||||
32768,
|
||||
32768
|
||||
49152,
|
||||
49152
|
||||
],
|
||||
[
|
||||
46,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
32,
|
||||
4,
|
||||
[
|
||||
158509,
|
||||
158509
|
||||
321286,
|
||||
321286
|
||||
],
|
||||
[
|
||||
712,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
60,
|
||||
4,
|
||||
[
|
||||
131780,
|
||||
131780
|
||||
230615,
|
||||
230615
|
||||
],
|
||||
[
|
||||
102328,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
9,
|
||||
1,
|
||||
[
|
||||
215688,
|
||||
215688
|
||||
281224,
|
||||
281224
|
||||
],
|
||||
[
|
||||
58,
|
||||
@@ -32,4 +32,4 @@
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,8 @@
|
||||
36,
|
||||
4,
|
||||
[
|
||||
143344,
|
||||
143344
|
||||
250852,
|
||||
250852
|
||||
],
|
||||
[
|
||||
194056,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
52,
|
||||
4,
|
||||
[
|
||||
296872,
|
||||
296872
|
||||
685542,
|
||||
685542
|
||||
],
|
||||
[
|
||||
79084,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
27,
|
||||
1,
|
||||
[
|
||||
150144,
|
||||
150144
|
||||
215680,
|
||||
215680
|
||||
],
|
||||
[
|
||||
406,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
15,
|
||||
1,
|
||||
[
|
||||
98816,
|
||||
98816
|
||||
164352,
|
||||
164352
|
||||
],
|
||||
[
|
||||
82,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
15,
|
||||
1,
|
||||
[
|
||||
98816,
|
||||
98816
|
||||
164352,
|
||||
164352
|
||||
],
|
||||
[
|
||||
82,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
21,
|
||||
1,
|
||||
[
|
||||
196608,
|
||||
196608
|
||||
262144,
|
||||
262144
|
||||
],
|
||||
[
|
||||
118,
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
13,
|
||||
1,
|
||||
[
|
||||
131072,
|
||||
131072
|
||||
196608,
|
||||
196608
|
||||
],
|
||||
[
|
||||
70,
|
||||
|
||||
@@ -88,7 +88,7 @@ void Mailbox::push(std::unique_ptr<Message> message) {
|
||||
}};
|
||||
|
||||
{
|
||||
MLN_TRACE_ZONE(push_lock);
|
||||
MLN_TRACE_ZONE(push lock);
|
||||
std::lock_guard<std::mutex> pushingLock(pushingMutex);
|
||||
|
||||
if (closed) {
|
||||
@@ -98,7 +98,7 @@ void Mailbox::push(std::unique_ptr<Message> message) {
|
||||
|
||||
bool wasEmpty = false;
|
||||
{
|
||||
MLN_TRACE_ZONE(queue_lock);
|
||||
MLN_TRACE_ZONE(queue lock);
|
||||
std::lock_guard<std::mutex> queueLock(queueMutex);
|
||||
wasEmpty = queue.empty();
|
||||
queue.push(std::move(message));
|
||||
@@ -168,4 +168,4 @@ void Mailbox::scheduleToRecieve(const std::optional<util::SimpleIdentity>& tag)
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mbgl
|
||||
} // namespace mbgl
|
||||
@@ -98,6 +98,8 @@ Context::Context(RendererBackend& backend_)
|
||||
#if MLN_DRAWABLE_RENDERER
|
||||
uboAllocator = std::make_unique<gl::UniformBufferAllocator>();
|
||||
#endif
|
||||
|
||||
texturePool = std::make_unique<Texture2DPool>(this);
|
||||
}
|
||||
|
||||
Context::~Context() noexcept {
|
||||
@@ -105,6 +107,10 @@ Context::~Context() noexcept {
|
||||
backend.getThreadPool().runRenderJobs(true /* closeQueue */);
|
||||
|
||||
reset();
|
||||
|
||||
// Delete all pooled resources while the context is still valid
|
||||
texturePool.reset();
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
Log::Debug(Event::General, "Rendering Stats:\n" + stats.toString("\n"));
|
||||
#endif
|
||||
@@ -265,18 +271,14 @@ void Context::verifyProgramLinkage(ProgramID program_) {
|
||||
throw std::runtime_error("program failed to link");
|
||||
}
|
||||
|
||||
UniqueTexture Context::createUniqueTexture() {
|
||||
UniqueTexture Context::createUniqueTexture(const Size& size,
|
||||
gfx::TexturePixelType format,
|
||||
gfx::TextureChannelDataType type) {
|
||||
MLN_TRACE_FUNC();
|
||||
|
||||
if (pooledTextures.empty()) {
|
||||
pooledTextures.resize(TextureMax);
|
||||
MBGL_CHECK_ERROR(glGenTextures(TextureMax, pooledTextures.data()));
|
||||
stats.numCreatedTextures += TextureMax;
|
||||
}
|
||||
assert(texturePool);
|
||||
TextureID id = texturePool->alloc(size, format, type);
|
||||
|
||||
TextureID id = pooledTextures.back();
|
||||
pooledTextures.pop_back();
|
||||
stats.numActiveTextures++;
|
||||
// NOLINTNEXTLINE(performance-move-const-arg)
|
||||
return UniqueTexture{std::move(id), {this}};
|
||||
}
|
||||
@@ -306,27 +308,13 @@ std::unique_ptr<gfx::TextureResource> Context::createTextureResource(const Size
|
||||
const gfx::TextureChannelDataType type) {
|
||||
MLN_TRACE_FUNC();
|
||||
|
||||
auto obj = createUniqueTexture();
|
||||
int textureByteSize = gl::TextureResource::getStorageSize(size, format, type);
|
||||
stats.memTextures += textureByteSize;
|
||||
std::unique_ptr<gfx::TextureResource> resource = std::make_unique<gl::TextureResource>(std::move(obj),
|
||||
textureByteSize);
|
||||
auto obj = createUniqueTexture(size, format, type);
|
||||
std::unique_ptr<gfx::TextureResource> resource = std::make_unique<gl::TextureResource>(std::move(obj));
|
||||
|
||||
// Always use texture unit 0 for manipulating it.
|
||||
activeTextureUnit = 0;
|
||||
texture[0] = static_cast<gl::TextureResource&>(*resource).texture;
|
||||
|
||||
// Creates an empty texture with the specified size and format.
|
||||
MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
Enum<gfx::TexturePixelType>::sizedFor(format, type),
|
||||
size.width,
|
||||
size.height,
|
||||
0,
|
||||
Enum<gfx::TexturePixelType>::to(format),
|
||||
Enum<gfx::TextureChannelDataType>::to(type),
|
||||
nullptr));
|
||||
|
||||
// We are using clamp to edge here since OpenGL ES doesn't allow GL_REPEAT
|
||||
// on NPOT textures. We use those when the pixelRatio isn't a power of two,
|
||||
// e.g. on iPhone 6 Plus.
|
||||
@@ -530,9 +518,10 @@ std::unique_ptr<gfx::DrawScopeResource> Context::createDrawScopeResource() {
|
||||
void Context::reset() {
|
||||
MLN_TRACE_FUNC();
|
||||
|
||||
std::copy(pooledTextures.begin(), pooledTextures.end(), std::back_inserter(abandonedTextures));
|
||||
pooledTextures.resize(0);
|
||||
performCleanup();
|
||||
|
||||
// Delete all unused pooled resources
|
||||
texturePool->shrink();
|
||||
}
|
||||
|
||||
#if MLN_DRAWABLE_RENDERER
|
||||
@@ -883,10 +872,8 @@ void Context::performCleanup() {
|
||||
binding.setDirty();
|
||||
}
|
||||
}
|
||||
texturePool->release(id);
|
||||
}
|
||||
MBGL_CHECK_ERROR(glDeleteTextures(int(abandonedTextures.size()), abandonedTextures.data()));
|
||||
stats.numCreatedTextures -= int(abandonedTextures.size());
|
||||
assert(stats.numCreatedTextures >= 0);
|
||||
abandonedTextures.clear();
|
||||
}
|
||||
|
||||
@@ -923,6 +910,8 @@ void Context::reduceMemoryUsage() {
|
||||
MLN_TRACE_FUNC_GL();
|
||||
|
||||
performCleanup();
|
||||
assert(texturePool);
|
||||
texturePool->shrink();
|
||||
|
||||
// Ensure that all pending actions are executed to ensure that they happen
|
||||
// before the app goes to the background.
|
||||
@@ -949,5 +938,10 @@ void Context::clearStencilBuffer(const int32_t bits) {
|
||||
stats.stencilClears++;
|
||||
}
|
||||
|
||||
Texture2DPool& Context::getTexturePool() {
|
||||
assert(texturePool);
|
||||
return *texturePool;
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <mbgl/gl/state.hpp>
|
||||
#include <mbgl/gl/value.hpp>
|
||||
#include <mbgl/gl/framebuffer.hpp>
|
||||
#include <mbgl/gl/resource_pool.hpp>
|
||||
#include <mbgl/gl/vertex_array.hpp>
|
||||
#include <mbgl/gl/types.hpp>
|
||||
#include <mbgl/platform/gl_functions.hpp>
|
||||
@@ -29,7 +30,6 @@
|
||||
namespace mbgl {
|
||||
namespace gl {
|
||||
|
||||
constexpr size_t TextureMax = 64;
|
||||
using ProcAddress = void (*)();
|
||||
class RendererBackend;
|
||||
|
||||
@@ -58,7 +58,7 @@ public:
|
||||
UniqueProgram createProgram(ShaderID vertexShader, ShaderID fragmentShader, const char* location0AttribName);
|
||||
void verifyProgramLinkage(ProgramID);
|
||||
void linkProgram(ProgramID);
|
||||
UniqueTexture createUniqueTexture();
|
||||
UniqueTexture createUniqueTexture(const Size& size, gfx::TexturePixelType format, gfx::TextureChannelDataType type);
|
||||
|
||||
Framebuffer createFramebuffer(const gfx::Renderbuffer<gfx::RenderbufferPixelType::RGBA>&,
|
||||
const gfx::Renderbuffer<gfx::RenderbufferPixelType::DepthStencil>&);
|
||||
@@ -93,18 +93,23 @@ public:
|
||||
|
||||
// Actually remove the objects we marked as abandoned with the above methods.
|
||||
// Only call this while the OpenGL context is exclusive to this thread.
|
||||
// Pooled textures are retained
|
||||
void performCleanup() override;
|
||||
|
||||
// Called when the app receives a memory warning and before it goes to the background.
|
||||
// Calls performCleanup and destroy all pooled resources
|
||||
void reduceMemoryUsage() override;
|
||||
|
||||
// Drain pools and remove abandoned objects, in preparation for destroying the store.
|
||||
// Only call this while the OpenGL context is exclusive to this thread.
|
||||
void reset();
|
||||
|
||||
bool empty() const {
|
||||
return pooledTextures.empty() && abandonedPrograms.empty() && abandonedShaders.empty() &&
|
||||
abandonedBuffers.empty() && abandonedTextures.empty() && abandonedVertexArrays.empty() &&
|
||||
abandonedFramebuffers.empty();
|
||||
// Returns whether there are any objects that need to be cleaned up.
|
||||
// If considerPool is true, it will also check if resources are still pooled.
|
||||
bool empty(bool considerPool = false) const {
|
||||
return abandonedPrograms.empty() && abandonedShaders.empty() && abandonedBuffers.empty() &&
|
||||
abandonedTextures.empty() && abandonedVertexArrays.empty() && abandonedFramebuffers.empty() &&
|
||||
(considerPool ? texturePool->empty() : true);
|
||||
}
|
||||
|
||||
extension::Debugging* getDebuggingExtension() const { return debugging.get(); }
|
||||
@@ -151,6 +156,8 @@ public:
|
||||
|
||||
void setDirtyState() override;
|
||||
|
||||
Texture2DPool& getTexturePool();
|
||||
|
||||
private:
|
||||
RendererBackend& backend;
|
||||
bool cleanupOnDestruction = true;
|
||||
@@ -230,8 +237,6 @@ private:
|
||||
friend detail::FramebufferDeleter;
|
||||
friend detail::RenderbufferDeleter;
|
||||
|
||||
std::vector<TextureID> pooledTextures;
|
||||
|
||||
std::vector<ProgramID> abandonedPrograms;
|
||||
std::vector<ShaderID> abandonedShaders;
|
||||
std::vector<BufferID> abandonedBuffers;
|
||||
@@ -240,6 +245,8 @@ private:
|
||||
std::vector<FramebufferID> abandonedFramebuffers;
|
||||
std::vector<RenderbufferID> abandonedRenderbuffers;
|
||||
|
||||
std::unique_ptr<Texture2DPool> texturePool;
|
||||
|
||||
public:
|
||||
#if !defined(NDEBUG)
|
||||
public:
|
||||
|
||||
@@ -23,13 +23,10 @@ void BufferDeleter::operator()(BufferID id) const {
|
||||
|
||||
void TextureDeleter::operator()(TextureID id) const {
|
||||
assert(context);
|
||||
if (context->pooledTextures.size() >= TextureMax) {
|
||||
context->abandonedTextures.push_back(id);
|
||||
} else {
|
||||
context->pooledTextures.push_back(id);
|
||||
}
|
||||
context->abandonedTextures.push_back(id);
|
||||
context->renderingStats().numActiveTextures--;
|
||||
assert(context->renderingStats().numActiveTextures >= 0);
|
||||
assert(context->renderingStats().numActiveTextures < context->renderingStats().numCreatedTextures);
|
||||
}
|
||||
|
||||
void VertexArrayDeleter::operator()(VertexArrayID id) const {
|
||||
|
||||
251
src/mbgl/gl/resource_pool.cpp
Normal file
251
src/mbgl/gl/resource_pool.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
#include <mbgl/gl/resource_pool.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
|
||||
#include <mbgl/gl/context.hpp>
|
||||
#include <mbgl/gl/defines.hpp>
|
||||
#include <mbgl/gl/enum.hpp>
|
||||
#include <mbgl/gl/texture_resource.hpp>
|
||||
#include <mbgl/util/hash.hpp>
|
||||
#include <mbgl/util/instrumentation.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace gl {
|
||||
|
||||
using namespace platform;
|
||||
|
||||
namespace {
|
||||
|
||||
size_t storageSize(const Texture2DDesc& desc) {
|
||||
return static_cast<size_t>(TextureResource::getStorageSize(desc.size, desc.pixelFormat, desc.channelType));
|
||||
}
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
void logDebugMessage(const std::string& message) {
|
||||
Log::Debug(Event::General, "Texture2DPool: " + message);
|
||||
}
|
||||
#else
|
||||
void logDebugMessage(const std::string&) {}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
std::size_t Texture2DDescHash::operator()(const Texture2DDesc& desc) const {
|
||||
std::size_t seed = 0;
|
||||
util::hash_combine(seed, desc.size.width);
|
||||
util::hash_combine(seed, desc.size.height);
|
||||
util::hash_combine(seed, std::underlying_type_t<gfx::TexturePixelType>(desc.pixelFormat));
|
||||
util::hash_combine(seed, std::underlying_type_t<gfx::TextureChannelDataType>(desc.channelType));
|
||||
return seed;
|
||||
}
|
||||
|
||||
Texture2DPool::Texture2DPool(Context* context_, size_t maxPoolStorage_)
|
||||
: context(context_),
|
||||
maxPoolStorage(maxPoolStorage_) {
|
||||
assert(context);
|
||||
}
|
||||
|
||||
Texture2DPool::~Texture2DPool() {
|
||||
assert(usedStorage() == 0);
|
||||
assert(unusedStorage() == poolStorage);
|
||||
shrink();
|
||||
assert(usedStorage() == 0);
|
||||
assert(unusedStorage() == 0);
|
||||
assert(poolStorage == 0);
|
||||
assert(lru_cache.empty());
|
||||
assert(descriptions.empty());
|
||||
assert(pool.empty());
|
||||
logDebugMessage("total allocations " + util::toString(allocCount));
|
||||
logDebugMessage("total reused allocations " + util::toString(reuseCount));
|
||||
logDebugMessage("total textures released " + util::toString(releaseCount));
|
||||
logDebugMessage("total textures released and deallocated " + util::toString(freedCount));
|
||||
}
|
||||
|
||||
TextureID Texture2DPool::alloc(const Texture2DDesc& desc) {
|
||||
MLN_TRACE_FUNC();
|
||||
|
||||
context->renderingStats().numActiveTextures++;
|
||||
|
||||
// Check if the texture is already allocated
|
||||
auto set_it = pool.find(desc);
|
||||
if (set_it != pool.end() && !set_it->second.free.empty()) {
|
||||
// Reuse an existing texture
|
||||
TextureID id = *set_it->second.free.begin();
|
||||
set_it->second.free.erase(id);
|
||||
set_it->second.used.insert(id);
|
||||
assert(lru_cache.isHit(id));
|
||||
lru_cache.remove(id);
|
||||
++reuseCount;
|
||||
return id;
|
||||
}
|
||||
|
||||
// Allocate a new texture
|
||||
++allocCount;
|
||||
return allocateGLMemory(desc);
|
||||
}
|
||||
|
||||
void Texture2DPool::release(TextureID id) {
|
||||
MLN_TRACE_FUNC();
|
||||
|
||||
assert(isUsed(id));
|
||||
auto desc = descriptions.at(id);
|
||||
auto set_it = pool.find(desc);
|
||||
set_it->second.used.erase(id);
|
||||
set_it->second.free.insert(id);
|
||||
lru_cache.touch(id);
|
||||
// Evict old textures if necessary
|
||||
evict();
|
||||
|
||||
++releaseCount;
|
||||
}
|
||||
|
||||
TextureID Texture2DPool::allocateGLMemory(const Texture2DDesc& desc) {
|
||||
MLN_TRACE_FUNC();
|
||||
|
||||
// Create handle
|
||||
TextureID id = 0;
|
||||
MBGL_CHECK_ERROR(glGenTextures(1, &id));
|
||||
assert(id != 0);
|
||||
|
||||
// Allocate memory
|
||||
// Bind to TU 0 and upload
|
||||
context->activeTextureUnit = 0;
|
||||
context->texture[0] = id;
|
||||
MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
Enum<gfx::TexturePixelType>::sizedFor(desc.pixelFormat, desc.channelType),
|
||||
desc.size.width,
|
||||
desc.size.height,
|
||||
0,
|
||||
Enum<gfx::TexturePixelType>::to(desc.pixelFormat),
|
||||
Enum<gfx::TextureChannelDataType>::to(desc.channelType),
|
||||
nullptr));
|
||||
|
||||
// Update stats
|
||||
context->renderingStats().numCreatedTextures++;
|
||||
|
||||
// Add to descriptions
|
||||
descriptions.emplace(id, desc);
|
||||
|
||||
// Add to pool
|
||||
pool[desc].used.insert(id);
|
||||
|
||||
// Update poolStorage
|
||||
auto storage = storageSize(desc);
|
||||
poolStorage += storage;
|
||||
context->renderingStats().memTextures += storage;
|
||||
MLN_TRACE_ALLOC_TEXTURE(id, storage);
|
||||
|
||||
// Evict old textures if necessary
|
||||
evict();
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
void Texture2DPool::freeAllocatedGLMemory(TextureID id) {
|
||||
MLN_TRACE_FUNC();
|
||||
|
||||
// Remove from descriptions
|
||||
auto desc_it = descriptions.find(id);
|
||||
assert(desc_it != descriptions.end());
|
||||
auto desc = desc_it->second;
|
||||
descriptions.erase(desc_it);
|
||||
|
||||
// Remove from descriptions lru_cache if it exists
|
||||
lru_cache.remove(id);
|
||||
|
||||
// Remove from pool
|
||||
auto set_it = pool.find(desc);
|
||||
assert(set_it != pool.end());
|
||||
auto free_it = set_it->second.free.find(id);
|
||||
assert(free_it != set_it->second.free.end());
|
||||
set_it->second.free.erase(free_it);
|
||||
if (set_it->second.free.empty() && set_it->second.used.empty()) {
|
||||
pool.erase(set_it);
|
||||
}
|
||||
|
||||
// Update poolStorage
|
||||
auto storage = storageSize(desc);
|
||||
poolStorage -= storage;
|
||||
context->renderingStats().memTextures -= storage;
|
||||
assert(context->renderingStats().memTextures >= 0);
|
||||
MLN_TRACE_FREE_TEXTURE(id);
|
||||
|
||||
// Delete the texture
|
||||
MBGL_CHECK_ERROR(glDeleteTextures(1, &id));
|
||||
// Update stats
|
||||
context->renderingStats().numCreatedTextures--;
|
||||
|
||||
++freedCount;
|
||||
}
|
||||
|
||||
void Texture2DPool::evict() {
|
||||
while (poolStorage > maxPoolStorage && !lru_cache.empty()) {
|
||||
TextureID id = lru_cache.evict();
|
||||
freeAllocatedGLMemory(id);
|
||||
}
|
||||
}
|
||||
|
||||
void Texture2DPool::shrink() {
|
||||
MLN_TRACE_FUNC();
|
||||
|
||||
while (!lru_cache.empty()) {
|
||||
TextureID id = lru_cache.evict();
|
||||
freeAllocatedGLMemory(id);
|
||||
}
|
||||
}
|
||||
|
||||
bool Texture2DPool::isPooled(TextureID id) const {
|
||||
return descriptions.find(id) != descriptions.end();
|
||||
}
|
||||
|
||||
bool Texture2DPool::isUsed(TextureID id) const {
|
||||
auto desc_it = descriptions.find(id);
|
||||
if (desc_it == descriptions.end()) {
|
||||
return false;
|
||||
}
|
||||
const auto& desc = desc_it->second;
|
||||
const auto& used_set = pool.at(desc).used;
|
||||
return used_set.find(id) != used_set.end();
|
||||
}
|
||||
|
||||
bool Texture2DPool::isUnused(TextureID id) const {
|
||||
auto desc_it = descriptions.find(id);
|
||||
if (desc_it == descriptions.end()) {
|
||||
return false;
|
||||
}
|
||||
const auto& desc = desc_it->second;
|
||||
const auto& free_set = pool.at(desc).free;
|
||||
return free_set.find(id) != free_set.end();
|
||||
}
|
||||
|
||||
size_t Texture2DPool::usedStorage() const {
|
||||
size_t total = 0;
|
||||
for (const auto& set : pool) {
|
||||
total += storageSize(set.first) * set.second.used.size();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
size_t Texture2DPool::unusedStorage() const {
|
||||
size_t total = 0;
|
||||
for (const auto& set : pool) {
|
||||
total += storageSize(set.first) * set.second.free.size();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
const Texture2DDesc& Texture2DPool::desc(TextureID id) const {
|
||||
assert(descriptions.find(id) != descriptions.end());
|
||||
return descriptions.at(id);
|
||||
}
|
||||
|
||||
size_t Texture2DPool::storage(TextureID id) const {
|
||||
assert(descriptions.find(id) != descriptions.end());
|
||||
return storageSize(descriptions.at(id));
|
||||
}
|
||||
|
||||
} // namespace gl
|
||||
} // namespace mbgl
|
||||
118
src/mbgl/gl/resource_pool.hpp
Normal file
118
src/mbgl/gl/resource_pool.hpp
Normal file
@@ -0,0 +1,118 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <mbgl/gfx/types.hpp>
|
||||
#include <mbgl/gl/types.hpp>
|
||||
#include <mbgl/util/lru_cache.hpp>
|
||||
#include <mbgl/util/size.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace gl {
|
||||
|
||||
class Context;
|
||||
|
||||
struct Texture2DDesc {
|
||||
Size size;
|
||||
gfx::TexturePixelType pixelFormat;
|
||||
gfx::TextureChannelDataType channelType;
|
||||
};
|
||||
|
||||
struct Texture2DDescHash {
|
||||
std::size_t operator()(const Texture2DDesc& desc) const;
|
||||
};
|
||||
|
||||
inline bool operator==(const Texture2DDesc& desc1, const Texture2DDesc& desc2) {
|
||||
return desc1.size == desc2.size && desc1.pixelFormat == desc2.pixelFormat && desc1.channelType == desc2.channelType;
|
||||
}
|
||||
|
||||
inline bool operator!=(const Texture2DDesc& desc1, const Texture2DDesc& desc2) {
|
||||
return !(desc1 == desc2);
|
||||
}
|
||||
|
||||
class Texture2DPool {
|
||||
public:
|
||||
// Create a texture pool with a maximum storage size maxPoolStorage in bytes
|
||||
// If maxPoolStorage is 0, pooling will disabled
|
||||
Texture2DPool(Context* context, size_t maxPoolStorage = 16 * 1024 * 1024);
|
||||
|
||||
// Deallocate all storage
|
||||
~Texture2DPool();
|
||||
|
||||
// Allocate a texture with the given size, pixel format, and channel type
|
||||
// If the texture is already allocated in the pool, the existing texture ID will be returned
|
||||
TextureID alloc(const Size& size, gfx::TexturePixelType pixelFormat, gfx::TextureChannelDataType channelType) {
|
||||
return alloc({size, pixelFormat, channelType});
|
||||
}
|
||||
|
||||
TextureID alloc(const Texture2DDesc& desc);
|
||||
|
||||
// Release the texture but keep it in the pool
|
||||
void release(TextureID id);
|
||||
|
||||
// Free all unused textures
|
||||
void shrink();
|
||||
|
||||
// Get the description of a texture
|
||||
const Texture2DDesc& desc(TextureID id) const;
|
||||
|
||||
// Get the storage size in byte of a texture
|
||||
size_t storage(TextureID id) const;
|
||||
|
||||
// Get the storage size in byte of all textures being used
|
||||
size_t usedStorage() const;
|
||||
|
||||
// Get the storage size in byte of all unused textures
|
||||
size_t unusedStorage() const;
|
||||
|
||||
// Get the storage size in byte of all textures in the pool
|
||||
size_t storage() const { return poolStorage; }
|
||||
|
||||
// Get the maximum storage size before starting to evict unused textures
|
||||
size_t maxStorage() const { return maxPoolStorage; }
|
||||
|
||||
bool empty() const { return poolStorage == 0; }
|
||||
|
||||
// Check if a texture is pooled
|
||||
bool isPooled(TextureID id) const;
|
||||
|
||||
// Check if a texture is pooled and being used
|
||||
bool isUsed(TextureID id) const;
|
||||
|
||||
// Check if a texture is pooled but released
|
||||
bool isUnused(TextureID id) const;
|
||||
|
||||
private:
|
||||
struct TextureSet {
|
||||
std::unordered_set<TextureID> free;
|
||||
std::unordered_set<TextureID> used;
|
||||
};
|
||||
|
||||
// Effectively glGenTextures and glTexImage2D
|
||||
TextureID allocateGLMemory(const Texture2DDesc& desc);
|
||||
|
||||
// Effectively call glDeleteTextures
|
||||
void freeAllocatedGLMemory(TextureID id);
|
||||
|
||||
// Evict the least recently used textures until the pool storage is below maxPoolStorage
|
||||
void evict();
|
||||
|
||||
private:
|
||||
Context* context = nullptr;
|
||||
size_t maxPoolStorage = 0;
|
||||
std::size_t poolStorage = 0;
|
||||
std::unordered_map<Texture2DDesc, TextureSet, Texture2DDescHash> pool;
|
||||
std::unordered_map<TextureID, Texture2DDesc> descriptions;
|
||||
LRU<TextureID> lru_cache;
|
||||
// stats
|
||||
int64_t allocCount = 0; // total allocations
|
||||
int64_t reuseCount = 0; // total reused allocations
|
||||
int64_t releaseCount = 0; // total textures released
|
||||
int64_t freedCount = 0; // total textures released and deallocated
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
} // namespace mbgl
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <mbgl/gl/defines.hpp>
|
||||
#include <mbgl/gl/enum.hpp>
|
||||
#include <mbgl/platform/gl_functions.hpp>
|
||||
#include <mbgl/util/instrumentation.hpp>
|
||||
|
||||
#include <mbgl/gl/texture_resource.hpp>
|
||||
#include <mbgl/gfx/texture.hpp>
|
||||
@@ -70,46 +71,30 @@ size_t Texture2D::numChannels() const noexcept {
|
||||
}
|
||||
}
|
||||
|
||||
void Texture2D::createObject() noexcept {
|
||||
void Texture2D::allocateTexture() noexcept {
|
||||
MLN_TRACE_FUNC();
|
||||
|
||||
// Create a new texture object
|
||||
assert(!textureResource);
|
||||
auto obj = context.createUniqueTexture();
|
||||
const auto storageSize = gl::TextureResource::getStorageSize(size, pixelFormat, channelType);
|
||||
context.renderingStats().memTextures += storageSize;
|
||||
auto obj = context.createUniqueTexture(size, pixelFormat, channelType);
|
||||
|
||||
// @TODO: TextureResource is still needed while we have legacy rendering pathways
|
||||
textureResource = std::make_unique<gl::TextureResource>(std::move(obj), storageSize);
|
||||
textureResource = std::make_unique<gl::TextureResource>(std::move(obj));
|
||||
}
|
||||
|
||||
void Texture2D::createStorage(const void* data) noexcept {
|
||||
assert(textureResource);
|
||||
void Texture2D::updateTextureData(const void* data) noexcept {
|
||||
MLN_TRACE_FUNC();
|
||||
if (data) {
|
||||
uploadSubRegion(data, size, 0, 0);
|
||||
}
|
||||
|
||||
// Create backing storage for our texture object
|
||||
using namespace platform;
|
||||
|
||||
// Bind to TU 0 and upload
|
||||
context.activeTextureUnit = 0;
|
||||
context.texture[0] = getTextureID();
|
||||
context.pixelStoreUnpack = {1};
|
||||
MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
Enum<gfx::TexturePixelType>::sizedFor(pixelFormat, channelType),
|
||||
size.width,
|
||||
size.height,
|
||||
0,
|
||||
Enum<gfx::TexturePixelType>::to(pixelFormat),
|
||||
Enum<gfx::TextureChannelDataType>::to(channelType),
|
||||
data));
|
||||
storageDirty = false;
|
||||
updateSamplerConfiguration();
|
||||
}
|
||||
|
||||
void Texture2D::create() noexcept {
|
||||
if (!textureResource) {
|
||||
createObject();
|
||||
}
|
||||
allocateTexture();
|
||||
if (storageDirty) {
|
||||
createStorage();
|
||||
updateTextureData();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,19 +169,10 @@ void Texture2D::upload(const void* pixelData, const Size& size_) noexcept {
|
||||
if (!textureResource || storageDirty || size_ == Size{0, 0} || size_ != size) {
|
||||
size = size_;
|
||||
|
||||
// Create the texture object if we don't already have one
|
||||
if (!textureResource) {
|
||||
createObject();
|
||||
}
|
||||
|
||||
createStorage(pixelData);
|
||||
|
||||
} else {
|
||||
if (pixelData) {
|
||||
// Upload to existing memory
|
||||
uploadSubRegion(pixelData, size, 0, 0);
|
||||
}
|
||||
// Create the texture object if we don't already have one or if storage is dirty
|
||||
allocateTexture();
|
||||
}
|
||||
updateTextureData(pixelData);
|
||||
}
|
||||
|
||||
void Texture2D::uploadSubRegion(const void* pixelData, const Size& size_, uint16_t xOffset, uint16_t yOffset) noexcept {
|
||||
|
||||
@@ -32,18 +32,10 @@ static int channelStorageSize(gfx::TextureChannelDataType type) {
|
||||
}
|
||||
}
|
||||
|
||||
TextureResource::TextureResource(UniqueTexture&& texture_, int byteSize_)
|
||||
: texture(std::move(texture_)),
|
||||
byteSize(byteSize_) {
|
||||
MLN_TRACE_ALLOC_TEXTURE(texture.get(), byteSize);
|
||||
}
|
||||
TextureResource::TextureResource(UniqueTexture&& texture_)
|
||||
: texture(std::move(texture_)) {}
|
||||
|
||||
TextureResource::~TextureResource() noexcept {
|
||||
auto& stats = texture.get_deleter().context->renderingStats();
|
||||
MLN_TRACE_FREE_TEXTURE(texture.get());
|
||||
stats.memTextures -= byteSize;
|
||||
assert(stats.memTextures >= 0);
|
||||
}
|
||||
TextureResource::~TextureResource() noexcept {}
|
||||
|
||||
int TextureResource::getStorageSize(const Size& size, gfx::TexturePixelType format, gfx::TextureChannelDataType type) {
|
||||
return size.width * size.height * channelCount(format) * channelStorageSize(type);
|
||||
|
||||
@@ -8,7 +8,7 @@ namespace gl {
|
||||
|
||||
class TextureResource : public gfx::TextureResource {
|
||||
public:
|
||||
TextureResource(UniqueTexture&& texture_, int byteSize_);
|
||||
TextureResource(UniqueTexture&& texture_);
|
||||
~TextureResource() noexcept override;
|
||||
|
||||
static int getStorageSize(const Size& size, gfx::TexturePixelType format, gfx::TextureChannelDataType type);
|
||||
@@ -18,7 +18,6 @@ public:
|
||||
gfx::TextureMipMapType mipmap = gfx::TextureMipMapType::No;
|
||||
gfx::TextureWrapType wrapX = gfx::TextureWrapType::Clamp;
|
||||
gfx::TextureWrapType wrapY = gfx::TextureWrapType::Clamp;
|
||||
int byteSize;
|
||||
};
|
||||
|
||||
} // namespace gl
|
||||
|
||||
@@ -76,12 +76,12 @@ std::unique_ptr<gfx::TextureResource> UploadPass::createTextureResource(const Si
|
||||
const void* data,
|
||||
gfx::TexturePixelType format,
|
||||
gfx::TextureChannelDataType type) {
|
||||
auto obj = commandEncoder.context.createUniqueTexture();
|
||||
const int textureByteSize = gl::TextureResource::getStorageSize(size, format, type);
|
||||
commandEncoder.context.renderingStats().memTextures += textureByteSize;
|
||||
auto resource = std::make_unique<gl::TextureResource>(std::move(obj), textureByteSize);
|
||||
MLN_TRACE_FUNC();
|
||||
|
||||
auto obj = commandEncoder.context.createUniqueTexture(size, format, type);
|
||||
auto resource = std::make_unique<gl::TextureResource>(std::move(obj));
|
||||
commandEncoder.context.pixelStoreUnpack = {1};
|
||||
updateTextureResource(*resource, size, data, format, type);
|
||||
updateTextureResourceSub(*resource, 0, 0, size, data, format, type);
|
||||
// We are using clamp to edge here since OpenGL ES doesn't allow GL_REPEAT
|
||||
// on NPOT textures. We use those when the pixelRatio isn't a power of two,
|
||||
// e.g. on iPhone 6 Plus.
|
||||
@@ -97,18 +97,9 @@ void UploadPass::updateTextureResource(gfx::TextureResource& resource,
|
||||
const void* data,
|
||||
gfx::TexturePixelType format,
|
||||
gfx::TextureChannelDataType type) {
|
||||
// Always use texture unit 0 for manipulating it.
|
||||
commandEncoder.context.activeTextureUnit = 0;
|
||||
commandEncoder.context.texture[0] = static_cast<gl::TextureResource&>(resource).texture;
|
||||
MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
Enum<gfx::TexturePixelType>::to(format),
|
||||
size.width,
|
||||
size.height,
|
||||
0,
|
||||
Enum<gfx::TexturePixelType>::to(format),
|
||||
Enum<gfx::TextureChannelDataType>::to(type),
|
||||
data));
|
||||
MLN_TRACE_FUNC();
|
||||
|
||||
updateTextureResourceSub(resource, 0, 0, size, data, format, type);
|
||||
}
|
||||
|
||||
void UploadPass::updateTextureResourceSub(gfx::TextureResource& resource,
|
||||
@@ -118,9 +109,20 @@ void UploadPass::updateTextureResourceSub(gfx::TextureResource& resource,
|
||||
const void* data,
|
||||
gfx::TexturePixelType format,
|
||||
gfx::TextureChannelDataType type) {
|
||||
MLN_TRACE_FUNC();
|
||||
|
||||
auto& ctx = commandEncoder.context;
|
||||
assert(ctx.getTexturePool().isUsed(static_cast<gl::TextureResource&>(resource).texture));
|
||||
assert(ctx.getTexturePool().desc(static_cast<gl::TextureResource&>(resource).texture).channelType == type);
|
||||
assert(ctx.getTexturePool().desc(static_cast<gl::TextureResource&>(resource).texture).pixelFormat == format);
|
||||
assert(ctx.getTexturePool().desc(static_cast<gl::TextureResource&>(resource).texture).size.width >=
|
||||
xOffset + size.width);
|
||||
assert(ctx.getTexturePool().desc(static_cast<gl::TextureResource&>(resource).texture).size.height >=
|
||||
yOffset + size.height);
|
||||
|
||||
// Always use texture unit 0 for manipulating it.
|
||||
commandEncoder.context.activeTextureUnit = 0;
|
||||
commandEncoder.context.texture[0] = static_cast<const gl::TextureResource&>(resource).texture;
|
||||
ctx.activeTextureUnit = 0;
|
||||
ctx.texture[0] = static_cast<const gl::TextureResource&>(resource).texture;
|
||||
MBGL_CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D,
|
||||
0,
|
||||
xOffset,
|
||||
|
||||
@@ -111,6 +111,7 @@ add_library(
|
||||
${PROJECT_SOURCE_DIR}/test/util/token.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/util/url.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/util/tile_server_options.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/util/lru_cache.test.cpp
|
||||
)
|
||||
|
||||
# MapSnapshotter uses headless backend that is rendering image on a background thread. QT / macOS adaptation for headless backend creates
|
||||
@@ -135,6 +136,7 @@ if(MLN_WITH_OPENGL)
|
||||
${PROJECT_SOURCE_DIR}/test/gl/context.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/gl/gl_functions.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/gl/object.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/gl/resource_pool.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/renderer/backend_scope.test.cpp
|
||||
${PROJECT_SOURCE_DIR}/test/util/offscreen_texture.test.cpp
|
||||
)
|
||||
|
||||
@@ -55,17 +55,22 @@ TEST(GLObject, Store) {
|
||||
gl::Context context{backend};
|
||||
EXPECT_TRUE(context.empty());
|
||||
|
||||
gl::UniqueTexture texture = context.createUniqueTexture();
|
||||
gl::UniqueTexture texture = context.createUniqueTexture(
|
||||
{8, 8}, gfx::TexturePixelType::RGBA, gfx::TextureChannelDataType::UnsignedByte);
|
||||
EXPECT_NE(texture.get(), 0u);
|
||||
texture.reset();
|
||||
EXPECT_FALSE(context.empty());
|
||||
EXPECT_FALSE(context.empty(false));
|
||||
EXPECT_FALSE(context.empty(true));
|
||||
context.performCleanup();
|
||||
EXPECT_FALSE(context.empty());
|
||||
EXPECT_TRUE(context.empty(false));
|
||||
EXPECT_FALSE(context.empty(true));
|
||||
context.reset();
|
||||
EXPECT_TRUE(context.empty());
|
||||
EXPECT_TRUE(context.empty(false));
|
||||
EXPECT_TRUE(context.empty(true));
|
||||
|
||||
context.reset();
|
||||
EXPECT_TRUE(context.empty());
|
||||
EXPECT_TRUE(context.empty(false));
|
||||
EXPECT_TRUE(context.empty(true));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
123
test/gl/resource_pool.test.cpp
Normal file
123
test/gl/resource_pool.test.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
#if MLN_RENDER_BACKEND_OPENGL
|
||||
#include <mbgl/test/util.hpp>
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#include <mbgl/gfx/backend_scope.hpp>
|
||||
#include <mbgl/gfx/types.hpp>
|
||||
#include <mbgl/gl/headless_backend.hpp>
|
||||
#include <mbgl/gl/context.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto oneMB = 1024 * 1024;
|
||||
|
||||
constexpr gl::Texture2DDesc makeDescOneMB() {
|
||||
return {{1024, 1024}, gfx::TexturePixelType::Luminance, gfx::TextureChannelDataType::UnsignedByte};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(ResourcePool, TexturePool) {
|
||||
gl::HeadlessBackend backend{{32, 32}};
|
||||
gfx::BackendScope scope{backend};
|
||||
|
||||
gl::Context context{backend};
|
||||
EXPECT_TRUE(context.empty(true));
|
||||
|
||||
auto& pool = context.getTexturePool();
|
||||
EXPECT_LE(oneMB, pool.maxStorage());
|
||||
|
||||
EXPECT_EQ(pool.usedStorage(), 0);
|
||||
EXPECT_EQ(pool.unusedStorage(), 0);
|
||||
EXPECT_EQ(pool.storage(), 0);
|
||||
EXPECT_FALSE(pool.isUsed(3));
|
||||
EXPECT_FALSE(pool.isUnused(3));
|
||||
EXPECT_FALSE(pool.isPooled(3));
|
||||
|
||||
auto id = pool.alloc(makeDescOneMB());
|
||||
EXPECT_EQ(pool.usedStorage(), oneMB);
|
||||
EXPECT_EQ(pool.unusedStorage(), 0);
|
||||
EXPECT_EQ(pool.storage(), oneMB);
|
||||
EXPECT_TRUE(pool.isUsed(id));
|
||||
EXPECT_FALSE(pool.isUnused(id));
|
||||
EXPECT_TRUE(pool.isPooled(id));
|
||||
|
||||
pool.release(id);
|
||||
EXPECT_EQ(pool.usedStorage(), 0u);
|
||||
EXPECT_EQ(pool.unusedStorage(), oneMB);
|
||||
EXPECT_EQ(pool.storage(), oneMB);
|
||||
EXPECT_FALSE(pool.isUsed(id));
|
||||
EXPECT_TRUE(pool.isUnused(id));
|
||||
EXPECT_TRUE(pool.isPooled(id));
|
||||
|
||||
auto reused_id = pool.alloc(makeDescOneMB());
|
||||
EXPECT_EQ(reused_id, id);
|
||||
EXPECT_EQ(pool.usedStorage(), oneMB);
|
||||
EXPECT_EQ(pool.unusedStorage(), 0);
|
||||
EXPECT_EQ(pool.storage(), oneMB);
|
||||
EXPECT_TRUE(pool.isUsed(id));
|
||||
EXPECT_FALSE(pool.isUnused(id));
|
||||
EXPECT_TRUE(pool.isPooled(id));
|
||||
|
||||
std::unordered_set<gl::TextureID> ids = {id};
|
||||
while (pool.storage() <= pool.maxStorage()) {
|
||||
auto new_id = pool.alloc(makeDescOneMB());
|
||||
EXPECT_TRUE(ids.find(new_id) == ids.end());
|
||||
ids.insert(new_id);
|
||||
EXPECT_EQ(pool.usedStorage(), oneMB * ids.size());
|
||||
EXPECT_EQ(pool.unusedStorage(), 0);
|
||||
EXPECT_EQ(pool.storage(), pool.usedStorage());
|
||||
EXPECT_TRUE(pool.isUsed(new_id));
|
||||
EXPECT_FALSE(pool.isUnused(new_id));
|
||||
EXPECT_TRUE(pool.isPooled(new_id));
|
||||
}
|
||||
|
||||
EXPECT_LE(pool.maxStorage(), pool.usedStorage());
|
||||
EXPECT_EQ(pool.usedStorage(), oneMB * ids.size());
|
||||
EXPECT_EQ(pool.unusedStorage(), 0);
|
||||
EXPECT_EQ(pool.storage(), pool.usedStorage());
|
||||
EXPECT_TRUE(pool.isUsed(id));
|
||||
EXPECT_FALSE(pool.isUnused(id));
|
||||
EXPECT_TRUE(pool.isPooled(id));
|
||||
|
||||
pool.release(id);
|
||||
ids.erase(id);
|
||||
EXPECT_GE(pool.maxStorage(), pool.usedStorage());
|
||||
EXPECT_EQ(pool.usedStorage(), oneMB * ids.size());
|
||||
EXPECT_EQ(pool.unusedStorage(), 0);
|
||||
EXPECT_EQ(pool.storage(), pool.usedStorage());
|
||||
EXPECT_FALSE(pool.isUsed(id));
|
||||
EXPECT_FALSE(pool.isUnused(id));
|
||||
EXPECT_FALSE(pool.isPooled(id));
|
||||
|
||||
size_t used = pool.usedStorage();
|
||||
size_t unused = 0;
|
||||
while (!ids.empty()) {
|
||||
auto i = *ids.begin();
|
||||
pool.release(i);
|
||||
ids.erase(i);
|
||||
used -= oneMB;
|
||||
unused += oneMB;
|
||||
EXPECT_EQ(pool.usedStorage(), used);
|
||||
EXPECT_EQ(pool.unusedStorage(), unused);
|
||||
EXPECT_EQ(pool.storage(), used + unused);
|
||||
EXPECT_FALSE(pool.isUsed(i));
|
||||
EXPECT_TRUE(pool.isUnused(i));
|
||||
EXPECT_TRUE(pool.isPooled(i));
|
||||
}
|
||||
|
||||
context.reduceMemoryUsage();
|
||||
context.renderingStats().numActiveTextures = 0;
|
||||
EXPECT_EQ(pool.usedStorage(), 0);
|
||||
EXPECT_EQ(pool.unusedStorage(), 0);
|
||||
EXPECT_EQ(pool.storage(), 0);
|
||||
EXPECT_FALSE(pool.isUsed(id));
|
||||
EXPECT_FALSE(pool.isUnused(id));
|
||||
EXPECT_FALSE(pool.isPooled(id));
|
||||
EXPECT_TRUE(context.empty(true));
|
||||
}
|
||||
|
||||
#endif
|
||||
38
test/util/lru_cache.test.cpp
Normal file
38
test/util/lru_cache.test.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include <mbgl/util/lru_cache.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
using namespace mbgl;
|
||||
|
||||
TEST(LRU, LRU) {
|
||||
LRU<int> lru;
|
||||
EXPECT_TRUE(lru.empty());
|
||||
|
||||
lru.touch(1);
|
||||
lru.touch(2);
|
||||
lru.touch(3);
|
||||
EXPECT_FALSE(lru.empty());
|
||||
EXPECT_EQ(lru.size(), 3);
|
||||
|
||||
lru.remove(2);
|
||||
lru.remove(77);
|
||||
EXPECT_EQ(lru.evict(), 1);
|
||||
EXPECT_FALSE(lru.empty());
|
||||
EXPECT_EQ(lru.size(), 1);
|
||||
|
||||
lru.touch(4);
|
||||
EXPECT_EQ(lru.evict(), 3);
|
||||
EXPECT_FALSE(lru.empty());
|
||||
EXPECT_EQ(lru.size(), 1);
|
||||
|
||||
EXPECT_FALSE(lru.isHit(3));
|
||||
EXPECT_FALSE(lru.isHit(33));
|
||||
EXPECT_TRUE(lru.isHit(4));
|
||||
|
||||
EXPECT_EQ(lru.evict(), 4);
|
||||
EXPECT_TRUE(lru.empty());
|
||||
EXPECT_FALSE(lru.isHit(4));
|
||||
EXPECT_THROW(lru.evict(), std::runtime_error);
|
||||
}
|
||||
Reference in New Issue
Block a user