Skip to content

Commit 6ac2e47

Browse files
committed
mac: use dynamic IOSurface pool for buffering
1 parent 200ca1d commit 6ac2e47

File tree

2 files changed

+110
-39
lines changed

2 files changed

+110
-39
lines changed

rlawt.h

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,20 +46,33 @@
4646
# include <wglext.h>
4747
#endif
4848

49+
#ifdef __APPLE__
50+
#define RLAWT_MAX_POOL 8
51+
52+
typedef struct {
53+
IOSurfaceRef surface;
54+
CGFloat scale;
55+
GLuint tex;
56+
GLuint fbo;
57+
} IOSurfaceEntry;
58+
59+
typedef struct {
60+
IOSurfaceEntry entries[RLAWT_MAX_POOL];
61+
int count; // entries currently allocated (0..RLAWT_MAX_POOL)
62+
int back; // current render target index
63+
int front; // most recently presented surface index
64+
} IOSurfacePool;
65+
#endif
66+
4967
typedef struct {
5068
JAWT awt;
5169
JAWT_DrawingSurface *ds;
5270
bool contextCreated;
5371

5472
#ifdef __APPLE__
5573
CALayer *layer;
56-
IOSurfaceRef buffer[2];
57-
CGFloat bufferScale[2];
5874
CGLContextObj context;
59-
60-
GLuint tex[2];
61-
GLuint fbo[2];
62-
int back;
75+
IOSurfacePool pool;
6376

6477
int offsetX;
6578
int offsetY;

rlawt_mac.m

Lines changed: 91 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,41 @@ static void propsPutInt(CFMutableDictionaryRef props, const CFStringRef key, int
109109
CFRelease(boxedValue);
110110
}
111111

112-
static bool rlawtCreateIOSurface(JNIEnv *env, AWTContext *ctx) {
113-
CGFloat scale = ctx->layer.superlayer.contentsScale;
114-
CGSize size = ctx->layer.frame.size;
112+
// -- IOSurfacePool helpers --
113+
114+
static void poolInit(IOSurfacePool *pool) {
115+
memset(pool, 0, sizeof(*pool));
116+
GLuint textures[RLAWT_MAX_POOL], framebuffers[RLAWT_MAX_POOL];
117+
glGenTextures(RLAWT_MAX_POOL, textures);
118+
glGenFramebuffers(RLAWT_MAX_POOL, framebuffers);
119+
for (int i = 0; i < RLAWT_MAX_POOL; i++) {
120+
pool->entries[i].tex = textures[i];
121+
pool->entries[i].fbo = framebuffers[i];
122+
}
123+
}
124+
125+
static void poolDestroy(IOSurfacePool *pool) {
126+
for (int i = 0; i < RLAWT_MAX_POOL; i++) {
127+
glDeleteTextures(1, &pool->entries[i].tex);
128+
glDeleteFramebuffers(1, &pool->entries[i].fbo);
129+
if (pool->entries[i].surface) {
130+
CFRelease(pool->entries[i].surface);
131+
}
132+
}
133+
}
134+
135+
static bool poolEntryMatchesSize(IOSurfaceEntry *entry, CALayer *layer) {
136+
if (!entry->surface) return false;
137+
CGFloat scale = entry->scale;
138+
CGSize size = layer.frame.size;
139+
return IOSurfaceGetWidth(entry->surface) == (size_t)(size.width * scale)
140+
&& IOSurfaceGetHeight(entry->surface) == (size_t)(size.height * scale)
141+
&& layer.superlayer.contentsScale == scale;
142+
}
143+
144+
static bool poolCreateSurface(JNIEnv *env, IOSurfaceEntry *entry, CALayer *layer, CGLContextObj cglCtx) {
145+
CGFloat scale = layer.superlayer.contentsScale;
146+
CGSize size = layer.frame.size;
115147
size.width *= scale;
116148
size.height *= scale;
117149

@@ -127,20 +159,20 @@ static bool rlawtCreateIOSurface(JNIEnv *env, AWTContext *ctx) {
127159
rlawtThrow(env, "unable to create io surface");
128160
return false;
129161
}
130-
162+
131163
const GLuint target = GL_TEXTURE_RECTANGLE;
132164
glActiveTexture(GL_TEXTURE0);
133-
glBindTexture(target, ctx->tex[ctx->back]);
165+
glBindTexture(target, entry->tex);
134166
CGLError err = CGLTexImageIOSurface2D(
135-
ctx->context,
167+
cglCtx,
136168
target, GL_RGBA,
137169
size.width, size.height,
138170
GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,
139-
buf,
171+
buf,
140172
0);
141173
glBindTexture(target, 0);
142-
glBindFramebuffer(GL_FRAMEBUFFER, ctx->fbo[ctx->back]);
143-
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, ctx->tex[ctx->back], 0);
174+
glBindFramebuffer(GL_FRAMEBUFFER, entry->fbo);
175+
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, target, entry->tex, 0);
144176

145177
if (err != kCGLNoError) {
146178
rlawtThrowCGLError(env, "unable to bind io surface to texture", err);
@@ -155,17 +187,39 @@ static bool rlawtCreateIOSurface(JNIEnv *env, AWTContext *ctx) {
155187
goto freeSurface;
156188
}
157189

158-
if (ctx->buffer[ctx->back]) {
159-
CFRelease(ctx->buffer[ctx->back]);
190+
if (entry->surface) {
191+
CFRelease(entry->surface);
160192
}
161-
ctx->buffer[ctx->back] = buf;
162-
ctx->bufferScale[ctx->back] = scale;
193+
entry->surface = buf;
194+
entry->scale = scale;
163195
return true;
164196
freeSurface:
165197
CFRelease(buf);
166198
return false;
167199
}
168200

201+
static int poolNextBack(IOSurfacePool *pool) {
202+
// Find any non-front surface the compositor isn't using
203+
for (int i = 0; i < pool->count; i++) {
204+
if (i == pool->front) continue;
205+
if (pool->entries[i].surface && !IOSurfaceIsInUse(pool->entries[i].surface)) {
206+
return i;
207+
}
208+
}
209+
210+
// Grow pool if nothing was available
211+
if (pool->count < RLAWT_MAX_POOL) {
212+
return pool->count++;
213+
}
214+
215+
// Pool full, all in use -- evict first non-front entry.
216+
// count >= 2 is guaranteed here (pool grows on first swap), so this always finds one.
217+
for (int i = 0; i < pool->count; i++) {
218+
if (i != pool->front) return i;
219+
}
220+
return 0; // unreachable
221+
}
222+
169223
JNIEXPORT void JNICALL Java_net_runelite_rlawt_AWTContext_createGLContext(JNIEnv *env, jobject self) {
170224
AWTContext *ctx = rlawtGetContext(env, self);
171225
if (!ctx || !rlawtContextState(env, ctx, false)) {
@@ -215,8 +269,7 @@ JNIEXPORT void JNICALL Java_net_runelite_rlawt_AWTContext_createGLContext(JNIEnv
215269
goto freeContext;
216270
}
217271

218-
glGenTextures(2, &ctx->tex[0]);
219-
glGenFramebuffers(2, &ctx->fbo[0]);
272+
poolInit(&ctx->pool);
220273

221274
dispatch_sync(dispatch_get_main_queue(), ^{
222275
RLLayer *layer = [[RLLayer alloc] init];
@@ -237,7 +290,8 @@ JNIEXPORT void JNICALL Java_net_runelite_rlawt_AWTContext_createGLContext(JNIEnv
237290
dsi->bounds.height);
238291
});
239292

240-
if (!rlawtCreateIOSurface(env, ctx)) {
293+
ctx->pool.count = 1;
294+
if (!poolCreateSurface(env, &ctx->pool.entries[0], ctx->layer, ctx->context)) {
241295
goto freeContext;
242296
}
243297

@@ -257,12 +311,7 @@ void rlawtContextFreePlatform(JNIEnv *env, AWTContext *ctx) {
257311
if (ctx->context) {
258312
CGLDestroyContext(ctx->context);
259313
}
260-
if (ctx->buffer[0]) {
261-
CFRelease(ctx->buffer[0]);
262-
}
263-
if (ctx->buffer[1]) {
264-
CFRelease(ctx->buffer[1]);
265-
}
314+
poolDestroy(&ctx->pool);
266315
if (ctx->layer) {
267316
dispatch_sync(dispatch_get_main_queue(), ^{
268317
[ctx->layer removeFromSuperlayer];
@@ -300,20 +349,26 @@ JNIEXPORT void JNICALL Java_net_runelite_rlawt_AWTContext_swapBuffers(JNIEnv *en
300349
}
301350

302351
glFlush();
352+
353+
IOSurfacePool *pool = &ctx->pool;
354+
355+
// Present the current back buffer
356+
pool->front = pool->back;
303357
RLLayer *rlLayer = (RLLayer*) ctx->layer;
304-
rlLayer->newScale = ctx->bufferScale[ctx->back];
358+
rlLayer->newScale = pool->entries[pool->front].scale;
305359
[rlLayer performSelectorOnMainThread:
306360
@selector(displayIOSurface:)
307-
withObject: (id)(ctx->buffer[ctx->back])
361+
withObject: (id)(pool->entries[pool->front].surface)
308362
waitUntilDone: true];
309-
310-
ctx->back ^= 1;
311-
312-
if (!ctx->buffer[ctx->back]
313-
|| IOSurfaceGetWidth(ctx->buffer[ctx->back]) != (size_t) (ctx->layer.frame.size.width * ctx->bufferScale[ctx->back])
314-
|| IOSurfaceGetHeight(ctx->buffer[ctx->back]) != (size_t) (ctx->layer.frame.size.height * ctx->bufferScale[ctx->back])
315-
|| ctx->layer.superlayer.contentsScale != ctx->bufferScale[ctx->back]) {
316-
if (!rlawtCreateIOSurface(env, ctx)) {
363+
364+
// Find a free surface for the next frame
365+
pool->back = poolNextBack(pool);
366+
IOSurfaceEntry *back = &pool->entries[pool->back];
367+
368+
// Create/recreate surface if needed (new slot, wrong size, or still held by compositor)
369+
if (!poolEntryMatchesSize(back, ctx->layer)
370+
|| IOSurfaceIsInUse(back->surface)) {
371+
if (!poolCreateSurface(env, back, ctx->layer, ctx->context)) {
317372
return;
318373
}
319374
}
@@ -325,7 +380,10 @@ JNIEXPORT jint JNICALL Java_net_runelite_rlawt_AWTContext_getFramebuffer(JNIEnv
325380
return 0;
326381
}
327382

328-
return ctx->fbo[ctx->back ^ front];
383+
if (front) {
384+
return ctx->pool.entries[ctx->pool.front].fbo;
385+
}
386+
return ctx->pool.entries[ctx->pool.back].fbo;
329387
}
330388

331389
#endif

0 commit comments

Comments
 (0)