mkxp/src/texpool.cpp

222 lines
4.7 KiB
C++
Raw Normal View History

2013-09-01 14:27:21 +00:00
/*
** texpool.cpp
**
** This file is part of mkxp.
**
** Copyright (C) 2013 Jonas Kulla <Nyocurio@gmail.com>
**
** mkxp 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 2 of the License, or
** (at your option) any later version.
**
** mkxp 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 mkxp. If not, see <http://www.gnu.org/licenses/>.
*/
#include "texpool.h"
2013-09-03 13:22:00 +00:00
#include "exception.h"
#include "sharedstate.h"
2013-09-03 13:22:00 +00:00
#include "glstate.h"
#include "boost-hash.h"
#include "debugwriter.h"
2013-09-01 14:27:21 +00:00
#include <list>
#include <utility>
#include <assert.h>
#include <string.h>
2013-09-01 14:27:21 +00:00
typedef std::pair<uint16_t, uint16_t> Size;
2013-09-01 14:27:21 +00:00
2013-09-04 11:30:14 +00:00
static uint32_t byteCount(Size &s)
2013-09-01 14:27:21 +00:00
{
return s.first * s.second * 4;
}
struct CacheNode
2013-09-01 14:27:21 +00:00
{
2013-09-06 10:26:41 +00:00
TEXFBO obj;
std::list<TEXFBO>::iterator prioIter;
2013-09-01 14:27:21 +00:00
bool operator==(const CacheNode &o) const
2013-09-01 14:27:21 +00:00
{
return obj == o.obj;
}
};
typedef std::list<CacheNode> CNodeList;
2013-09-01 14:27:21 +00:00
struct TexPoolPrivate
{
/* Contains all cached TexFBOs, grouped by size */
BoostHash<Size, CNodeList> poolHash;
2013-09-01 14:27:21 +00:00
/* Contains all cached TexFBOs, sorted by release time */
std::list<TEXFBO> priorityQueue;
2013-09-01 14:27:21 +00:00
/* Maximal allowed cache memory */
2013-09-04 11:30:14 +00:00
const uint32_t maxMemSize;
2013-09-01 14:27:21 +00:00
/* Current amound of memory consumed by the cache */
2013-09-04 11:30:14 +00:00
uint32_t memSize;
2013-09-01 14:27:21 +00:00
/* Current amount of TexFBOs cached */
2013-09-04 11:30:14 +00:00
uint16_t objCount;
2013-09-01 14:27:21 +00:00
/* Has this pool been disabled? */
bool disabled;
2013-09-04 11:30:14 +00:00
TexPoolPrivate(uint32_t maxMemSize)
2013-09-01 14:27:21 +00:00
: maxMemSize(maxMemSize),
memSize(0),
objCount(0),
disabled(false)
{}
};
2013-09-04 11:30:14 +00:00
TexPool::TexPool(uint32_t maxMemSize)
2013-09-01 14:27:21 +00:00
{
p = new TexPoolPrivate(maxMemSize);
}
TexPool::~TexPool()
{
std::list<TEXFBO>::iterator iter;
for (iter = p->priorityQueue.begin();
iter != p->priorityQueue.end();
++iter)
2013-09-01 14:27:21 +00:00
{
TEXFBO obj = *iter;
2013-09-06 10:26:41 +00:00
TEXFBO::fini(obj);
2013-09-01 14:27:21 +00:00
--p->objCount;
}
assert(p->objCount == 0);
2013-09-01 14:27:21 +00:00
delete p;
}
2013-09-06 10:26:41 +00:00
TEXFBO TexPool::request(int width, int height)
2013-09-01 14:27:21 +00:00
{
CacheNode cnode;
2013-09-01 14:27:21 +00:00
Size size(width, height);
/* See if we can statisfy request from cache */
CNodeList &bucket = p->poolHash[size];
2013-09-01 14:27:21 +00:00
if (!bucket.empty())
2013-09-01 14:27:21 +00:00
{
/* Found one! */
cnode = bucket.back();
bucket.pop_back();
2013-09-01 14:27:21 +00:00
p->priorityQueue.erase(cnode.prioIter);
2013-09-01 14:27:21 +00:00
p->memSize -= byteCount(size);
--p->objCount;
// Debug() << "TexPool: <?+> (" << width << height << ")";
2013-09-01 14:27:21 +00:00
return cnode.obj;
2013-09-01 14:27:21 +00:00
}
2013-09-03 13:22:00 +00:00
int maxSize = glState.caps.maxTexSize;
if (width > maxSize || height > maxSize)
{
char buffer[128];
snprintf(buffer, sizeof(buffer),
"Texture dimensions [%d, %d] exceed hardware capabilities",
width, height);
throw Exception(Exception::MKXPError, buffer);
}
2013-09-01 14:27:21 +00:00
/* Nope, create it instead */
TEXFBO::init(cnode.obj);
TEXFBO::allocEmpty(cnode.obj, width, height);
TEXFBO::linkFBO(cnode.obj);
2013-09-01 14:27:21 +00:00
// Debug() << "TexPool: <?-> (" << width << height << ")";
2013-09-01 14:27:21 +00:00
return cnode.obj;
2013-09-01 14:27:21 +00:00
}
2013-09-06 10:26:41 +00:00
void TexPool::release(TEXFBO &obj)
2013-09-01 14:27:21 +00:00
{
2013-09-06 10:26:41 +00:00
if (obj.tex == TEX::ID(0) || obj.fbo == FBO::ID(0))
2013-09-01 14:27:21 +00:00
{
2013-09-06 10:26:41 +00:00
TEXFBO::fini(obj);
2013-09-01 14:27:21 +00:00
return;
}
if (p->disabled)
{
/* If we're disabled, delete without caching */
// Debug() << "TexPool: <!#> (" << obj.width << obj.height << ")";
2013-09-06 10:26:41 +00:00
TEXFBO::fini(obj);
2013-09-01 14:27:21 +00:00
return;
}
Size size(obj.width, obj.height);
2013-09-04 11:30:14 +00:00
uint32_t newMemSize = p->memSize + byteCount(size);
2013-09-01 14:27:21 +00:00
/* If caching this object would spill over the allowed memory budget,
* delete least used objects until we're good again */
while (newMemSize > p->maxMemSize)
{
if (p->objCount == 0)
break;
// Debug() << "TexPool: <!~> Size:" << p->memSize;
2013-09-01 14:27:21 +00:00
/* Retrieve object with lowest priority for deletion */
CacheNode last;
last.obj = p->priorityQueue.back();
2013-09-01 14:27:21 +00:00
Size removedSize(last.obj.width, last.obj.height);
CNodeList &bucket = p->poolHash[removedSize];
std::list<CacheNode>::iterator toRemove =
std::find(bucket.begin(), bucket.end(), last);
assert(toRemove != bucket.end());
bucket.erase(toRemove);
p->priorityQueue.pop_back();
2013-09-01 14:27:21 +00:00
2013-09-06 10:26:41 +00:00
TEXFBO::fini(last.obj);
2013-09-01 14:27:21 +00:00
newMemSize -= byteCount(removedSize);
2013-09-01 14:27:21 +00:00
--p->objCount;
// Debug() << "TexPool: <!-> (" << last.obj.width << last.obj.height << ")";
2013-09-01 14:27:21 +00:00
}
2013-11-23 11:40:23 +00:00
p->memSize = newMemSize;
2013-09-01 14:27:21 +00:00
/* Retain object */
p->priorityQueue.push_front(obj);
CacheNode cnode;
cnode.obj = obj;
cnode.prioIter = p->priorityQueue.begin();
CNodeList &bucket = p->poolHash[size];
bucket.push_back(cnode);
2013-09-01 14:27:21 +00:00
++p->objCount;
// Debug() << "TexPool: <!+> (" << obj.width << obj.height << ") Current size:" << p->memSize;
2013-09-01 14:27:21 +00:00
}
void TexPool::disable()
{
p->disabled = true;
}