GLRenderer: initial commit of async FB read (including jme panels)
This commit is contained in:
parent
e8f344a0db
commit
860de88298
jme3-core/src/main/java/com/jme3/renderer/opengl
jme3-desktop/src/main/java/com/jme3/system/awt
280
jme3-core/src/main/java/com/jme3/renderer/opengl/AsyncFrameReader.java
Executable file
280
jme3-core/src/main/java/com/jme3/renderer/opengl/AsyncFrameReader.java
Executable file
@ -0,0 +1,280 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.renderer.opengl;
|
||||
|
||||
import com.jme3.renderer.RenderContext;
|
||||
import com.jme3.renderer.RendererException;
|
||||
import com.jme3.texture.FrameBuffer;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
/**
|
||||
* @author Kirill Vainer
|
||||
*/
|
||||
final class AsyncFrameReader {
|
||||
|
||||
private final ArrayList<PixelBuffer> pboPool = new ArrayList<PixelBuffer>();
|
||||
private final List<FrameBufferReadRequest> pending = Collections.synchronizedList(new ArrayList<FrameBufferReadRequest>());
|
||||
private final GLRenderer renderer;
|
||||
private final GL gl;
|
||||
private final GLExt glext;
|
||||
private final IntBuffer intBuf = BufferUtils.createIntBuffer(1);
|
||||
private final RenderContext context;
|
||||
private final Thread glThread;
|
||||
|
||||
AsyncFrameReader(GLRenderer renderer, GL gl, GLExt glext, RenderContext context) {
|
||||
this.renderer = renderer;
|
||||
this.gl = gl;
|
||||
this.glext = glext;
|
||||
this.context = context;
|
||||
this.glThread = Thread.currentThread();
|
||||
}
|
||||
|
||||
private PixelBuffer acquirePixelBuffer(int dataSize) {
|
||||
PixelBuffer pb;
|
||||
|
||||
if (pboPool.isEmpty()) {
|
||||
// create PBO
|
||||
pb = new PixelBuffer();
|
||||
intBuf.clear();
|
||||
gl.glGenBuffers(intBuf);
|
||||
pb.id = intBuf.get(0);
|
||||
} else {
|
||||
// reuse PBO.
|
||||
pb = pboPool.remove(pboPool.size() - 1);
|
||||
}
|
||||
|
||||
// resize or allocate PBO if required.
|
||||
if (pb.size != dataSize) {
|
||||
if (context.boundPixelPackPBO != pb.id) {
|
||||
gl.glBindBuffer(GLExt.GL_PIXEL_PACK_BUFFER_ARB, pb.id);
|
||||
context.boundPixelPackPBO = pb.id;
|
||||
}
|
||||
gl.glBufferData(GLExt.GL_PIXEL_PACK_BUFFER_ARB, dataSize, GL.GL_STREAM_READ);
|
||||
}
|
||||
|
||||
pb.size = dataSize;
|
||||
|
||||
return pb;
|
||||
}
|
||||
|
||||
private void readFrameBufferFromPBO(FrameBufferReadRequest fbrr) {
|
||||
// assumes waitForCompletion was already called!
|
||||
if (context.boundPixelPackPBO != fbrr.pb.id) {
|
||||
gl.glBindBuffer(GLExt.GL_PIXEL_PACK_BUFFER_ARB, fbrr.pb.id);
|
||||
context.boundPixelPackPBO = fbrr.pb.id;
|
||||
}
|
||||
gl.glGetBufferSubData(GLExt.GL_PIXEL_PACK_BUFFER_ARB, 0, fbrr.targetBuf);
|
||||
}
|
||||
|
||||
private boolean waitForCompletion(FrameBufferReadRequest fbrr, long time, TimeUnit unit, boolean flush) {
|
||||
int flags = flush ? GLExt.GL_SYNC_FLUSH_COMMANDS_BIT : 0;
|
||||
long nanos = unit.toNanos(time);
|
||||
switch (glext.glClientWaitSync(fbrr.fence, flags, nanos)) {
|
||||
case GLExt.GL_ALREADY_SIGNALED:
|
||||
case GLExt.GL_CONDITION_SATISFIED:
|
||||
return true;
|
||||
case GLExt.GL_TIMEOUT_EXPIRED:
|
||||
return false;
|
||||
case GLExt.GL_WAIT_FAILED:
|
||||
throw new RendererException("Waiting for fence failed");
|
||||
default:
|
||||
throw new RendererException("Unexpected result from glClientWaitSync");
|
||||
}
|
||||
}
|
||||
|
||||
private void signalFinished(FrameBufferReadRequest fbrr) {
|
||||
fbrr.lock.lock();
|
||||
try {
|
||||
fbrr.done = true;
|
||||
fbrr.cond.signalAll();
|
||||
} finally {
|
||||
fbrr.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void signalCancelled(FrameBufferReadRequest fbrr) {
|
||||
fbrr.lock.lock();
|
||||
try {
|
||||
fbrr.cancelled = true;
|
||||
fbrr.cond.signalAll();
|
||||
} finally {
|
||||
fbrr.lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateReadRequests() {
|
||||
// Update requests in the order they were made (e.g. earliest first)
|
||||
for (Iterator<FrameBufferReadRequest> it = pending.iterator(); it.hasNext();) {
|
||||
FrameBufferReadRequest fbrr = it.next();
|
||||
|
||||
// Check status for the user... (non-blocking)
|
||||
if (!fbrr.cancelled && !fbrr.done) {
|
||||
// Request a flush if we know clients are waiting
|
||||
// (to speed up the process, or make it take finite time ..)
|
||||
boolean flush = false; // fbrr.clientsWaiting.get() > 0;
|
||||
if (waitForCompletion(fbrr, 0, TimeUnit.NANOSECONDS, flush)) {
|
||||
if (!fbrr.cancelled) {
|
||||
// Operation completed.
|
||||
// Read data into user's ByteBuffer
|
||||
readFrameBufferFromPBO(fbrr);
|
||||
|
||||
// Signal any waiting threads that we are done.
|
||||
// Also, set the done flag.
|
||||
signalFinished(fbrr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fbrr.cancelled || fbrr.done) {
|
||||
// Cleanup
|
||||
// Return the pixel buffer back into the pool.
|
||||
if (!pboPool.contains(fbrr.pb)) {
|
||||
pboPool.add(fbrr.pb);
|
||||
}
|
||||
|
||||
// Remove this request from the pending requests list.
|
||||
it.remove();
|
||||
|
||||
// Get rid of the fence
|
||||
glext.glDeleteSync(fbrr.fence);
|
||||
|
||||
fbrr.pb = null;
|
||||
fbrr.fence = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ByteBuffer getFrameBufferData(FrameBufferReadRequest fbrr, long time, TimeUnit unit)
|
||||
throws InterruptedException, ExecutionException, TimeoutException {
|
||||
|
||||
if (fbrr.cancelled) {
|
||||
throw new CancellationException();
|
||||
}
|
||||
|
||||
if (fbrr.done) {
|
||||
return fbrr.targetBuf;
|
||||
}
|
||||
|
||||
if (glThread == Thread.currentThread()) {
|
||||
// Running on GL thread, hence can use GL commands ..
|
||||
try {
|
||||
// Wait until we reach the fence..
|
||||
|
||||
// PROBLEM: if the user is holding any locks,
|
||||
// they will not be released here,
|
||||
// causing a potential deadlock!
|
||||
if (!waitForCompletion(fbrr, time, unit, true)) {
|
||||
throw new TimeoutException();
|
||||
}
|
||||
|
||||
// Command stream reached this point.
|
||||
if (fbrr.cancelled) {
|
||||
// User not interested in this anymore.
|
||||
throw new CancellationException();
|
||||
} else {
|
||||
// Read data into user's ByteBuffer
|
||||
readFrameBufferFromPBO(fbrr);
|
||||
}
|
||||
|
||||
// Mark it as done, so future get() calls always return.
|
||||
signalFinished(fbrr);
|
||||
|
||||
return fbrr.targetBuf;
|
||||
} catch (RendererException ex) {
|
||||
throw new ExecutionException(ex);
|
||||
}
|
||||
} else {
|
||||
long nanos = unit.toNanos(time);
|
||||
|
||||
fbrr.lock.lock();
|
||||
try {
|
||||
// Not running on GL thread, indicate that we are running
|
||||
// so GL thread can request GPU to finish quicker ...
|
||||
fbrr.clientsWaiting.getAndIncrement();
|
||||
|
||||
// Wait until we finish
|
||||
while (!fbrr.done && !fbrr.cancelled) {
|
||||
if (nanos <= 0L) {
|
||||
throw new TimeoutException();
|
||||
}
|
||||
|
||||
nanos = fbrr.cond.awaitNanos(nanos);
|
||||
}
|
||||
|
||||
if (fbrr.cancelled) {
|
||||
throw new CancellationException();
|
||||
}
|
||||
|
||||
return fbrr.targetBuf;
|
||||
} finally {
|
||||
fbrr.lock.unlock();
|
||||
fbrr.clientsWaiting.getAndDecrement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Future<ByteBuffer> readFrameBufferLater(FrameBuffer fb, ByteBuffer byteBuf) {
|
||||
// Create & allocate a PBO (or reuse an existing one if available)
|
||||
FrameBufferReadRequest fbrr = new FrameBufferReadRequest(this);
|
||||
fbrr.targetBuf = byteBuf;
|
||||
|
||||
int desiredSize = fb.getWidth() * fb.getHeight() * 4;
|
||||
|
||||
if (byteBuf.remaining() != desiredSize) {
|
||||
throw new IllegalArgumentException("Ensure buffer size matches framebuffer size");
|
||||
}
|
||||
|
||||
fbrr.pb = acquirePixelBuffer(desiredSize);
|
||||
|
||||
// Read into PBO (asynchronous)
|
||||
// renderer.readFrameBufferWithGLFormat(fb, null, GL2.GL_BGRA, GL2.GL_UNSIGNED_BYTE, fbrr.pb.id);
|
||||
|
||||
// Insert fence into command stream.
|
||||
fbrr.fence = glext.glFenceSync(GLExt.GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||
|
||||
// Insert into FIFO
|
||||
pending.add(fbrr);
|
||||
|
||||
return fbrr;
|
||||
}
|
||||
}
|
98
jme3-core/src/main/java/com/jme3/renderer/opengl/FrameBufferReadRequest.java
Executable file
98
jme3-core/src/main/java/com/jme3/renderer/opengl/FrameBufferReadRequest.java
Executable file
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.renderer.opengl;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.Condition;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
class PixelBuffer {
|
||||
int id = -1;
|
||||
int size = -1;
|
||||
}
|
||||
|
||||
class FrameBufferReadRequest implements Future<ByteBuffer> {
|
||||
|
||||
AsyncFrameReader reader;
|
||||
Object fence;
|
||||
PixelBuffer pb;
|
||||
ByteBuffer targetBuf;
|
||||
boolean cancelled;
|
||||
boolean done;
|
||||
|
||||
final ReentrantLock lock = new ReentrantLock();
|
||||
final Condition cond = lock.newCondition();
|
||||
final AtomicInteger clientsWaiting = new AtomicInteger(0);
|
||||
|
||||
public FrameBufferReadRequest(AsyncFrameReader reader) {
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||
if (isDone()) {
|
||||
return false;
|
||||
}
|
||||
reader.signalCancelled(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return cancelled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDone() {
|
||||
return done;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer get(long l, TimeUnit tu) throws InterruptedException, ExecutionException, TimeoutException {
|
||||
return reader.getFrameBufferData(this, l, tu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer get() throws InterruptedException, ExecutionException {
|
||||
try {
|
||||
return get(1, TimeUnit.SECONDS);
|
||||
} catch (TimeoutException ex) {
|
||||
throw new ExecutionException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,6 +34,7 @@ package com.jme3.system.awt;
|
||||
import com.jme3.post.SceneProcessor;
|
||||
import com.jme3.renderer.RenderManager;
|
||||
import com.jme3.renderer.ViewPort;
|
||||
import com.jme3.renderer.opengl.GLRenderer;
|
||||
import com.jme3.renderer.queue.RenderQueue;
|
||||
import com.jme3.texture.FrameBuffer;
|
||||
import com.jme3.texture.Image.Format;
|
||||
@ -47,20 +48,19 @@ import java.awt.image.AffineTransformOp;
|
||||
import java.awt.image.BufferStrategy;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class AwtPanel extends Canvas implements SceneProcessor {
|
||||
public class AwtPanel extends Canvas implements JmePanel, SceneProcessor {
|
||||
|
||||
private boolean attachAsMain = false;
|
||||
|
||||
private BufferedImage img;
|
||||
private FrameBuffer fb;
|
||||
private boolean srgb = false;
|
||||
private ByteBuffer byteBuf;
|
||||
private IntBuffer intBuf;
|
||||
// private FrameBuffer fb;
|
||||
private RenderManager rm;
|
||||
private PaintMode paintMode;
|
||||
private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>();
|
||||
@ -75,35 +75,47 @@ public class AwtPanel extends Canvas implements SceneProcessor {
|
||||
// Reshape vars
|
||||
private int newWidth = 1;
|
||||
private int newHeight = 1;
|
||||
private AtomicBoolean reshapeNeeded = new AtomicBoolean(false);
|
||||
private AtomicBoolean reshapeNeeded = new AtomicBoolean(true);
|
||||
private final Object lock = new Object();
|
||||
|
||||
public AwtPanel(PaintMode paintMode){
|
||||
this(paintMode, false);
|
||||
}
|
||||
// Buffer pool and pending buffers
|
||||
private int NUM_FRAMES = 3;
|
||||
private final ArrayBlockingQueue<Future<ByteBuffer>> pendingFrames = new ArrayBlockingQueue<Future<ByteBuffer>>(NUM_FRAMES);
|
||||
private final ArrayBlockingQueue<ByteBuffer> bufferPool = new ArrayBlockingQueue<ByteBuffer>(NUM_FRAMES);
|
||||
private final ArrayList<FrameBuffer> fbs = new ArrayList<FrameBuffer>(NUM_FRAMES);
|
||||
private int frameIndex = 0;
|
||||
|
||||
|
||||
private final ComponentAdapter resizeListener = new ComponentAdapter() {
|
||||
@Override
|
||||
public void componentResized(ComponentEvent e) {
|
||||
onResize(e);
|
||||
}
|
||||
};
|
||||
|
||||
public AwtPanel(PaintMode paintMode, boolean srgb){
|
||||
this.paintMode = paintMode;
|
||||
this.srgb = srgb;
|
||||
|
||||
invalidatePendingFrames();
|
||||
|
||||
if (paintMode == PaintMode.Accelerated){
|
||||
setIgnoreRepaint(true);
|
||||
}
|
||||
|
||||
addComponentListener(new ComponentAdapter(){
|
||||
@Override
|
||||
public void componentResized(ComponentEvent e) {
|
||||
synchronized (lock){
|
||||
int newWidth2 = Math.max(getWidth(), 1);
|
||||
int newHeight2 = Math.max(getHeight(), 1);
|
||||
if (newWidth != newWidth2 || newHeight != newHeight2){
|
||||
newWidth = newWidth2;
|
||||
newHeight = newHeight2;
|
||||
reshapeNeeded.set(true);
|
||||
System.out.println("EDT: componentResized " + newWidth + ", " + newHeight);
|
||||
}
|
||||
}
|
||||
addComponentListener(resizeListener);
|
||||
}
|
||||
|
||||
public void onResize(ComponentEvent e) {
|
||||
synchronized (lock) {
|
||||
int newWidth2 = Math.max(getWidth(), 1);
|
||||
int newHeight2 = Math.max(getHeight(), 1);
|
||||
if (newWidth != newWidth2 || newHeight != newHeight2) {
|
||||
newWidth = newWidth2;
|
||||
newHeight = newHeight2;
|
||||
reshapeNeeded.set(true);
|
||||
System.out.println("EDT: componentResized " + newWidth + ", " + newHeight);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -128,13 +140,13 @@ public class AwtPanel extends Canvas implements SceneProcessor {
|
||||
super.removeNotify();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g){
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
synchronized (lock){
|
||||
g2d.drawImage(img, transformOp, 0, 0);
|
||||
}
|
||||
}
|
||||
// @Override
|
||||
// public void paint(Graphics g){
|
||||
// Graphics2D g2d = (Graphics2D) g;
|
||||
// synchronized (lock){
|
||||
// g2d.drawImage(img, transformOp, 0, 0);
|
||||
// }
|
||||
// }
|
||||
|
||||
public boolean checkVisibilityState(){
|
||||
if (!hasNativePeer.get()){
|
||||
@ -157,24 +169,85 @@ public class AwtPanel extends Canvas implements SceneProcessor {
|
||||
return currentShowing;
|
||||
}
|
||||
|
||||
public void repaintInThread(){
|
||||
// Convert screenshot.
|
||||
byteBuf.clear();
|
||||
rm.getRenderer().readFrameBuffer(fb, byteBuf);
|
||||
// public void repaintInThread(){
|
||||
// // Convert screenshot.
|
||||
// byteBuf.clear();
|
||||
// rm.getRenderer().readFrameBuffer(fb, byteBuf);
|
||||
//
|
||||
// synchronized (lock){
|
||||
// // All operations on img must be synchronized
|
||||
// // as it is accessed from EDT.
|
||||
// Screenshots.convertScreenShot2(intBuf, img);
|
||||
// repaint();
|
||||
// }
|
||||
// }
|
||||
|
||||
public ByteBuffer acquireNextFrame() {
|
||||
if (pendingFrames.isEmpty()) {
|
||||
System.out.println("!!! No pending frames, returning null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
synchronized (lock){
|
||||
// All operations on img must be synchronized
|
||||
// as it is accessed from EDT.
|
||||
Screenshots.convertScreenShot2(intBuf, img);
|
||||
repaint();
|
||||
try {
|
||||
ByteBuffer nextFrame = null;
|
||||
|
||||
// while (!pendingFrames.isEmpty() && pendingFrames.peek().isDone()) {
|
||||
// nextFrame = pendingFrames.take().get();
|
||||
// }
|
||||
//
|
||||
// if (nextFrame != null) {
|
||||
// return nextFrame;
|
||||
// }
|
||||
//
|
||||
// if (pendingFrames.remainingCapacity() == 0) {
|
||||
// Force it to finish ..
|
||||
return pendingFrames.take().get();
|
||||
// }
|
||||
|
||||
// Some frames are pending, none are finished though.
|
||||
// return null;
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
} catch (ExecutionException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void drawFrameInThread(){
|
||||
// Convert screenshot.
|
||||
byteBuf.clear();
|
||||
rm.getRenderer().readFrameBuffer(fb, byteBuf);
|
||||
Screenshots.convertScreenShot2(intBuf, img);
|
||||
public void readNextFrame() {
|
||||
if (bufferPool.isEmpty()) {
|
||||
System.out.println("??? Too many pending frames!");
|
||||
return; // need to draw more frames ..
|
||||
}
|
||||
|
||||
try {
|
||||
int size = fbs.get(frameIndex).getWidth() * fbs.get(frameIndex).getHeight() * 4;
|
||||
ByteBuffer byteBuf = bufferPool.take();
|
||||
byteBuf = BufferUtils.ensureLargeEnough(byteBuf, size);
|
||||
byteBuf.clear();
|
||||
|
||||
GLRenderer renderer = (GLRenderer) rm.getRenderer();
|
||||
Future<ByteBuffer> future = renderer.readFrameBufferLater(fbs.get(frameIndex), byteBuf);
|
||||
if (!pendingFrames.offer(future)) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
frameIndex ++;
|
||||
if (frameIndex >= NUM_FRAMES) {
|
||||
frameIndex = 0;
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void drawFrameInThread(ByteBuffer byteBuf){
|
||||
// Convert the frame into the image so it can be rendered.
|
||||
Screenshots.convertScreenShot2(byteBuf.asIntBuffer(), img);
|
||||
|
||||
// return the frame back to its rightful owner.
|
||||
if (!bufferPool.offer(byteBuf)) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
|
||||
synchronized (lock){
|
||||
// All operations on strategy should be synchronized (?)
|
||||
@ -235,44 +308,65 @@ public class AwtPanel extends Canvas implements SceneProcessor {
|
||||
if (this.rm == null){
|
||||
// First time called in OGL thread
|
||||
this.rm = rm;
|
||||
reshapeInThread(1, 1);
|
||||
// reshapeInThread(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateAccelerated() {
|
||||
readNextFrame();
|
||||
ByteBuffer byteBuf = acquireNextFrame();
|
||||
if (byteBuf != null) {
|
||||
drawFrameInThread(byteBuf);
|
||||
}
|
||||
}
|
||||
|
||||
private void invalidatePendingFrames() {
|
||||
// NOTE: all pending read requests are invalid!
|
||||
for (Future<ByteBuffer> pendingRequest : pendingFrames) {
|
||||
pendingRequest.cancel(true);
|
||||
}
|
||||
pendingFrames.clear();
|
||||
bufferPool.clear();
|
||||
|
||||
// Populate buffer pool.
|
||||
int cap = bufferPool.remainingCapacity();
|
||||
for (int i = 0; i < cap; i++) {
|
||||
bufferPool.add(BufferUtils.createByteBuffer(1 * 1 * 4));
|
||||
}
|
||||
}
|
||||
|
||||
private void reshapeInThread(int width, int height) {
|
||||
byteBuf = BufferUtils.ensureLargeEnough(byteBuf, width * height * 4);
|
||||
intBuf = byteBuf.asIntBuffer();
|
||||
|
||||
if (fb != null) {
|
||||
invalidatePendingFrames();
|
||||
|
||||
for (FrameBuffer fb : fbs) {
|
||||
fb.dispose();
|
||||
fb = null;
|
||||
}
|
||||
fbs.clear();
|
||||
|
||||
for (int i = 0; i < NUM_FRAMES; i++) {
|
||||
FrameBuffer fb = new FrameBuffer(width, height, 1);
|
||||
fb.setDepthBuffer(Format.Depth);
|
||||
fb.setColorBuffer(Format.RGBA8);
|
||||
fbs.add(fb);
|
||||
}
|
||||
|
||||
fb = new FrameBuffer(width, height, 1);
|
||||
fb.setDepthBuffer(Format.Depth);
|
||||
fb.setColorBuffer(Format.RGB8);
|
||||
fb.setSrgb(srgb);
|
||||
|
||||
if (attachAsMain){
|
||||
rm.getRenderer().setMainFrameBufferOverride(fb);
|
||||
}
|
||||
// if (attachAsMain){
|
||||
// rm.getRenderer().setMainFrameBufferOverride(fb);
|
||||
// }
|
||||
|
||||
synchronized (lock){
|
||||
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_BGR);
|
||||
}
|
||||
|
||||
// synchronized (lock){
|
||||
// img = (BufferedImage) getGraphicsConfiguration().createCompatibleImage(width, height);
|
||||
// }
|
||||
|
||||
AffineTransform tx = AffineTransform.getScaleInstance(1, -1);
|
||||
tx.translate(0, -img.getHeight());
|
||||
transformOp = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
|
||||
|
||||
for (ViewPort vp : viewPorts){
|
||||
if (!attachAsMain){
|
||||
vp.setOutputFrameBuffer(fb);
|
||||
}
|
||||
// if (!attachAsMain){
|
||||
// vp.setOutputFrameBuffer(fb);
|
||||
// }
|
||||
vp.getCamera().resize(width, height, true);
|
||||
|
||||
// NOTE: Hack alert. This is done ONLY for custom framebuffers.
|
||||
@ -283,8 +377,9 @@ public class AwtPanel extends Canvas implements SceneProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return fb != null;
|
||||
return rm != null;
|
||||
}
|
||||
|
||||
public void preFrame(float tpf) {
|
||||
@ -298,8 +393,21 @@ public class AwtPanel extends Canvas implements SceneProcessor {
|
||||
// For "PaintMode.OnDemand" only.
|
||||
repaintRequest.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getComponent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameBegin() {
|
||||
if (attachAsMain && rm != null){
|
||||
rm.getRenderer().setMainFrameBufferOverride(fbs.get(frameIndex));
|
||||
}
|
||||
}
|
||||
|
||||
void onFrameEnd() {
|
||||
@Override
|
||||
public void onFrameEnd() {
|
||||
if (reshapeNeeded.getAndSet(false)) {
|
||||
reshapeInThread(newWidth, newHeight);
|
||||
} else {
|
||||
@ -309,26 +417,24 @@ public class AwtPanel extends Canvas implements SceneProcessor {
|
||||
|
||||
switch (paintMode) {
|
||||
case Accelerated:
|
||||
drawFrameInThread();
|
||||
break;
|
||||
case Repaint:
|
||||
repaintInThread();
|
||||
break;
|
||||
case OnRequest:
|
||||
if (repaintRequest.getAndSet(false)) {
|
||||
repaintInThread();
|
||||
}
|
||||
updateAccelerated();
|
||||
break;
|
||||
// case Repaint:
|
||||
// repaintInThread();
|
||||
// break;
|
||||
// case OnRequest:
|
||||
// if (repaintRequest.getAndSet(false)) {
|
||||
// repaintInThread();
|
||||
// }
|
||||
// break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void postFrame(FrameBuffer out) {
|
||||
if (!attachAsMain && out != fb){
|
||||
throw new IllegalStateException("Why did you change the output framebuffer?");
|
||||
}
|
||||
|
||||
// onFrameEnd();
|
||||
// if (!attachAsMain && out != fb){
|
||||
// throw new IllegalStateException("Why did you change the output framebuffer?");
|
||||
// }
|
||||
}
|
||||
|
||||
public void reshape(ViewPort vp, int w, int h) {
|
||||
|
@ -46,8 +46,8 @@ public class AwtPanelsContext implements JmeContext {
|
||||
protected JmeContext actualContext;
|
||||
protected AppSettings settings = new AppSettings(true);
|
||||
protected SystemListener listener;
|
||||
protected ArrayList<AwtPanel> panels = new ArrayList<AwtPanel>();
|
||||
protected AwtPanel inputSource;
|
||||
protected ArrayList<JmePanel> panels = new ArrayList<JmePanel>();
|
||||
protected JmePanel inputSource;
|
||||
|
||||
protected AwtMouseInput mouseInput = new AwtMouseInput();
|
||||
protected AwtKeyInput keyInput = new AwtKeyInput();
|
||||
@ -92,13 +92,13 @@ public class AwtPanelsContext implements JmeContext {
|
||||
}
|
||||
}
|
||||
|
||||
public void setInputSource(AwtPanel panel){
|
||||
public void setInputSource(JmePanel panel){
|
||||
if (!panels.contains(panel))
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
inputSource = panel;
|
||||
mouseInput.setInputSource(panel);
|
||||
keyInput.setInputSource(panel);
|
||||
mouseInput.setInputSource(panel.getComponent());
|
||||
keyInput.setInputSource(panel.getComponent());
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
@ -148,14 +148,14 @@ public class AwtPanelsContext implements JmeContext {
|
||||
public AwtPanelsContext(){
|
||||
}
|
||||
|
||||
public AwtPanel createPanel(PaintMode paintMode){
|
||||
AwtPanel panel = new AwtPanel(paintMode);
|
||||
public JmePanel createPanel(PaintMode paintMode){
|
||||
JmePanel panel = new SwingPanel(paintMode, true);
|
||||
panels.add(panel);
|
||||
return panel;
|
||||
}
|
||||
|
||||
public AwtPanel createPanel(PaintMode paintMode, boolean srgb){
|
||||
AwtPanel panel = new AwtPanel(paintMode, srgb);
|
||||
public JmePanel createPanel(PaintMode paintMode, boolean srgb){
|
||||
JmePanel panel = new SwingPanel(paintMode, srgb);
|
||||
panels.add(panel);
|
||||
return panel;
|
||||
}
|
||||
@ -168,18 +168,18 @@ public class AwtPanelsContext implements JmeContext {
|
||||
// Check if throttle required
|
||||
boolean needThrottle = true;
|
||||
|
||||
for (AwtPanel panel : panels){
|
||||
for (JmePanel panel : panels){
|
||||
if (panel.isActiveDrawing()){
|
||||
needThrottle = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastThrottleState != needThrottle){
|
||||
if (lastThrottleState != needThrottle) {
|
||||
lastThrottleState = needThrottle;
|
||||
if (lastThrottleState){
|
||||
if (lastThrottleState) {
|
||||
System.out.println("OGL: Throttling update loop.");
|
||||
}else{
|
||||
} else {
|
||||
System.out.println("OGL: Ceased throttling update loop.");
|
||||
}
|
||||
}
|
||||
@ -191,9 +191,13 @@ public class AwtPanelsContext implements JmeContext {
|
||||
}
|
||||
}
|
||||
|
||||
for (JmePanel panel : panels){
|
||||
panel.onFrameBegin();
|
||||
}
|
||||
|
||||
listener.update();
|
||||
|
||||
for (AwtPanel panel : panels){
|
||||
for (JmePanel panel : panels){
|
||||
panel.onFrameEnd();
|
||||
}
|
||||
}
|
||||
|
48
jme3-desktop/src/main/java/com/jme3/system/awt/JmePanel.java
Executable file
48
jme3-desktop/src/main/java/com/jme3/system/awt/JmePanel.java
Executable file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.system.awt;
|
||||
|
||||
import com.jme3.renderer.ViewPort;
|
||||
import java.awt.Component;
|
||||
|
||||
public interface JmePanel {
|
||||
|
||||
public void attachTo(boolean overrideMainFramebuffer, ViewPort ... vps);
|
||||
|
||||
public boolean isActiveDrawing();
|
||||
|
||||
public void onFrameBegin();
|
||||
|
||||
public void onFrameEnd();
|
||||
|
||||
public Component getComponent();
|
||||
}
|
382
jme3-desktop/src/main/java/com/jme3/system/awt/SwingPanel.java
Executable file
382
jme3-desktop/src/main/java/com/jme3/system/awt/SwingPanel.java
Executable file
@ -0,0 +1,382 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2015 jMonkeyEngine
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.jme3.system.awt;
|
||||
|
||||
import com.jme3.post.SceneProcessor;
|
||||
import com.jme3.renderer.RenderManager;
|
||||
import com.jme3.renderer.ViewPort;
|
||||
import com.jme3.renderer.opengl.GLRenderer;
|
||||
import com.jme3.renderer.queue.RenderQueue;
|
||||
import com.jme3.texture.FrameBuffer;
|
||||
import com.jme3.texture.Image;
|
||||
import com.jme3.util.BufferUtils;
|
||||
import com.jme3.util.Screenshots;
|
||||
import java.awt.AWTException;
|
||||
import java.awt.BufferCapabilities;
|
||||
import java.awt.Canvas;
|
||||
import java.awt.Component;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.ImageCapabilities;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.event.ComponentAdapter;
|
||||
import java.awt.event.ComponentEvent;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.image.AffineTransformOp;
|
||||
import java.awt.image.BufferStrategy;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
public class SwingPanel extends JPanel implements JmePanel, SceneProcessor {
|
||||
|
||||
private boolean attachAsMain = false;
|
||||
|
||||
private BufferedImage img;
|
||||
// private FrameBuffer fb;
|
||||
private RenderManager rm;
|
||||
private PaintMode paintMode;
|
||||
private ArrayList<ViewPort> viewPorts = new ArrayList<ViewPort>();
|
||||
|
||||
// Visibility/drawing vars
|
||||
private AffineTransformOp transformOp;
|
||||
private AtomicBoolean hasNativePeer = new AtomicBoolean(false);
|
||||
private AtomicBoolean showing = new AtomicBoolean(false);
|
||||
private AtomicBoolean repaintRequest = new AtomicBoolean(false);
|
||||
|
||||
// Reshape vars
|
||||
private int newWidth = 1;
|
||||
private int newHeight = 1;
|
||||
private AtomicBoolean reshapeNeeded = new AtomicBoolean(true);
|
||||
private final Object lock = new Object();
|
||||
|
||||
// Buffer pool and pending buffers
|
||||
private final int NUM_FRAMES = 2;
|
||||
private final ArrayBlockingQueue<Future<ByteBuffer>> pendingFrames = new ArrayBlockingQueue<Future<ByteBuffer>>(NUM_FRAMES);
|
||||
private final ArrayBlockingQueue<ByteBuffer> bufferPool = new ArrayBlockingQueue<ByteBuffer>(NUM_FRAMES);
|
||||
private final ArrayList<FrameBuffer> fbs = new ArrayList<FrameBuffer>(NUM_FRAMES);
|
||||
private int frameIndex = 0;
|
||||
|
||||
private final ComponentAdapter resizeListener = new ComponentAdapter() {
|
||||
@Override
|
||||
public void componentResized(ComponentEvent e) {
|
||||
onResize(e);
|
||||
}
|
||||
};
|
||||
|
||||
public SwingPanel(PaintMode paintMode, boolean srgb){
|
||||
this.paintMode = paintMode;
|
||||
invalidatePendingFrames();
|
||||
addComponentListener(resizeListener);
|
||||
}
|
||||
|
||||
public void onResize(ComponentEvent e) {
|
||||
synchronized (lock) {
|
||||
int newWidth2 = Math.max(getWidth(), 1);
|
||||
int newHeight2 = Math.max(getHeight(), 1);
|
||||
if (newWidth != newWidth2 || newHeight != newHeight2) {
|
||||
newWidth = newWidth2;
|
||||
newHeight = newHeight2;
|
||||
reshapeNeeded.set(true);
|
||||
System.out.println("EDT: componentResized " + newWidth + ", " + newHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getComponent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addNotify(){
|
||||
super.addNotify();
|
||||
|
||||
synchronized (lock){
|
||||
hasNativePeer.set(true);
|
||||
System.out.println("EDT: addNotify");
|
||||
}
|
||||
|
||||
requestFocusInWindow();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeNotify(){
|
||||
synchronized (lock){
|
||||
hasNativePeer.set(false);
|
||||
System.out.println("EDT: removeNotify");
|
||||
}
|
||||
|
||||
super.removeNotify();
|
||||
}
|
||||
|
||||
public boolean checkVisibilityState() {
|
||||
if (!hasNativePeer.get()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean currentShowing = isShowing();
|
||||
if (showing.getAndSet(currentShowing) != currentShowing) {
|
||||
if (currentShowing) {
|
||||
System.out.println("OGL: Enter showing state.");
|
||||
} else {
|
||||
System.out.println("OGL: Exit showing state.");
|
||||
}
|
||||
}
|
||||
return currentShowing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintComponent(Graphics g){
|
||||
Graphics2D g2d = (Graphics2D) g;
|
||||
|
||||
g2d.setRenderingHint(RenderingHints.KEY_RENDERING,
|
||||
RenderingHints.VALUE_RENDER_SPEED);
|
||||
|
||||
ByteBuffer byteBuf = null;
|
||||
|
||||
synchronized (lock){
|
||||
if (pendingFrames.size() > NUM_FRAMES - 1) {
|
||||
byteBuf = acquireNextFrame();
|
||||
}
|
||||
|
||||
if (byteBuf != null) {
|
||||
// Convert the frame into the image so it can be rendered.
|
||||
Screenshots.convertScreenShot2(byteBuf.asIntBuffer(), img);
|
||||
|
||||
try {
|
||||
// return the frame back to its rightful owner.
|
||||
bufferPool.put(byteBuf);
|
||||
} catch (InterruptedException ex) {
|
||||
Logger.getLogger(SwingPanel.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g2d.drawImage(img, transformOp, 0, 0);
|
||||
}
|
||||
|
||||
public ByteBuffer acquireNextFrame() {
|
||||
if (pendingFrames.isEmpty()) {
|
||||
System.out.println("!!! No pending frames, returning null.");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return pendingFrames.take().get();
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
} catch (ExecutionException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Grabs an available buffer from the available frames pool,
|
||||
* reads the OpenGL backbuffer into it, then adds it to the pending frames pool.
|
||||
*/
|
||||
public void readNextFrame() {
|
||||
if (bufferPool.isEmpty()) {
|
||||
System.out.println("??? Too many pending frames!");
|
||||
return; // need to draw more frames ..
|
||||
}
|
||||
|
||||
try {
|
||||
int size = fbs.get(frameIndex).getWidth() * fbs.get(frameIndex).getHeight() * 4;
|
||||
ByteBuffer byteBuf = bufferPool.take();
|
||||
byteBuf = BufferUtils.ensureLargeEnough(byteBuf, size);
|
||||
byteBuf.clear();
|
||||
|
||||
GLRenderer renderer = (GLRenderer) rm.getRenderer();
|
||||
// Future<ByteBuffer> future = renderer.readFrameBufferLater(fbs.get(frameIndex), byteBuf);
|
||||
// if (!pendingFrames.offer(future)) {
|
||||
// throw new AssertionError();
|
||||
// }
|
||||
|
||||
frameIndex ++;
|
||||
if (frameIndex >= NUM_FRAMES) {
|
||||
frameIndex = 0;
|
||||
}
|
||||
} catch (InterruptedException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActiveDrawing() {
|
||||
return paintMode != PaintMode.OnRequest && showing.get();
|
||||
}
|
||||
|
||||
public void attachTo(boolean overrideMainFramebuffer, ViewPort ... vps){
|
||||
if (viewPorts.size() > 0){
|
||||
for (ViewPort vp : viewPorts){
|
||||
vp.setOutputFrameBuffer(null);
|
||||
}
|
||||
viewPorts.get(viewPorts.size()-1).removeProcessor(this);
|
||||
}
|
||||
|
||||
viewPorts.addAll(Arrays.asList(vps));
|
||||
viewPorts.get(viewPorts.size()-1).addProcessor(this);
|
||||
|
||||
this.attachAsMain = overrideMainFramebuffer;
|
||||
}
|
||||
|
||||
public void initialize(RenderManager rm, ViewPort vp) {
|
||||
if (this.rm == null){
|
||||
// First time called in OGL thread
|
||||
this.rm = rm;
|
||||
// reshapeInThread(1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void invalidatePendingFrames() {
|
||||
// NOTE: all pending read requests are invalid!
|
||||
for (Future<ByteBuffer> pendingRequest : pendingFrames) {
|
||||
pendingRequest.cancel(true);
|
||||
}
|
||||
pendingFrames.clear();
|
||||
bufferPool.clear();
|
||||
|
||||
// Populate buffer pool.
|
||||
int cap = bufferPool.remainingCapacity();
|
||||
for (int i = 0; i < cap; i++) {
|
||||
bufferPool.add(BufferUtils.createByteBuffer(1 * 1 * 4));
|
||||
}
|
||||
}
|
||||
|
||||
private void reshapeInThread(int width, int height) {
|
||||
invalidatePendingFrames();
|
||||
|
||||
for (FrameBuffer fb : fbs) {
|
||||
fb.dispose();
|
||||
}
|
||||
|
||||
fbs.clear();
|
||||
|
||||
for (int i = 0; i < NUM_FRAMES; i++) {
|
||||
FrameBuffer fb = new FrameBuffer(width, height, 1);
|
||||
fb.setDepthBuffer(Image.Format.Depth);
|
||||
fb.setColorBuffer(Image.Format.RGBA8);
|
||||
fbs.add(fb);
|
||||
}
|
||||
|
||||
synchronized (lock){
|
||||
img = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
AffineTransform tx = AffineTransform.getScaleInstance(1, -1);
|
||||
tx.translate(0, -img.getHeight());
|
||||
transformOp = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
|
||||
}
|
||||
|
||||
if (attachAsMain) {
|
||||
rm.notifyReshape(width, height);
|
||||
} else {
|
||||
for (ViewPort vp : viewPorts){
|
||||
vp.getCamera().resize(width, height, true);
|
||||
|
||||
// NOTE: Hack alert. This is done ONLY for custom framebuffers.
|
||||
// Main framebuffer should use RenderManager.notifyReshape().
|
||||
for (SceneProcessor sp : vp.getProcessors()){
|
||||
sp.reshape(vp, width, height);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInitialized() {
|
||||
return rm != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preFrame(float tpf) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postQueue(RenderQueue rq) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate(){
|
||||
// For "PaintMode.OnDemand" only.
|
||||
repaintRequest.set(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameBegin() {
|
||||
if (attachAsMain && rm != null){
|
||||
rm.getRenderer().setMainFrameBufferOverride(fbs.get(frameIndex));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFrameEnd() {
|
||||
if (reshapeNeeded.getAndSet(false)) {
|
||||
reshapeInThread(newWidth, newHeight);
|
||||
} else {
|
||||
if (!checkVisibilityState()) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (paintMode) {
|
||||
case Accelerated:
|
||||
case Repaint:
|
||||
readNextFrame();
|
||||
repaint();
|
||||
break;
|
||||
case OnRequest:
|
||||
if (repaintRequest.getAndSet(false)) {
|
||||
readNextFrame();
|
||||
repaint();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void postFrame(FrameBuffer out) {
|
||||
}
|
||||
|
||||
public void reshape(ViewPort vp, int w, int h) {
|
||||
}
|
||||
|
||||
public void cleanup() {
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user