Skip to content

Commit 9c3f072

Browse files
author
Jasper Geurtz
committed
initial attempt at remapping
1 parent 7db7bf0 commit 9c3f072

File tree

3 files changed

+352
-212
lines changed

3 files changed

+352
-212
lines changed
Lines changed: 145 additions & 197 deletions
Original file line numberDiff line numberDiff line change
@@ -1,197 +1,145 @@
1-
package bwapi;
2-
3-
import sun.misc.Unsafe;
4-
5-
import java.util.ArrayList;
6-
import java.util.concurrent.locks.Condition;
7-
import java.util.concurrent.locks.Lock;
8-
import java.util.concurrent.locks.ReentrantLock;
9-
10-
/**
11-
* Circular buffer of game states.
12-
*/
13-
class FrameBuffer {
14-
private static final int BUFFER_SIZE = ClientData.GameData.SIZE;
15-
private static final Unsafe unsafe = UnsafeTools.getUnsafe();
16-
17-
private WrappedBuffer liveData;
18-
private PerformanceMetrics performanceMetrics;
19-
private final BWClientConfiguration configuration;
20-
private final int capacity;
21-
private int stepGame = 0;
22-
private int stepBot = 0;
23-
private final ArrayList<WrappedBuffer> dataBuffer = new ArrayList<>();
24-
25-
private final Lock lockWrite = new ReentrantLock();
26-
final Lock lockSize = new ReentrantLock();
27-
final Condition conditionSize = lockSize.newCondition();
28-
29-
FrameBuffer(BWClientConfiguration configuration) {
30-
this.capacity = configuration.getAsyncFrameBufferCapacity();
31-
this.configuration = configuration;
32-
while(dataBuffer.size() < capacity) {
33-
dataBuffer.add(new WrappedBuffer(BUFFER_SIZE));
34-
}
35-
}
36-
37-
/**
38-
* Resets for a new game
39-
*/
40-
void initialize(WrappedBuffer liveData, PerformanceMetrics performanceMetrics) {
41-
this.liveData = liveData;
42-
this.performanceMetrics = performanceMetrics;
43-
stepGame = 0;
44-
stepBot = 0;
45-
}
46-
47-
/**
48-
* @return The number of frames currently buffered ahead of the bot's current frame
49-
*/
50-
synchronized int framesBuffered() {
51-
return stepGame - stepBot;
52-
}
53-
54-
/**
55-
* @return Number of frames currently stored in the buffer
56-
*/
57-
int size() {
58-
lockSize.lock();
59-
try {
60-
return framesBuffered();
61-
} finally {
62-
lockSize.unlock();
63-
}
64-
}
65-
66-
/**
67-
* @return Whether the frame buffer is empty and has no frames available for the bot to consume.
68-
*/
69-
boolean empty() {
70-
return size() <= 0;
71-
}
72-
73-
/**
74-
* @return Whether the frame buffer is full and can not buffer any additional frames.
75-
* When the frame buffer is full, JBWAPI must wait for the bot to complete a frame before returning control to StarCraft.
76-
*/
77-
boolean full() {
78-
lockSize.lock();
79-
try {
80-
return framesBuffered() >= capacity;
81-
} finally {
82-
lockSize.unlock();
83-
}
84-
}
85-
86-
private int indexGame() {
87-
return stepGame % capacity;
88-
}
89-
90-
private int indexBot() {
91-
return stepBot % capacity;
92-
}
93-
94-
/**
95-
* Copy dataBuffer from shared memory into the head of the frame buffer.
96-
*/
97-
void enqueueFrame() {
98-
lockWrite.lock();
99-
try {
100-
lockSize.lock();
101-
try {
102-
while (full()) {
103-
configuration.log("Main: Waiting for frame buffer capacity");
104-
performanceMetrics.getIntentionallyBlocking().startTiming();
105-
conditionSize.awaitUninterruptibly();
106-
}
107-
performanceMetrics.getIntentionallyBlocking().stopTiming();
108-
} finally { lockSize.unlock(); };
109-
110-
// For the first frame of the game, populate all buffers completely
111-
// This is to ensure all buffers have access to immutable data like regions/walkability/buildability
112-
// Afterwards, we want to shorten this process by only copying important and mutable data
113-
if (stepGame == 0) {
114-
for (WrappedBuffer frameBuffer : dataBuffer) {
115-
copyBuffer(liveData, frameBuffer, true);
116-
}
117-
} else {
118-
performanceMetrics.getCopyingToBuffer().time(() -> {
119-
WrappedBuffer dataTarget = dataBuffer.get(indexGame());
120-
copyBuffer(liveData, dataTarget, false);
121-
});
122-
}
123-
124-
lockSize.lock();
125-
try {
126-
performanceMetrics.getFrameBufferSize().record(framesBuffered());
127-
++stepGame;
128-
conditionSize.signalAll();
129-
} finally { lockSize.unlock(); }
130-
} finally { lockWrite.unlock(); }
131-
}
132-
133-
/**
134-
* Peeks the front-most value in the buffer.
135-
*/
136-
WrappedBuffer peek() {
137-
lockSize.lock();
138-
try {
139-
while(empty()) conditionSize.awaitUninterruptibly();
140-
return dataBuffer.get(indexBot());
141-
} finally { lockSize.unlock(); }
142-
}
143-
144-
/**
145-
* Removes the front-most frame in the buffer.
146-
*/
147-
void dequeue() {
148-
lockSize.lock();
149-
try {
150-
while(empty()) conditionSize.awaitUninterruptibly();
151-
++stepBot;
152-
conditionSize.signalAll();
153-
} finally { lockSize.unlock(); }
154-
}
155-
156-
/**
157-
*
158-
* @param source Address to copy from
159-
* @param destination Address to copy to
160-
* @param size Number of bytes to copy
161-
*/
162-
private void copyBuffer(WrappedBuffer source, WrappedBuffer destination, long offset, int size) {
163-
long addressSource = source.getAddress() + offset;
164-
long addressDestination = destination.getAddress() + offset;
165-
unsafe.copyMemory(addressSource, addressDestination, size);
166-
}
167-
168-
void copyBuffer(WrappedBuffer source, WrappedBuffer destination, boolean copyEverything) {
169-
/*
170-
The speed at which we copy data into the frame buffer is a major cost of JBWAPI's asynchronous operation.
171-
Copy times observed in the wild for the complete buffer usually range from 2.6ms - 19ms
172-
but are prone to large amounts of variance.
173-
174-
The normal Java way to execute this copy is via ByteBuffer.put(), which has reasonably good performance characteristics.
175-
*/
176-
177-
if (copyEverything) {
178-
copyBuffer(source, destination, 0, FrameBuffer.BUFFER_SIZE);
179-
} else {
180-
// After the buffer has been filled the first time,
181-
// we can omit copying blocks of data which are unused or which don't change after game start.
182-
// These blocks account for *most* of the 33MB shared memory,
183-
// so omitting them drastically reduces the copy duration
184-
final int STATICTILES_START = 3447004; // getGroundHeight, isWalkable, isBuildable
185-
final int STATICTILES_END = 4823260;
186-
final int REGION_START = 5085404; // getMapTileRegionId, ..., getRegions
187-
final int REGION_END = 10586480;
188-
final int STRINGSSHAPES_START = 10962632; // getStringCount, ... getShapes
189-
final int STRINGSHAPES_END = 32242636;
190-
final int UNITFINDER_START = 32962644;
191-
copyBuffer(source, destination, 0, STATICTILES_START);
192-
copyBuffer(source, destination, STATICTILES_END, REGION_START - STATICTILES_END);
193-
copyBuffer(source, destination, REGION_END, STRINGSSHAPES_START - REGION_END);
194-
copyBuffer(source, destination, STRINGSHAPES_END, UNITFINDER_START - STRINGSHAPES_END);
195-
}
196-
}
197-
}
1+
package bwapi;
2+
3+
import java.util.ArrayList;
4+
import java.util.concurrent.locks.Condition;
5+
import java.util.concurrent.locks.Lock;
6+
import java.util.concurrent.locks.ReentrantLock;
7+
8+
public class FrameBuffer {
9+
private PerformanceMetrics performanceMetrics;
10+
private final BWClientConfiguration configuration;
11+
private final int capacity;
12+
private int stepGame = 0;
13+
private int stepBot = 0;
14+
private final ArrayList<WrappedBufferOffset> dataBuffer = new ArrayList<>();
15+
16+
private final Lock lockWrite = new ReentrantLock();
17+
final Lock lockSize = new ReentrantLock();
18+
final Condition conditionSize = lockSize.newCondition();
19+
20+
FrameBuffer(BWClientConfiguration configuration) {
21+
this.capacity = configuration.getAsyncFrameBufferCapacity();
22+
this.configuration = configuration;
23+
while(dataBuffer.size() < capacity) {
24+
dataBuffer.add(new WrappedBufferOffset());
25+
}
26+
}
27+
28+
/**
29+
* Resets for a new game
30+
*/
31+
void initialize(WrappedBuffer liveData, PerformanceMetrics performanceMetrics) {
32+
this.performanceMetrics = performanceMetrics;
33+
stepGame = 0;
34+
stepBot = 0;
35+
dataBuffer.forEach(b -> b.setSourceBuffer(liveData));
36+
}
37+
38+
/**
39+
* @return The number of frames currently buffered ahead of the bot's current frame
40+
*/
41+
synchronized int framesBuffered() {
42+
return stepGame - stepBot;
43+
}
44+
45+
/**
46+
* @return Number of frames currently stored in the buffer
47+
*/
48+
int size() {
49+
lockSize.lock();
50+
try {
51+
return framesBuffered();
52+
} finally {
53+
lockSize.unlock();
54+
}
55+
}
56+
57+
/**
58+
* @return Whether the frame buffer is empty and has no frames available for the bot to consume.
59+
*/
60+
boolean empty() {
61+
return size() <= 0;
62+
}
63+
64+
/**
65+
* @return Whether the frame buffer is full and can not buffer any additional frames.
66+
* When the frame buffer is full, JBWAPI must wait for the bot to complete a frame before returning control to StarCraft.
67+
*/
68+
boolean full() {
69+
lockSize.lock();
70+
try {
71+
return framesBuffered() >= capacity;
72+
} finally {
73+
lockSize.unlock();
74+
}
75+
}
76+
77+
private int indexGame() {
78+
return stepGame % capacity;
79+
}
80+
81+
private int indexBot() {
82+
return stepBot % capacity;
83+
}
84+
85+
/**
86+
* Copy dataBuffer from shared memory into the head of the frame buffer.
87+
*/
88+
void enqueueFrame() {
89+
lockWrite.lock();
90+
try {
91+
lockSize.lock();
92+
try {
93+
while (full()) {
94+
configuration.log("Main: Waiting for frame buffer capacity");
95+
performanceMetrics.getIntentionallyBlocking().startTiming();
96+
conditionSize.awaitUninterruptibly();
97+
}
98+
performanceMetrics.getIntentionallyBlocking().stopTiming();
99+
} finally { lockSize.unlock(); };
100+
101+
performanceMetrics.getCopyingToBuffer().time(() -> {
102+
WrappedBufferOffset dataTarget = dataBuffer.get(indexGame());
103+
dataTarget.initialize();
104+
});
105+
106+
lockSize.lock();
107+
try {
108+
performanceMetrics.getFrameBufferSize().record(framesBuffered());
109+
++stepGame;
110+
conditionSize.signalAll();
111+
} finally {
112+
lockSize.unlock();
113+
}
114+
} finally {
115+
lockWrite.unlock();
116+
}
117+
}
118+
119+
/**
120+
* Peeks the front-most value in the buffer.
121+
*/
122+
WrappedBuffer peek() {
123+
lockSize.lock();
124+
try {
125+
while(empty()) conditionSize.awaitUninterruptibly();
126+
return dataBuffer.get(indexBot());
127+
} finally {
128+
lockSize.unlock();
129+
}
130+
}
131+
132+
/**
133+
* Removes the front-most frame in the buffer.
134+
*/
135+
void dequeue() {
136+
lockSize.lock();
137+
try {
138+
while(empty()) conditionSize.awaitUninterruptibly();
139+
++stepBot;
140+
conditionSize.signalAll();
141+
} finally {
142+
lockSize.unlock();
143+
}
144+
}
145+
}

0 commit comments

Comments
 (0)