Refactor rendering business object management and improve rendering pipeline
- Introduced RendererWorkingSet and RendererWorkingSetImpl to manage rendering business objects (BOs) more effectively. - Updated RenderingBoPool to support acquiring and releasing BOs with unique IDs. - Enhanced G2DRenderer to utilize the new RendererWorkingSet for managing rendering operations. - Modified ControlRenderingBo and its implementations to use control IDs instead of generic IDs for better identification. - Improved G2DAbstractRenderingBo and its subclasses to streamline rendering logic and resource management. - Added unit tests for RendererWorkingSetImpl to ensure correct behavior of adding, committing, and retrieving rendering BOs. - Removed unnecessary methods and cleaned up code for better readability and maintainability.
This commit is contained in:
@@ -20,6 +20,9 @@ public class ModuleExecutorImpl implements ModuleExecutor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute() {
|
public void execute() {
|
||||||
|
|
||||||
|
// TODO Subscriptions for modules to run only on certain conditions
|
||||||
|
|
||||||
this.runTasks(module -> () -> {
|
this.runTasks(module -> () -> {
|
||||||
if (module instanceof final AbstractClientModule clientModule && this.moduleManager.getModulesDisplayedClientModules().contains(clientModule)) {
|
if (module instanceof final AbstractClientModule clientModule && this.moduleManager.getModulesDisplayedClientModules().contains(clientModule)) {
|
||||||
this.invokeSafe(clientModule, AbstractClientModule::runUiPre);
|
this.invokeSafe(clientModule, AbstractClientModule::runUiPre);
|
||||||
|
|||||||
@@ -1,14 +1,10 @@
|
|||||||
package de.extio.game_engine.renderer;
|
package de.extio.game_engine.renderer;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import de.extio.game_engine.renderer.model.RenderingBo;
|
|
||||||
|
|
||||||
public interface Renderer {
|
public interface Renderer {
|
||||||
|
|
||||||
void show();
|
void show();
|
||||||
|
|
||||||
void run(List<RenderingBo> renderingBOs) throws InterruptedException;
|
void run() throws InterruptedException;
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ import org.springframework.context.annotation.Bean;
|
|||||||
import de.extio.game_engine.event.EventService;
|
import de.extio.game_engine.event.EventService;
|
||||||
import de.extio.game_engine.keyboard.KeycodeRegistry;
|
import de.extio.game_engine.keyboard.KeycodeRegistry;
|
||||||
import de.extio.game_engine.renderer.model.RenderingBo;
|
import de.extio.game_engine.renderer.model.RenderingBo;
|
||||||
|
import de.extio.game_engine.renderer.work.RendererWorkingSet;
|
||||||
|
import de.extio.game_engine.renderer.work.RendererWorkingSetImpl;
|
||||||
|
import de.extio.game_engine.renderer.work.RenderingBoPool;
|
||||||
|
import de.extio.game_engine.renderer.work.RenderingBoPoolImpl;
|
||||||
import de.extio.game_engine.storage.StorageService;
|
import de.extio.game_engine.storage.StorageService;
|
||||||
|
|
||||||
@AutoConfiguration
|
@AutoConfiguration
|
||||||
@@ -37,6 +41,12 @@ public class RendererAutoConfiguration {
|
|||||||
return new RenderingBoPoolImpl(mapping);
|
return new RenderingBoPoolImpl(mapping);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
@ConditionalOnMissingBean
|
||||||
|
RendererWorkingSet rendererWorkingSet(final RenderingBoPool rendererBoPool) {
|
||||||
|
return new RendererWorkingSetImpl(rendererBoPool);
|
||||||
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
RendererData rendererData(final ApplicationContext applicationContext,
|
RendererData rendererData(final ApplicationContext applicationContext,
|
||||||
final Renderer renderer,
|
final Renderer renderer,
|
||||||
@@ -44,9 +54,10 @@ public class RendererAutoConfiguration {
|
|||||||
final RenderingBoPool renderingBoPool,
|
final RenderingBoPool renderingBoPool,
|
||||||
final KeycodeRegistry keycodeRegistry,
|
final KeycodeRegistry keycodeRegistry,
|
||||||
final EventService eventService,
|
final EventService eventService,
|
||||||
final StorageService storageService) {
|
final StorageService storageService,
|
||||||
|
final RendererWorkingSet rendererWorkingSet) {
|
||||||
|
|
||||||
final var rendererData = new RendererData(applicationContext, renderer, rendererControl, renderingBoPool, keycodeRegistry, eventService, storageService);
|
final var rendererData = new RendererData(applicationContext, renderer, rendererControl, renderingBoPool, keycodeRegistry, eventService, storageService, rendererWorkingSet);
|
||||||
renderer.setRendererData(rendererData);
|
renderer.setRendererData(rendererData);
|
||||||
rendererControl.setRendererData(rendererData);
|
rendererControl.setRendererData(rendererData);
|
||||||
renderingBoPool.setRendererData(rendererData);
|
renderingBoPool.setRendererData(rendererData);
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import de.extio.game_engine.event.EventService;
|
|||||||
import de.extio.game_engine.keyboard.KeycodeRegistry;
|
import de.extio.game_engine.keyboard.KeycodeRegistry;
|
||||||
import de.extio.game_engine.renderer.model.options.UiOptions;
|
import de.extio.game_engine.renderer.model.options.UiOptions;
|
||||||
import de.extio.game_engine.renderer.model.options.VideoOptions;
|
import de.extio.game_engine.renderer.model.options.VideoOptions;
|
||||||
|
import de.extio.game_engine.renderer.work.RendererWorkingSet;
|
||||||
|
import de.extio.game_engine.renderer.work.RenderingBoPool;
|
||||||
import de.extio.game_engine.storage.StorageService;
|
import de.extio.game_engine.storage.StorageService;
|
||||||
|
|
||||||
public class RendererData {
|
public class RendererData {
|
||||||
@@ -33,9 +35,11 @@ public class RendererData {
|
|||||||
|
|
||||||
private final StorageService storageService;
|
private final StorageService storageService;
|
||||||
|
|
||||||
|
private final RendererWorkingSet rendererWorkingSet;
|
||||||
|
|
||||||
private RendererLoop rendererLoop;
|
private RendererLoop rendererLoop;
|
||||||
|
|
||||||
public RendererData(final ApplicationContext applicationContext, final Renderer renderer, final RendererControl rendererControl, final RenderingBoPool renderingBoPool, final KeycodeRegistry keycodeRegistry, final EventService eventService, final StorageService storageService) {
|
public RendererData(final ApplicationContext applicationContext, final Renderer renderer, final RendererControl rendererControl, final RenderingBoPool renderingBoPool, final KeycodeRegistry keycodeRegistry, final EventService eventService, final StorageService storageService, final RendererWorkingSet rendererWorkingSet) {
|
||||||
this.applicationContext = applicationContext;
|
this.applicationContext = applicationContext;
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
this.rendererControl = rendererControl;
|
this.rendererControl = rendererControl;
|
||||||
@@ -43,6 +47,7 @@ public class RendererData {
|
|||||||
this.keycodeRegistry = keycodeRegistry;
|
this.keycodeRegistry = keycodeRegistry;
|
||||||
this.eventService = eventService;
|
this.eventService = eventService;
|
||||||
this.storageService = storageService;
|
this.storageService = storageService;
|
||||||
|
this.rendererWorkingSet = rendererWorkingSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ApplicationContext getApplicationContext() {
|
public ApplicationContext getApplicationContext() {
|
||||||
@@ -96,4 +101,8 @@ public class RendererData {
|
|||||||
public StorageService getStorageService() {
|
public StorageService getStorageService() {
|
||||||
return storageService;
|
return storageService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RendererWorkingSet getRendererWorkingSet() {
|
||||||
|
return rendererWorkingSet;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package de.extio.game_engine.renderer;
|
package de.extio.game_engine.renderer;
|
||||||
|
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -41,7 +40,8 @@ public class RendererLoop implements Runnable {
|
|||||||
while (!this.shutdown) {
|
while (!this.shutdown) {
|
||||||
try {
|
try {
|
||||||
this.rendererData.nextFrame();
|
this.rendererData.nextFrame();
|
||||||
this.rendererData.getRenderer().run(new ArrayList<>()); // TODO: Pass actual renderables
|
// TODO run modules here
|
||||||
|
this.rendererData.getRenderer().run();
|
||||||
exceptionCount = 0;
|
exceptionCount = 0;
|
||||||
}
|
}
|
||||||
catch (final InterruptedException e) {
|
catch (final InterruptedException e) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package de.extio.game_engine.renderer.container;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
import de.extio.game_engine.renderer.RendererControl;
|
import de.extio.game_engine.renderer.RendererControl;
|
||||||
import de.extio.game_engine.renderer.RenderingBoPool;
|
import de.extio.game_engine.renderer.work.RenderingBoPool;
|
||||||
import de.extio.game_engine.spatial2.model.Area2;
|
import de.extio.game_engine.spatial2.model.Area2;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import java.util.function.Predicate;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import de.extio.game_engine.renderer.RenderingBoPool;
|
|
||||||
import de.extio.game_engine.renderer.model.RenderingBo;
|
import de.extio.game_engine.renderer.model.RenderingBo;
|
||||||
import de.extio.game_engine.renderer.model.RenderingBoLayer;
|
import de.extio.game_engine.renderer.model.RenderingBoLayer;
|
||||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo;
|
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo;
|
||||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.SliderControl;
|
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.SliderControl;
|
||||||
import de.extio.game_engine.renderer.model.color.RgbaColor;
|
import de.extio.game_engine.renderer.model.color.RgbaColor;
|
||||||
import de.extio.game_engine.renderer.model.event.UiControlEvent;
|
import de.extio.game_engine.renderer.model.event.UiControlEvent;
|
||||||
|
import de.extio.game_engine.renderer.work.RenderingBoPool;
|
||||||
import de.extio.game_engine.spatial2.model.Area2;
|
import de.extio.game_engine.spatial2.model.Area2;
|
||||||
import de.extio.game_engine.spatial2.model.CoordI2;
|
import de.extio.game_engine.spatial2.model.CoordI2;
|
||||||
|
|
||||||
@@ -57,8 +57,8 @@ public class ScrollBar {
|
|||||||
public void render(final List<RenderingBo> renderingBo, final Area2 renderingArea, final int numElements, final RenderingBoPool renderingBoPool) {
|
public void render(final List<RenderingBo> renderingBo, final Area2 renderingArea, final int numElements, final RenderingBoPool renderingBoPool) {
|
||||||
this.lastNumElements = numElements;
|
this.lastNumElements = numElements;
|
||||||
|
|
||||||
final var bo = renderingBoPool.acquire(ControlRenderingBo.class)
|
final var bo = renderingBoPool.acquire(this.name + "_Slider", ControlRenderingBo.class)
|
||||||
.setId(this.name)
|
.setControlId(this.name)
|
||||||
.setType(SliderControl.class)
|
.setType(SliderControl.class)
|
||||||
.setCustomData(Boolean.FALSE)
|
.setCustomData(Boolean.FALSE)
|
||||||
.setCustomData2(Double.valueOf(1.0D - this.scrollPosition))
|
.setCustomData2(Double.valueOf(1.0D - this.scrollPosition))
|
||||||
|
|||||||
@@ -9,12 +9,12 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import de.extio.game_engine.renderer.RendererControl;
|
import de.extio.game_engine.renderer.RendererControl;
|
||||||
import de.extio.game_engine.renderer.RenderingBoPool;
|
|
||||||
import de.extio.game_engine.renderer.model.RenderingBo;
|
import de.extio.game_engine.renderer.model.RenderingBo;
|
||||||
import de.extio.game_engine.renderer.model.RenderingBoLayer;
|
import de.extio.game_engine.renderer.model.RenderingBoLayer;
|
||||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo;
|
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo;
|
||||||
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.WindowCloseButtonControl;
|
import de.extio.game_engine.renderer.model.bo.ControlRenderingBo.WindowCloseButtonControl;
|
||||||
import de.extio.game_engine.renderer.model.color.RgbaColor;
|
import de.extio.game_engine.renderer.model.color.RgbaColor;
|
||||||
|
import de.extio.game_engine.renderer.work.RenderingBoPool;
|
||||||
import de.extio.game_engine.renderer.model.bo.DrawWindowRenderingBo;
|
import de.extio.game_engine.renderer.model.bo.DrawWindowRenderingBo;
|
||||||
import de.extio.game_engine.spatial2.WorldUtils2;
|
import de.extio.game_engine.spatial2.WorldUtils2;
|
||||||
import de.extio.game_engine.spatial2.model.Area2;
|
import de.extio.game_engine.spatial2.model.Area2;
|
||||||
@@ -173,7 +173,7 @@ public class Window implements HasPositionAndDimension2, AutoCloseable {
|
|||||||
this.updateArea();
|
this.updateArea();
|
||||||
}
|
}
|
||||||
|
|
||||||
var bo = this.renderingBoPool.acquire(DrawWindowRenderingBo.class)
|
var bo = this.renderingBoPool.acquire(this.name + "_Window", DrawWindowRenderingBo.class)
|
||||||
.setThickBorder(this.draggable)
|
.setThickBorder(this.draggable)
|
||||||
.withDimensionAbsolute(this.area.getDimension())
|
.withDimensionAbsolute(this.area.getDimension())
|
||||||
.withPositionAbsoluteAnchorTopLeft(this.area.getPosition())
|
.withPositionAbsoluteAnchorTopLeft(this.area.getPosition())
|
||||||
@@ -184,9 +184,9 @@ public class Window implements HasPositionAndDimension2, AutoCloseable {
|
|||||||
consumer.accept(renderingBo, bo);
|
consumer.accept(renderingBo, bo);
|
||||||
|
|
||||||
if (this.drawCloseButton) {
|
if (this.drawCloseButton) {
|
||||||
bo = this.renderingBoPool.acquire(ControlRenderingBo.class)
|
bo = this.renderingBoPool.acquire(this.name + "_Close", ControlRenderingBo.class)
|
||||||
.setType(WindowCloseButtonControl.class)
|
.setType(WindowCloseButtonControl.class)
|
||||||
.setId(this.name + "_Close")
|
.setControlId(this.name + "_Close")
|
||||||
.setCustomData(this.draggable)
|
.setCustomData(this.draggable)
|
||||||
.setEnabled(true)
|
.setEnabled(true)
|
||||||
.setVisible(true)
|
.setVisible(true)
|
||||||
|
|||||||
@@ -8,9 +8,8 @@ import java.awt.image.BufferedImage;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.HashSet;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.locks.Lock;
|
import java.util.concurrent.locks.Lock;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
@@ -25,7 +24,6 @@ import de.extio.game_engine.renderer.RendererData;
|
|||||||
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.G2DDrawFpsHistory;
|
import de.extio.game_engine.renderer.g2d.bo.rendering.G2DDrawFpsHistory;
|
||||||
import de.extio.game_engine.renderer.g2d.bo.rendering.G2DDrawImage;
|
|
||||||
import de.extio.game_engine.renderer.g2d.control.G2DDrawControl;
|
import de.extio.game_engine.renderer.g2d.control.G2DDrawControl;
|
||||||
import de.extio.game_engine.renderer.g2d.control.G2DDrawControlTooltip;
|
import de.extio.game_engine.renderer.g2d.control.G2DDrawControlTooltip;
|
||||||
import de.extio.game_engine.renderer.model.RenderingBo;
|
import de.extio.game_engine.renderer.model.RenderingBo;
|
||||||
@@ -43,10 +41,12 @@ public class G2DRenderer implements Renderer {
|
|||||||
|
|
||||||
private final RingBuffer<Integer> fpsHistory = new RingBuffer<>(90);
|
private final RingBuffer<Integer> fpsHistory = new RingBuffer<>(90);
|
||||||
|
|
||||||
private final Set<Class<? extends RenderingBo>> releasedBoClasses = new HashSet<>();
|
|
||||||
|
|
||||||
private final StringBuilder fpsStringBuilder = new StringBuilder(15);
|
private final StringBuilder fpsStringBuilder = new StringBuilder(15);
|
||||||
|
|
||||||
|
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true);
|
||||||
|
|
||||||
|
private final List<RenderingBo> renderingBOs = new ArrayList<>();
|
||||||
|
|
||||||
private volatile G2DMainFrame mainFrame;
|
private volatile G2DMainFrame mainFrame;
|
||||||
|
|
||||||
private RendererData rendererData;
|
private RendererData rendererData;
|
||||||
@@ -61,8 +61,6 @@ public class G2DRenderer implements Renderer {
|
|||||||
|
|
||||||
private long frameDur;
|
private long frameDur;
|
||||||
|
|
||||||
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(true);
|
|
||||||
|
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
private volatile boolean takeScreenshot;
|
private volatile boolean takeScreenshot;
|
||||||
@@ -125,7 +123,7 @@ public class G2DRenderer implements Renderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run(final List<RenderingBo> renderingBOs) throws InterruptedException {
|
public void run() throws InterruptedException {
|
||||||
try {
|
try {
|
||||||
this.rwLock.readLock().lockInterruptibly();
|
this.rwLock.readLock().lockInterruptibly();
|
||||||
try {
|
try {
|
||||||
@@ -150,8 +148,9 @@ public class G2DRenderer implements Renderer {
|
|||||||
screenshotGraphics.setRenderingHints(G2DRenderingHintFactory.createDefault());
|
screenshotGraphics.setRenderingHints(G2DRenderingHintFactory.createDefault());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addStaticRenderingBOs(renderingBOs);
|
this.addStaticRenderingBOs();
|
||||||
renderingBOs.sort((bo0, bo1) -> bo0.getLayer().compareTo(bo1.getLayer()));
|
this.rendererData.getRendererWorkingSet().getLiveSet(this.renderingBOs);
|
||||||
|
this.renderingBOs.sort((bo0, bo1) -> bo0.getLayer().compareTo(bo1.getLayer()));
|
||||||
|
|
||||||
Graphics2D screenGraphics = null;
|
Graphics2D screenGraphics = null;
|
||||||
try {
|
try {
|
||||||
@@ -166,11 +165,11 @@ 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());
|
||||||
|
|
||||||
for (final RenderingBo renderingBO : renderingBOs) {
|
for (final RenderingBo renderingBO : this.renderingBOs) {
|
||||||
if (renderingBO instanceof G2DAbstractRenderingBo) {
|
if (renderingBO instanceof final G2DAbstractRenderingBo g2dAbstractRenderingBo) {
|
||||||
((G2DAbstractRenderingBo) renderingBO).render(screenGraphics, this.rendererData.getRendererControl().getScaleFactor(), false);
|
g2dAbstractRenderingBo.render(screenGraphics, this.rendererData.getRendererControl().getScaleFactor(), false);
|
||||||
if (takeScreenshot_ && ((G2DAbstractRenderingBo) renderingBO).isScreenshotRelevant()) {
|
if (takeScreenshot_ && g2dAbstractRenderingBo.isScreenshotRelevant()) {
|
||||||
((G2DAbstractRenderingBo) renderingBO).render(screenshotGraphics, this.rendererData.getRendererControl().getScaleFactor(), true);
|
g2dAbstractRenderingBo.render(screenshotGraphics, this.rendererData.getRendererControl().getScaleFactor(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,14 +191,6 @@ public class G2DRenderer implements Renderer {
|
|||||||
if (screenGraphics != null) {
|
if (screenGraphics != null) {
|
||||||
screenGraphics.dispose();
|
screenGraphics.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.releasedBoClasses.clear();
|
|
||||||
for (final RenderingBo renderingBO : renderingBOs) {
|
|
||||||
if (this.releasedBoClasses.add(renderingBO.getClass()) && renderingBO instanceof final G2DAbstractRenderingBo g2dRenderingBo) {
|
|
||||||
g2dRenderingBo.closeStatic();
|
|
||||||
}
|
|
||||||
this.rendererData.getRenderingBoPool().release(renderingBO);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (takeScreenshot_) {
|
if (takeScreenshot_) {
|
||||||
@@ -207,6 +198,7 @@ public class G2DRenderer implements Renderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
this.renderingBOs.clear();
|
||||||
this.rwLock.readLock().unlock();
|
this.rwLock.readLock().unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,13 +209,14 @@ public class G2DRenderer implements Renderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addStaticRenderingBOs(final List<RenderingBo> renderingBOs) {
|
private void addStaticRenderingBOs() {
|
||||||
renderingBOs.add(this.rendererData.getRenderingBoPool().acquire(G2DDrawBackground.class));
|
this.rendererData.getRendererWorkingSet().add("G2DRenderer", this.rendererData.getRenderingBoPool().acquire("G2DRenderer_background", G2DDrawBackground.class));
|
||||||
renderingBOs.add(this.rendererData.getRenderingBoPool().acquire(G2DDrawControlTooltip.class));
|
this.rendererData.getRendererWorkingSet().add("G2DRenderer", this.rendererData.getRenderingBoPool().acquire("G2DRenderer_control", G2DDrawControlTooltip.class));
|
||||||
this.drawStatistics(renderingBOs);
|
this.drawStatistics();
|
||||||
|
this.rendererData.getRendererWorkingSet().commit("G2DRenderer", false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawStatistics(final List<RenderingBo> renderingBOs) {
|
private void drawStatistics() {
|
||||||
if (!this.rendererData.getUiOptions().isDrawFps()) {
|
if (!this.rendererData.getUiOptions().isDrawFps()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -236,20 +229,20 @@ public class G2DRenderer implements Renderer {
|
|||||||
fpsStringBuilder.append(this.frameDur);
|
fpsStringBuilder.append(this.frameDur);
|
||||||
fpsStringBuilder.append("ms");
|
fpsStringBuilder.append("ms");
|
||||||
|
|
||||||
final var drawFont = this.rendererData.getRenderingBoPool().acquire(DrawFontRenderingBo.class)
|
final var drawFont = this.rendererData.getRenderingBoPool().acquire("g2DRenderer_fpsText", DrawFontRenderingBo.class)
|
||||||
.setText(fpsStringBuilder.toString())
|
.setText(fpsStringBuilder.toString())
|
||||||
.setSize(14)
|
.setSize(14)
|
||||||
.setColor(RgbaColor.WHITE)
|
.setColor(RgbaColor.WHITE)
|
||||||
.setLayer(RenderingBoLayer.TOP)
|
.setLayer(RenderingBoLayer.TOP)
|
||||||
.withPositionAbsoluteAnchorTopRight(100, 35);
|
.withPositionAbsoluteAnchorTopRight(100, 35);
|
||||||
renderingBOs.add(drawFont);
|
this.rendererData.getRendererWorkingSet().add("G2DRenderer", drawFont);
|
||||||
|
|
||||||
this.fpsHistory.add(Integer.valueOf((int) this.frameDur));
|
this.fpsHistory.add(Integer.valueOf((int) this.frameDur));
|
||||||
final var drawFpsHistory = this.rendererData.getRenderingBoPool().acquire(G2DDrawFpsHistory.class)
|
final var drawFpsHistory = this.rendererData.getRenderingBoPool().acquire("g2DRenderer_fpsHistory", G2DDrawFpsHistory.class)
|
||||||
.setHistory(this.fpsHistory)
|
.setHistory(this.fpsHistory)
|
||||||
.setLayer(RenderingBoLayer.TOP)
|
.setLayer(RenderingBoLayer.TOP)
|
||||||
.withPositionAbsoluteAnchorTopRight(100, 50);
|
.withPositionAbsoluteAnchorTopRight(100, 50);
|
||||||
renderingBOs.add(drawFpsHistory);
|
this.rendererData.getRendererWorkingSet().add("G2DRenderer", drawFpsHistory);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void frameCap() throws InterruptedException {
|
private void frameCap() throws InterruptedException {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ public abstract class G2DAbstractRenderingBo extends AbstractRenderingBo {
|
|||||||
|
|
||||||
public abstract void render(Graphics2D graphics, double scaleFactor, boolean force);
|
public abstract void render(Graphics2D graphics, double scaleFactor, boolean force);
|
||||||
|
|
||||||
|
@Override
|
||||||
public void closeStatic() {
|
public void closeStatic() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,9 +80,6 @@ public class G2DDrawBackground extends G2DAbstractRenderingBo {
|
|||||||
this.loadImage(graphics, index, backgroundName);
|
this.loadImage(graphics, index, backgroundName);
|
||||||
}
|
}
|
||||||
if (G2DDrawBackground.BACKGROUND_IMAGE == null) {
|
if (G2DDrawBackground.BACKGROUND_IMAGE == null) {
|
||||||
if (index == 0) {
|
|
||||||
this.fillBlack(graphics, viewPort);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (BACKGROUND_IMAGE.validate(graphics.getDeviceConfiguration()) != VolatileImage.IMAGE_OK) {
|
else if (BACKGROUND_IMAGE.validate(graphics.getDeviceConfiguration()) != VolatileImage.IMAGE_OK) {
|
||||||
@@ -162,11 +159,6 @@ public class G2DDrawBackground extends G2DAbstractRenderingBo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fillBlack(final Graphics2D graphics, final CoordI2 viewPort) {
|
|
||||||
graphics.setColor(Color.BLACK);
|
|
||||||
graphics.fillRect(0, 0, viewPort.getX(), viewPort.getY());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadImage(final Graphics2D graphics, final int index, final String backgroundName) {
|
private void loadImage(final Graphics2D graphics, final int index, final String backgroundName) {
|
||||||
if (G2DDrawBackground.BACKGROUND_IMAGE != null) {
|
if (G2DDrawBackground.BACKGROUND_IMAGE != null) {
|
||||||
G2DDrawBackground.BACKGROUND_IMAGE.flush();
|
G2DDrawBackground.BACKGROUND_IMAGE.flush();
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public class G2DDrawControl extends G2DAbstractRenderingBo implements ControlRen
|
|||||||
|
|
||||||
public static Map<String, List<BaseControl>> CONTROL_GROUPS = new HashMap<>();
|
public static Map<String, List<BaseControl>> CONTROL_GROUPS = new HashMap<>();
|
||||||
|
|
||||||
private String id;
|
private String controlId;
|
||||||
|
|
||||||
private Class<? extends BaseControl> clazz;
|
private Class<? extends BaseControl> clazz;
|
||||||
|
|
||||||
@@ -68,14 +68,14 @@ public class G2DDrawControl extends G2DAbstractRenderingBo implements ControlRen
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ControlRenderingBo setId(final String id) {
|
public ControlRenderingBo setControlId(final String id) {
|
||||||
this.id = id;
|
this.controlId = id;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getControlId() {
|
||||||
return this.id;
|
return this.controlId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -146,7 +146,7 @@ public class G2DDrawControl extends G2DAbstractRenderingBo implements ControlRen
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render(final Graphics2D graphics, final double scaleFactor, final boolean force) {
|
public void render(final Graphics2D graphics, final double scaleFactor, final boolean force) {
|
||||||
var control = (G2DBaseControlImpl) CACHED_CONTROLS.get(this.id);
|
var control = (G2DBaseControlImpl) CACHED_CONTROLS.get(this.controlId);
|
||||||
if (control == null) {
|
if (control == null) {
|
||||||
if (this.clazz == ButtonControl.class) {
|
if (this.clazz == ButtonControl.class) {
|
||||||
control = new G2DButtonControlImpl();
|
control = new G2DButtonControlImpl();
|
||||||
@@ -189,7 +189,7 @@ public class G2DDrawControl extends G2DAbstractRenderingBo implements ControlRen
|
|||||||
control.setScaleFactor(scaleFactor)
|
control.setScaleFactor(scaleFactor)
|
||||||
.setControlGroup(this.controlGroup)
|
.setControlGroup(this.controlGroup)
|
||||||
.setRendererData(this.rendererData)
|
.setRendererData(this.rendererData)
|
||||||
.setId(this.id);
|
.setControlId(this.controlId);
|
||||||
if (this.clazz == TextfieldControl.class && this.customData instanceof Boolean) {
|
if (this.clazz == TextfieldControl.class && this.customData instanceof Boolean) {
|
||||||
((TextfieldControl) control).setMultiLine(((Boolean) this.customData).booleanValue());
|
((TextfieldControl) control).setMultiLine(((Boolean) this.customData).booleanValue());
|
||||||
}
|
}
|
||||||
@@ -197,7 +197,7 @@ public class G2DDrawControl extends G2DAbstractRenderingBo implements ControlRen
|
|||||||
|
|
||||||
control.build();
|
control.build();
|
||||||
|
|
||||||
CACHED_CONTROLS.put(this.id, control);
|
CACHED_CONTROLS.put(this.controlId, control);
|
||||||
if (this.controlGroup != null && !this.controlGroup.isEmpty()) {
|
if (this.controlGroup != null && !this.controlGroup.isEmpty()) {
|
||||||
var controls = CONTROL_GROUPS.get(this.controlGroup);
|
var controls = CONTROL_GROUPS.get(this.controlGroup);
|
||||||
if (controls == null) {
|
if (controls == null) {
|
||||||
@@ -315,7 +315,7 @@ public class G2DDrawControl extends G2DAbstractRenderingBo implements ControlRen
|
|||||||
public void close() throws Exception {
|
public void close() throws Exception {
|
||||||
super.close();
|
super.close();
|
||||||
|
|
||||||
this.id = null;
|
this.controlId = null;
|
||||||
this.clazz = null;
|
this.clazz = null;
|
||||||
this.caption = null;
|
this.caption = null;
|
||||||
this.controlGroup = null;
|
this.controlGroup = null;
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ public abstract class G2DBaseControlImpl implements BaseControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseControl setId(final String id) {
|
public BaseControl setControlId(final String id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -234,9 +234,9 @@ public class G2DTableControlImpl extends G2DBaseControlImpl implements TableCont
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseControl setId(final String id) {
|
public BaseControl setControlId(final String id) {
|
||||||
this.scrollbar.setId(id + "_scroll");
|
this.scrollbar.setControlId(id + "_scroll");
|
||||||
return super.setId(id);
|
return super.setControlId(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -353,9 +353,9 @@ public class G2DTextfieldControlImpl2 extends G2DBaseControlImpl implements Text
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BaseControl setId(final String id) {
|
public BaseControl setControlId(final String id) {
|
||||||
this.scrollbar.setId(id + "_scroll");
|
this.scrollbar.setControlId(id + "_scroll");
|
||||||
return super.setId(id);
|
return super.setControlId(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ public abstract class AbstractRenderingBo implements RenderingBo {
|
|||||||
|
|
||||||
protected final static Logger LOGGER = LoggerFactory.getLogger(AbstractRenderingBo.class);
|
protected final static Logger LOGGER = LoggerFactory.getLogger(AbstractRenderingBo.class);
|
||||||
|
|
||||||
|
protected String id;
|
||||||
|
|
||||||
protected RendererData rendererData;
|
protected RendererData rendererData;
|
||||||
|
|
||||||
protected RgbaColor color;
|
protected RgbaColor color;
|
||||||
@@ -36,6 +38,16 @@ public abstract class AbstractRenderingBo implements RenderingBo {
|
|||||||
this.defaultLayer = layer;
|
this.defaultLayer = layer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setId(final String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RenderingBo withPositionRelative(final int x, final int y) {
|
public RenderingBo withPositionRelative(final int x, final int y) {
|
||||||
this.localX = x;
|
this.localX = x;
|
||||||
@@ -203,6 +215,7 @@ public abstract class AbstractRenderingBo implements RenderingBo {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() throws Exception {
|
public void close() throws Exception {
|
||||||
|
this.id = null;
|
||||||
this.color = null;
|
this.color = null;
|
||||||
this.x = 0;
|
this.x = 0;
|
||||||
this.y = 0;
|
this.y = 0;
|
||||||
@@ -213,6 +226,11 @@ public abstract class AbstractRenderingBo implements RenderingBo {
|
|||||||
this.layer = this.defaultLayer;
|
this.layer = this.defaultLayer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void closeStatic() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setRendererData(final RendererData RendererData) {
|
public void setRendererData(final RendererData RendererData) {
|
||||||
this.rendererData = (RendererData) RendererData;
|
this.rendererData = (RendererData) RendererData;
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ import de.extio.game_engine.spatial2.model.HasPosition2;
|
|||||||
*/
|
*/
|
||||||
public interface RenderingBo extends AutoCloseable {
|
public interface RenderingBo extends AutoCloseable {
|
||||||
|
|
||||||
void setRendererData(RendererData RendererData);
|
void setId(String id);
|
||||||
|
|
||||||
|
String getId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a color you want to use for rendering. It depends on the rendering bo how this information is treated.
|
* Sets a color you want to use for rendering. It depends on the rendering bo how this information is treated.
|
||||||
@@ -105,4 +107,8 @@ public interface RenderingBo extends AutoCloseable {
|
|||||||
*/
|
*/
|
||||||
RenderingBo withPositionRelative(HasPosition2 position);
|
RenderingBo withPositionRelative(HasPosition2 position);
|
||||||
|
|
||||||
|
void closeStatic();
|
||||||
|
|
||||||
|
void setRendererData(RendererData RendererData);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,9 @@ import de.extio.game_engine.renderer.model.color.RgbaColor;
|
|||||||
*/
|
*/
|
||||||
public interface ControlRenderingBo extends RenderingBoHasDimension {
|
public interface ControlRenderingBo extends RenderingBoHasDimension {
|
||||||
|
|
||||||
ControlRenderingBo setId(String id);
|
ControlRenderingBo setControlId(String id);
|
||||||
|
|
||||||
String getId();
|
String getControlId();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the type of the control. See class tree of BaseControl
|
* Sets the type of the control. See class tree of BaseControl
|
||||||
@@ -63,7 +63,7 @@ public interface ControlRenderingBo extends RenderingBoHasDimension {
|
|||||||
|
|
||||||
boolean getInUse();
|
boolean getInUse();
|
||||||
|
|
||||||
BaseControl setId(String id);
|
BaseControl setControlId(String id);
|
||||||
|
|
||||||
BaseControl setCaption(String caption);
|
BaseControl setCaption(String caption);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package de.extio.game_engine.renderer.work;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import de.extio.game_engine.renderer.model.RenderingBo;
|
||||||
|
|
||||||
|
public interface RendererWorkingSet {
|
||||||
|
|
||||||
|
void add(String producer, RenderingBo work);
|
||||||
|
|
||||||
|
void add(String producer, List<RenderingBo> work);
|
||||||
|
|
||||||
|
RenderingBo get(String producer, String id);
|
||||||
|
|
||||||
|
Map<String, RenderingBo> getUncommittedWork(String producer);
|
||||||
|
|
||||||
|
Map<String, RenderingBo> commit(String producer, boolean clone);
|
||||||
|
|
||||||
|
void getLiveSet(List<RenderingBo> combinedLiveSet);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
package de.extio.game_engine.renderer.work;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import de.extio.game_engine.renderer.model.RenderingBo;
|
||||||
|
|
||||||
|
public class RendererWorkingSetImpl implements RendererWorkingSet {
|
||||||
|
|
||||||
|
private final ConcurrentMap<String, RendererWork> workingSet = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private final BlockingQueue<Map<String, RenderingBo>> mapsPool = new ArrayBlockingQueue<>(100);
|
||||||
|
|
||||||
|
private final BlockingQueue<Set<Class<? extends RenderingBo>>> setsPool = new ArrayBlockingQueue<>(100);
|
||||||
|
|
||||||
|
private final RenderingBoPool rendererBoPool;
|
||||||
|
|
||||||
|
public RendererWorkingSetImpl(final RenderingBoPool rendererBoPool) {
|
||||||
|
this.rendererBoPool = rendererBoPool;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(final String producer, final RenderingBo work) {
|
||||||
|
this.getWorkingSetByProducer(producer)
|
||||||
|
.next()
|
||||||
|
.put(work.getId(), work);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(final String producer, final List<RenderingBo> work) {
|
||||||
|
final var nextMap = this.getWorkingSetByProducer(producer).next();
|
||||||
|
for (final RenderingBo bo : work) {
|
||||||
|
nextMap.put(bo.getId(), bo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, RenderingBo> getUncommittedWork(final String producer) {
|
||||||
|
return this.getWorkingSetByProducer(producer).next();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingBo get(final String producer, final String id) {
|
||||||
|
return this.getWorkingSetByProducer(producer).next().get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, RenderingBo> commit(final String producer, final boolean clone) {
|
||||||
|
final AtomicReference<Map<String, RenderingBo>> previousWork = new AtomicReference<>();
|
||||||
|
final RendererWork rendererWork = this.workingSet.compute(producer, (k, v) -> {
|
||||||
|
if (v == null) {
|
||||||
|
return new RendererWork(this.obtainMapFromPool(), this.obtainMapFromPool());
|
||||||
|
}
|
||||||
|
previousWork.set(v.live());
|
||||||
|
if (clone) {
|
||||||
|
final Map<String, RenderingBo> newNext = this.obtainMapFromPool();
|
||||||
|
newNext.putAll(v.next());
|
||||||
|
return new RendererWork(v.next(), newNext);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return new RendererWork(v.next(), this.obtainMapFromPool());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
final var previousWorkMap = previousWork.get();
|
||||||
|
if (previousWorkMap != null) {
|
||||||
|
final var releasedBoClasses = this.obtainSetFromPool();
|
||||||
|
for (final RenderingBo renderingBO : previousWorkMap.values()) {
|
||||||
|
if (releasedBoClasses.add(renderingBO.getClass())) {
|
||||||
|
renderingBO.closeStatic();
|
||||||
|
}
|
||||||
|
this.rendererBoPool.release(renderingBO);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.returnMapToPool(previousWorkMap);
|
||||||
|
this.returnSetToPool(releasedBoClasses);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rendererWork.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getLiveSet(final List<RenderingBo> combinedLiveSet) {
|
||||||
|
this.workingSet.values().forEach(rendererWork -> combinedLiveSet.addAll(rendererWork.live().values()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private RendererWork getWorkingSetByProducer(final String producer) {
|
||||||
|
return this.workingSet.computeIfAbsent(producer, k -> new RendererWork(this.obtainMapFromPool(), this.obtainMapFromPool()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, RenderingBo> obtainMapFromPool() {
|
||||||
|
var map = this.mapsPool.poll();
|
||||||
|
if (map == null) {
|
||||||
|
map = Collections.synchronizedMap(new HashMap<>());
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void returnMapToPool(final Map<String, RenderingBo> map) {
|
||||||
|
map.clear();
|
||||||
|
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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package de.extio.game_engine.renderer;
|
package de.extio.game_engine.renderer.work;
|
||||||
|
|
||||||
|
import de.extio.game_engine.renderer.RendererData;
|
||||||
import de.extio.game_engine.renderer.model.RenderingBo;
|
import de.extio.game_engine.renderer.model.RenderingBo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -14,7 +15,7 @@ public interface RenderingBoPool {
|
|||||||
/**
|
/**
|
||||||
* Acquires a rendering business object by type from the pool.
|
* Acquires a rendering business object by type from the pool.
|
||||||
*/
|
*/
|
||||||
<T extends RenderingBo> T acquire(Class<T> clazz);
|
<T extends RenderingBo> T acquire(String id, Class<T> clazz);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Releases a rendering business object back to the pool. This is usually managed by the renderer.
|
* Releases a rendering business object back to the pool. This is usually managed by the renderer.
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
package de.extio.game_engine.renderer;
|
package de.extio.game_engine.renderer.work;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import de.extio.game_engine.renderer.RendererData;
|
||||||
import de.extio.game_engine.renderer.model.RenderingBo;
|
import de.extio.game_engine.renderer.model.RenderingBo;
|
||||||
|
|
||||||
public class RenderingBoPoolImpl implements RenderingBoPool {
|
public class RenderingBoPoolImpl implements RenderingBoPool {
|
||||||
@@ -41,7 +42,7 @@ public class RenderingBoPoolImpl implements RenderingBoPool {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T extends RenderingBo> T acquire(final Class<T> clazz) {
|
public synchronized <T extends RenderingBo> T acquire(final String id, final Class<T> clazz) {
|
||||||
Stack<RenderingBo> pooled;
|
Stack<RenderingBo> pooled;
|
||||||
Class<? extends RenderingBo> impl;
|
Class<? extends RenderingBo> impl;
|
||||||
|
|
||||||
@@ -71,8 +72,8 @@ public class RenderingBoPoolImpl implements RenderingBoPool {
|
|||||||
this.lastMapping.pooled = pooled;
|
this.lastMapping.pooled = pooled;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pooled.isEmpty()) {
|
|
||||||
T instance;
|
T instance;
|
||||||
|
if (pooled.isEmpty()) {
|
||||||
try {
|
try {
|
||||||
instance = clazz.cast(impl.getDeclaredConstructor().newInstance());
|
instance = clazz.cast(impl.getDeclaredConstructor().newInstance());
|
||||||
}
|
}
|
||||||
@@ -80,14 +81,17 @@ public class RenderingBoPoolImpl implements RenderingBoPool {
|
|||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
instance.setRendererData(this.rendererData);
|
instance.setRendererData(this.rendererData);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
instance = (T) pooled.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.setId(id);
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (T) pooled.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void release(final RenderingBo obj) {
|
public synchronized void release(final RenderingBo obj) {
|
||||||
try {
|
try {
|
||||||
obj.close();
|
obj.close();
|
||||||
|
|
||||||
@@ -0,0 +1,378 @@
|
|||||||
|
package de.extio.game_engine.renderer.work;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
import de.extio.game_engine.renderer.RendererData;
|
||||||
|
import de.extio.game_engine.renderer.model.RenderingBo;
|
||||||
|
import de.extio.game_engine.renderer.model.RenderingBoLayer;
|
||||||
|
import de.extio.game_engine.renderer.model.color.RgbaColor;
|
||||||
|
import de.extio.game_engine.spatial2.model.HasPosition2;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
public class RendererWorkingSetImplTest {
|
||||||
|
|
||||||
|
private RendererWorkingSetImpl workingSet;
|
||||||
|
|
||||||
|
private RenderingBoPool renderingBoPool;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
this.renderingBoPool = mock(RenderingBoPool.class);
|
||||||
|
this.workingSet = new RendererWorkingSetImpl(this.renderingBoPool);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addSingleAddsToUncommittedWork() {
|
||||||
|
final RenderingBo bo1 = mock(RenderingBo.class);
|
||||||
|
when(bo1.getId()).thenReturn("bo1");
|
||||||
|
this.workingSet.add("producer", bo1);
|
||||||
|
|
||||||
|
final var uncommitted = this.workingSet.getUncommittedWork("producer");
|
||||||
|
assertNotNull(uncommitted);
|
||||||
|
assertEquals(Map.of("bo1", bo1), uncommitted);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addListAddsAllToUncommittedWork() {
|
||||||
|
final RenderingBo bo1 = mock(RenderingBo.class);
|
||||||
|
when(bo1.getId()).thenReturn("bo1");
|
||||||
|
final RenderingBo bo2 = mock(RenderingBo.class);
|
||||||
|
when(bo2.getId()).thenReturn("bo2");
|
||||||
|
|
||||||
|
this.workingSet.add("producer", List.of(bo1, bo2));
|
||||||
|
|
||||||
|
final var uncommitted = this.workingSet.getUncommittedWork("producer");
|
||||||
|
assertEquals(Map.of("bo1", bo1, "bo2", bo2), uncommitted);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void commitWithoutCloneMovesUncommittedToLiveAndClearsUncommitted() {
|
||||||
|
final RenderingBo bo1 = mock(RenderingBo.class);
|
||||||
|
when(bo1.getId()).thenReturn("bo1");
|
||||||
|
final RenderingBo bo2 = mock(RenderingBo.class);
|
||||||
|
when(bo2.getId()).thenReturn("bo2");
|
||||||
|
|
||||||
|
this.workingSet.add("producer", bo1);
|
||||||
|
this.workingSet.add("producer", bo2);
|
||||||
|
|
||||||
|
final var returnedUncommitted = this.workingSet.commit("producer", false);
|
||||||
|
final var currentUncommitted = this.workingSet.getUncommittedWork("producer");
|
||||||
|
|
||||||
|
assertSame(currentUncommitted, returnedUncommitted);
|
||||||
|
assertTrue(currentUncommitted.isEmpty());
|
||||||
|
|
||||||
|
final List<RenderingBo> live = new ArrayList<>();
|
||||||
|
this.workingSet.getLiveSet(live);
|
||||||
|
assertEquals(2, live.size());
|
||||||
|
assertTrue(live.containsAll(List.of(bo1, bo2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void commitWithCloneCopiesUncommittedIntoNewUncommitted() {
|
||||||
|
final RenderingBo bo1 = mock(RenderingBo.class);
|
||||||
|
when(bo1.getId()).thenReturn("bo1");
|
||||||
|
final RenderingBo bo2 = mock(RenderingBo.class);
|
||||||
|
when(bo2.getId()).thenReturn("bo2");
|
||||||
|
final RenderingBo bo3 = mock(RenderingBo.class);
|
||||||
|
when(bo3.getId()).thenReturn("bo3");
|
||||||
|
|
||||||
|
this.workingSet.add("producer", bo1);
|
||||||
|
this.workingSet.add("producer", bo2);
|
||||||
|
|
||||||
|
final var returnedUncommitted = this.workingSet.commit("producer", true);
|
||||||
|
final var currentUncommitted = this.workingSet.getUncommittedWork("producer");
|
||||||
|
|
||||||
|
assertSame(currentUncommitted, returnedUncommitted);
|
||||||
|
assertEquals(Map.of("bo1", bo1, "bo2", bo2), currentUncommitted);
|
||||||
|
|
||||||
|
final List<RenderingBo> live = new ArrayList<>();
|
||||||
|
this.workingSet.getLiveSet(live);
|
||||||
|
assertEquals(2, live.size());
|
||||||
|
assertTrue(live.containsAll(List.of(bo1, bo2)));
|
||||||
|
|
||||||
|
this.workingSet.add("producer", bo3);
|
||||||
|
final List<RenderingBo> liveAfterAdd = new ArrayList<>();
|
||||||
|
this.workingSet.getLiveSet(liveAfterAdd);
|
||||||
|
assertEquals(2, liveAfterAdd.size());
|
||||||
|
assertTrue(liveAfterAdd.containsAll(List.of(bo1, bo2)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void liveSetContainsAllProducersCommittedWork() {
|
||||||
|
final RenderingBo bo1 = mock(RenderingBo.class);
|
||||||
|
when(bo1.getId()).thenReturn("bo1");
|
||||||
|
final RenderingBo bo2 = mock(RenderingBo.class);
|
||||||
|
when(bo2.getId()).thenReturn("bo2");
|
||||||
|
final RenderingBo bo3 = mock(RenderingBo.class);
|
||||||
|
when(bo3.getId()).thenReturn("bo3");
|
||||||
|
|
||||||
|
this.workingSet.add("p1", bo1);
|
||||||
|
this.workingSet.add("p1", bo2);
|
||||||
|
this.workingSet.add("p2", bo3);
|
||||||
|
|
||||||
|
this.workingSet.commit("p1", false);
|
||||||
|
this.workingSet.commit("p2", false);
|
||||||
|
|
||||||
|
final List<RenderingBo> live = new ArrayList<>();
|
||||||
|
this.workingSet.getLiveSet(live);
|
||||||
|
assertEquals(3, live.size());
|
||||||
|
assertTrue(live.containsAll(List.of(bo1, bo2, bo3)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void commitOnUnknownProducerCreatesEmptyState() {
|
||||||
|
final var returnedUncommitted = this.workingSet.commit("unknown", false);
|
||||||
|
assertNotNull(returnedUncommitted);
|
||||||
|
assertTrue(returnedUncommitted.isEmpty());
|
||||||
|
|
||||||
|
final List<RenderingBo> live = new ArrayList<>();
|
||||||
|
this.workingSet.getLiveSet(live);
|
||||||
|
assertTrue(live.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void secondCommitReleasesPreviousLiveWorkAndCallsCloseStaticOncePerClass() {
|
||||||
|
final var bo1 = new TestBoA();
|
||||||
|
bo1.setId("bo1");
|
||||||
|
final var bo2 = new TestBoA();
|
||||||
|
bo2.setId("bo2");
|
||||||
|
|
||||||
|
this.workingSet.add("producer", bo1);
|
||||||
|
this.workingSet.add("producer", bo2);
|
||||||
|
this.workingSet.commit("producer", false);
|
||||||
|
|
||||||
|
final var bo3 = new TestBoB();
|
||||||
|
bo3.setId("bo3");
|
||||||
|
this.workingSet.add("producer", bo3);
|
||||||
|
this.workingSet.commit("producer", false);
|
||||||
|
|
||||||
|
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
|
||||||
|
void getReturnsRenderingBoFromUncommittedWork() {
|
||||||
|
final RenderingBo bo1 = mock(RenderingBo.class);
|
||||||
|
when(bo1.getId()).thenReturn("bo1");
|
||||||
|
final RenderingBo bo2 = mock(RenderingBo.class);
|
||||||
|
when(bo2.getId()).thenReturn("bo2");
|
||||||
|
|
||||||
|
this.workingSet.add("producer", bo1);
|
||||||
|
this.workingSet.add("producer", bo2);
|
||||||
|
|
||||||
|
final var retrieved1 = this.workingSet.get("producer", "bo1");
|
||||||
|
final var retrieved2 = this.workingSet.get("producer", "bo2");
|
||||||
|
final var notFound = this.workingSet.get("producer", "nonexistent");
|
||||||
|
|
||||||
|
assertSame(bo1, retrieved1);
|
||||||
|
assertSame(bo2, retrieved2);
|
||||||
|
assertTrue(notFound == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void getRetrievesFromUncommittedWorkOnly() {
|
||||||
|
final RenderingBo bo1 = mock(RenderingBo.class);
|
||||||
|
when(bo1.getId()).thenReturn("bo1");
|
||||||
|
final RenderingBo bo2 = mock(RenderingBo.class);
|
||||||
|
when(bo2.getId()).thenReturn("bo2");
|
||||||
|
|
||||||
|
this.workingSet.add("producer", bo1);
|
||||||
|
this.workingSet.add("producer", bo2);
|
||||||
|
final var committedWork = this.workingSet.commit("producer", false);
|
||||||
|
|
||||||
|
// After commit without clone, the uncommitted work should be empty
|
||||||
|
assertTrue(committedWork.isEmpty());
|
||||||
|
|
||||||
|
// get() only retrieves from next set, not live set, so it returns null after commit
|
||||||
|
final var retrieved1 = this.workingSet.get("producer", "bo1");
|
||||||
|
final var retrieved2 = this.workingSet.get("producer", "bo2");
|
||||||
|
|
||||||
|
assertTrue(retrieved1 == null);
|
||||||
|
assertTrue(retrieved2 == null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void addWithSameIdReplacesExistingRenderingBo() {
|
||||||
|
final RenderingBo bo1 = mock(RenderingBo.class);
|
||||||
|
when(bo1.getId()).thenReturn("bo1");
|
||||||
|
final RenderingBo bo1_replacement = mock(RenderingBo.class);
|
||||||
|
when(bo1_replacement.getId()).thenReturn("bo1");
|
||||||
|
|
||||||
|
this.workingSet.add("producer", bo1);
|
||||||
|
final var uncommittedBefore = this.workingSet.getUncommittedWork("producer");
|
||||||
|
assertEquals(1, uncommittedBefore.size());
|
||||||
|
assertSame(bo1, uncommittedBefore.get("bo1"));
|
||||||
|
|
||||||
|
this.workingSet.add("producer", bo1_replacement);
|
||||||
|
final var uncommittedAfter = this.workingSet.getUncommittedWork("producer");
|
||||||
|
assertEquals(1, uncommittedAfter.size());
|
||||||
|
assertSame(bo1_replacement, uncommittedAfter.get("bo1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static abstract class TestBoBase implements RenderingBo {
|
||||||
|
|
||||||
|
private final AtomicInteger closeStaticCalls = new AtomicInteger();
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setId(final String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return this.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingBo setColor(final RgbaColor color) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getX() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getY() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLocalX() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLocalY() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingBo setLayer(final RenderingBoLayer layer) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingBoLayer getLayer() {
|
||||||
|
return RenderingBoLayer.UI0_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingBo withPositionAbsoluteAnchorTopLeft(final int x, final int y) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingBo withPositionAbsoluteAnchorTopLeft(final HasPosition2 position) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingBo withPositionAbsoluteAnchorTopRight(final int x, final int y) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingBo withPositionAbsoluteAnchorTopRight(final HasPosition2 position) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingBo withPositionAbsoluteAnchorBottomLeft(final int x, final int y) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingBo withPositionAbsoluteAnchorBottomLeft(final HasPosition2 position) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingBo withPositionAbsoluteAnchorBottomRight(final int x, final int y) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingBo withPositionAbsoluteAnchorBottomRight(final HasPosition2 position) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingBo withPositionPercentual(final double x, final double y) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingBo withPositionIncrementalAbsolute(final int x, final int y) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingBo withPositionIncrementalAbsolute(final HasPosition2 position) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingBo withPositionIncrementalPercentual(final double x, final double y) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingBo withPositionRelative(final int x, final int y) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RenderingBo withPositionRelative(final HasPosition2 position) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void closeStatic() {
|
||||||
|
this.closeStaticCalls.incrementAndGet();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int closeStaticCalls() {
|
||||||
|
return this.closeStaticCalls.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setRendererData(final RendererData RendererData) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class TestBoA extends TestBoBase {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class TestBoB extends TestBoBase {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user