First version of ThreadedTexLoader

This commit is contained in:
Benau 2017-03-08 15:54:59 +08:00
parent 35139877f6
commit d864995c7d
20 changed files with 541 additions and 125 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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()
{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View 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

View File

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