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.Color;
import java.awt.EventQueue; import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.image.BufferedImage; 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.module.AbstractClientModule;
import de.extio.game_engine.renderer.Renderer; import de.extio.game_engine.renderer.Renderer;
import de.extio.game_engine.renderer.RendererData; 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.G2DAbstractRenderingBo;
import de.extio.game_engine.renderer.g2d.bo.rendering.G2DDrawBackground; import de.extio.game_engine.renderer.g2d.bo.rendering.G2DDrawBackground;
import de.extio.game_engine.renderer.g2d.bo.rendering.G2DDrawFont; 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 List<RenderingBo> renderingBOs = new ArrayList<>();
private final Map<Class<? extends RenderingBo>, RenderingBo> usedRenderingBoTypes = new HashMap<>();
private volatile G2DMainFrame mainFrame; private volatile G2DMainFrame mainFrame;
private String rendererModuleId; private String rendererModuleId;
@@ -190,8 +190,10 @@ public class G2DRenderer implements Renderer {
screenGraphics.setColor(Color.BLACK); screenGraphics.setColor(Color.BLACK);
screenGraphics.fillRect(0, 0, viewPortDimension.getX(), viewPortDimension.getY()); screenGraphics.fillRect(0, 0, viewPortDimension.getX(), viewPortDimension.getY());
usedRenderingBoTypes.clear();
for (final RenderingBo renderingBO : this.renderingBOs) { for (final RenderingBo renderingBO : this.renderingBOs) {
if (renderingBO instanceof final G2DAbstractRenderingBo g2dAbstractRenderingBo) { if (renderingBO instanceof final G2DAbstractRenderingBo g2dAbstractRenderingBo) {
usedRenderingBoTypes.putIfAbsent(g2dAbstractRenderingBo.getClass(), g2dAbstractRenderingBo);
g2dAbstractRenderingBo.render(screenGraphics, this.rendererData.getRendererControl().getScaleFactor(), false); g2dAbstractRenderingBo.render(screenGraphics, this.rendererData.getRendererControl().getScaleFactor(), false);
if (takeScreenshot_ && g2dAbstractRenderingBo.isScreenshotRelevant()) { if (takeScreenshot_ && g2dAbstractRenderingBo.isScreenshotRelevant()) {
g2dAbstractRenderingBo.render(screenshotGraphics, this.rendererData.getRendererControl().getScaleFactor(), true); 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 // Rendering cycle END

View File

@@ -18,6 +18,8 @@ public interface RendererWorkingSet {
<T extends RenderingBo> T getOrAcquire(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); Map<String, RenderingBo> getUncommittedWork(String producerId);
Map<String, RenderingBo> commit(String producerId, boolean clone); Map<String, RenderingBo> commit(String producerId, boolean clone);

View File

@@ -31,22 +31,38 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
@Override @Override
public void put(final String producerId, final RenderingBo work) { public void put(final String producerId, final RenderingBo work) {
this.getWorkingSetByProducer(producerId) final var previous = this.getWorkingSetByProducer(producerId)
.next() .next()
.put(work.getId(), work); .put(work.getId(), work);
if (previous != null) {
this.rendererBoPool.release(previous);
}
} }
@Override @Override
public void put(final String producerId, final List<RenderingBo> work) { public void put(final String producerId, final List<RenderingBo> work) {
final var nextMap = this.getWorkingSetByProducer(producerId).next(); final var nextMap = this.getWorkingSetByProducer(producerId).next();
for (final RenderingBo bo : work) { 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 @Override
public Map<String, RenderingBo> getUncommittedWork(final String producerId) { public Map<String, RenderingBo> getUncommittedWork(final String producerId) {
return this.getWorkingSetByProducer(producerId).next(); return Map.copyOf(this.getWorkingSetByProducer(producerId).next());
} }
@Override @Override
@@ -74,25 +90,26 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
@Override @Override
public Map<String, RenderingBo> commit(final String producerId, final boolean clone) { 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) -> { final RendererWork rendererWork = this.workingSet.compute(producerId, (k, v) -> {
if (v == null) { if (v == null) {
return new RendererWork(this.obtainMapFromPool(), this.obtainMapFromPool()); return new RendererWork(this.obtainMapFromPool(), this.obtainMapFromPool());
} }
previousWork.set(v.live()); previousLiveRef.set(v.live());
if (clone) { if (clone) {
final Map<String, RenderingBo> newNext = this.obtainMapFromPool(); final Map<String, RenderingBo> newNext = this.obtainMapFromPool();
newNext.putAll(v.next()); newNext.putAll(v.next());
return new RendererWork(v.next(), newNext); return new RendererWork(v.next(), newNext);
} }
else { else {
v.live().values().forEach(this.rendererBoPool::release);
return new RendererWork(v.next(), this.obtainMapFromPool()); return new RendererWork(v.next(), this.obtainMapFromPool());
} }
}); });
final var previousWorkMap = previousWork.get(); final var previousLiveSet = previousLiveRef.get();
if (previousWorkMap != null) { if (previousLiveSet != null) {
this.releaseBoClasses(previousWorkMap); this.returnMapToPool(previousLiveSet);
} }
return rendererWork.next(); return rendererWork.next();
@@ -102,8 +119,8 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
public void clear(final String producerId) { public void clear(final String producerId) {
final RendererWork rendererWork = this.workingSet.remove(producerId); final RendererWork rendererWork = this.workingSet.remove(producerId);
if (rendererWork != null) { if (rendererWork != null) {
this.releaseBoClasses(rendererWork.live()); rendererWork.live().values().forEach(this.rendererBoPool::release);
this.releaseBoClasses(rendererWork.next()); 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())); 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() { private Map<String, RenderingBo> obtainMapFromPool() {
var map = this.mapsPool.poll(); var map = this.mapsPool.poll();
if (map == null) { if (map == null) {
@@ -150,19 +150,6 @@ public class RendererWorkingSetImpl implements RendererWorkingSet {
this.mapsPool.offer(map); 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) { 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.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; 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 returnedUncommitted = this.workingSet.commit(this.moduleA.getId(), false);
final var currentUncommitted = this.workingSet.getUncommittedWork(this.moduleA.getId()); final var currentUncommitted = this.workingSet.getUncommittedWork(this.moduleA.getId());
assertSame(currentUncommitted, returnedUncommitted); assertTrue(returnedUncommitted.isEmpty());
assertTrue(currentUncommitted.isEmpty()); assertTrue(currentUncommitted.isEmpty());
assertEquals(returnedUncommitted, currentUncommitted);
final List<RenderingBo> live = new ArrayList<>(); final List<RenderingBo> live = new ArrayList<>();
this.workingSet.getLiveSet(live, null); this.workingSet.getLiveSet(live, null);
@@ -109,7 +109,7 @@ public class RendererWorkingSetImplTest {
final var returnedUncommitted = this.workingSet.commit(this.moduleA.getId(), true); final var returnedUncommitted = this.workingSet.commit(this.moduleA.getId(), true);
final var currentUncommitted = this.workingSet.getUncommittedWork(this.moduleA.getId()); final var currentUncommitted = this.workingSet.getUncommittedWork(this.moduleA.getId());
assertSame(currentUncommitted, returnedUncommitted); assertEquals(currentUncommitted, returnedUncommitted);
assertEquals(Map.of("bo1", bo1, "bo2", bo2), currentUncommitted); assertEquals(Map.of("bo1", bo1, "bo2", bo2), currentUncommitted);
final List<RenderingBo> live = new ArrayList<>(); 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(bo1);
verify(this.renderingBoPool, times(1)).release(bo2); verify(this.renderingBoPool, times(1)).release(bo2);
assertEquals(1, bo1.closeStaticCalls());
assertEquals(1, bo2.closeStaticCalls());
} }
@Test @Test
@@ -206,7 +204,7 @@ public class RendererWorkingSetImplTest {
} }
@Test @Test
void secondCommitReleasesPreviousLiveWorkAndCallsCloseStaticOncePerClass() { void secondCommitReleasesPreviousLiveWork() {
final var bo1 = new TestBoA(); final var bo1 = new TestBoA();
bo1.setId("bo1"); bo1.setId("bo1");
final var bo2 = new TestBoA(); 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(bo1);
verify(this.renderingBoPool, times(1)).release(bo2); 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 @Test
@@ -362,8 +355,6 @@ public class RendererWorkingSetImplTest {
private static abstract class TestBoBase implements RenderingBo { private static abstract class TestBoBase implements RenderingBo {
private final AtomicInteger closeStaticCalls = new AtomicInteger();
private String id; private String id;
@Override @Override
@@ -483,11 +474,7 @@ public class RendererWorkingSetImplTest {
@Override @Override
public void closeStatic() { public void closeStatic() {
this.closeStaticCalls.incrementAndGet();
}
public int closeStaticCalls() {
return this.closeStaticCalls.get();
} }
@Override @Override