First version of ThreadedTexLoader
This commit is contained in:
parent
35139877f6
commit
d864995c7d
@ -42,7 +42,9 @@ public:
|
||||
//! Creates a surface from the file
|
||||
/** \param file File handle to check.
|
||||
\return Pointer to newly created image, or 0 upon error. */
|
||||
virtual IImage* loadImage(io::IReadFile* file) const = 0;
|
||||
virtual IImage* loadImage(io::IReadFile* file, bool skip_checking = false) const = 0;
|
||||
virtual core::dimension2du getImageSize(io::IReadFile* file) const { return core::dimension2du(0, 0); }
|
||||
virtual bool supportThreadedLoading() const { return false; }
|
||||
};
|
||||
|
||||
|
||||
|
@ -1186,7 +1186,7 @@ namespace video
|
||||
\return The created image.
|
||||
If you no longer need the image, you should call IImage::drop().
|
||||
See IReferenceCounted::drop() for more information. */
|
||||
virtual IImage* createImageFromFile(io::IReadFile* file) =0;
|
||||
virtual IImage* createImageFromFile(io::IReadFile* file, video::IImageLoader** loader = NULL) =0;
|
||||
|
||||
//! Writes the provided image to a file.
|
||||
/** Requires that there is a suitable image writer registered
|
||||
|
@ -216,7 +216,7 @@ void CImageLoaderBMP::decompress4BitRLE(u8*& bmpData, s32 size, s32 width, s32 h
|
||||
|
||||
|
||||
//! creates a surface from the file
|
||||
IImage* CImageLoaderBMP::loadImage(io::IReadFile* file) const
|
||||
IImage* CImageLoaderBMP::loadImage(io::IReadFile* file, bool skip_checking) const
|
||||
{
|
||||
SBMPHeader header;
|
||||
|
||||
|
@ -81,7 +81,7 @@ public:
|
||||
virtual bool isALoadableFileFormat(io::IReadFile* file) const;
|
||||
|
||||
//! creates a surface from the file
|
||||
virtual IImage* loadImage(io::IReadFile* file) const;
|
||||
virtual IImage* loadImage(io::IReadFile* file, bool skip_checking = false) const;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -135,7 +135,7 @@ bool CImageLoaderJPG::isALoadableFileFormat(io::IReadFile* file) const
|
||||
}
|
||||
|
||||
//! creates a surface from the file
|
||||
IImage* CImageLoaderJPG::loadImage(io::IReadFile* file) const
|
||||
IImage* CImageLoaderJPG::loadImage(io::IReadFile* file, bool skip_checking) const
|
||||
{
|
||||
#ifndef _IRR_COMPILE_WITH_LIBJPEG_
|
||||
os::Printer::log("Can't load as not compiled with _IRR_COMPILE_WITH_LIBJPEG_:", file->getFileName(), ELL_DEBUG);
|
||||
|
@ -49,7 +49,7 @@ public:
|
||||
virtual bool isALoadableFileFormat(io::IReadFile* file) const;
|
||||
|
||||
//! creates a surface from the file
|
||||
virtual IImage* loadImage(io::IReadFile* file) const;
|
||||
virtual IImage* loadImage(io::IReadFile* file, bool skip_checking = false) const;
|
||||
|
||||
private:
|
||||
|
||||
|
@ -27,14 +27,14 @@ namespace video
|
||||
// PNG function for error handling
|
||||
static void png_cpexcept_error(png_structp png_ptr, png_const_charp msg)
|
||||
{
|
||||
os::Printer::log("PNG fatal error", msg, ELL_ERROR);
|
||||
printf("PNG fatal error: %s\n", msg);
|
||||
longjmp(png_jmpbuf(png_ptr), 1);
|
||||
}
|
||||
|
||||
// PNG function for warning handling
|
||||
static void png_cpexcept_warn(png_structp png_ptr, png_const_charp msg)
|
||||
{
|
||||
os::Printer::log("PNG warning", msg, ELL_WARNING);
|
||||
//os::Printer::log("PNG warning", msg, ELL_WARNING);
|
||||
}
|
||||
|
||||
// PNG function for file reading
|
||||
@ -86,7 +86,7 @@ bool CImageLoaderPng::isALoadableFileFormat(io::IReadFile* file) const
|
||||
|
||||
|
||||
// load in the image data
|
||||
IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
|
||||
IImage* CImageLoaderPng::loadImage(io::IReadFile* file, bool skip_checking) const
|
||||
{
|
||||
#ifdef _IRR_COMPILE_WITH_LIBPNG_
|
||||
if (!file)
|
||||
@ -96,27 +96,17 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
|
||||
//Used to point to image rows
|
||||
u8** RowPointers = 0;
|
||||
|
||||
png_byte buffer[8];
|
||||
// Read the first few bytes of the PNG file
|
||||
if( file->read(buffer, 8) != 8 )
|
||||
{
|
||||
os::Printer::log("LOAD PNG: can't read file\n", file->getFileName(), ELL_ERROR);
|
||||
if (skip_checking)
|
||||
file->seek(8);
|
||||
else if (!isALoadableFileFormat(file))
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check if it really is a PNG file
|
||||
if( png_sig_cmp(buffer, 0, 8) )
|
||||
{
|
||||
os::Printer::log("LOAD PNG: not really a png\n", file->getFileName(), ELL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allocate the png read struct
|
||||
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
|
||||
NULL, (png_error_ptr)png_cpexcept_error, (png_error_ptr)png_cpexcept_warn);
|
||||
if (!png_ptr)
|
||||
{
|
||||
os::Printer::log("LOAD PNG: Internal PNG create read struct failure\n", file->getFileName(), ELL_ERROR);
|
||||
//os::Printer::log("LOAD PNG: Internal PNG create read struct failure\n", file->getFileName(), ELL_ERROR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -124,7 +114,7 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
|
||||
png_infop info_ptr = png_create_info_struct(png_ptr);
|
||||
if (!info_ptr)
|
||||
{
|
||||
os::Printer::log("LOAD PNG: Internal PNG create info struct failure\n", file->getFileName(), ELL_ERROR);
|
||||
//os::Printer::log("LOAD PNG: Internal PNG create info struct failure\n", file->getFileName(), ELL_ERROR);
|
||||
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
@ -229,7 +219,7 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
|
||||
image = new CImage(ECF_R8G8B8, core::dimension2d<u32>(Width, Height));
|
||||
if (!image)
|
||||
{
|
||||
os::Printer::log("LOAD PNG: Internal PNG create image struct failure\n", file->getFileName(), ELL_ERROR);
|
||||
//os::Printer::log("LOAD PNG: Internal PNG create image struct failure\n", file->getFileName(), ELL_ERROR);
|
||||
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||
return 0;
|
||||
}
|
||||
@ -238,7 +228,7 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
|
||||
RowPointers = new png_bytep[Height];
|
||||
if (!RowPointers)
|
||||
{
|
||||
os::Printer::log("LOAD PNG: Internal PNG create row pointers failure\n", file->getFileName(), ELL_ERROR);
|
||||
//os::Printer::log("LOAD PNG: Internal PNG create row pointers failure\n", file->getFileName(), ELL_ERROR);
|
||||
png_destroy_read_struct(&png_ptr, NULL, NULL);
|
||||
delete image;
|
||||
return 0;
|
||||
@ -276,6 +266,26 @@ IImage* CImageLoaderPng::loadImage(io::IReadFile* file) const
|
||||
#endif // _IRR_COMPILE_WITH_LIBPNG_
|
||||
}
|
||||
|
||||
core::dimension2du CImageLoaderPng::getImageSize(io::IReadFile* file) const
|
||||
{
|
||||
#ifdef _IRR_COMPILE_WITH_LIBPNG_
|
||||
if (!file || !isALoadableFileFormat(file))
|
||||
return core::dimension2du(0, 0);
|
||||
core::dimension2d<u32> dim;
|
||||
file->seek(16);
|
||||
file->read(&dim.Width, 4);
|
||||
file->seek(20);
|
||||
file->read(&dim.Height, 4);
|
||||
file->seek(0);
|
||||
#ifndef __BIG_ENDIAN__
|
||||
dim.Width = os::Byteswap::byteswap(dim.Width);
|
||||
dim.Height = os::Byteswap::byteswap(dim.Height);
|
||||
#endif
|
||||
return dim;
|
||||
#else
|
||||
return core::dimension2du(0, 0);
|
||||
#endif // _IRR_COMPILE_WITH_LIBPNG_
|
||||
}
|
||||
|
||||
IImageLoader* createImageLoaderPNG()
|
||||
{
|
||||
|
@ -25,15 +25,17 @@ class CImageLoaderPng : public IImageLoader
|
||||
{
|
||||
public:
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
//! based on the file extension (e.g. ".png")
|
||||
virtual bool isALoadableFileExtension(const io::path& filename) const;
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
//! based on the file extension (e.g. ".png")
|
||||
virtual bool isALoadableFileExtension(const io::path& filename) const;
|
||||
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
virtual bool isALoadableFileFormat(io::IReadFile* file) const;
|
||||
//! returns true if the file maybe is able to be loaded by this class
|
||||
virtual bool isALoadableFileFormat(io::IReadFile* file) const;
|
||||
|
||||
//! creates a surface from the file
|
||||
virtual IImage* loadImage(io::IReadFile* file) const;
|
||||
//! creates a surface from the file
|
||||
virtual IImage* loadImage(io::IReadFile* file, bool skip_checking = false) const;
|
||||
virtual core::dimension2du getImageSize(io::IReadFile* file) const;
|
||||
virtual bool supportThreadedLoading() const { return true; }
|
||||
};
|
||||
|
||||
|
||||
|
@ -103,6 +103,7 @@ CNullDriver::CNullDriver(io::IFileSystem* io, const core::dimension2d<u32>& scre
|
||||
// DriverAttributes->addInt("MaxGeometryVerticesOut", 0);
|
||||
// DriverAttributes->addFloat("MaxTextureLODBias", 0.f);
|
||||
DriverAttributes->addInt("Version", 1);
|
||||
DriverAttributes->setAttribute("MAX_TEXTURE_SIZE", core::dimension2du(16384, 16384));
|
||||
// DriverAttributes->addInt("ShaderLanguageVersion", 0);
|
||||
// DriverAttributes->addInt("AntiAlias", 0);
|
||||
|
||||
@ -1278,7 +1279,7 @@ IImage* CNullDriver::createImageFromFile(const io::path& filename)
|
||||
|
||||
|
||||
//! Creates a software image from a file.
|
||||
IImage* CNullDriver::createImageFromFile(io::IReadFile* file)
|
||||
IImage* CNullDriver::createImageFromFile(io::IReadFile* file, video::IImageLoader** loader)
|
||||
{
|
||||
if (!file)
|
||||
return 0;
|
||||
@ -1292,6 +1293,11 @@ IImage* CNullDriver::createImageFromFile(io::IReadFile* file)
|
||||
{
|
||||
if (SurfaceLoader[i]->isALoadableFileExtension(file->getFileName()))
|
||||
{
|
||||
if (loader)
|
||||
{
|
||||
*loader = SurfaceLoader[i];
|
||||
return 0;
|
||||
}
|
||||
// reset file position which might have changed due to previous loadImage calls
|
||||
file->seek(0);
|
||||
image = SurfaceLoader[i]->loadImage(file);
|
||||
@ -1307,6 +1313,11 @@ IImage* CNullDriver::createImageFromFile(io::IReadFile* file)
|
||||
file->seek(0);
|
||||
if (SurfaceLoader[i]->isALoadableFileFormat(file))
|
||||
{
|
||||
if (loader)
|
||||
{
|
||||
*loader = SurfaceLoader[i];
|
||||
return 0;
|
||||
}
|
||||
file->seek(0);
|
||||
image = SurfaceLoader[i]->loadImage(file);
|
||||
if (image)
|
||||
|
@ -348,7 +348,7 @@ namespace video
|
||||
virtual IImage* createImageFromFile(const io::path& filename);
|
||||
|
||||
//! Creates a software image from a file.
|
||||
virtual IImage* createImageFromFile(io::IReadFile* file);
|
||||
virtual IImage* createImageFromFile(io::IReadFile* file, video::IImageLoader** loader = NULL);
|
||||
|
||||
//! Creates a software image from a byte array.
|
||||
/** \param useForeignMemory: If true, the image will use the data pointer
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Modify this file to change the last-modified date when you add/remove a file.
|
||||
# Modify this file to change the last-modified date when you add/remove a file.
|
||||
# This will then trigger a new cmake run automatically.
|
||||
file(GLOB_RECURSE STK_HEADERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.hpp")
|
||||
file(GLOB_RECURSE STK_SOURCES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.cpp")
|
||||
|
@ -52,6 +52,7 @@ void CentralVideoSettings::init()
|
||||
hasGS = false;
|
||||
hasTextureFilterAnisotropic = false;
|
||||
hasTextureSwizzle = false;
|
||||
hasPixelBufferObject = false;
|
||||
|
||||
#if defined(USE_GLES2)
|
||||
hasBGRA = false;
|
||||
@ -196,6 +197,11 @@ void CentralVideoSettings::init()
|
||||
hasTextureSwizzle = true;
|
||||
Log::info("GLDriver", "ARB Texture Swizzle Present");
|
||||
}
|
||||
if (hasGLExtension("GL_ARB_pixel_buffer_object"))
|
||||
{
|
||||
hasPixelBufferObject = true;
|
||||
Log::info("GLDriver", "ARB Pixel Buffer Object Present");
|
||||
}
|
||||
// Only unset the high def textures if they are set as default. If the
|
||||
// user has enabled them (bit 1 set), then leave them enabled.
|
||||
if (GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_HIGHDEFINITION_TEXTURES) &&
|
||||
@ -237,8 +243,9 @@ void CentralVideoSettings::init()
|
||||
{
|
||||
hasTextureStorage = true;
|
||||
hasTextureSwizzle = true;
|
||||
hasPixelBufferObject = true;
|
||||
}
|
||||
|
||||
|
||||
if (!GraphicsRestrictions::isDisabled(GraphicsRestrictions::GR_EXPLICIT_ATTRIB_LOCATION) &&
|
||||
m_glsl == true)
|
||||
{
|
||||
@ -476,4 +483,14 @@ bool CentralVideoSettings::isARBTextureSwizzleUsable() const
|
||||
return m_glsl && hasTextureSwizzle;
|
||||
}
|
||||
|
||||
bool CentralVideoSettings::isARBPixelBufferObjectUsable() const
|
||||
{
|
||||
return hasPixelBufferObject;
|
||||
}
|
||||
|
||||
bool CentralVideoSettings::supportsThreadedTextureLoading() const
|
||||
{
|
||||
return isARBPixelBufferObjectUsable() && supportsAsyncInstanceUpload();
|
||||
}
|
||||
|
||||
#endif // !SERVER_ONLY
|
||||
|
@ -44,6 +44,7 @@ private:
|
||||
bool hasMultiDrawIndirect;
|
||||
bool hasTextureFilterAnisotropic;
|
||||
bool hasTextureSwizzle;
|
||||
bool hasPixelBufferObject;
|
||||
|
||||
#if defined(USE_GLES2)
|
||||
bool hasBGRA;
|
||||
@ -84,6 +85,7 @@ public:
|
||||
bool isARBExplicitAttribLocationUsable() const;
|
||||
bool isEXTTextureFilterAnisotropicUsable() const;
|
||||
bool isARBTextureSwizzleUsable() const;
|
||||
bool isARBPixelBufferObjectUsable() const;
|
||||
|
||||
#if defined(USE_GLES2)
|
||||
bool isEXTTextureFormatBGRA8888Usable() const;
|
||||
@ -98,6 +100,7 @@ public:
|
||||
bool supportsComputeShadersFiltering() const;
|
||||
bool supportsAsyncInstanceUpload() const;
|
||||
bool supportsHardwareSkinning() const;
|
||||
bool supportsThreadedTextureLoading() const;
|
||||
|
||||
// "Macro" around feature support and user config
|
||||
bool isShadowEnabled() const;
|
||||
|
@ -18,15 +18,46 @@
|
||||
#include "graphics/stk_tex_manager.hpp"
|
||||
#include "graphics/central_settings.hpp"
|
||||
#include "graphics/materials.hpp"
|
||||
#include "graphics/threaded_tex_loader.hpp"
|
||||
#include "graphics/stk_texture.hpp"
|
||||
#include "io/file_manager.hpp"
|
||||
#include "utils/string_utils.hpp"
|
||||
#include "utils/log.hpp"
|
||||
|
||||
#include <thread>
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
STKTexManager::STKTexManager() : m_pbo(0), m_pbo_ptr(NULL), m_pbo_size(0),
|
||||
m_thread_size(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);
|
||||
for (unsigned i = 0; i < m_thread_size; i++)
|
||||
m_all_tex_loaders.push_back(new ThreadedTexLoader(i, this));
|
||||
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
} // STKTexManager
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
STKTexManager::~STKTexManager()
|
||||
{
|
||||
removeTexture(NULL/*texture*/, true/*remove_all*/);
|
||||
for (ThreadedTexLoader* ttl : m_all_tex_loaders)
|
||||
delete ttl;
|
||||
} // ~STKTexManager
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@ -95,7 +126,14 @@ video::ITexture* STKTexManager::getTexture(const std::string& path, bool srgb,
|
||||
delete new_texture;
|
||||
return NULL;
|
||||
}
|
||||
if (new_texture->useThreadedLoading())
|
||||
{
|
||||
m_threaded_loading_textures.lock();
|
||||
m_threaded_loading_textures.getData().push_back(new_texture);
|
||||
m_threaded_loading_textures.unlock();
|
||||
}
|
||||
}
|
||||
uploadBatch();
|
||||
|
||||
if (create_if_unfound && !no_upload)
|
||||
addTexture(new_texture);
|
||||
@ -271,3 +309,65 @@ void STKTexManager::setTextureErrorMessage(const std::string &error,
|
||||
else
|
||||
m_texture_error_message = StringUtils::insertValues(error, detail);
|
||||
} // setTextureErrorMessage
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
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();
|
||||
}
|
||||
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
|
||||
|
@ -21,14 +21,18 @@
|
||||
#include "graphics/gl_headers.hpp"
|
||||
#include "utils/no_copy.hpp"
|
||||
#include "utils/singleton.hpp"
|
||||
#include "utils/synchronised.hpp"
|
||||
|
||||
#include "irrString.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
class STKTexture;
|
||||
class ThreadedTexLoader;
|
||||
namespace irr
|
||||
{
|
||||
namespace video { class ITexture; class SColor; }
|
||||
@ -43,12 +47,22 @@ private:
|
||||
* This is used to specify details like: "while loading kart '...'" */
|
||||
std::string m_texture_error_message;
|
||||
|
||||
Synchronised<std::vector<STKTexture*> > m_threaded_loading_textures;
|
||||
|
||||
std::vector<ThreadedTexLoader*> m_all_tex_loaders;
|
||||
|
||||
GLuint m_pbo;
|
||||
|
||||
uint8_t* m_pbo_ptr;
|
||||
|
||||
unsigned m_pbo_size, m_thread_size;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
STKTexture* findTextureInFileSystem(const std::string& filename,
|
||||
std::string* full_path);
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
STKTexManager() {}
|
||||
STKTexManager();
|
||||
// ------------------------------------------------------------------------
|
||||
~STKTexManager();
|
||||
// ------------------------------------------------------------------------
|
||||
@ -127,6 +141,37 @@ public:
|
||||
return getTexture(filename, std::string(error_message),
|
||||
std::string(detail));
|
||||
} // getTexture
|
||||
// ------------------------------------------------------------------------
|
||||
void uploadBatch();
|
||||
// ------------------------------------------------------------------------
|
||||
const unsigned getThreadedLoadingTextureNum() const
|
||||
{
|
||||
m_threaded_loading_textures.lock();
|
||||
const unsigned num = m_threaded_loading_textures.getData().size();
|
||||
m_threaded_loading_textures.unlock();
|
||||
return num;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
STKTexture* getThreadedLoadingTexture(unsigned num) const
|
||||
{
|
||||
m_threaded_loading_textures.lock();
|
||||
assert(num < m_threaded_loading_textures.getData().size());
|
||||
STKTexture* t = m_threaded_loading_textures.getData()[num];
|
||||
m_threaded_loading_textures.unlock();
|
||||
return t;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
const bool isThreadedLoaderEmpty() const
|
||||
{
|
||||
m_threaded_loading_textures.lock();
|
||||
const bool empty = m_threaded_loading_textures.getData().empty();
|
||||
m_threaded_loading_textures.unlock();
|
||||
return empty;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
uint8_t* getPBOPtr() { return m_pbo_ptr; }
|
||||
// ------------------------------------------------------------------------
|
||||
unsigned getNumLoadingThread() const { return m_thread_size; }
|
||||
|
||||
}; // STKTexManager
|
||||
|
||||
|
@ -39,7 +39,8 @@ STKTexture::STKTexture(const std::string& path, bool srgb, bool premul_alpha,
|
||||
: video::ITexture(path.c_str()), m_texture_handle(0), m_srgb(srgb),
|
||||
m_premul_alpha(premul_alpha), m_mesh_texture(mesh_tex),
|
||||
m_single_channel(single_channel), m_material(NULL),
|
||||
m_texture_name(0), m_texture_size(0), m_texture_image(NULL)
|
||||
m_texture_name(0), m_texture_size(0), m_texture_image(NULL),
|
||||
m_file(NULL), m_img_loader(NULL)
|
||||
{
|
||||
if (set_material)
|
||||
{
|
||||
@ -62,7 +63,8 @@ STKTexture::STKTexture(uint8_t* data, const std::string& name, size_t size,
|
||||
: video::ITexture(name.c_str()), m_texture_handle(0), m_srgb(false),
|
||||
m_premul_alpha(false), m_mesh_texture(false),
|
||||
m_single_channel(single_channel), m_material(NULL),
|
||||
m_texture_name(0), m_texture_size(0), m_texture_image(NULL)
|
||||
m_texture_name(0), m_texture_size(0), m_texture_image(NULL),
|
||||
m_file(NULL), m_img_loader(NULL)
|
||||
{
|
||||
m_size.Width = size;
|
||||
m_size.Height = size;
|
||||
@ -75,7 +77,8 @@ STKTexture::STKTexture(video::IImage* img, const std::string& name)
|
||||
: video::ITexture(name.c_str()), m_texture_handle(0), m_srgb(false),
|
||||
m_premul_alpha(false), m_mesh_texture(false),
|
||||
m_single_channel(false), m_material(NULL), m_texture_name(0),
|
||||
m_texture_size(0), m_texture_image(NULL)
|
||||
m_texture_size(0), m_texture_image(NULL),
|
||||
m_file(NULL), m_img_loader(NULL)
|
||||
{
|
||||
reload(false/*no_upload*/, NULL/*preload_data*/, img);
|
||||
} // STKTexture
|
||||
@ -108,7 +111,6 @@ void STKTexture::reload(bool no_upload, uint8_t* preload_data,
|
||||
return;
|
||||
}
|
||||
#ifndef SERVER_ONLY
|
||||
irr_driver->getDevice()->getLogger()->setLogLevel(ELL_NONE);
|
||||
|
||||
std::string compressed_texture;
|
||||
#if !defined(USE_GLES2)
|
||||
@ -157,31 +159,56 @@ void STKTexture::reload(bool no_upload, uint8_t* preload_data,
|
||||
uint8_t* data = preload_data;
|
||||
if (data == NULL)
|
||||
{
|
||||
orig_img = preload_img ? preload_img :
|
||||
irr_driver->getVideoDriver()->createImageFromFile(NamedPath);
|
||||
if (orig_img == NULL)
|
||||
if (preload_img)
|
||||
orig_img = preload_img;
|
||||
else
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (orig_img->getDimension().Width == 0 ||
|
||||
orig_img->getDimension().Height == 0)
|
||||
{
|
||||
orig_img->drop();
|
||||
return;
|
||||
m_file = irr_driver->getDevice()->getFileSystem()
|
||||
->createAndOpenFile(NamedPath);
|
||||
if (m_file == NULL)
|
||||
return;
|
||||
irr_driver->getVideoDriver()->createImageFromFile(m_file,
|
||||
&m_img_loader);
|
||||
if (m_img_loader == NULL)
|
||||
return;
|
||||
m_file->seek(0);
|
||||
m_orig_size = m_img_loader->getImageSize(m_file);
|
||||
if (m_orig_size.Width <= 1024 && m_orig_size.Height <= 1024 &&
|
||||
(!m_material || m_material->getAlphaMask().empty()) &&
|
||||
useThreadedLoading() && !no_upload)
|
||||
{
|
||||
if (m_orig_size.Width == 0 || m_orig_size.Height == 0)
|
||||
{
|
||||
m_file->drop();
|
||||
m_file = NULL;
|
||||
m_img_loader = NULL;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
orig_img = m_img_loader->loadImage(m_file);
|
||||
m_file->drop();
|
||||
m_file = NULL;
|
||||
if (orig_img == NULL || orig_img->getDimension().Width == 0 ||
|
||||
orig_img->getDimension().Height == 0)
|
||||
{
|
||||
if (orig_img)
|
||||
orig_img->drop();
|
||||
return;
|
||||
}
|
||||
m_img_loader = NULL;
|
||||
}
|
||||
}
|
||||
orig_img = resizeImage(orig_img, &m_orig_size, &m_size);
|
||||
applyMask(orig_img);
|
||||
data = (uint8_t*)orig_img->lock();
|
||||
if (m_single_channel)
|
||||
data = orig_img ? (uint8_t*)orig_img->lock() : NULL;
|
||||
if (m_single_channel && !useThreadedLoading())
|
||||
{
|
||||
uint8_t* sc = new uint8_t[m_size.Width * m_size.Height];
|
||||
for (unsigned int i = 0; i < m_size.Width * m_size.Height; i++)
|
||||
sc[i] = data[4 * i + 3];
|
||||
data = singleChannelConversion(data);
|
||||
orig_img->unlock();
|
||||
orig_img->drop();
|
||||
orig_img = NULL;
|
||||
data = sc;
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,31 +230,8 @@ void STKTexture::reload(bool no_upload, uint8_t* preload_data,
|
||||
m_single_channel ? GL_R8 : m_srgb ? GL_SRGB_ALPHA : GL_RGBA;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(USE_GLES2)
|
||||
if (!CVS->isEXTTextureFormatBGRA8888Usable() && !m_single_channel)
|
||||
{
|
||||
format = GL_RGBA;
|
||||
for (unsigned int i = 0; i < w * h; i++)
|
||||
{
|
||||
uint8_t tmp_val = data[i * 4];
|
||||
data[i * 4] = data[i * 4 + 2];
|
||||
data[i * 4 + 2] = tmp_val;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (m_premul_alpha && !m_single_channel)
|
||||
{
|
||||
for (unsigned int i = 0; i < w * h; i++)
|
||||
{
|
||||
float alpha = data[4 * i + 3];
|
||||
if (alpha > 0.0f)
|
||||
alpha = pow(alpha / 255.f, 1.f / 2.2f);
|
||||
data[i * 4] = (uint8_t)(data[i * 4] * alpha);
|
||||
data[i * 4 + 1] = (uint8_t)(data[i * 4 + 1] * alpha);
|
||||
data[i * 4 + 2] = (uint8_t)(data[i * 4 + 2] * alpha);
|
||||
}
|
||||
}
|
||||
if (!useThreadedLoading())
|
||||
formatConversion(data, &format, w, h);
|
||||
|
||||
if (!no_upload)
|
||||
{
|
||||
@ -249,14 +253,14 @@ void STKTexture::reload(bool no_upload, uint8_t* preload_data,
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, w, h, 0, format,
|
||||
GL_UNSIGNED_BYTE, data);
|
||||
}
|
||||
else
|
||||
else if (!useThreadedLoading())
|
||||
{
|
||||
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, format,
|
||||
GL_UNSIGNED_BYTE, data);
|
||||
}
|
||||
if (orig_img)
|
||||
orig_img->unlock();
|
||||
if (hasMipMaps())
|
||||
if (hasMipMaps() && !useThreadedLoading())
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
}
|
||||
|
||||
@ -273,54 +277,68 @@ void STKTexture::reload(bool no_upload, uint8_t* preload_data,
|
||||
if (!no_upload)
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
irr_driver->getDevice()->getLogger()->setLogLevel(ELL_WARNING);
|
||||
#endif // !SERVER_ONLY
|
||||
} // reload
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void STKTexture::formatConversion(uint8_t* data, unsigned int* format,
|
||||
unsigned int w, unsigned int h) const
|
||||
{
|
||||
#if defined(USE_GLES2)
|
||||
if (!CVS->isEXTTextureFormatBGRA8888Usable() && !m_single_channel)
|
||||
{
|
||||
if (format)
|
||||
*format = GL_RGBA;
|
||||
for (unsigned int i = 0; i < w * h; i++)
|
||||
{
|
||||
uint8_t tmp_val = data[i * 4];
|
||||
data[i * 4] = data[i * 4 + 2];
|
||||
data[i * 4 + 2] = tmp_val;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (m_premul_alpha && !m_single_channel)
|
||||
{
|
||||
for (unsigned int i = 0; i < w * h; i++)
|
||||
{
|
||||
float alpha = data[4 * i + 3];
|
||||
if (alpha > 0.0f)
|
||||
alpha = pow(alpha / 255.f, 1.f / 2.2f);
|
||||
data[i * 4] = (uint8_t)(data[i * 4] * alpha);
|
||||
data[i * 4 + 1] = (uint8_t)(data[i * 4 + 1] * alpha);
|
||||
data[i * 4 + 2] = (uint8_t)(data[i * 4 + 2] * alpha);
|
||||
}
|
||||
}
|
||||
} // formatConversion
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
video::IImage* STKTexture::resizeImage(video::IImage* orig_img,
|
||||
core::dimension2du* new_img_size,
|
||||
core::dimension2du* new_tex_size)
|
||||
core::dimension2du* orig_size,
|
||||
core::dimension2du* final_size) const
|
||||
{
|
||||
video::IImage* image = orig_img;
|
||||
#ifndef SERVER_ONLY
|
||||
const core::dimension2du& old_size = image->getDimension();
|
||||
core::dimension2du img_size = old_size;
|
||||
|
||||
const float ratio = float(img_size.Width) / float(img_size.Height);
|
||||
const unsigned int drv_max_size =
|
||||
irr_driver->getVideoDriver()->getMaxTextureSize().Width;
|
||||
|
||||
if ((img_size.Width > drv_max_size) && (ratio >= 1.0f))
|
||||
{
|
||||
img_size.Width = drv_max_size;
|
||||
img_size.Height = (unsigned)(drv_max_size / ratio);
|
||||
}
|
||||
else if (img_size.Height > drv_max_size)
|
||||
{
|
||||
img_size.Height = drv_max_size;
|
||||
img_size.Width = (unsigned)(drv_max_size * ratio);
|
||||
}
|
||||
|
||||
if (img_size != old_size)
|
||||
{
|
||||
video::IImage* new_img = irr_driver->getVideoDriver()
|
||||
->createImage(video::ECF_A8R8G8B8, img_size);
|
||||
image->copyToScaling(new_img);
|
||||
image->drop();
|
||||
image = new_img;
|
||||
}
|
||||
|
||||
if (image == NULL)
|
||||
assert(orig_size && orig_size->Width > 0 && orig_size->Height > 0);
|
||||
core::dimension2du img_size = image ? image->getDimension() : *orig_size;
|
||||
core::dimension2du tex_size = img_size.getOptimalSize
|
||||
(!irr_driver->getVideoDriver()->queryFeature(video::EVDF_TEXTURE_NPOT));
|
||||
const core::dimension2du& max_size = irr_driver->getVideoDriver()
|
||||
->getDriverAttributes().getAttributeAsDimension2d("MAX_TEXTURE_SIZE");
|
||||
|
||||
if (max_size.Width > 0 && tex_size.Width > max_size.Width)
|
||||
if (tex_size.Width > max_size.Width)
|
||||
tex_size.Width = max_size.Width;
|
||||
if (max_size.Height > 0 && tex_size.Height > max_size.Height)
|
||||
if (tex_size.Height > max_size.Height)
|
||||
tex_size.Height = max_size.Height;
|
||||
|
||||
if (orig_size && final_size)
|
||||
{
|
||||
*orig_size = img_size;
|
||||
*final_size = tex_size;
|
||||
}
|
||||
if (image == NULL)
|
||||
return NULL;
|
||||
|
||||
if (image->getColorFormat() != video::ECF_A8R8G8B8 ||
|
||||
tex_size != img_size)
|
||||
{
|
||||
@ -334,11 +352,6 @@ video::IImage* STKTexture::resizeImage(video::IImage* orig_img,
|
||||
image = new_texture;
|
||||
}
|
||||
|
||||
if (new_img_size && new_tex_size)
|
||||
{
|
||||
*new_img_size = img_size;
|
||||
*new_tex_size = tex_size;
|
||||
}
|
||||
#endif // !SERVER_ONLY
|
||||
return image;
|
||||
} // resizeImage
|
||||
@ -548,3 +561,50 @@ void STKTexture::unloadHandle()
|
||||
}
|
||||
#endif
|
||||
} // unloadHandle
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
bool STKTexture::useThreadedLoading() const
|
||||
{
|
||||
#ifdef SERVER_ONLY
|
||||
return false;
|
||||
#else
|
||||
return CVS->supportsThreadedTextureLoading() &&
|
||||
!CVS->isTextureCompressionEnabled() && m_mesh_texture &&
|
||||
m_img_loader && m_img_loader->supportThreadedLoading();
|
||||
#endif
|
||||
} // useThreadedLoading
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void STKTexture::threadedReload(void* ptr) const
|
||||
{
|
||||
video::IImage* orig_img =
|
||||
m_img_loader->loadImage(m_file, true/*skip_checking*/);
|
||||
orig_img = resizeImage(orig_img);
|
||||
uint8_t* data = (uint8_t*)orig_img->lock();
|
||||
if (m_single_channel)
|
||||
{
|
||||
data = singleChannelConversion(data);
|
||||
orig_img->unlock();
|
||||
orig_img->drop();
|
||||
orig_img = NULL;
|
||||
}
|
||||
formatConversion(data, NULL, m_size.Width, m_size.Height);
|
||||
memcpy(ptr, data, m_texture_size);
|
||||
|
||||
if (orig_img)
|
||||
{
|
||||
orig_img->unlock();
|
||||
orig_img->drop();
|
||||
}
|
||||
else
|
||||
delete[] data;
|
||||
} // threadedReload
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
void STKTexture::cleanThreadedLoader()
|
||||
{
|
||||
assert(m_file);
|
||||
m_file->drop();
|
||||
m_file = NULL;
|
||||
m_img_loader = NULL;
|
||||
} // cleanThreadedLoader
|
||||
|
@ -24,6 +24,12 @@
|
||||
#include <string>
|
||||
#include <ITexture.h>
|
||||
|
||||
namespace irr
|
||||
{
|
||||
namespace io { class IReadFile; }
|
||||
namespace video { class IImageLoader; }
|
||||
}
|
||||
|
||||
using namespace irr;
|
||||
|
||||
class Material;
|
||||
@ -45,16 +51,31 @@ private:
|
||||
|
||||
video::IImage* m_texture_image;
|
||||
|
||||
io::IReadFile* m_file;
|
||||
|
||||
video::IImageLoader* m_img_loader;
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
video::IImage* resizeImage(video::IImage* orig_img,
|
||||
core::dimension2du* new_img_size = NULL,
|
||||
core::dimension2du* new_tex_size = NULL);
|
||||
core::dimension2du* orig_size = NULL,
|
||||
core::dimension2du* final_size = NULL) const;
|
||||
// ------------------------------------------------------------------------
|
||||
void applyMask(video::IImage* orig_img);
|
||||
// ------------------------------------------------------------------------
|
||||
bool loadCompressedTexture(const std::string& file_name);
|
||||
// ------------------------------------------------------------------------
|
||||
void saveCompressedTexture(const std::string& file_name);
|
||||
// ------------------------------------------------------------------------
|
||||
void formatConversion(uint8_t* data, unsigned int* format, unsigned int w,
|
||||
unsigned int h) const;
|
||||
// ------------------------------------------------------------------------
|
||||
uint8_t* singleChannelConversion(uint8_t* data) const
|
||||
{
|
||||
uint8_t* sc = new uint8_t[m_size.Width * m_size.Height];
|
||||
for (unsigned int i = 0; i < m_size.Width * m_size.Height; i++)
|
||||
sc[i] = data[4 * i + 3];
|
||||
return sc;
|
||||
}
|
||||
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
@ -114,6 +135,8 @@ public:
|
||||
// ------------------------------------------------------------------------
|
||||
bool isMeshTexture() const { return m_mesh_texture; }
|
||||
// ------------------------------------------------------------------------
|
||||
bool isSingleChannel() const { return m_single_channel; }
|
||||
// ------------------------------------------------------------------------
|
||||
void setMeshTexture(bool val) { m_mesh_texture = val; }
|
||||
// ------------------------------------------------------------------------
|
||||
unsigned int getTextureSize() const { return m_texture_size; }
|
||||
@ -122,6 +145,12 @@ public:
|
||||
video::IImage* preload_img = NULL);
|
||||
// ------------------------------------------------------------------------
|
||||
video::IImage* getTextureImage() { return m_texture_image; }
|
||||
// ------------------------------------------------------------------------
|
||||
bool useThreadedLoading() const;
|
||||
// ------------------------------------------------------------------------
|
||||
void threadedReload(void* ptr) const;
|
||||
// ------------------------------------------------------------------------
|
||||
void cleanThreadedLoader();
|
||||
|
||||
}; // STKTexture
|
||||
|
||||
|
59
src/graphics/threaded_tex_loader.cpp
Normal file
59
src/graphics/threaded_tex_loader.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2017 SuperTuxKart-Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#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"
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
void* ThreadedTexLoader::startRoutine(void *obj)
|
||||
{
|
||||
ThreadedTexLoader* ttl = (ThreadedTexLoader*)obj;
|
||||
STKTexManager* stktm = ttl->m_stktm;
|
||||
while (!ttl->m_destroy)
|
||||
{
|
||||
bool empty = stktm->isThreadedLoaderEmpty();
|
||||
while (!empty)
|
||||
{
|
||||
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++;
|
||||
}
|
||||
StkTime::sleep(50);
|
||||
}
|
||||
return NULL;
|
||||
} // startRoutine
|
75
src/graphics/threaded_tex_loader.hpp
Normal file
75
src/graphics/threaded_tex_loader.hpp
Normal file
@ -0,0 +1,75 @@
|
||||
// SuperTuxKart - a fun racing game with go-kart
|
||||
// Copyright (C) 2017 SuperTuxKart-Team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU General Public License
|
||||
// as published by the Free Software Foundation; either version 3
|
||||
// of the License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program; if not, write to the Free Software
|
||||
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
|
||||
#ifndef HEADER_THREADED_TEX_LOADER_HPP
|
||||
#define HEADER_THREADED_TEX_LOADER_HPP
|
||||
|
||||
#include "utils/no_copy.hpp"
|
||||
#include "utils/synchronised.hpp"
|
||||
#include "utils/types.hpp"
|
||||
|
||||
class STKTexture;
|
||||
class STKTexManager;
|
||||
|
||||
class ThreadedTexLoader : public NoCopy
|
||||
{
|
||||
private:
|
||||
unsigned m_id, m_tex_loaded, m_wait_count;
|
||||
|
||||
bool m_destroy;
|
||||
|
||||
pthread_t m_thread;
|
||||
|
||||
Synchronised<unsigned> m_tex_size_used;
|
||||
|
||||
STKTexManager* m_stktm;
|
||||
|
||||
public:
|
||||
// ------------------------------------------------------------------------
|
||||
static void* startRoutine(void *obj);
|
||||
// ------------------------------------------------------------------------
|
||||
ThreadedTexLoader(unsigned id, STKTexManager* stktm)
|
||||
{
|
||||
m_destroy = false;
|
||||
m_stktm = stktm;
|
||||
m_id = id;
|
||||
reset();
|
||||
pthread_create(&m_thread, NULL, &startRoutine, this);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
~ThreadedTexLoader()
|
||||
{
|
||||
m_destroy = true;
|
||||
pthread_join(m_thread, NULL);
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
void reset()
|
||||
{
|
||||
m_tex_size_used.setAtomic(0);
|
||||
m_tex_loaded = 0;
|
||||
m_wait_count = 0;
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
unsigned getTextureLoaded() const { return m_tex_loaded; }
|
||||
// ------------------------------------------------------------------------
|
||||
bool finishedLoading() const
|
||||
{
|
||||
return m_tex_size_used.getAtomic() > 1024 * 1024 * 4;
|
||||
}
|
||||
}; // ThreadedTexLoader
|
||||
|
||||
#endif
|
@ -25,6 +25,7 @@
|
||||
#include "config/user_config.hpp"
|
||||
#include "graphics/irr_driver.hpp"
|
||||
#include "graphics/material_manager.hpp"
|
||||
#include "graphics/stk_tex_manager.hpp"
|
||||
#include "guiengine/engine.hpp"
|
||||
#include "input/input_manager.hpp"
|
||||
#include "input/wiimote_manager.hpp"
|
||||
@ -264,6 +265,8 @@ void MainLoop::run()
|
||||
GUIEngine::update(dt);
|
||||
PROFILER_POP_CPU_MARKER();
|
||||
|
||||
STKTexManager::getInstance()->uploadBatch();
|
||||
|
||||
// Update sfx and music after graphics, so that graphics code
|
||||
// can use as many threads as possible without interfering
|
||||
// with audio
|
||||
|
Loading…
Reference in New Issue
Block a user