Skip to content

Commit 9d589c8

Browse files
committed
chore: improve swap chain performance by reducing synchronization needs
1 parent bef3263 commit 9d589c8

File tree

2 files changed

+79
-53
lines changed

2 files changed

+79
-53
lines changed
Lines changed: 46 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,73 @@
11
package dev.silenium.compose.gl.fbo
22

33
import androidx.compose.ui.unit.IntSize
4-
import java.util.concurrent.ArrayBlockingQueue
5-
import java.util.concurrent.locks.ReentrantLock
6-
import kotlin.concurrent.withLock
4+
import java.util.concurrent.ConcurrentLinkedQueue
5+
import java.util.concurrent.atomic.AtomicReference
76

8-
class FBOFifoSwapChain(capacity: Int, override val fboCreator: (IntSize) -> FBO) : FBOSwapChain() {
7+
class FBOFifoSwapChain(private val capacity: Int, override val fboCreator: (IntSize) -> FBO) : FBOSwapChain() {
98
override var size: IntSize = IntSize.Zero
109
private set
11-
private val displayLock = ReentrantLock()
12-
private var current: FBO? = null
13-
private val renderQueue = ArrayBlockingQueue<FBO>(capacity)
14-
private val displayQueue = ArrayBlockingQueue<FBO>(capacity)
10+
private var fbos = AtomicReference<Array<FBO>?>(null)
11+
private var current: Int = -1
12+
private val displayQueue = ConcurrentLinkedQueue<Int>()
13+
private val renderQueue = ConcurrentLinkedQueue<Int>()
1514

16-
override fun <R> display(block: (FBO) -> R) = displayLock.withLock {
17-
val result = current?.let(block)
18-
if (!displayQueue.isEmpty()) {
19-
current?.let {
20-
if (it.size != size) it.destroy()
21-
else renderQueue.offer(it)
22-
}
23-
current = displayQueue.poll()
15+
override fun <R> display(block: (FBO) -> R): R? {
16+
val next = displayQueue.poll()
17+
if (next != null && next != -1) {
18+
renderQueue.add(current)
19+
current = next
2420
}
25-
return@withLock result
21+
if (current == -1) return null
22+
val fbo = fbos.get()?.get(current) ?: return null
23+
val result = block(fbo)
24+
return result
2625
}
2726

2827
override fun <R> render(block: (FBO) -> R): R? {
29-
val fbo = renderQueue.poll() ?: return null
30-
if (fbo.size != size) {
31-
fbo.destroy()
32-
return null
28+
val next = renderQueue.poll() ?: return null
29+
val nextFbos = fbos.updateAndGet {
30+
if (it?.get(next)?.size != size) {
31+
it?.get(next)?.destroy()
32+
it?.set(next, fboCreator(size))
33+
}
34+
it
3335
}
3436
try {
35-
val result = block(fbo)
36-
displayQueue.offer(fbo)
37+
val nextFbo = nextFbos?.get(next) ?: return null
38+
val result = block(nextFbo)
39+
current = next
3740
return result
3841
} catch (e: Throwable) {
39-
renderQueue.add(fbo)
4042
throw e
4143
}
4244
}
4345

4446
override fun resize(size: IntSize) {
4547
this.size = size
46-
renderQueue.onEach { it.destroy() }.clear()
47-
displayQueue.onEach { it.destroy() }.clear()
48-
renderQueue.fillRenderQueue(fboCreator, size)
48+
renderQueue.clear()
49+
displayQueue.clear()
50+
fbos.get()?.forEachIndexed { index, fbo ->
51+
if (fbo.size != size && index != current) {
52+
fbo.destroy()
53+
}
54+
}
55+
val currentFbo = fbos.get()?.getOrNull(current)
56+
fbos.set(Array(capacity) {
57+
if (it == current) currentFbo ?: fboCreator(size)
58+
else fboCreator(size)
59+
}.also {
60+
renderQueue.addAll(it.indices - current)
61+
})
4962
}
5063

5164
override fun destroyFBOs() {
52-
renderQueue.onEach { it.destroy() }.clear()
53-
displayQueue.onEach { it.destroy() }.clear()
54-
displayLock.withLock {
55-
current?.destroy()
56-
current = null
65+
renderQueue.clear()
66+
displayQueue.clear()
67+
current = -1
68+
fbos.updateAndGet {
69+
it?.forEach(FBO::destroy)
70+
null
5771
}
5872
}
5973
}
Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,59 @@
11
package dev.silenium.compose.gl.fbo
22

33
import androidx.compose.ui.unit.IntSize
4-
import java.util.concurrent.locks.ReentrantReadWriteLock
5-
import kotlin.concurrent.read
6-
import kotlin.concurrent.write
4+
import java.util.concurrent.atomic.AtomicReference
75

86
class FBOMailboxSwapChain(private val capacity: Int, override val fboCreator: (IntSize) -> FBO) : FBOSwapChain() {
97
override var size: IntSize = IntSize.Zero
108
private set
11-
private val stateLock = ReentrantReadWriteLock()
12-
private var fbos: Array<FBO>? = null
9+
private var fbos = AtomicReference<Array<FBO>?>(null)
1310
private var current: Int = -1
1411

15-
override fun <R> display(block: (FBO) -> R): R? = stateLock.read stateLock@{
16-
if (current == -1) return@stateLock null
17-
val fbo = fbos?.get(current) ?: return@stateLock null
12+
override fun <R> display(block: (FBO) -> R): R? {
13+
if (current == -1) return null
14+
val fbo = fbos.get()?.get(current) ?: return null
1815
val result = block(fbo)
19-
return@stateLock result
16+
return result
2017
}
2118

22-
override fun <R> render(block: (FBO) -> R): R? = stateLock.read {
23-
val fbos = fbos ?: return null
24-
val next = (current + 1) % fbos.size
25-
if (fbos[next].size != size) {
26-
fbos[next].destroy()
27-
fbos[next] = fboCreator(size)
19+
override fun <R> render(block: (FBO) -> R): R? {
20+
val next = (current + 1) % capacity
21+
val nextFbos = fbos.updateAndGet {
22+
if (it?.get(next)?.size != size) {
23+
it?.get(next)?.destroy()
24+
it?.set(next, fboCreator(size))
25+
}
26+
it
2827
}
2928
try {
30-
val result = block(fbos[next])
29+
val nextFbo = nextFbos?.get(next) ?: return null
30+
val result = block(nextFbo)
3131
current = next
3232
return result
3333
} catch (e: Throwable) {
3434
throw e
3535
}
3636
}
3737

38-
override fun resize(size: IntSize): Unit = stateLock.write {
38+
override fun resize(size: IntSize) {
3939
this.size = size
40-
fbos?.forEach(FBO::destroy)
41-
fbos = Array(capacity) { fboCreator(size) }
40+
fbos.get()?.forEachIndexed { index, fbo ->
41+
if (fbo.size != size && index != current) {
42+
fbo.destroy()
43+
}
44+
}
45+
val currentFbo = fbos.get()?.getOrNull(current)
46+
fbos.set(Array(capacity) {
47+
if (it == current) currentFbo ?: fboCreator(size)
48+
else fboCreator(size)
49+
})
4250
}
4351

44-
override fun destroyFBOs(): Unit = stateLock.write {
45-
fbos?.forEach(FBO::destroy)
52+
override fun destroyFBOs() {
53+
current = -1
54+
fbos.updateAndGet {
55+
it?.forEach(FBO::destroy)
56+
null
57+
}
4658
}
4759
}

0 commit comments

Comments
 (0)