From 8bb2f6dec8ab731b07387b947715fa1959c680e4 Mon Sep 17 00:00:00 2001 From: Sven Göthel Date: Sat, 17 Feb 2024 20:26:10 +0100 Subject: Bug 1489: Lock-Free Double-Buffered 'renderedShapes' causes data-race between rendering & input-edt, use synchronized tripple-buffering Tripple-buffering _almost_ produces zero data-race collisions, however .. it still does rarely -> hence synchronize on the used ArrayList<>. This adds a minimal chance for blocking the input-EDT, but gives correct code & results. Double-buffered 'renderedShapes' was introduced to resolve Bug 1489 in commit 5f9fb7159fa33bc979e5050d384b6939658049bd This solution is tested by passing '-swapInterval 0' via CommandlineOptions for FontView01, UIMediaGrid01 .., i.e. rendering faster than picking and hence provoking the data-race condition. --- src/graphui/classes/com/jogamp/graph/ui/Scene.java | 76 +++++++++++++--------- 1 file changed, 46 insertions(+), 30 deletions(-) (limited to 'src/graphui/classes/com/jogamp/graph/ui/Scene.java') diff --git a/src/graphui/classes/com/jogamp/graph/ui/Scene.java b/src/graphui/classes/com/jogamp/graph/ui/Scene.java index 56756de18..55033286c 100644 --- a/src/graphui/classes/com/jogamp/graph/ui/Scene.java +++ b/src/graphui/classes/com/jogamp/graph/ui/Scene.java @@ -134,7 +134,9 @@ public final class Scene implements Container, GLEventListener { private Shape[] displayShapeArray = new Shape[0]; // reduce memory re-alloc @ display private final List renderedShapesB0 = new ArrayList(); private final List renderedShapesB1 = new ArrayList(); + private final List renderedShapesB2 = new ArrayList(); private volatile List renderedShapes = renderedShapesB1; + private int renderedShapesIdx = 1; private final AtomicReference toolTipActive = new AtomicReference(); private final AtomicReference toolTipHUD = new AtomicReference(); private final List topLevel = new ArrayList(); @@ -490,37 +492,47 @@ public final class Scene implements Container, GLEventListener { Arrays.sort(shapeArray, 0, shapeCount, Shape.ZAscendingComparator); // TreeTool.cullShapes(shapeArray, shapeCount); - final List iShapes = renderedShapes == renderedShapesB0 ? renderedShapesB1 : renderedShapesB0; - iShapes.clear(); - final GL2ES2 gl = drawable.getGL().getGL2ES2(); + final PMVMatrix4f pmv = renderer.getMatrix(); + final List iShapes; + final int iShapeIdx; + switch(renderedShapesIdx) { + case 0: iShapeIdx = 1; iShapes = renderedShapesB1; break; + case 1: iShapeIdx = 2; iShapes = renderedShapesB2; break; + default: iShapeIdx = 0; iShapes = renderedShapesB0; break; + } if( null != clearColor ) { gl.glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]); gl.glClear(clearMask); } - final PMVMatrix4f pmv = renderer.getMatrix(); - renderer.enable(gl, true); - for(int i=0; i shapes = cont.getRenderedShapes(); Shape picked = null; - - for(int i=shapes.size()-1; null == picked && i>=0; --i) { - final Shape s = shapes.get(i); - pmv.pushMv(); - s.applyMatToMv(pmv); - picked = v.visit(s, pmv); - if( s instanceof Container ) { - final Shape childPick = pickForAllRenderedDesc((Container)s, pmv, v); - if( null != childPick ) { - picked = childPick; // child picked overrides group parent! + final List shapes = cont.getRenderedShapes(); + synchronized( shapes ) { // tripple-buffering is just almost enough + for(int i=shapes.size()-1; null == picked && i>=0; --i) { + final Shape s = shapes.get(i); + pmv.pushMv(); + s.applyMatToMv(pmv); + picked = v.visit(s, pmv); + if( s instanceof Container ) { + final Shape childPick = pickForAllRenderedDesc((Container)s, pmv, v); + if( null != childPick ) { + picked = childPick; // child picked overrides group parent! + } } + pmv.popMv(); } - pmv.popMv(); } return picked; } -- cgit v1.2.3