diff --git a/src/framework/core/filestream.cpp b/src/framework/core/filestream.cpp index de92dc9a..d2f04e8b 100644 --- a/src/framework/core/filestream.cpp +++ b/src/framework/core/filestream.cpp @@ -28,6 +28,7 @@ FileStream::FileStream(const std::string& name, PHYSFS_File *fileHandle) { m_name = name; m_fileHandle = fileHandle; + m_cacheReadPos = 0; } FileStream::~FileStream() @@ -35,6 +36,24 @@ FileStream::~FileStream() close(); } +void FileStream::cache() +{ + if(!m_fileHandle) + logTraceError("no file handle to cache"); + + // cache entire file into cache buffer + m_cacheReadPos = PHYSFS_tell(m_fileHandle); + PHYSFS_seek(m_fileHandle, 0); + int size = PHYSFS_fileLength(m_fileHandle); + m_cacheBuffer.resize(size); + int res = PHYSFS_read(m_fileHandle, &m_cacheBuffer[0], size, 1); + if(res == -1) + logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); + + PHYSFS_close(m_fileHandle); + m_fileHandle = nullptr; +} + bool FileStream::close() { if(m_fileHandle) { @@ -43,12 +62,18 @@ bool FileStream::close() m_fileHandle = nullptr; return true; + } else { + m_cacheBuffer.clear(); + m_cacheReadPos = 0; + return true; } - return false; } bool FileStream::flush() { + if(!m_fileHandle) + return false; + if(PHYSFS_flush(m_fileHandle) == 0) { logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); return false; @@ -58,71 +83,143 @@ bool FileStream::flush() int FileStream::read(void *buffer, int size, int nmemb) { - int res = PHYSFS_read(m_fileHandle, buffer, size, nmemb); - if(res == -1) { - logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); - return 0; + if(m_fileHandle) { + int res = PHYSFS_read(m_fileHandle, buffer, size, nmemb); + if(res == -1) { + logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); + return 0; + } + return res; + } else { + uint maxReadPos = m_cacheBuffer.size()-1; + int writePos = 0; + uint8 *outBuffer = (uint8*)buffer; + for(int i=0;i maxReadPos) + return i; + + for(int j=0;j (int)m_cacheBuffer.size() || pos < 0) { + logTraceError("operation failed on '", m_name, "': seek pos cannot be greater than file length"); + return false; + } + m_cacheReadPos = pos; } return true; } int FileStream::size() { - return PHYSFS_fileLength(m_fileHandle); + if(m_fileHandle) + return PHYSFS_fileLength(m_fileHandle); + else + return m_cacheBuffer.size(); } int FileStream::tell() { - return PHYSFS_tell(m_fileHandle); + if(m_fileHandle) + return PHYSFS_tell(m_fileHandle); + else + return m_cacheReadPos; } uint8 FileStream::getU8() { uint8 v = 0; - if(PHYSFS_read(m_fileHandle, &v, 1, 1) != 1) - logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); + if(m_fileHandle) { + if(PHYSFS_read(m_fileHandle, &v, 1, 1) != 1) + logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); + } else { + if(m_cacheReadPos+1 >= m_cacheBuffer.size()) { + logTraceError("operation failed on '", m_name, "': reached file eof"); + return 0; + } + + v = m_cacheBuffer[m_cacheReadPos]; + m_cacheReadPos += 1; + } return v; } uint16 FileStream::getU16() { uint16 v = 0; - if(PHYSFS_readULE16(m_fileHandle, &v) == 0) - logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); + if(m_fileHandle) { + if(PHYSFS_readULE16(m_fileHandle, &v) == 0) + logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); + } else { + if(m_cacheReadPos+2 >= m_cacheBuffer.size()) { + logTraceError("operation failed on '", m_name, "': reached file eof"); + return 0; + } + + v = Fw::readLE16(&m_cacheBuffer[m_cacheReadPos]); + m_cacheReadPos += 2; + } return v; } uint32 FileStream::getU32() { uint32 v = 0; - if(PHYSFS_readULE32(m_fileHandle, &v) == 0) - logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); + if(m_fileHandle) { + if(PHYSFS_readULE32(m_fileHandle, &v) == 0) + logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); + } else { + if(m_cacheReadPos+4 >= m_cacheBuffer.size()) { + logTraceError("operation failed on '", m_name, "': reached file eof"); + return 0; + } + + v = Fw::readLE32(&m_cacheBuffer[m_cacheReadPos]); + m_cacheReadPos += 4; + } return v; } uint64 FileStream::getU64() { uint64 v = 0; - if(PHYSFS_readULE64(m_fileHandle, (PHYSFS_uint64*)&v) == 0) - logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); + if(m_fileHandle) { + if(PHYSFS_readULE64(m_fileHandle, (PHYSFS_uint64*)&v) == 0) + logTraceError("operation failed on '", m_name, "': ", PHYSFS_getLastError()); + } else { + if(m_cacheReadPos+8 >= m_cacheBuffer.size()) { + logTraceError("operation failed on '", m_name, "': reached file eof"); + return 0; + } + + v = Fw::readLE64(&m_cacheBuffer[m_cacheReadPos]); + m_cacheReadPos += 8; + } return v; } diff --git a/src/framework/core/filestream.h b/src/framework/core/filestream.h index 93bb6b6e..2510f1c2 100644 --- a/src/framework/core/filestream.h +++ b/src/framework/core/filestream.h @@ -37,6 +37,7 @@ protected: public: ~FileStream(); + void cache(); bool close(); bool flush(); bool write(void *buffer, int count); @@ -60,6 +61,9 @@ public: private: std::string m_name; PHYSFS_File *m_fileHandle; + + std::vector m_cacheBuffer; + uint m_cacheReadPos; }; #endif diff --git a/src/framework/graphics/coordsbuffer.cpp b/src/framework/graphics/coordsbuffer.cpp index d802c0fa..17043a52 100644 --- a/src/framework/graphics/coordsbuffer.cpp +++ b/src/framework/graphics/coordsbuffer.cpp @@ -100,7 +100,13 @@ void CoordsBuffer::updateCaches() if(!m_hardwareCaching || m_hardwareCached) return; - if(m_vertexArray.vertexCount() > 0) { + int vertexCount = m_vertexArray.vertexCount(); + + // there is only performance improvement when caching a lot of vertices + if(vertexCount < CACHE_MIN_VERTICES_COUNT) + return; + + if(vertexCount > 0) { if(!m_hardwareVertexArray && m_vertexArray.vertexCount() > 0) m_hardwareVertexArray = new HardwareBuffer(HardwareBuffer::VertexBuffer); m_hardwareVertexArray->bind(); diff --git a/src/framework/graphics/coordsbuffer.h b/src/framework/graphics/coordsbuffer.h index c65ca780..72940ca1 100644 --- a/src/framework/graphics/coordsbuffer.h +++ b/src/framework/graphics/coordsbuffer.h @@ -28,6 +28,9 @@ class CoordsBuffer { + enum { + CACHE_MIN_VERTICES_COUNT = 48 + }; public: CoordsBuffer(); ~CoordsBuffer(); diff --git a/src/framework/graphics/painter.cpp b/src/framework/graphics/painter.cpp index b6e84ad1..f68a7ee9 100644 --- a/src/framework/graphics/painter.cpp +++ b/src/framework/graphics/painter.cpp @@ -104,25 +104,27 @@ void Painter::setTexture(Texture* texture) m_texture = texture; + GLuint glTextureId; if(texture) { setTextureMatrix(texture->getTransformMatrix()); + glTextureId = texture->getId(); + } else + glTextureId = 0; - GLuint glTextureId = texture->getId(); - if(glTextureId != m_glTextureId) { - m_glTextureId = glTextureId; - updateGlTexture(); - } + if(m_glTextureId != glTextureId) { + m_glTextureId = glTextureId; + updateGlTexture(); } } void Painter::updateGlTexture() { - glBindTexture(GL_TEXTURE_2D, m_glTextureId); + if(m_glTextureId != 0) + glBindTexture(GL_TEXTURE_2D, m_glTextureId); } void Painter::updateGlCompositionMode() { - glEnable(GL_BLEND); switch(m_compositionMode) { case CompositionMode_Normal: glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); diff --git a/src/framework/graphics/painterogl1.cpp b/src/framework/graphics/painterogl1.cpp index cd766baf..ba58def6 100644 --- a/src/framework/graphics/painterogl1.cpp +++ b/src/framework/graphics/painterogl1.cpp @@ -62,7 +62,12 @@ void PainterOGL1::drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode) if(vertexCount == 0) return; - bool textured = coordsBuffer.getTextureCoordCount() != 0; + bool textured = coordsBuffer.getTextureCoordCount() != 0 && m_texture; + + // skip drawing of empty textures + if(textured && m_texture->isEmpty()) + return; + if(textured != m_textureEnabled) { if(textured) glEnable(GL_TEXTURE_2D); @@ -121,7 +126,7 @@ void PainterOGL1::drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode) void PainterOGL1::drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr& texture) { - if(!texture->getId()) + if(texture->isEmpty()) return; setTexture(texture.get()); @@ -130,7 +135,7 @@ void PainterOGL1::drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr void PainterOGL1::drawTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src) { - if(dest.isEmpty() || src.isEmpty() || !texture->getId()) + if(dest.isEmpty() || src.isEmpty() || texture->isEmpty()) return; setTexture(texture.get()); @@ -142,7 +147,7 @@ void PainterOGL1::drawTexturedRect(const Rect& dest, const TexturePtr& texture, void PainterOGL1::drawRepeatedTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src) { - if(dest.isEmpty() || src.isEmpty() || !texture->getId()) + if(dest.isEmpty() || src.isEmpty() || texture->isEmpty()) return; setTexture(texture.get()); diff --git a/src/framework/graphics/painterogl2.cpp b/src/framework/graphics/painterogl2.cpp index 51b9cdcb..d9140de9 100644 --- a/src/framework/graphics/painterogl2.cpp +++ b/src/framework/graphics/painterogl2.cpp @@ -62,7 +62,11 @@ void PainterOGL2::drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode) if(vertexCount == 0) return; - bool textured = coordsBuffer.getTextureCoordCount() > 0; + bool textured = coordsBuffer.getTextureCoordCount() > 0 && m_texture; + + // skip drawing of empty textures + if(textured && m_texture->isEmpty()) + return; // update shader with the current painter state m_drawProgram->bind(); @@ -100,7 +104,7 @@ void PainterOGL2::drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode) void PainterOGL2::drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr& texture) { - if(!texture->getId()) + if(texture->isEmpty()) return; setDrawProgram(m_shaderProgram ? m_shaderProgram : g_shaders.getDrawTexturedProgram().get()); @@ -110,7 +114,7 @@ void PainterOGL2::drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr void PainterOGL2::drawTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src) { - if(dest.isEmpty() || src.isEmpty() || !texture->getId()) + if(dest.isEmpty() || src.isEmpty() || texture->isEmpty()) return; setDrawProgram(m_shaderProgram ? m_shaderProgram : g_shaders.getDrawTexturedProgram().get()); @@ -123,7 +127,7 @@ void PainterOGL2::drawTexturedRect(const Rect& dest, const TexturePtr& texture, void PainterOGL2::drawRepeatedTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src) { - if(dest.isEmpty() || src.isEmpty() || !texture->getId()) + if(dest.isEmpty() || src.isEmpty() || texture->isEmpty()) return; setDrawProgram(m_shaderProgram ? m_shaderProgram : g_shaders.getDrawTexturedProgram().get()); diff --git a/src/framework/graphics/texture.cpp b/src/framework/graphics/texture.cpp index 0e538189..4ac366ad 100644 --- a/src/framework/graphics/texture.cpp +++ b/src/framework/graphics/texture.cpp @@ -179,10 +179,12 @@ void Texture::generateSoftwareMipmaps(std::vector inPixels) Size inSize = getSize(); Size outSize = inSize / 2; - std::vector outPixels(outSize.area()*4); + std::vector outPixels; int mipmap = 1; while(true) { + outPixels.resize(outSize.area()*4); + // this is a simple bilinear filtering algorithm, it combines every 4 pixels in one pixel for(int x=0;xcache(); + char magic[4]; file->read(magic, 4); file->seek(0); diff --git a/src/otclient/core/spritemanager.cpp b/src/otclient/core/spritemanager.cpp index c0411ad7..d5bd48c3 100644 --- a/src/otclient/core/spritemanager.cpp +++ b/src/otclient/core/spritemanager.cpp @@ -41,6 +41,9 @@ bool SpriteManager::load(const std::string& file) if(!m_spritesFile) return false; + // cache file buffer to avoid lags from hard drive + m_spritesFile->cache(); + m_signature = m_spritesFile->getU32(); m_spritesCount = m_spritesFile->getU16(); m_sprites.resize(m_spritesCount);