feat(renderer): Implement getOrAcquire and remove methods in RendererWorkingSet interface and update RendererWorkingSetImpl

This commit is contained in:
extio-de
2025-12-25 13:23:25 +01:00
parent 5d80ba3f9e
commit 3e17849093
4 changed files with 42 additions and 62 deletions

View File

@@ -2,7 +2,6 @@ package de.extio.game_engine.renderer.g2d;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
@@ -25,7 +24,6 @@ import org.slf4j.LoggerFactory;
import de.extio.game_engine.module.AbstractClientModule;
import de.extio.game_engine.renderer.Renderer;
import de.extio.game_engine.renderer.RendererData;
import de.extio.game_engine.renderer.g2d.G2DRenderer.G2DRendererModule;
import de.extio.game_engine.renderer.g2d.bo.rendering.G2DAbstractRenderingBo;
import de.extio.game_engine.renderer.g2d.bo.rendering.G2DDrawBackground;
import de.extio.game_engine.renderer.g2d.bo.rendering.G2DDrawFont;
@@ -54,6 +52,8 @@ public class G2DRenderer implements Renderer {
private final List<RenderingBo> renderingBOs = new ArrayList<>();
private final Map<Class<? extends RenderingBo>, RenderingBo> usedRenderingBoTypes = new HashMap<>();
private volatile G2DMainFrame mainFrame;
private String rendererModuleId;
@@ -190,8 +190,10 @@ public class G2DRenderer implements Renderer {
screenGraphics.setColor(Color.BLACK);
screenGraphics.fillRect(0, 0, viewPortDimension.getX(), viewPortDimension.getY());
usedRenderingBoTypes.clear();
for (final RenderingBo renderingBO : this.renderingBOs) {
if (renderingBO instanceof final G2DAbstractRenderingBo g2dAbstractRenderingBo) {
usedRenderingBoTypes.putIfAbsent(g2dAbstractRenderingBo.getClass(), g2dAbstractRenderingBo);
g2dAbstractRenderingBo.render(screenGraphics, this.rendererData.getRendererControl().getScaleFactor(), false);
if (takeScreenshot_ && g2dAbstractRenderingBo.isScreenshotRelevant()) {
g2dAbstractRenderingBo.render(screenshotGraphics, this.rendererData.getRendererControl().getScaleFactor(), true);
@@ -199,7 +201,9 @@ public class G2DRenderer implements Renderer {
}
}
this.rendererData.getRendererWorkingSet().get(this.rendererModuleId, "G2DRenderer_tooltip").closeStatic();
for (final RenderingBo bo : usedRenderingBoTypes.values()) {
bo.closeStatic();
}
//
// Rendering cycle END

View File

@@ -17,6 +17,8 @@ public interface RendererWorkingSet {
<T extends RenderingBo> T get(String producerId, String id, Class<T> type);
<T extends RenderingBo> T getOrAcquire(String producerId, String id, Class<T> type);
void remove(String producerId, String id);
Map<String, RenderingBo> getUncommittedWork(String producerId);

View File

@@ -31,22 +31,38 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
@Override
public void put(final String producerId, final RenderingBo work) {
this.getWorkingSetByProducer(producerId)
final var previous = this.getWorkingSetByProducer(producerId)
.next()
.put(work.getId(), work);
if (previous != null) {
this.rendererBoPool.release(previous);
}
}
@Override
public void put(final String producerId, final List<RenderingBo> work) {
final var nextMap = this.getWorkingSetByProducer(producerId).next();
for (final RenderingBo bo : work) {
nextMap.put(bo.getId(), bo);
final var previous = nextMap.put(bo.getId(), bo);
if (previous != null) {
this.rendererBoPool.release(previous);
}
}
}
@Override
public void remove(final String producerId, final String id) {
final var previous = this.getWorkingSetByProducer(producerId)
.next()
.remove(id);
if (previous != null) {
this.rendererBoPool.release(previous);
}
}
@Override
public Map<String, RenderingBo> getUncommittedWork(final String producerId) {
return this.getWorkingSetByProducer(producerId).next();
return Map.copyOf(this.getWorkingSetByProducer(producerId).next());
}
@Override
@@ -74,25 +90,26 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
@Override
public Map<String, RenderingBo> commit(final String producerId, final boolean clone) {
final AtomicReference<Map<String, RenderingBo>> previousWork = new AtomicReference<>();
final AtomicReference<Map<String, RenderingBo>> previousLiveRef = new AtomicReference<>();
final RendererWork rendererWork = this.workingSet.compute(producerId, (k, v) -> {
if (v == null) {
return new RendererWork(this.obtainMapFromPool(), this.obtainMapFromPool());
}
previousWork.set(v.live());
previousLiveRef.set(v.live());
if (clone) {
final Map<String, RenderingBo> newNext = this.obtainMapFromPool();
newNext.putAll(v.next());
return new RendererWork(v.next(), newNext);
}
else {
v.live().values().forEach(this.rendererBoPool::release);
return new RendererWork(v.next(), this.obtainMapFromPool());
}
});
final var previousWorkMap = previousWork.get();
if (previousWorkMap != null) {
this.releaseBoClasses(previousWorkMap);
final var previousLiveSet = previousLiveRef.get();
if (previousLiveSet != null) {
this.returnMapToPool(previousLiveSet);
}
return rendererWork.next();
@@ -102,8 +119,8 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
public void clear(final String producerId) {
final RendererWork rendererWork = this.workingSet.remove(producerId);
if (rendererWork != null) {
this.releaseBoClasses(rendererWork.live());
this.releaseBoClasses(rendererWork.next());
rendererWork.live().values().forEach(this.rendererBoPool::release);
rendererWork.next().values().forEach(this.rendererBoPool::release);
}
}
@@ -120,23 +137,6 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
return this.workingSet.computeIfAbsent(producerId, k -> new RendererWork(this.obtainMapFromPool(), this.obtainMapFromPool()));
}
private void releaseBoClasses(final Map<String, RenderingBo> work) {
if (work == null || work.isEmpty()) {
return;
}
final var releasedBoClasses = this.obtainSetFromPool();
for (final RenderingBo renderingBO : work.values()) {
if (releasedBoClasses.add(renderingBO.getClass())) {
renderingBO.closeStatic();
}
this.rendererBoPool.release(renderingBO);
}
this.returnMapToPool(work);
this.returnSetToPool(releasedBoClasses);
}
private Map<String, RenderingBo> obtainMapFromPool() {
var map = this.mapsPool.poll();
if (map == null) {
@@ -150,19 +150,6 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
this.mapsPool.offer(map);
}
private Set<Class<? extends RenderingBo>> obtainSetFromPool() {
var set = this.setsPool.poll();
if (set == null) {
set = new HashSet<>();
}
return set;
}
private void returnSetToPool(final Set<Class<? extends RenderingBo>> set) {
set.clear();
this.setsPool.offer(set);
}
private static record RendererWork(Map<String, RenderingBo> live, Map<String, RenderingBo> next) {
}

View File

@@ -10,7 +10,6 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -85,8 +84,9 @@ public class RendererWorkingSetImplTest {
final var returnedUncommitted = this.workingSet.commit(this.moduleA.getId(), false);
final var currentUncommitted = this.workingSet.getUncommittedWork(this.moduleA.getId());
assertSame(currentUncommitted, returnedUncommitted);
assertTrue(returnedUncommitted.isEmpty());
assertTrue(currentUncommitted.isEmpty());
assertEquals(returnedUncommitted, currentUncommitted);
final List<RenderingBo> live = new ArrayList<>();
this.workingSet.getLiveSet(live, null);
@@ -109,7 +109,7 @@ public class RendererWorkingSetImplTest {
final var returnedUncommitted = this.workingSet.commit(this.moduleA.getId(), true);
final var currentUncommitted = this.workingSet.getUncommittedWork(this.moduleA.getId());
assertSame(currentUncommitted, returnedUncommitted);
assertEquals(currentUncommitted, returnedUncommitted);
assertEquals(Map.of("bo1", bo1, "bo2", bo2), currentUncommitted);
final List<RenderingBo> live = new ArrayList<>();
@@ -186,8 +186,6 @@ public class RendererWorkingSetImplTest {
verify(this.renderingBoPool, times(1)).release(bo1);
verify(this.renderingBoPool, times(1)).release(bo2);
assertEquals(1, bo1.closeStaticCalls());
assertEquals(1, bo2.closeStaticCalls());
}
@Test
@@ -206,7 +204,7 @@ public class RendererWorkingSetImplTest {
}
@Test
void secondCommitReleasesPreviousLiveWorkAndCallsCloseStaticOncePerClass() {
void secondCommitReleasesPreviousLiveWork() {
final var bo1 = new TestBoA();
bo1.setId("bo1");
final var bo2 = new TestBoA();
@@ -223,11 +221,6 @@ public class RendererWorkingSetImplTest {
verify(this.renderingBoPool, times(1)).release(bo1);
verify(this.renderingBoPool, times(1)).release(bo2);
// closeStatic() is called exactly once per class, so one of bo1 or bo2 has it called
final int totalCloseStaticCallsForTestBoA = bo1.closeStaticCalls() + bo2.closeStaticCalls();
assertEquals(1, totalCloseStaticCallsForTestBoA);
assertEquals(0, bo3.closeStaticCalls());
}
@Test
@@ -362,8 +355,6 @@ public class RendererWorkingSetImplTest {
private static abstract class TestBoBase implements RenderingBo {
private final AtomicInteger closeStaticCalls = new AtomicInteger();
private String id;
@Override
@@ -483,13 +474,9 @@ public class RendererWorkingSetImplTest {
@Override
public void closeStatic() {
this.closeStaticCalls.incrementAndGet();
}
public int closeStaticCalls() {
return this.closeStaticCalls.get();
}
@Override
public void setRendererData(final RendererData RendererData) {
}