Try to use pbo for each ThreadedTexLoader
This commit is contained in:
parent
d864995c7d
commit
68e36268aa
@ -28,24 +28,31 @@
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
STKTexManager::STKTexManager() : m_pbo(0), m_pbo_ptr(NULL), m_pbo_size(0),
|
||||
m_thread_size(0)
|
||||
m_thread_size(0), m_tlt_added(0)
|
||||
{
|
||||
#ifndef SERVER_ONLY
|
||||
if (CVS->supportsThreadedTextureLoading())
|
||||
{
|
||||
m_thread_size =
|
||||
std::max(unsigned(1), std::thread::hardware_concurrency());
|
||||
Log::info("STKTexManager", "%d thread(s) for texture loading.",
|
||||
m_thread_size);
|
||||
m_pbo_size = m_thread_size * 4 * 1024 * 1024;
|
||||
glGenBuffers(1, &m_pbo);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_pbo);
|
||||
glBufferStorage(GL_PIXEL_UNPACK_BUFFER, m_pbo_size, NULL,
|
||||
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
|
||||
m_pbo_ptr = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0,
|
||||
m_pbo_size, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
|
||||
std::max(unsigned(1), std::thread::hardware_concurrency());
|
||||
static const unsigned max_pbo_size = 32 * 1024 * 1024;
|
||||
const unsigned each_capacity = max_pbo_size / m_thread_size;
|
||||
Log::info("STKTexManager", "%d thread(s) for texture loading,"
|
||||
" each capacity %d MB", m_thread_size,
|
||||
each_capacity / 1024 / 1024);
|
||||
GLuint pbo;
|
||||
uint8_t* pbo_ptr;
|
||||
for (unsigned i = 0; i < m_thread_size; i++)
|
||||
m_all_tex_loaders.push_back(new ThreadedTexLoader(i, this));
|
||||
{
|
||||
glGenBuffers(1, &pbo);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
|
||||
glBufferStorage(GL_PIXEL_UNPACK_BUFFER, each_capacity, NULL,
|
||||
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
|
||||
pbo_ptr = (uint8_t*)glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0,
|
||||
each_capacity, GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
|
||||
m_all_tex_loaders.push_back(new ThreadedTexLoader(each_capacity,
|
||||
pbo, pbo_ptr));
|
||||
}
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
@ -128,9 +135,8 @@ video::ITexture* STKTexManager::getTexture(const std::string& path, bool srgb,
|
||||
}
|
||||
if (new_texture->useThreadedLoading())
|
||||
{
|
||||
m_threaded_loading_textures.lock();
|
||||
m_threaded_loading_textures.getData().push_back(new_texture);
|
||||
m_threaded_loading_textures.unlock();
|
||||
m_all_tex_loaders[m_tlt_added++ % m_thread_size]
|
||||
->addTexture(new_texture);
|
||||
}
|
||||
}
|
||||
uploadBatch();
|
||||
@ -315,59 +321,10 @@ void STKTexManager::uploadBatch()
|
||||
{
|
||||
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||
if (!CVS->supportsThreadedTextureLoading()) return;
|
||||
|
||||
for (ThreadedTexLoader* ttl : m_all_tex_loaders)
|
||||
if (!ttl->finishedLoading()) return;
|
||||
|
||||
m_threaded_loading_textures.lock();
|
||||
glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_pbo);
|
||||
for (unsigned i = 0; i < m_thread_size; i++)
|
||||
{
|
||||
size_t offset = 0;
|
||||
for (unsigned j = 0; j < m_all_tex_loaders[i]->getTextureLoaded(); j++)
|
||||
{
|
||||
const unsigned target_tex = j * m_thread_size + i;
|
||||
assert(target_tex < m_threaded_loading_textures.getData().size());
|
||||
STKTexture* stkt =
|
||||
m_threaded_loading_textures.getData()[target_tex];
|
||||
assert(!stkt->useThreadedLoading());
|
||||
glBindTexture(GL_TEXTURE_2D, stkt->getOpenGLTextureName());
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, stkt->getSize().Width,
|
||||
stkt->getSize().Height, stkt->isSingleChannel() ? GL_RED :
|
||||
GL_BGRA, GL_UNSIGNED_BYTE,
|
||||
(const void*)(offset + i * 4 * 1024 * 1024));
|
||||
if (stkt->hasMipMaps())
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
offset += stkt->getTextureSize();
|
||||
}
|
||||
m_all_tex_loaders[i]->reset();
|
||||
if (ttl->finishedLoading())
|
||||
ttl->handleCompletedTextures();
|
||||
}
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
auto p = m_threaded_loading_textures.getData().begin();
|
||||
while (p != m_threaded_loading_textures.getData().end())
|
||||
{
|
||||
if (!(*p)->useThreadedLoading())
|
||||
{
|
||||
p = m_threaded_loading_textures.getData().erase(p);
|
||||
continue;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
GLenum reason = glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 0);
|
||||
if (reason != GL_ALREADY_SIGNALED)
|
||||
{
|
||||
do
|
||||
{
|
||||
reason = glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT,
|
||||
1000000);
|
||||
}
|
||||
while (reason == GL_TIMEOUT_EXPIRED);
|
||||
}
|
||||
glDeleteSync(sync);
|
||||
|
||||
m_threaded_loading_textures.unlock();
|
||||
#endif
|
||||
} // uploadBatch
|
||||
|
@ -55,7 +55,7 @@ private:
|
||||
|
||||
uint8_t* m_pbo_ptr;
|
||||
|
||||
unsigned m_pbo_size, m_thread_size;
|
||||
unsigned m_pbo_size, m_thread_size, m_tlt_added;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
STKTexture* findTextureInFileSystem(const std::string& filename,
|
||||
|
@ -17,43 +17,104 @@
|
||||
|
||||
#include "graphics/threaded_tex_loader.hpp"
|
||||
#include "graphics/stk_texture.hpp"
|
||||
#include "graphics/stk_tex_manager.hpp"
|
||||
#include "utils/log.hpp"
|
||||
#include "utils/time.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void* ThreadedTexLoader::startRoutine(void *obj)
|
||||
{
|
||||
ThreadedTexLoader* ttl = (ThreadedTexLoader*)obj;
|
||||
STKTexManager* stktm = ttl->m_stktm;
|
||||
while (!ttl->m_destroy)
|
||||
{
|
||||
bool empty = stktm->isThreadedLoaderEmpty();
|
||||
while (!empty)
|
||||
if (ttl->finishedLoading())
|
||||
{
|
||||
empty = stktm->isThreadedLoaderEmpty();
|
||||
if (empty || ttl->finishedLoading())
|
||||
break;
|
||||
const unsigned target_tex = ttl->m_tex_loaded *
|
||||
stktm->getNumLoadingThread() + ttl->m_id;
|
||||
if (target_tex + 1 > stktm->getThreadedLoadingTextureNum())
|
||||
{
|
||||
if (ttl->m_wait_count++ > 10)
|
||||
ttl->m_tex_size_used.setAtomic(1000000000);
|
||||
continue;
|
||||
}
|
||||
STKTexture* stkt = stktm->getThreadedLoadingTexture(target_tex);
|
||||
const unsigned prev_used_size = ttl->m_tex_size_used.getAtomic();
|
||||
ttl->m_tex_size_used.setAtomic(prev_used_size +
|
||||
stkt->getTextureSize());
|
||||
if (ttl->finishedLoading())
|
||||
break;
|
||||
stkt->threadedReload(stktm->getPBOPtr() +
|
||||
ttl->m_id * 4 * 1024 * 1024 + prev_used_size);
|
||||
stkt->cleanThreadedLoader();
|
||||
ttl->m_tex_loaded++;
|
||||
continue;
|
||||
}
|
||||
StkTime::sleep(50);
|
||||
ttl->m_threaded_loading_textures.lock();
|
||||
bool queue_empty = ttl->m_threaded_loading_textures.getData().empty();
|
||||
ttl->m_completed_textures.lock();
|
||||
bool no_unloaded_tex = ttl->m_completed_textures.getData().empty();
|
||||
ttl->m_completed_textures.unlock();
|
||||
bool waiting = queue_empty && no_unloaded_tex;
|
||||
while (waiting)
|
||||
{
|
||||
pthread_cond_wait(&ttl->m_cond_request,
|
||||
ttl->m_threaded_loading_textures.getMutex());
|
||||
waiting = ttl->m_threaded_loading_textures.getData().empty();
|
||||
}
|
||||
if (queue_empty)
|
||||
{
|
||||
if (ttl->m_waiting_timeout++ > 10)
|
||||
{
|
||||
ttl->m_finished_loading.setAtomic(true);
|
||||
ttl->m_tex_size_loaded = 0;
|
||||
ttl->m_waiting_timeout = 0;
|
||||
}
|
||||
ttl->m_threaded_loading_textures.unlock();
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
ttl->m_waiting_timeout = 0;
|
||||
}
|
||||
STKTexture* target_tex = ttl->m_threaded_loading_textures.getData()[0];
|
||||
if (target_tex->getTextureSize() + ttl->m_tex_size_loaded >
|
||||
ttl->m_tex_capacity)
|
||||
{
|
||||
ttl->m_finished_loading.setAtomic(true);
|
||||
ttl->m_tex_size_loaded = 0;
|
||||
ttl->m_waiting_timeout = 0;
|
||||
ttl->m_threaded_loading_textures.unlock();
|
||||
continue;
|
||||
}
|
||||
ttl->m_threaded_loading_textures.getData().erase
|
||||
(ttl->m_threaded_loading_textures.getData().begin());
|
||||
ttl->m_threaded_loading_textures.unlock();
|
||||
target_tex->threadedReload(ttl->m_pbo_ptr + ttl->m_tex_size_loaded);
|
||||
target_tex->cleanThreadedLoader();
|
||||
ttl->m_tex_size_loaded += target_tex->getTextureSize();
|
||||
ttl->m_completed_textures.lock();
|
||||
ttl->m_completed_textures.getData().push_back(target_tex);
|
||||
ttl->m_completed_textures.unlock();
|
||||
}
|
||||
pthread_exit(NULL);
|
||||
return NULL;
|
||||
} // startRoutine
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void ThreadedTexLoader::handleCompletedTextures()
|
||||
{
|
||||
#if !(defined(SERVER_ONLY) || defined(USE_GLES2))
|
||||
m_completed_textures.lock();
|
||||
glMemoryBarrier(GL_CLIENT_MAPPED_BUFFER_BARRIER_BIT);
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, m_pbo);
|
||||
size_t offset = 0;
|
||||
for (STKTexture* stkt : m_completed_textures.getData())
|
||||
{
|
||||
assert(!stkt->useThreadedLoading());
|
||||
glBindTexture(GL_TEXTURE_2D, stkt->getOpenGLTextureName());
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, stkt->getSize().Width,
|
||||
stkt->getSize().Height, stkt->isSingleChannel() ? GL_RED : GL_BGRA,
|
||||
GL_UNSIGNED_BYTE, (const void*)offset);
|
||||
if (stkt->hasMipMaps())
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
offset += stkt->getTextureSize();
|
||||
}
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
m_completed_textures.getData().clear();
|
||||
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
GLenum reason = glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 0);
|
||||
if (reason != GL_ALREADY_SIGNALED)
|
||||
{
|
||||
do
|
||||
{
|
||||
reason =
|
||||
glClientWaitSync(sync, GL_SYNC_FLUSH_COMMANDS_BIT, 1000000);
|
||||
}
|
||||
while (reason == GL_TIMEOUT_EXPIRED);
|
||||
}
|
||||
glDeleteSync(sync);
|
||||
m_finished_loading.setAtomic(false);
|
||||
m_completed_textures.unlock();
|
||||
#endif
|
||||
} // handleCompletedTextures
|
||||
|
@ -18,58 +18,70 @@
|
||||
#ifndef HEADER_THREADED_TEX_LOADER_HPP
|
||||
#define HEADER_THREADED_TEX_LOADER_HPP
|
||||
|
||||
#include "graphics/gl_headers.hpp"
|
||||
#include "utils/no_copy.hpp"
|
||||
#include "utils/synchronised.hpp"
|
||||
#include "utils/types.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class STKTexture;
|
||||
class STKTexManager;
|
||||
|
||||
class ThreadedTexLoader : public NoCopy
|
||||
{
|
||||
private:
|
||||
unsigned m_id, m_tex_loaded, m_wait_count;
|
||||
const unsigned m_tex_capacity;
|
||||
|
||||
bool m_destroy;
|
||||
GLuint m_pbo;
|
||||
|
||||
uint8_t* m_pbo_ptr;
|
||||
|
||||
unsigned m_tex_size_loaded, m_waiting_timeout;
|
||||
|
||||
pthread_t m_thread;
|
||||
|
||||
Synchronised<unsigned> m_tex_size_used;
|
||||
pthread_cond_t m_cond_request;
|
||||
|
||||
STKTexManager* m_stktm;
|
||||
Synchronised<bool> m_finished_loading;
|
||||
|
||||
bool m_destroy;
|
||||
|
||||
Synchronised<std::vector<STKTexture*> >
|
||||
m_threaded_loading_textures, m_completed_textures;
|
||||
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
static void* startRoutine(void *obj);
|
||||
// ------------------------------------------------------------------------
|
||||
ThreadedTexLoader(unsigned id, STKTexManager* stktm)
|
||||
ThreadedTexLoader(unsigned capacity, GLuint pbo, uint8_t* pbo_ptr)
|
||||
: m_tex_capacity(capacity), m_pbo(pbo), m_pbo_ptr(pbo_ptr),
|
||||
m_tex_size_loaded(0), m_waiting_timeout(0),
|
||||
m_finished_loading(false), m_destroy(false)
|
||||
{
|
||||
m_destroy = false;
|
||||
m_stktm = stktm;
|
||||
m_id = id;
|
||||
reset();
|
||||
pthread_cond_init(&m_cond_request, NULL);
|
||||
pthread_create(&m_thread, NULL, &startRoutine, this);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
~ThreadedTexLoader()
|
||||
{
|
||||
m_destroy = true;
|
||||
pthread_cond_destroy(&m_cond_request);
|
||||
pthread_join(m_thread, NULL);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void reset()
|
||||
void addTexture(STKTexture* t)
|
||||
{
|
||||
m_tex_size_used.setAtomic(0);
|
||||
m_tex_loaded = 0;
|
||||
m_wait_count = 0;
|
||||
m_threaded_loading_textures.lock();
|
||||
m_threaded_loading_textures.getData().push_back(t);
|
||||
pthread_cond_signal(&m_cond_request);
|
||||
m_threaded_loading_textures.unlock();
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
unsigned getTextureLoaded() const { return m_tex_loaded; }
|
||||
bool finishedLoading() const { return m_finished_loading.getAtomic(); }
|
||||
// ------------------------------------------------------------------------
|
||||
bool finishedLoading() const
|
||||
{
|
||||
return m_tex_size_used.getAtomic() > 1024 * 1024 * 4;
|
||||
}
|
||||
void handleCompletedTextures();
|
||||
|
||||
}; // ThreadedTexLoader
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user