3 |
package gzz.mem; |
package gzz.mem; |
4 |
import gzz.Obs; |
import gzz.Obs; |
5 |
|
|
6 |
|
import java.util.*; |
7 |
|
|
8 |
|
|
9 |
/** A static API which divides the available memory between |
/** A static API which divides the available memory between |
10 |
* MemoryConsumers. |
* MemoryConsumers. |
15 |
*/ |
*/ |
16 |
public static int memory = 64 * 1024 * 1024; |
public static int memory = 64 * 1024 * 1024; |
17 |
|
|
18 |
|
|
19 |
|
static Stack currentScene = new Stack(); |
20 |
|
static { |
21 |
|
currentScene.push(null); |
22 |
|
} |
23 |
|
|
24 |
|
static public void start(Object id) { |
25 |
|
currentScene.push(id); |
26 |
|
} |
27 |
|
static public void end(Object id) { |
28 |
|
currentScene.pop(); |
29 |
|
} |
30 |
|
|
31 |
static class ConsumerRecord { |
static class ConsumerRecord { |
32 |
// float |
/** The current maximum importance. |
33 |
|
* May be set to -1 at any time by the background thread. |
34 |
|
*/ |
35 |
|
float maxImportance = -1; |
36 |
|
float maxQuality = -1; |
37 |
|
|
38 |
|
/** The filtered importance. |
39 |
|
*/ |
40 |
|
float curImportance = -1; |
41 |
|
float curQuality = -1; |
42 |
|
|
43 |
|
/** The number of bytes requested from the consumer. |
44 |
|
*/ |
45 |
|
int setBytes = 0; |
46 |
|
int setQuality = -1; |
47 |
|
|
48 |
|
/** The number of bytes the consumer reports using currently. |
49 |
|
*/ |
50 |
|
int gotBytes = 0; |
51 |
|
int gotQuality = -1; |
52 |
|
|
53 |
|
Obs o; |
54 |
|
|
55 |
|
/** Update curImportance and curQuality towards the observed |
56 |
|
* values. |
57 |
|
*/ |
58 |
|
void bgUpdate() { |
59 |
|
float i = maxImportance; |
60 |
|
maxImportance = -1; |
61 |
|
float q = maxQuality; |
62 |
|
maxQuality = -1; |
63 |
|
|
64 |
|
if(i < 0) { |
65 |
|
curImportance *= .98; |
66 |
|
} else { |
67 |
|
if(i >= curImportance) |
68 |
|
curImportance = i; |
69 |
|
else { |
70 |
|
curImportance -= .02 * (curImportance - i); |
71 |
|
} |
72 |
|
} |
73 |
|
// We won't decay: will always remain an interpolate |
74 |
|
if(q >= 0) { |
75 |
|
if(q >= curQuality) |
76 |
|
curQuality = q; |
77 |
|
else |
78 |
|
curQuality -= .02 * (curQuality - q); |
79 |
|
} |
80 |
|
} |
81 |
|
|
82 |
|
void update(float importance, float quality, Obs o) { |
83 |
|
if(importance > maxImportance) |
84 |
|
maxImportance = importance; |
85 |
|
if(quality > maxQuality) |
86 |
|
maxQuality = quality; |
87 |
|
this.o = o; |
88 |
|
} |
89 |
|
|
90 |
} |
} |
91 |
|
|
92 |
// Map consumer2 |
static Map consumer2record = Collections.synchronizedMap( |
93 |
|
new WeakHashMap()); |
94 |
|
|
95 |
/** Indicate that a request for the data of the given consumer was made. |
/** Indicate that a request for the data of the given consumer was made. |
96 |
* @param consumer The consumer whose data was requested. |
* @param consumer The consumer whose data was requested. |
99 |
* @param quality An abstract value to be passed to |
* @param quality An abstract value to be passed to |
100 |
* {@link MemoryConsumer#setReservation}. For example, |
* {@link MemoryConsumer#setReservation}. For example, |
101 |
* this could be the DPI resolution of an image. |
* this could be the DPI resolution of an image. |
102 |
|
* 0 = no quality at all, negative values |
103 |
|
* not allowed. |
104 |
* @param o An observer to be called (if non-null) |
* @param o An observer to be called (if non-null) |
105 |
* when the consumer's size is changed the next time. |
* when the consumer's size is changed the next time. |
106 |
* Note that the semantics are different from {@link MemoryConsumer.setReservation}'s |
* Note that the semantics are different from {@link MemoryConsumer.setReservation}'s |
108 |
* This difference is up to MemoryPartitioner to handle internally. |
* This difference is up to MemoryPartitioner to handle internally. |
109 |
* @see MemoryConsumer |
* @see MemoryConsumer |
110 |
*/ |
*/ |
111 |
public void request(MemoryConsumer consumer, float importance, float quality, Obs o) { |
static public void request(MemoryConsumer consumer, float importance, float quality, Obs o) { |
112 |
|
ConsumerRecord rec = (ConsumerRecord)consumer2record.get(consumer); |
113 |
|
if(rec == null) { |
114 |
|
rec = new ConsumerRecord(); |
115 |
|
consumer2record.put(consumer, rec); |
116 |
|
} |
117 |
|
rec.update(importance, quality, o); |
118 |
|
|
119 |
} |
} |
120 |
|
|
121 |
static Thread repartitioner = new Thread() { |
static Thread repartitioner = new Thread() { |
122 |
public void run() { |
public void run() { |
123 |
|
int sum1 = 0; |
124 |
|
int sumReduced = 0; |
125 |
|
for(int round = 0; ; round++) { |
126 |
|
for(Iterator i = consumer2record.keySet().iterator(); i.hasNext(); ) { |
127 |
|
MemoryConsumer cons = (MemoryConsumer)i.next(); |
128 |
|
ConsumerRecord rec = (ConsumerRecord)consumer2record.get(cons); |
129 |
|
if(round == 0) rec.bgUpdate(); |
130 |
|
float imp = 0; |
131 |
|
if(rec.curImportance > 0) |
132 |
|
imp = (float)Math.pow(rec.curImportance, round); |
133 |
|
else if(round == 0) |
134 |
|
imp = 1; |
135 |
|
int byt = cons.getMaxBytes(rec.curQuality); |
136 |
|
if(rec.curImportance == 1) |
137 |
|
sum1 += byt; |
138 |
|
sumReduced += (int)(imp * byt); |
139 |
|
} |
140 |
|
if(sum1 > memory) { |
141 |
|
// Problem! Not enough memory for all 1-importances |
142 |
|
} |
143 |
|
if(sumReduced <= memory) { |
144 |
|
// Hey, now it fits! |
145 |
|
} |
146 |
|
} |
147 |
} |
} |
148 |
}; |
}; |
149 |
} |
} |