feat(renderer): Refactor rendering object pool management and update related tests
This commit is contained in:
@@ -205,6 +205,8 @@ public class G2DRenderer implements Renderer {
|
||||
bo.staticCleanupAfterFrame();
|
||||
}
|
||||
|
||||
this.rendererData.getRenderingBoPool().releasePending();
|
||||
|
||||
//
|
||||
// Rendering cycle END
|
||||
//
|
||||
|
||||
@@ -26,9 +26,9 @@ public class G2DRendererControl implements RendererControl {
|
||||
|
||||
private RendererData rendererData;
|
||||
|
||||
private CoordI2 absoluteViewportDimension = ImmutableCoordI2.one();
|
||||
private CoordI2 absoluteViewportDimension = RendererControl.REFERENCE_RESOLUTION;
|
||||
|
||||
private CoordI2 effectiveViewportDimension = ImmutableCoordI2.one();
|
||||
private CoordI2 effectiveViewportDimension = RendererControl.REFERENCE_RESOLUTION;
|
||||
|
||||
private double scaleFactor = 1.0;
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
|
||||
.next()
|
||||
.put(work.getId(), work);
|
||||
if (previous != null && previous != work) {
|
||||
this.rendererBoPool.release(previous);
|
||||
this.rendererBoPool.returnToPool(previous);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
|
||||
for (final RenderingBo bo : work) {
|
||||
final var previous = nextMap.put(bo.getId(), bo);
|
||||
if (previous != null && previous != bo) {
|
||||
this.rendererBoPool.release(previous);
|
||||
this.rendererBoPool.returnToPool(previous);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
|
||||
.next()
|
||||
.remove(id);
|
||||
if (previous != null) {
|
||||
this.rendererBoPool.release(previous);
|
||||
this.rendererBoPool.returnToPool(previous);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
|
||||
final var previousLiveSet = previousLiveRef.get();
|
||||
if (previousLiveSet != null) {
|
||||
if (clone) {
|
||||
previousLiveSet.values().forEach(this.rendererBoPool::release);
|
||||
previousLiveSet.values().forEach(this.rendererBoPool::returnToPool);
|
||||
}
|
||||
this.returnMapToPool(previousLiveSet);
|
||||
}
|
||||
@@ -119,7 +119,7 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
|
||||
@Override
|
||||
public void clearNext(final String producerId) {
|
||||
final var next = this.getWorkingSetByProducer(producerId).next();
|
||||
next.values().forEach(this.rendererBoPool::release);
|
||||
next.values().forEach(this.rendererBoPool::returnToPool);
|
||||
next.clear();
|
||||
}
|
||||
|
||||
@@ -127,8 +127,8 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
|
||||
public void clear(final String producerId) {
|
||||
final RendererWork rendererWork = this.workingSet.remove(producerId);
|
||||
if (rendererWork != null) {
|
||||
rendererWork.live().values().forEach(this.rendererBoPool::release);
|
||||
rendererWork.next().values().forEach(this.rendererBoPool::release);
|
||||
rendererWork.live().values().forEach(this.rendererBoPool::returnToPool);
|
||||
rendererWork.next().values().forEach(this.rendererBoPool::returnToPool);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,9 +20,14 @@ public interface RenderingBoPool {
|
||||
<T extends RenderingBo> T copy(T original);
|
||||
|
||||
/**
|
||||
* Releases a rendering business object back to the pool. This is usually managed by the renderer.
|
||||
* Queues a rendering business object for return back to the pool. Renderer will release the BO after the current rendering cycle is complete.
|
||||
*/
|
||||
void release(RenderingBo obj);
|
||||
void returnToPool(RenderingBo obj);
|
||||
|
||||
/**
|
||||
* Releases all pending rendering business objects back to the pool. This is managed by the renderer.
|
||||
*/
|
||||
void releasePending();
|
||||
|
||||
void setRendererData(RendererData rendererData);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package de.extio.game_engine.renderer.work;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
|
||||
import de.extio.game_engine.renderer.RendererData;
|
||||
import de.extio.game_engine.renderer.model.RenderingBo;
|
||||
@@ -14,6 +18,10 @@ public class RenderingBoPoolImpl implements RenderingBoPool {
|
||||
|
||||
private final Map<Class<? extends RenderingBo>, Stack<RenderingBo>> pool = new HashMap<>();
|
||||
|
||||
private final BlockingQueue<RenderingBo> returnQueue = new ArrayBlockingQueue<>(1000);
|
||||
|
||||
private final Deque<RenderingBo> returnSink = new ArrayDeque<>(1000);
|
||||
|
||||
private final LastMapping lastMapping = new LastMapping();
|
||||
|
||||
private final LastReverseMapping lastReverseMapping = new LastReverseMapping();
|
||||
@@ -98,28 +106,37 @@ public class RenderingBoPoolImpl implements RenderingBoPool {
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void release(final RenderingBo obj) {
|
||||
public void returnToPool(final RenderingBo obj) {
|
||||
this.returnQueue.offer(obj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void releasePending() {
|
||||
try {
|
||||
obj.close();
|
||||
this.returnQueue.drainTo(this.returnSink);
|
||||
RenderingBo obj;
|
||||
while ((obj = this.returnSink.poll()) != null) {
|
||||
obj.close();
|
||||
|
||||
final Class<? extends RenderingBo> clazz = obj.getClass();
|
||||
Stack<RenderingBo> pooled;
|
||||
final Class<? extends RenderingBo> clazz = obj.getClass();
|
||||
Stack<RenderingBo> pooled;
|
||||
|
||||
if (this.lastReverseMapping != null && this.lastReverseMapping.clazz == clazz) {
|
||||
pooled = this.lastReverseMapping.pooled;
|
||||
}
|
||||
else {
|
||||
pooled = this.pool.get(clazz);
|
||||
if (pooled == null) {
|
||||
pooled = new Stack<>();
|
||||
this.pool.put(clazz, pooled);
|
||||
if (this.lastReverseMapping != null && this.lastReverseMapping.clazz == clazz) {
|
||||
pooled = this.lastReverseMapping.pooled;
|
||||
}
|
||||
else {
|
||||
pooled = this.pool.get(clazz);
|
||||
if (pooled == null) {
|
||||
pooled = new Stack<>();
|
||||
this.pool.put(clazz, pooled);
|
||||
}
|
||||
|
||||
this.lastReverseMapping.clazz = clazz;
|
||||
this.lastReverseMapping.pooled = pooled;
|
||||
}
|
||||
|
||||
this.lastReverseMapping.clazz = clazz;
|
||||
this.lastReverseMapping.pooled = pooled;
|
||||
pooled.push(obj);
|
||||
}
|
||||
|
||||
pooled.push(obj);
|
||||
}
|
||||
catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
|
||||
@@ -194,8 +194,8 @@ public class RendererWorkingSetImplTest {
|
||||
final var uncommittedAfter = this.workingSet.getUncommittedWork(this.moduleA.getId());
|
||||
assertTrue(uncommittedAfter.isEmpty());
|
||||
|
||||
verify(this.renderingBoPool, times(1)).release(bo1);
|
||||
verify(this.renderingBoPool, times(1)).release(bo2);
|
||||
verify(this.renderingBoPool, times(1)).returnToPool(bo1);
|
||||
verify(this.renderingBoPool, times(1)).returnToPool(bo2);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -240,8 +240,8 @@ public class RendererWorkingSetImplTest {
|
||||
this.workingSet.put(this.moduleA.getId(), bo3);
|
||||
this.workingSet.commit(this.moduleA.getId(), true);
|
||||
|
||||
verify(this.renderingBoPool, times(1)).release(bo1);
|
||||
verify(this.renderingBoPool, times(1)).release(bo2);
|
||||
verify(this.renderingBoPool, times(1)).returnToPool(bo1);
|
||||
verify(this.renderingBoPool, times(1)).returnToPool(bo2);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package de.extio.game_engine.renderer.work;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
@@ -89,7 +90,8 @@ public class RenderingBoPoolImplTest {
|
||||
assertEquals("id1", acquired1.getId());
|
||||
assertEquals(0, acquired1.getCloseCallCount());
|
||||
|
||||
this.pool.release(acquired1);
|
||||
this.pool.returnToPool(acquired1);
|
||||
this.pool.releasePending();
|
||||
assertEquals(1, acquired1.getCloseCallCount());
|
||||
|
||||
final TestRenderingBo acquired2 = this.pool.acquire("id2", TestRenderingBo.class);
|
||||
@@ -106,10 +108,12 @@ public class RenderingBoPoolImplTest {
|
||||
final TestRenderingBo bo = this.pool.acquire("id", TestRenderingBo.class);
|
||||
assertEquals(0, bo.getCloseCallCount());
|
||||
|
||||
this.pool.release(bo);
|
||||
this.pool.returnToPool(bo);
|
||||
this.pool.releasePending();
|
||||
assertEquals(1, bo.getCloseCallCount());
|
||||
|
||||
this.pool.release(bo);
|
||||
this.pool.returnToPool(bo);
|
||||
this.pool.releasePending();
|
||||
assertEquals(2, bo.getCloseCallCount());
|
||||
}
|
||||
|
||||
@@ -169,10 +173,12 @@ public class RenderingBoPoolImplTest {
|
||||
assertEquals(0, second.getCloseCallCount());
|
||||
assertNotSame(first, second);
|
||||
|
||||
this.pool.release(first);
|
||||
this.pool.returnToPool(first);
|
||||
this.pool.releasePending();
|
||||
assertEquals(1, first.getCloseCallCount());
|
||||
|
||||
this.pool.release(second);
|
||||
this.pool.returnToPool(second);
|
||||
this.pool.releasePending();
|
||||
assertEquals(1, second.getCloseCallCount());
|
||||
|
||||
final TestRenderingBo reacquired = this.pool.acquire("id3", TestRenderingBo.class);
|
||||
@@ -182,6 +188,66 @@ public class RenderingBoPoolImplTest {
|
||||
assertTrue(reacquired == first || reacquired == second);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testReturnToPoolWithoutReleasePending() {
|
||||
final TestRenderingBo first = this.pool.acquire("id1", TestRenderingBo.class);
|
||||
final int id1 = System.identityHashCode(first);
|
||||
assertEquals(0, first.getCloseCallCount());
|
||||
|
||||
this.pool.returnToPool(first);
|
||||
assertEquals(0, first.getCloseCallCount());
|
||||
|
||||
final TestRenderingBo second = this.pool.acquire("id2", TestRenderingBo.class);
|
||||
final int id2 = System.identityHashCode(second);
|
||||
|
||||
assertNotSame(first, second);
|
||||
assertNotEquals(id1, id2);
|
||||
assertEquals("id2", second.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBatchReturnToPoolThenReleasePending() {
|
||||
final TestRenderingBo first = this.pool.acquire("id1", TestRenderingBo.class);
|
||||
final TestRenderingBo second = this.pool.acquire("id2", TestRenderingBo.class);
|
||||
final TestRenderingBo third = this.pool.acquire("id3", TestRenderingBo.class);
|
||||
|
||||
assertEquals(0, first.getCloseCallCount());
|
||||
assertEquals(0, second.getCloseCallCount());
|
||||
assertEquals(0, third.getCloseCallCount());
|
||||
|
||||
this.pool.returnToPool(first);
|
||||
this.pool.returnToPool(second);
|
||||
this.pool.returnToPool(third);
|
||||
|
||||
assertEquals(0, first.getCloseCallCount());
|
||||
assertEquals(0, second.getCloseCallCount());
|
||||
assertEquals(0, third.getCloseCallCount());
|
||||
|
||||
this.pool.releasePending();
|
||||
|
||||
assertEquals(1, first.getCloseCallCount());
|
||||
assertEquals(1, second.getCloseCallCount());
|
||||
assertEquals(1, third.getCloseCallCount());
|
||||
|
||||
final TestRenderingBo reacquired = this.pool.acquire("id4", TestRenderingBo.class);
|
||||
assertTrue(reacquired == first || reacquired == second || reacquired == third);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMultipleReleasePendingCalls() {
|
||||
final TestRenderingBo bo = this.pool.acquire("id1", TestRenderingBo.class);
|
||||
this.pool.returnToPool(bo);
|
||||
|
||||
this.pool.releasePending();
|
||||
assertEquals(1, bo.getCloseCallCount());
|
||||
|
||||
this.pool.releasePending();
|
||||
assertEquals(1, bo.getCloseCallCount());
|
||||
|
||||
this.pool.releasePending();
|
||||
assertEquals(1, bo.getCloseCallCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testConcurrentAccess() throws InterruptedException {
|
||||
final int[] thread1Count = {0};
|
||||
@@ -192,7 +258,7 @@ public class RenderingBoPoolImplTest {
|
||||
final TestRenderingBo bo = this.pool.acquire("id-" + i, TestRenderingBo.class);
|
||||
assertNotNull(bo);
|
||||
assertNotNull(bo.getId());
|
||||
this.pool.release(bo);
|
||||
this.pool.returnToPool(bo);
|
||||
thread1Count[0]++;
|
||||
}
|
||||
});
|
||||
@@ -202,7 +268,7 @@ public class RenderingBoPoolImplTest {
|
||||
final TestRenderingBo bo = this.pool.acquire("id-" + i, TestRenderingBo.class);
|
||||
assertNotNull(bo);
|
||||
assertNotNull(bo.getId());
|
||||
this.pool.release(bo);
|
||||
this.pool.returnToPool(bo);
|
||||
thread2Count[0]++;
|
||||
}
|
||||
});
|
||||
@@ -216,6 +282,8 @@ public class RenderingBoPoolImplTest {
|
||||
assertEquals(10, thread1Count[0]);
|
||||
assertEquals(10, thread2Count[0]);
|
||||
|
||||
this.pool.releasePending();
|
||||
|
||||
final TestRenderingBo bo = this.pool.acquire("final-id", TestRenderingBo.class);
|
||||
assertNotNull(bo);
|
||||
assertEquals("final-id", bo.getId());
|
||||
|
||||
Reference in New Issue
Block a user