/*
This file is part of jpcsp.
Jpcsp 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.
Jpcsp 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 Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.graphics;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import jpcsp.graphics.RE.IRenderingEngine;
import jpcsp.util.CacheStatistics;
import jpcsp.util.DurationStatistics;
public class VertexCache {
public static final int cacheMaxSize = 30000;
public static final float cacheLoadFactor = 0.75f;
protected static VertexCache instance = null;
private LinkedHashMap<Integer, VertexInfo> cache;
protected CacheStatistics statistics = new CacheStatistics("Vertex", cacheMaxSize);
// Remember which vertex have already been checked during one display
// (for applications reusing the same vertex multiple times in one display)
private Set<Integer> vertexAlreadyChecked;
public static VertexCache getInstance() {
if (instance == null) {
instance = new VertexCache();
}
return instance;
}
protected VertexCache() {
//
// Create a cache having
// - initial size large enough so that no rehash will occur
// - the LinkedList is based on access-order for LRU
//
cache = new LinkedHashMap<Integer, VertexInfo>((int) (cacheMaxSize / cacheLoadFactor) + 1, cacheLoadFactor, true);
vertexAlreadyChecked = new HashSet<Integer>();
}
public void exit() {
if (DurationStatistics.collectStatistics) {
VideoEngine.log.info(statistics);
}
}
private static Integer getKey(VertexInfo vertexInfo) {
return new Integer(vertexInfo.ptr_vertex + vertexInfo.ptr_index);
}
public boolean hasVertex(VertexInfo vertexInfo) {
return cache.containsKey(getKey(vertexInfo));
}
protected synchronized VertexInfo getVertex(VertexInfo vertexInfo) {
return cache.get(getKey(vertexInfo));
}
public synchronized void addVertex(IRenderingEngine re, VertexInfo vertexInfo, int numberOfVertex, float[][] boneMatrix, int numberOfWeightsForShader) {
Integer key = getKey(vertexInfo);
VertexInfo previousVertex = cache.get(key);
if (previousVertex != null) {
vertexInfo.reuseCachedBuffer(previousVertex);
previousVertex.deleteVertex(re);
} else {
// Check if the cache is not growing too large
if (cache.size() >= cacheMaxSize) {
// Remove the LRU cache entry
Iterator<Map.Entry<Integer, VertexInfo>> it = cache.entrySet().iterator();
if (it.hasNext()) {
Map.Entry<Integer, VertexInfo> entry = it.next();
entry.getValue().deleteVertex(re);
it.remove();
statistics.entriesRemoved++;
}
}
}
vertexInfo.prepareForCache(this, numberOfVertex, boneMatrix, numberOfWeightsForShader);
cache.put(key, vertexInfo);
if (cache.size() > statistics.maxSizeUsed) {
statistics.maxSizeUsed = cache.size();
}
}
public VertexInfo getVertex(VertexInfo vertexInfo, int numberOfVertex, float[][] boneMatrix, int numberOfWeightsForShader) {
statistics.totalHits++;
VertexInfo vertex = getVertex(vertexInfo);
if (vertex == null) {
statistics.notPresentHits++;
return vertex;
}
if (vertex.equals(vertexInfo, numberOfVertex, boneMatrix, numberOfWeightsForShader)) {
statistics.successfulHits++;
return vertex;
}
statistics.changedHits++;
return null;
}
public void resetVertexAlreadyChecked() {
vertexAlreadyChecked.clear();
}
public boolean vertexAlreadyChecked(VertexInfo vertexInfo) {
return vertexAlreadyChecked.contains(getKey(vertexInfo));
}
public void setVertexAlreadyChecked(VertexInfo vertexInfo) {
vertexAlreadyChecked.add(getKey(vertexInfo));
}
public synchronized void reset(IRenderingEngine re) {
for (VertexInfo vertexInfo : cache.values()) {
vertexInfo.deleteVertex(re);
}
cache.clear();
}
}