Try to use pbo for each ThreadedTexLoader

This commit is contained in:
Benau 2017-03-09 16:41:15 +08:00
parent d864995c7d
commit 68e36268aa
4 changed files with 143 additions and 113 deletions

View File

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

View File

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

View File

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

View File

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