├── README.md ├── build.xml ├── lwjglfx.jks ├── res ├── gears.css ├── gears.fxml └── lwjgl_32x32.png └── src ├── lwjglfx ├── GUIController.java ├── Gears.java └── JavaFXGears.java └── org └── lwjgl └── util └── stream ├── RenderStream.java ├── RenderStreamINTEL.java ├── RenderStreamPBO.java ├── RenderStreamPBOAMD.java ├── RenderStreamPBOCopy.java ├── RenderStreamPBODefault.java ├── StreamBuffered.java ├── StreamBufferedPBO.java ├── StreamHandler.java ├── StreamUtil.java ├── TextureStream.java ├── TextureStreamINTEL.java ├── TextureStreamPBO.java ├── TextureStreamPBODefault.java └── TextureStreamPBORange.java /README.md: -------------------------------------------------------------------------------- 1 | ## LWJGL-JavaFX Integration Demo 2 | 3 | This repository contains an example of two-way LWJGL/JavaFX integration: 4 | 5 | - LWJGL renders a 3D scene onto an offscreen framebuffer object and the result is displayed inside a JavaFX node. 6 | - A JavaFX node is rendered to an Image and then uploaded to an OpenGL texture, which in turn is rendered within the LWJGL 3D scene. 7 | 8 | The same idea could be applied to windowing systems other than JavaFX. This is what it currently looks like: 9 | 10 | ![Screenshot](http://cloud.github.com/downloads/Spasi/LWJGL-FX/lwjgl_javafx.jpg) 11 | 12 | ## How to Build 13 | 14 | This demo requires JDK 7 or higher and [Apache Ant](http://ant.apache.org/) to build and run. Also: 15 | 16 | - Download an LWJGL distribution and extract it in the lib folder. Preferably the latest [nightly build](http://ci.newdawnsoftware.com/job/LWJGL-git-dist/lastBuild/). 17 | - Open build.xml and set the first two properties, _JDK_ and _LWJGL\_PATH_ to the appropriate values. 18 | 19 | Then type _ant_ or _ant run_. 20 | 21 | ## Implementation Notes 22 | 23 | - **IMPORTANT**: This is a proof-of-concept demo and is not meant to be used in production. The performance overhead is horrible and burns tons of unnecessary 24 | bandwidth/power. I wrote this demo just to showcase what would be possible if JavaFX was open to native OpenGL integration. An efficient integration would 25 | require JavaFX to support (at least) GPU-to-GPU texture/framebuffer copies (via the OpenGL pipeline or even with WGL\_NV\_DX\_interop on Windows). There are 26 | currently no (known) plans for this to happen. 27 | 28 | - The OpenGL rendering currently synchronizes to 60Hz independently of the JavaFX rendering loop. It would be more efficient, and probably result in smoother 29 | animation, if the two were in sync. 30 | 31 | - There are more efficient ways to implement the data transfers from/to the GPU, by taking advantage of the independent copy engines present on modern GPUs. 32 | Such techniques require more threads and OpenGL contexts, but this demo is already complex enough. More details can be found [here](http://on-demand.gputechconf.com/gtc/2012/presentations/S0356-GTC2012-Texture-Transfers.pdf). -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 57 | 60 | 61 | 64 | 67 | 68 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 161 | 162 | 163 | 164 | 165 | 166 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /lwjglfx.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spasi/LWJGL-FX/60484e3df34ae6232e18c8da29a7d36b457921ca/lwjglfx.jks -------------------------------------------------------------------------------- /res/gears.css: -------------------------------------------------------------------------------- 1 | .fps-label { 2 | -fx-effect: dropshadow(three-pass-box, black, 2.0, 1.0, 0.0, 0.0); 3 | } 4 | 5 | .canvas-controls { 6 | -fx-background-color: #ffffff20; 7 | -fx-background-radius: 8px; 8 | -fx-border-width: 1px; 9 | -fx-border-color: #FFF500; 10 | -fx-border-radius: 8px; 11 | } 12 | 13 | .canvas-label { 14 | -fx-effect: dropshadow(three-pass-box, black, 2.0, 0.75, 0.0, 0.0); 15 | } 16 | 17 | .slider > .axis { 18 | -fx-tick-label-fill: #FFF500; 19 | -fx-effect: dropshadow(three-pass-box, black, 2.0, 0.75, 0.0, 0.0); 20 | } -------------------------------------------------------------------------------- /res/gears.fxml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 31 | 36 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /res/lwjgl_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Spasi/LWJGL-FX/60484e3df34ae6232e18c8da29a7d36b457921ca/res/lwjgl_32x32.png -------------------------------------------------------------------------------- /src/lwjglfx/GUIController.java: -------------------------------------------------------------------------------- 1 | package lwjglfx;/* 2 | * Copyright (c) 2002-2012 LWJGL Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of 'LWJGL' nor the names of 17 | * its contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | import org.lwjgl.util.stream.StreamHandler; 34 | import org.lwjgl.util.stream.StreamUtil; 35 | import org.lwjgl.util.stream.StreamUtil.RenderStreamFactory; 36 | import org.lwjgl.util.stream.StreamUtil.TextureStreamFactory; 37 | 38 | import java.net.URL; 39 | import java.nio.ByteBuffer; 40 | import java.util.List; 41 | import java.util.ResourceBundle; 42 | import java.util.concurrent.Callable; 43 | import java.util.concurrent.CountDownLatch; 44 | import java.util.concurrent.Semaphore; 45 | 46 | import javafx.animation.AnimationTimer; 47 | import javafx.animation.KeyFrame; 48 | import javafx.animation.Timeline; 49 | import javafx.application.Platform; 50 | import javafx.beans.property.ReadOnlyIntegerProperty; 51 | import javafx.beans.value.ChangeListener; 52 | import javafx.beans.value.ObservableValue; 53 | import javafx.event.*; 54 | import javafx.fxml.FXML; 55 | import javafx.fxml.Initializable; 56 | import javafx.scene.Node; 57 | import javafx.scene.SnapshotParameters; 58 | import javafx.scene.SnapshotResult; 59 | import javafx.scene.control.CheckBox; 60 | import javafx.scene.control.ChoiceBox; 61 | import javafx.scene.control.Label; 62 | import javafx.scene.control.Slider; 63 | import javafx.scene.image.ImageView; 64 | import javafx.scene.image.PixelFormat; 65 | import javafx.scene.image.PixelWriter; 66 | import javafx.scene.image.WritableImage; 67 | import javafx.scene.input.MouseEvent; 68 | import javafx.scene.layout.AnchorPane; 69 | import javafx.scene.web.WebEngine; 70 | import javafx.scene.web.WebEvent; 71 | import javafx.scene.web.WebView; 72 | import javafx.util.Callback; 73 | import javafx.util.Duration; 74 | import javafx.util.StringConverter; 75 | 76 | import static javafx.beans.binding.Bindings.*; 77 | import static javafx.collections.FXCollections.*; 78 | import static org.lwjgl.opengl.GL11.*; 79 | 80 | /** The JavaFX application GUI controller. */ 81 | public class GUIController implements Initializable { 82 | 83 | @FXML private AnchorPane gearsRoot; 84 | @FXML private ImageView gearsView; 85 | 86 | @FXML private Label fpsLabel; 87 | @FXML private Label javaInfoLabel; 88 | @FXML private Label systemInfoLabel; 89 | @FXML private Label glInfoLabel; 90 | 91 | @FXML private CheckBox vsync; 92 | 93 | @FXML private ChoiceBox renderChoice; 94 | @FXML private ChoiceBox textureChoice; 95 | @FXML private ChoiceBox bufferingChoice; 96 | 97 | @FXML private Slider msaaSamples; 98 | 99 | @FXML private WebView webView; 100 | 101 | private Gears gears; 102 | 103 | public GUIController() { 104 | } 105 | 106 | public void initialize(final URL url, final ResourceBundle resourceBundle) { 107 | gearsView.fitWidthProperty().bind(gearsRoot.widthProperty()); 108 | gearsView.fitHeightProperty().bind(gearsRoot.heightProperty()); 109 | 110 | final StringBuilder info = new StringBuilder(128); 111 | info 112 | .append(System.getProperty("java.vm.name")) 113 | .append(' ') 114 | .append(System.getProperty("java.version")) 115 | .append(' ') 116 | .append(System.getProperty("java.vm.version")); 117 | 118 | javaInfoLabel.setText(info.toString()); 119 | 120 | info.setLength(0); 121 | info 122 | .append(System.getProperty("os.name")) 123 | .append(" - JavaFX ") 124 | .append(System.getProperty("javafx.runtime.version")); 125 | 126 | systemInfoLabel.setText(info.toString()); 127 | 128 | bufferingChoice.setItems(observableArrayList(BufferingChoice.values())); 129 | 130 | msaaSamples.setMin(0); 131 | msaaSamples.setMax(0); 132 | if ( System.getProperty("javafx.runtime.version").startsWith("2") ) 133 | // The label formatter was not working until JavaFX 8. 134 | for ( Node n : msaaSamples.getParent().getChildrenUnmodifiable() ) { 135 | if ( !(n instanceof Label) ) 136 | continue; 137 | 138 | Label l = (Label)n; 139 | if ( "MSAA Samples".equals(l.getText()) ) { 140 | l.setText("MSAA Samples (2^x)"); 141 | break; 142 | } 143 | } 144 | else 145 | msaaSamples.setLabelFormatter(new StringConverter() { 146 | @Override 147 | public String toString(final Double object) { 148 | return Integer.toString(1 << object.intValue()); 149 | } 150 | 151 | @Override 152 | public Double fromString(final String string) { 153 | return null; 154 | } 155 | }); 156 | } 157 | 158 | private StreamHandler getReadHandler() { 159 | return new StreamHandler() { 160 | 161 | private WritableImage renderImage; 162 | 163 | private long frame; 164 | private long lastUpload; 165 | 166 | { 167 | new AnimationTimer() { 168 | @Override 169 | public void handle(final long now) { 170 | frame++; 171 | } 172 | }.start(); 173 | } 174 | 175 | public int getWidth() { 176 | return (int)gearsView.getFitWidth(); 177 | } 178 | 179 | public int getHeight() { 180 | return (int)gearsView.getFitHeight(); 181 | } 182 | 183 | public void process(final int width, final int height, final ByteBuffer data, final int stride, final Semaphore signal) { 184 | // This method runs in the background rendering thread 185 | Platform.runLater(new Runnable() { 186 | public void run() { 187 | try { 188 | // If we're quitting, discard update 189 | if ( !gearsView.isVisible() ) 190 | return; 191 | 192 | // Detect resize and recreate the image 193 | if ( renderImage == null || (int)renderImage.getWidth() != width || (int)renderImage.getHeight() != height ) { 194 | renderImage = new WritableImage(width, height); 195 | gearsView.setImage(renderImage); 196 | } 197 | 198 | // Throttling, only update the JavaFX view once per frame. 199 | // *NOTE*: The +1 is weird here, but apparently setPixels triggers a new pulse within the current frame. 200 | // If we ignore that, we'd get a) worse performance from uploading double the frames and b) exceptions 201 | // on certain configurations (e.g. Nvidia GPU with the D3D pipeline). 202 | if ( frame <= lastUpload + 1 ) 203 | return; 204 | 205 | lastUpload = frame; 206 | 207 | // Upload the image to JavaFX 208 | PixelWriter pw = renderImage.getPixelWriter(); 209 | pw.setPixels(0, 0, width, height, pw.getPixelFormat(), data, stride); 210 | } finally { 211 | // Notify the render thread that we're done processing 212 | signal.release(); 213 | } 214 | } 215 | }); 216 | } 217 | }; 218 | } 219 | 220 | private StreamHandler getWriteHandler() { 221 | return new StreamHandler() { 222 | 223 | private WritableImage webImage; 224 | 225 | public int getWidth() { 226 | return (int)webView.getWidth(); 227 | } 228 | 229 | public int getHeight() { 230 | return (int)webView.getHeight(); 231 | } 232 | 233 | public void process(final int width, final int height, final ByteBuffer buffer, final int stride, final Semaphore signal) { 234 | // This method runs in the background rendering thread 235 | Platform.runLater(new Runnable() { 236 | public void run() { 237 | if ( webImage == null || webImage.getWidth() != width || webImage.getHeight() != height ) 238 | webImage = new WritableImage(width, height); 239 | 240 | webView.snapshot(new Callback() { 241 | public Void call(final SnapshotResult snapshotResult) { 242 | snapshotResult.getImage().getPixelReader().getPixels(0, 0, width, height, PixelFormat.getByteBgraPreInstance(), buffer, stride); 243 | 244 | signal.release(); 245 | return null; 246 | 247 | } 248 | }, new SnapshotParameters(), webImage); 249 | } 250 | }); 251 | } 252 | }; 253 | } 254 | 255 | // This method will run in the background rendering thread 256 | void runGears(final CountDownLatch runningLatch) { 257 | try { 258 | gears = new Gears( 259 | getReadHandler(), 260 | getWriteHandler() 261 | ); 262 | } catch (Throwable t) { 263 | t.printStackTrace(); 264 | return; 265 | } 266 | 267 | final List renderStreamFactories = StreamUtil.getRenderStreamImplementations(); 268 | final List textureStreamFactories = StreamUtil.getTextureStreamImplementations(); 269 | 270 | final String vendor = glGetString(GL_VENDOR); 271 | final String version = glGetString(GL_VERSION); 272 | 273 | Platform.runLater(new Runnable() { 274 | public void run() { 275 | // Listen for FPS changes and update the fps label 276 | final ReadOnlyIntegerProperty fps = gears.fpsProperty(); 277 | 278 | fpsLabel.textProperty().bind(createStringBinding(new Callable() { 279 | public String call() throws Exception { 280 | return "FPS: " + fps.get(); 281 | } 282 | }, fps)); 283 | glInfoLabel.setText(vendor + " OpenGL " + version); 284 | 285 | renderChoice.setItems(observableList(renderStreamFactories)); 286 | for ( int i = 0; i < renderStreamFactories.size(); i++ ) { 287 | if ( renderStreamFactories.get(i) == gears.getRenderStreamFactory() ) { 288 | renderChoice.getSelectionModel().select(i); 289 | break; 290 | } 291 | } 292 | renderChoice.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { 293 | public void changed(final ObservableValue observableValue, final RenderStreamFactory oldValue, final RenderStreamFactory newValue) { 294 | gears.setRenderStreamFactory(newValue); 295 | } 296 | }); 297 | 298 | textureChoice.setItems(observableList(textureStreamFactories)); 299 | for ( int i = 0; i < textureStreamFactories.size(); i++ ) { 300 | if ( textureStreamFactories.get(i) == gears.getTextureStreamFactory() ) { 301 | textureChoice.getSelectionModel().select(i); 302 | break; 303 | } 304 | } 305 | textureChoice.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { 306 | public void changed(final ObservableValue observableValue, final TextureStreamFactory oldValue, final TextureStreamFactory newValue) { 307 | gears.setTextureStreamFactory(newValue); 308 | } 309 | }); 310 | 311 | bufferingChoice.getSelectionModel().select(gears.getTransfersToBuffer() - 1); 312 | bufferingChoice.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { 313 | public void changed(final ObservableValue observableValue, final BufferingChoice oldValue, final BufferingChoice newValue) { 314 | gears.setTransfersToBuffer(newValue.getTransfersToBuffer()); 315 | } 316 | }); 317 | 318 | vsync.selectedProperty().addListener(new ChangeListener() { 319 | public void changed(final ObservableValue observableValue, final Boolean oldValue, final Boolean newValue) { 320 | gears.setVsync(newValue); 321 | } 322 | }); 323 | 324 | final int maxSamples = gears.getMaxSamples(); 325 | if ( maxSamples == 1 ) 326 | msaaSamples.setDisable(true); 327 | else { 328 | msaaSamples.setMax(Integer.numberOfTrailingZeros(maxSamples)); 329 | msaaSamples.valueProperty().addListener(new ChangeListener() { 330 | public void changed(final ObservableValue observableValue, final Number oldValue, final Number newValue) { 331 | gears.setSamples(1 << newValue.intValue()); 332 | } 333 | }); 334 | } 335 | 336 | // Listen for changes to the WebView contents. 337 | final ChangeListener numberListener = new ChangeListener() { 338 | public void changed(final ObservableValue observableValue, final Number oldValue, final Number newValue) { 339 | gears.updateSnapshot(); 340 | } 341 | }; 342 | 343 | webView.widthProperty().addListener(numberListener); 344 | webView.heightProperty().addListener(numberListener); 345 | 346 | final WebEngine engine = webView.getEngine(); 347 | 348 | engine.getLoadWorker().progressProperty().addListener(numberListener); 349 | engine.setOnStatusChanged(new EventHandler>() { 350 | public void handle(final WebEvent e) { 351 | gears.updateSnapshot(); 352 | } 353 | }); 354 | 355 | webView.setEventDispatcher(new EventDispatcher() { 356 | private final EventDispatcher parent = webView.getEventDispatcher(); 357 | 358 | public Event dispatchEvent(final Event e, final EventDispatchChain dispatchChain) { 359 | // Mouse over events within the page will be triggered by the StatusChanged handler above. 360 | if ( e.getEventType() != MouseEvent.MOUSE_MOVED && gears != null ) 361 | gears.updateSnapshot(); 362 | 363 | return parent.dispatchEvent(e, dispatchChain); 364 | } 365 | }); 366 | 367 | // Force an update every 4 frames for carets. 368 | final Timeline timeline = new Timeline(); 369 | timeline.setCycleCount(Timeline.INDEFINITE); 370 | timeline.setAutoReverse(true); 371 | timeline.getKeyFrames().add(new KeyFrame(Duration.millis(4 * (1000 / 60)), new EventHandler() { 372 | public void handle(final ActionEvent e) { 373 | if ( webView.isFocused() ) 374 | gears.updateSnapshot(); 375 | } 376 | })); 377 | timeline.play(); 378 | 379 | // Do one last update on focus lost 380 | webView.focusedProperty().addListener(new ChangeListener() { 381 | public void changed(final ObservableValue observableValue, final Boolean oldValue, final Boolean newValue) { 382 | if ( !newValue ) 383 | gears.updateSnapshot(); 384 | } 385 | }); 386 | 387 | webView.getEngine().load("http://www.java-gaming.org"); 388 | } 389 | }); 390 | 391 | gears.execute(runningLatch); 392 | } 393 | 394 | private enum BufferingChoice { 395 | SINGLE(1, "No buffering"), 396 | DOUBLE(2, "Double buffering"), 397 | TRIPLE(3, "Triple buffering"); 398 | 399 | private final int transfersToBuffer; 400 | private final String description; 401 | 402 | private BufferingChoice(final int transfersToBuffer, final String description) { 403 | this.transfersToBuffer = transfersToBuffer; 404 | this.description = transfersToBuffer + "x - " + description; 405 | } 406 | 407 | public int getTransfersToBuffer() { 408 | return transfersToBuffer; 409 | } 410 | 411 | public String getDescription() { 412 | return description; 413 | } 414 | 415 | public String toString() { 416 | return description; 417 | } 418 | } 419 | 420 | } -------------------------------------------------------------------------------- /src/lwjglfx/Gears.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2012 LWJGL Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of 'LWJGL' nor the names of 17 | * its contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package lwjglfx; 33 | 34 | import org.lwjgl.BufferUtils; 35 | import org.lwjgl.LWJGLException; 36 | import org.lwjgl.opengl.*; 37 | import org.lwjgl.util.stream.RenderStream; 38 | import org.lwjgl.util.stream.StreamHandler; 39 | import org.lwjgl.util.stream.StreamUtil; 40 | import org.lwjgl.util.stream.StreamUtil.RenderStreamFactory; 41 | import org.lwjgl.util.stream.StreamUtil.TextureStreamFactory; 42 | import org.lwjgl.util.stream.TextureStream; 43 | 44 | import java.nio.FloatBuffer; 45 | import java.util.concurrent.ConcurrentLinkedQueue; 46 | import java.util.concurrent.CountDownLatch; 47 | import java.util.concurrent.atomic.AtomicLong; 48 | 49 | import javafx.application.Platform; 50 | import javafx.beans.property.ReadOnlyIntegerProperty; 51 | import javafx.beans.property.ReadOnlyIntegerWrapper; 52 | 53 | import static org.lwjgl.opengl.AMDDebugOutput.*; 54 | import static org.lwjgl.opengl.GL11.*; 55 | import static org.lwjgl.opengl.GL11.glGetInteger; 56 | import static org.lwjgl.opengl.GL30.*; 57 | import static org.lwjgl.opengl.KHRDebug.*; 58 | 59 | /** The LWJGL Gears test, modified to use the PBO reader & writer. */ 60 | final class Gears { 61 | 62 | private static final float VIEW_ROT_X = 10.0f; 63 | private static final float VIEW_ROT_Y = 25.0f; 64 | private static final float VIEW_ROT_Z = 0.0f; 65 | 66 | static Drawable drawable; 67 | 68 | private final ConcurrentLinkedQueue pendingRunnables; 69 | 70 | private final Pbuffer pbuffer; 71 | private final int maxSamples; 72 | 73 | private final ReadOnlyIntegerWrapper fps; 74 | 75 | private RenderStreamFactory renderStreamFactory; 76 | private RenderStream renderStream; 77 | 78 | private TextureStreamFactory textureStreamFactory; 79 | private TextureStream textureStream; 80 | 81 | private int gear1; 82 | private int gear2; 83 | private int gear3; 84 | 85 | private float angle; 86 | 87 | private boolean vsync = true; 88 | 89 | private int transfersToBuffer = 2; 90 | private int samples = 1; 91 | 92 | private final AtomicLong snapshotRequest; 93 | private long snapshotCurrent; 94 | 95 | Gears(final StreamHandler readHandler, final StreamHandler writeHandler) { 96 | this.pendingRunnables = new ConcurrentLinkedQueue(); 97 | 98 | this.fps = new ReadOnlyIntegerWrapper(this, "fps", 0); 99 | 100 | if ( (Pbuffer.getCapabilities() & Pbuffer.PBUFFER_SUPPORTED) == 0 ) 101 | throw new UnsupportedOperationException("Support for pbuffers is required."); 102 | 103 | try { 104 | pbuffer = new Pbuffer(1, 1, new PixelFormat(), null, null, new ContextAttribs().withDebug(true)); 105 | pbuffer.makeCurrent(); 106 | } catch (LWJGLException e) { 107 | throw new RuntimeException(e); 108 | } 109 | 110 | Gears.drawable = pbuffer; 111 | 112 | final ContextCapabilities caps = GLContext.getCapabilities(); 113 | 114 | if ( caps.OpenGL30 || (caps.GL_EXT_framebuffer_multisample && caps.GL_EXT_framebuffer_blit) ) 115 | maxSamples = glGetInteger(GL_MAX_SAMPLES); 116 | else 117 | maxSamples = 1; 118 | 119 | if ( caps.GL_KHR_debug ) { 120 | glDebugMessageCallback(new KHRDebugCallback()); 121 | glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DEBUG_SEVERITY_NOTIFICATION, null, false); // NV: Buffer object mallocs 122 | glDebugMessageControl(GL_DEBUG_SOURCE_API, GL_DEBUG_TYPE_PERFORMANCE, GL_DEBUG_SEVERITY_MEDIUM, null, false); // NV: Pixel-transfer synchronized with rendering 123 | } else if ( caps.GL_AMD_debug_output ) 124 | glDebugMessageCallbackAMD(new AMDDebugOutputCallback()); 125 | 126 | this.renderStreamFactory = StreamUtil.getRenderStreamImplementation(); 127 | this.renderStream = renderStreamFactory.create(readHandler, 1, transfersToBuffer); 128 | 129 | this.textureStreamFactory = StreamUtil.getTextureStreamImplementation(); 130 | this.textureStream = textureStreamFactory.create(writeHandler, transfersToBuffer); 131 | 132 | this.snapshotRequest = new AtomicLong(); 133 | this.snapshotCurrent = -1L; 134 | } 135 | 136 | public int getMaxSamples() { 137 | return maxSamples; 138 | } 139 | 140 | public RenderStreamFactory getRenderStreamFactory() { 141 | return renderStreamFactory; 142 | } 143 | 144 | public void setRenderStreamFactory(final RenderStreamFactory renderStreamFactory) { 145 | pendingRunnables.offer(new Runnable() { 146 | public void run() { 147 | if ( renderStream != null ) 148 | renderStream.destroy(); 149 | 150 | Gears.this.renderStreamFactory = renderStreamFactory; 151 | 152 | renderStream = renderStreamFactory.create(renderStream.getHandler(), samples, transfersToBuffer); 153 | } 154 | }); 155 | } 156 | 157 | public TextureStreamFactory getTextureStreamFactory() { 158 | return textureStreamFactory; 159 | } 160 | 161 | public void setTextureStreamFactory(final TextureStreamFactory textureStreamFactory) { 162 | pendingRunnables.offer(new Runnable() { 163 | public void run() { 164 | if ( textureStream != null ) 165 | textureStream.destroy(); 166 | 167 | Gears.this.textureStreamFactory = textureStreamFactory; 168 | 169 | textureStream = textureStreamFactory.create(textureStream.getHandler(), transfersToBuffer); 170 | updateSnapshot(); 171 | } 172 | }); 173 | 174 | } 175 | 176 | private void init() { 177 | // setup ogl 178 | FloatBuffer pos = BufferUtils.createFloatBuffer(4).put(new float[] { 5.0f, 5.0f, 10.0f, 0.0f }); 179 | FloatBuffer red = BufferUtils.createFloatBuffer(4).put(new float[] { 0.8f, 0.1f, 0.0f, 1.0f }); 180 | FloatBuffer green = BufferUtils.createFloatBuffer(4).put(new float[] { 0.0f, 0.8f, 0.2f, 1.0f }); 181 | FloatBuffer blue = BufferUtils.createFloatBuffer(4).put(new float[] { 0.2f, 0.2f, 1.0f, 1.0f }); 182 | 183 | pos.flip(); 184 | red.flip(); 185 | green.flip(); 186 | blue.flip(); 187 | 188 | glLight(GL_LIGHT0, GL_POSITION, pos); 189 | glEnable(GL_CULL_FACE); 190 | glEnable(GL_LIGHTING); 191 | glEnable(GL_LIGHT0); 192 | glEnable(GL_DEPTH_TEST); 193 | 194 | // make the gears 195 | gear1 = glGenLists(1); 196 | glNewList(gear1, GL_COMPILE); 197 | glMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red); 198 | gear(1.0f, 4.0f, 1.0f, 20, 0.7f); 199 | glEndList(); 200 | 201 | gear2 = glGenLists(1); 202 | glNewList(gear2, GL_COMPILE); 203 | glMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green); 204 | gear(0.5f, 2.0f, 2.0f, 10, 0.7f); 205 | glEndList(); 206 | 207 | gear3 = glGenLists(1); 208 | glNewList(gear3, GL_COMPILE); 209 | glMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue); 210 | gear(1.3f, 2.0f, 0.5f, 10, 0.7f); 211 | glEndList(); 212 | 213 | glEnable(GL_NORMALIZE); 214 | glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 215 | 216 | glMatrixMode(GL_PROJECTION); 217 | glLoadIdentity(); 218 | glFrustum(-1.0f, 1.0f, -1.0f, 1.0f, 5.0f, 60.0f); 219 | 220 | glMatrixMode(GL_MODELVIEW); 221 | glLoadIdentity(); 222 | glTranslatef(1.0f, -0.5f, -40.0f); 223 | } 224 | 225 | public void execute(final CountDownLatch running) { 226 | init(); 227 | 228 | loop(running); 229 | 230 | destroy(); 231 | } 232 | 233 | public ReadOnlyIntegerProperty fpsProperty() { 234 | return fps.getReadOnlyProperty(); 235 | } 236 | 237 | private void destroy() { 238 | renderStream.destroy(); 239 | textureStream.destroy(); 240 | pbuffer.destroy(); 241 | } 242 | 243 | public void updateSnapshot() { 244 | snapshotRequest.incrementAndGet(); 245 | } 246 | 247 | public void setVsync(final boolean vsync) { 248 | this.vsync = vsync; 249 | } 250 | 251 | public int getTransfersToBuffer() { 252 | return transfersToBuffer; 253 | } 254 | 255 | public void setTransfersToBuffer(final int transfersToBuffer) { 256 | if ( this.transfersToBuffer == transfersToBuffer ) 257 | return; 258 | 259 | this.transfersToBuffer = transfersToBuffer; 260 | resetStreams(); 261 | } 262 | 263 | public void setSamples(final int samples) { 264 | if ( this.samples == samples ) 265 | return; 266 | 267 | this.samples = samples; 268 | resetStreams(); 269 | } 270 | 271 | private void resetStreams() { 272 | pendingRunnables.offer(new Runnable() { 273 | public void run() { 274 | textureStream.destroy(); 275 | renderStream.destroy(); 276 | 277 | renderStream = renderStreamFactory.create(renderStream.getHandler(), samples, transfersToBuffer); 278 | textureStream = textureStreamFactory.create(textureStream.getHandler(), transfersToBuffer); 279 | 280 | updateSnapshot(); 281 | } 282 | }); 283 | } 284 | 285 | private void drainPendingActionsQueue() { 286 | Runnable runnable; 287 | 288 | while ( (runnable = pendingRunnables.poll()) != null ) 289 | runnable.run(); 290 | } 291 | 292 | private void loop(final CountDownLatch running) { 293 | final long FPS_UPD_INTERVAL = 1 * (1000L * 1000L * 1000L); 294 | 295 | long nextFPSUpdateTime = System.nanoTime() + FPS_UPD_INTERVAL; 296 | int frames = 0; 297 | 298 | long lastTime = System.nanoTime(); 299 | double timeDelta = 0.0; 300 | 301 | while ( 0 < running.getCount() ) { 302 | angle += 0.1f * timeDelta; // 0.1 degrees per ms == 100 degrees per second 303 | 304 | drainPendingActionsQueue(); 305 | 306 | final long snapshotRequestID = snapshotRequest.get(); 307 | if ( snapshotCurrent < snapshotRequestID ) { 308 | textureStream.snapshot(); 309 | snapshotCurrent = snapshotRequestID; 310 | } 311 | textureStream.tick(); 312 | 313 | renderStream.bind(); 314 | 315 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 316 | 317 | glPushMatrix(); 318 | glRotatef(VIEW_ROT_X, 1.0f, 0.0f, 0.0f); 319 | glRotatef(VIEW_ROT_Y, 0.0f, 1.0f, 0.0f); 320 | glRotatef(VIEW_ROT_Z, 0.0f, 0.0f, 1.0f); 321 | 322 | glDisable(GL_LIGHTING); 323 | glEnable(GL_TEXTURE_2D); 324 | 325 | textureStream.bind(); 326 | drawQuad(textureStream.getWidth(), textureStream.getHeight()); 327 | glBindTexture(GL_TEXTURE_2D, 0); 328 | 329 | glDisable(GL_TEXTURE_2D); 330 | glEnable(GL_LIGHTING); 331 | 332 | //for ( int i = -4; i < 4; i++ ) 333 | int i = 0; 334 | { 335 | glPushMatrix(); 336 | glTranslatef(-3.0f, -2.0f, i); 337 | glRotatef(angle, 0.0f, 0.0f, 1.0f); 338 | glCallList(gear1); 339 | glPopMatrix(); 340 | 341 | glPushMatrix(); 342 | glTranslatef(3.1f, -2.0f, i); 343 | glRotatef(-2.0f * angle - 9.0f, 0.0f, 0.0f, 1.0f); 344 | glCallList(gear2); 345 | glPopMatrix(); 346 | 347 | glPushMatrix(); 348 | glTranslatef(-3.1f, 4.2f, i); 349 | glRotatef(-2.0f * angle - 25.0f, 0.0f, 0.0f, 1.0f); 350 | glCallList(gear3); 351 | glPopMatrix(); 352 | } 353 | 354 | glPopMatrix(); 355 | 356 | renderStream.swapBuffers(); 357 | 358 | if ( vsync ) 359 | Display.sync(60); 360 | 361 | final long currentTime = System.nanoTime(); 362 | timeDelta = (currentTime - lastTime) / 1000000.0; 363 | lastTime = currentTime; 364 | 365 | frames++; 366 | if ( nextFPSUpdateTime <= currentTime ) { 367 | long timeUsed = FPS_UPD_INTERVAL + (currentTime - nextFPSUpdateTime); 368 | nextFPSUpdateTime = currentTime + FPS_UPD_INTERVAL; 369 | 370 | final int fpsAverage = (int)(frames * (1000L * 1000L * 1000L) / (timeUsed)); 371 | Platform.runLater(new Runnable() { 372 | public void run() { 373 | Gears.this.fps.set(fpsAverage); 374 | } 375 | }); 376 | frames = 0; 377 | } 378 | } 379 | } 380 | 381 | private static void drawQuad(final int width, final int height) { 382 | final float ratio = (float)width / height; 383 | 384 | final float SIZE = 16.0f; 385 | 386 | final float quadW; 387 | final float quadH; 388 | 389 | if ( ratio <= 1.0f ) { 390 | quadH = SIZE; 391 | quadW = quadH * ratio; 392 | } else { 393 | quadW = SIZE; 394 | quadH = quadW * ratio; 395 | } 396 | 397 | glPushMatrix(); 398 | 399 | glTranslatef(-quadW * 0.5f, -quadH * 0.5f, -4.0f); 400 | glBegin(GL_QUADS); 401 | { 402 | glTexCoord2f(0.0f, 1.0f); 403 | glVertex2f(0.0f, 0.0f); 404 | 405 | glTexCoord2f(1.0f, 1.0f); 406 | glVertex2f(quadW, 0.0f); 407 | 408 | glTexCoord2f(1.0f, 0.0f); 409 | glVertex2f(quadW, quadH); 410 | 411 | glTexCoord2f(0.0f, 0.0f); 412 | glVertex2f(0.0f, quadH); 413 | } 414 | glEnd(); 415 | glPopMatrix(); 416 | } 417 | 418 | private static float sin(float value) { 419 | return (float)Math.sin(value); 420 | } 421 | 422 | private static float cos(float value) { 423 | return (float)Math.cos(value); 424 | } 425 | 426 | /** 427 | * Draw a gear wheel. You'll probably want to call this function when 428 | * building a display list since we do a lot of trig here. 429 | * 430 | * @param inner_radius radius of hole at center 431 | * @param outer_radius radius at center of teeth 432 | * @param width width of gear 433 | * @param teeth number of teeth 434 | * @param tooth_depth depth of tooth 435 | */ 436 | private static void gear(float inner_radius, float outer_radius, float width, int teeth, float tooth_depth) { 437 | int i; 438 | float r0, r1, r2; 439 | float angle, da; 440 | float u, v, len; 441 | 442 | r0 = inner_radius; 443 | r1 = outer_radius - tooth_depth / 2.0f; 444 | r2 = outer_radius + tooth_depth / 2.0f; 445 | 446 | da = 2.0f * (float)Math.PI / teeth / 4.0f; 447 | 448 | glShadeModel(GL_FLAT); 449 | 450 | glNormal3f(0.0f, 0.0f, 1.0f); 451 | 452 | /* draw front face */ 453 | glBegin(GL_QUAD_STRIP); 454 | for ( i = 0; i <= teeth; i++ ) { 455 | angle = i * 2.0f * (float)Math.PI / teeth; 456 | glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5f); 457 | glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5f); 458 | if ( i < teeth ) { 459 | glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5f); 460 | glVertex3f(r1 * cos(angle + 3.0f * da), r1 * sin(angle + 3.0f * da), width * 0.5f); 461 | } 462 | } 463 | glEnd(); 464 | 465 | /* draw front sides of teeth */ 466 | glBegin(GL_QUADS); 467 | for ( i = 0; i < teeth; i++ ) { 468 | angle = i * 2.0f * (float)Math.PI / teeth; 469 | glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5f); 470 | glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5f); 471 | glVertex3f(r2 * cos(angle + 2.0f * da), r2 * sin(angle + 2.0f * da), width * 0.5f); 472 | glVertex3f(r1 * cos(angle + 3.0f * da), r1 * sin(angle + 3.0f * da), width * 0.5f); 473 | } 474 | glEnd(); 475 | 476 | /* draw back face */ 477 | glBegin(GL_QUAD_STRIP); 478 | for ( i = 0; i <= teeth; i++ ) { 479 | angle = i * 2.0f * (float)Math.PI / teeth; 480 | glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5f); 481 | glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5f); 482 | glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5f); 483 | glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5f); 484 | } 485 | glEnd(); 486 | 487 | /* draw back sides of teeth */ 488 | glBegin(GL_QUADS); 489 | for ( i = 0; i < teeth; i++ ) { 490 | angle = i * 2.0f * (float)Math.PI / teeth; 491 | glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5f); 492 | glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5f); 493 | glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5f); 494 | glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5f); 495 | } 496 | glEnd(); 497 | 498 | /* draw outward faces of teeth */ 499 | glBegin(GL_QUAD_STRIP); 500 | for ( i = 0; i < teeth; i++ ) { 501 | angle = i * 2.0f * (float)Math.PI / teeth; 502 | glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5f); 503 | glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5f); 504 | u = r2 * cos(angle + da) - r1 * cos(angle); 505 | v = r2 * sin(angle + da) - r1 * sin(angle); 506 | len = (float)Math.sqrt(u * u + v * v); 507 | u /= len; 508 | v /= len; 509 | glNormal3f(v, -u, 0.0f); 510 | glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5f); 511 | glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5f); 512 | glNormal3f(cos(angle), sin(angle), 0.0f); 513 | glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5f); 514 | glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5f); 515 | u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da); 516 | v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da); 517 | glNormal3f(v, -u, 0.0f); 518 | glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5f); 519 | glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5f); 520 | glNormal3f(cos(angle), sin(angle), 0.0f); 521 | } 522 | glVertex3f(r1 * cos(0), r1 * sin(0), width * 0.5f); 523 | glVertex3f(r1 * cos(0), r1 * sin(0), -width * 0.5f); 524 | glEnd(); 525 | 526 | glShadeModel(GL_SMOOTH); 527 | 528 | /* draw inside radius cylinder */ 529 | glBegin(GL_QUAD_STRIP); 530 | for ( i = 0; i <= teeth; i++ ) { 531 | angle = i * 2.0f * (float)Math.PI / teeth; 532 | glNormal3f(-cos(angle), -sin(angle), 0.0f); 533 | glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5f); 534 | glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5f); 535 | } 536 | glEnd(); 537 | } 538 | 539 | } -------------------------------------------------------------------------------- /src/lwjglfx/JavaFXGears.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2012 LWJGL Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of 'LWJGL' nor the names of 17 | * its contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package lwjglfx; 33 | 34 | import java.net.URL; 35 | import java.util.concurrent.CountDownLatch; 36 | 37 | import javafx.application.Application; 38 | import javafx.application.Platform; 39 | import javafx.event.EventHandler; 40 | import javafx.fxml.FXMLLoader; 41 | import javafx.geometry.Rectangle2D; 42 | import javafx.scene.Scene; 43 | import javafx.scene.image.Image; 44 | import javafx.scene.layout.Pane; 45 | import javafx.stage.Screen; 46 | import javafx.stage.Stage; 47 | import javafx.stage.WindowEvent; 48 | 49 | /** The JavaFX application entry point */ 50 | public class JavaFXGears extends Application { 51 | 52 | private final CountDownLatch runningLatch = new CountDownLatch(1); 53 | 54 | public JavaFXGears() { 55 | } 56 | 57 | public static void main(String[] args) { 58 | Application.launch(args); 59 | } 60 | 61 | @Override 62 | public void start(final Stage stage) { 63 | stage.setTitle("JavaFX Window"); 64 | 65 | stage.setMinWidth(640); 66 | stage.setMinHeight(480); 67 | 68 | stage.getIcons().add(new Image("lwjgl_32x32.png")); 69 | 70 | final Screen screen = Screen.getPrimary(); 71 | final Rectangle2D screenBounds = screen.getVisualBounds(); 72 | 73 | if ( screenBounds.getWidth() < stage.getWidth() || screenBounds.getHeight() < stage.getHeight() ) { 74 | stage.setX(screenBounds.getMinX()); 75 | stage.setY(screenBounds.getMinY()); 76 | 77 | stage.setWidth(screenBounds.getWidth()); 78 | stage.setHeight(screenBounds.getHeight()); 79 | } 80 | 81 | final URL fxmlURL = getClass().getClassLoader().getResource("gears.fxml"); 82 | final FXMLLoader fxmlLoader = new FXMLLoader(fxmlURL); 83 | 84 | Pane content; 85 | try { 86 | content = (Pane)fxmlLoader.load(); 87 | } catch (Exception e) { 88 | e.printStackTrace(); 89 | System.exit(-1); 90 | return; 91 | } 92 | 93 | final GUIController controller = fxmlLoader.getController(); 94 | 95 | try { 96 | final Scene scene = new Scene(content); 97 | scene.getStylesheets().add(getClass().getClassLoader().getResource("gears.css").toExternalForm()); 98 | 99 | stage.setScene(scene); 100 | stage.show(); 101 | } catch (Exception e) { 102 | e.printStackTrace(); 103 | System.exit(-1); 104 | return; 105 | } 106 | 107 | stage.setOnCloseRequest(new EventHandler() { 108 | public void handle(final WindowEvent e) { 109 | e.consume(); 110 | runningLatch.countDown(); 111 | } 112 | }); 113 | 114 | new Thread("LWJGL Renderer") { 115 | public void run() { 116 | controller.runGears(runningLatch); 117 | Platform.runLater(new Runnable() { 118 | public void run() { 119 | stage.close(); 120 | } 121 | }); 122 | } 123 | }.start(); 124 | } 125 | 126 | } -------------------------------------------------------------------------------- /src/org/lwjgl/util/stream/RenderStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2012 LWJGL Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of 'LWJGL' nor the names of 17 | * its contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package org.lwjgl.util.stream; 33 | 34 | /** @author Spasi */ 35 | public interface RenderStream { 36 | 37 | StreamHandler getHandler(); 38 | 39 | void bind(); 40 | 41 | void swapBuffers(); 42 | 43 | void destroy(); 44 | 45 | } -------------------------------------------------------------------------------- /src/org/lwjgl/util/stream/RenderStreamINTEL.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2012 LWJGL Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of 'LWJGL' nor the names of 17 | * its contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package org.lwjgl.util.stream; 33 | 34 | import org.lwjgl.BufferUtils; 35 | import org.lwjgl.opengl.ContextCapabilities; 36 | import org.lwjgl.opengl.GLContext; 37 | import org.lwjgl.util.stream.StreamUtil.RenderStreamFactory; 38 | 39 | import java.nio.ByteBuffer; 40 | import java.nio.IntBuffer; 41 | 42 | import static org.lwjgl.opengl.GL11.*; 43 | import static org.lwjgl.opengl.GL12.*; 44 | import static org.lwjgl.opengl.GL30.*; 45 | import static org.lwjgl.opengl.INTELMapTexture.*; 46 | 47 | /** 48 | * Optimized RenderStream for Intel IGPs: 49 | *

50 | * - We render to a standard FBO. 51 | * - We asynchronously blit to another FBO with INTEL_map_texture attachments (linear layout). 52 | * - We synchronously map the linear textures. 53 | *

54 | */ 55 | final class RenderStreamINTEL extends StreamBuffered implements RenderStream { 56 | 57 | public static final RenderStreamFactory FACTORY = new RenderStreamFactory("INTEL_map_texture") { 58 | public boolean isSupported(final ContextCapabilities caps) { 59 | // TODO: We currently require BlitFramebuffer. Relax and implement manually? 60 | return caps.GL_INTEL_map_texture && (caps.OpenGL30 || caps.GL_ARB_framebuffer_object || caps.GL_EXT_framebuffer_blit); 61 | } 62 | 63 | public RenderStream create(final StreamHandler handler, final int samples, final int transfersToBuffer) { 64 | return new RenderStreamINTEL(handler, samples, transfersToBuffer); 65 | } 66 | }; 67 | 68 | private final IntBuffer strideBuffer; 69 | private final IntBuffer layoutBuffer; 70 | 71 | private final StreamUtil.FBOUtil fboUtil; 72 | private final int renderFBO; 73 | 74 | private int rgbaBuffer; 75 | private int depthBuffer; 76 | 77 | private final int resolveFBO; 78 | private final int[] resolveBuffers; 79 | 80 | private int samples; 81 | 82 | private int synchronousFrames; 83 | 84 | RenderStreamINTEL(final StreamHandler handler, final int samples, final int transfersToBuffer) { 85 | super(handler, transfersToBuffer); 86 | 87 | final ContextCapabilities caps = GLContext.getCapabilities(); 88 | 89 | this.strideBuffer = BufferUtils.createIntBuffer(1); 90 | this.layoutBuffer = BufferUtils.createIntBuffer(1); 91 | 92 | fboUtil = StreamUtil.getFBOUtil(caps); 93 | renderFBO = fboUtil.genFramebuffers(); 94 | 95 | resolveFBO = fboUtil.genFramebuffers(); 96 | resolveBuffers = new int[transfersToBuffer]; 97 | 98 | this.samples = StreamUtil.checkSamples(samples, caps); 99 | } 100 | 101 | public StreamHandler getHandler() { 102 | return handler; 103 | } 104 | 105 | private void resize(final int width, final int height) { 106 | if ( width < 0 || height < 0 ) 107 | throw new IllegalArgumentException("Invalid dimensions: " + width + " x " + height); 108 | 109 | destroyObjects(); 110 | 111 | this.width = width; 112 | this.height = height; 113 | 114 | this.stride = StreamUtil.getStride(width); 115 | 116 | if ( width == 0 || height == 0 ) 117 | return; 118 | 119 | bufferIndex = synchronousFrames = transfersToBuffer - 1; 120 | 121 | // Setup render FBO 122 | 123 | fboUtil.bindFramebuffer(GL_DRAW_FRAMEBUFFER, renderFBO); 124 | 125 | rgbaBuffer = StreamUtil.createRenderBuffer(fboUtil, width, height, samples, GL_RGBA8); 126 | fboUtil.framebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rgbaBuffer); 127 | 128 | depthBuffer = StreamUtil.createRenderBuffer(fboUtil, width, height, samples, GL_DEPTH24_STENCIL8); 129 | fboUtil.framebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer); 130 | 131 | glViewport(0, 0, width, height); 132 | 133 | fboUtil.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); 134 | 135 | for ( int i = 0; i < resolveBuffers.length; i++ ) 136 | resolveBuffers[i] = genLayoutLinearTexture(width, height); 137 | 138 | glBindTexture(GL_TEXTURE_2D, 0); 139 | } 140 | 141 | private static int genLayoutLinearTexture(final int width, final int height) { 142 | final int texID = glGenTextures(); 143 | 144 | glBindTexture(GL_TEXTURE_2D, texID); 145 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 146 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 147 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MEMORY_LAYOUT_INTEL, GL_LAYOUT_LINEAR_CPU_CACHED_INTEL); 148 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, (ByteBuffer)null); 149 | 150 | return texID; 151 | } 152 | 153 | public void bind() { 154 | if ( this.width != handler.getWidth() || this.height != handler.getHeight() ) 155 | resize(handler.getWidth(), handler.getHeight()); 156 | 157 | fboUtil.bindFramebuffer(GL_DRAW_FRAMEBUFFER, renderFBO); 158 | } 159 | 160 | private void prepareFramebuffer(final int trgTEX) { 161 | // Back-pressure. Make sure we never buffer more than frames ahead. 162 | if ( processingState.get(trgTEX) ) 163 | waitForProcessingToComplete(trgTEX); 164 | 165 | fboUtil.bindFramebuffer(GL_READ_FRAMEBUFFER, renderFBO); 166 | fboUtil.bindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFBO); 167 | 168 | // Blit current texture 169 | fboUtil.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, resolveBuffers[trgTEX], 0); 170 | fboUtil.blitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); 171 | glFlush(); 172 | 173 | fboUtil.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); 174 | fboUtil.bindFramebuffer(GL_READ_FRAMEBUFFER, 0); 175 | } 176 | 177 | public void swapBuffers() { 178 | if ( width == 0 || height == 0 ) 179 | return; 180 | 181 | final int renderToTEX = (int)(bufferIndex % transfersToBuffer); 182 | final int readFromTEX = (int)((bufferIndex + 1) % transfersToBuffer); 183 | 184 | prepareFramebuffer(renderToTEX); 185 | 186 | // This will be non-zero for the first (transfersToBuffer - 1) frames 187 | // after start-up or a resize. 188 | if ( 0 < synchronousFrames ) { 189 | // The readFromTEX is currently empty. Wait for renderToTEX's ReadPixels to complete and copy the current frame to readFromTEX. 190 | // We do this to avoid sending an empty buffer for processing, which would cause a visible flicker on resize. 191 | copyFrames(renderToTEX, readFromTEX); 192 | synchronousFrames--; 193 | } 194 | 195 | // Time to process the readFromTEX 196 | 197 | pinBuffer(readFromTEX); 198 | 199 | // Send the buffer for processing 200 | 201 | processingState.set(readFromTEX, true); 202 | semaphores[readFromTEX].acquireUninterruptibly(); 203 | 204 | handler.process( 205 | width, height, 206 | pinnedBuffers[readFromTEX], 207 | stride, 208 | semaphores[readFromTEX] 209 | ); 210 | 211 | bufferIndex++; 212 | } 213 | 214 | private void copyFrames(final int src, final int trg) { 215 | pinnedBuffers[src] = glMapTexture2DINTEL(resolveBuffers[src], 0, height * stride, GL_MAP_READ_BIT, strideBuffer, layoutBuffer, pinnedBuffers[src]); 216 | pinnedBuffers[trg] = glMapTexture2DINTEL(resolveBuffers[trg], 0, height * stride, GL_MAP_WRITE_BIT, strideBuffer, layoutBuffer, pinnedBuffers[trg]); 217 | 218 | pinnedBuffers[trg].put(pinnedBuffers[src]); 219 | 220 | pinnedBuffers[src].flip(); 221 | pinnedBuffers[trg].flip(); 222 | 223 | glUnmapTexture2DINTEL(resolveBuffers[trg], 0); 224 | glUnmapTexture2DINTEL(resolveBuffers[src], 0); 225 | } 226 | 227 | private void pinBuffer(final int index) { 228 | final int texID = resolveBuffers[index]; 229 | 230 | pinnedBuffers[index] = glMapTexture2DINTEL(texID, 0, height * stride, GL_MAP_READ_BIT, strideBuffer, layoutBuffer, pinnedBuffers[index]); 231 | // TODO: Row alignment is currently hardcoded to 16 pixels 232 | // We wouldn't need to do that if we could create a ByteBuffer 233 | // from an arbitrary address + length. Consider for LWJGL 3.0? 234 | checkStride(index, texID); 235 | } 236 | 237 | private void checkStride(final int index, final int texID) { 238 | if ( strideBuffer.get(0) != stride ) { 239 | System.err.println("Wrong stride: " + stride + ". Should be: " + strideBuffer.get(0)); 240 | glUnmapTexture2DINTEL(texID, 0); 241 | stride = strideBuffer.get(0); 242 | pinnedBuffers[index] = glMapTexture2DINTEL(texID, 0, height * stride, GL_MAP_READ_BIT, strideBuffer, layoutBuffer, pinnedBuffers[index]); 243 | } 244 | } 245 | 246 | protected void postProcess(int index) { 247 | glUnmapTexture2DINTEL(resolveBuffers[index], 0); 248 | } 249 | 250 | private void destroyObjects() { 251 | for ( int i = 0; i < semaphores.length; i++ ) { 252 | if ( processingState.get(i) ) 253 | waitForProcessingToComplete(i); 254 | } 255 | 256 | if ( rgbaBuffer != 0 ) fboUtil.deleteRenderbuffers(rgbaBuffer); 257 | if ( depthBuffer != 0 ) fboUtil.deleteRenderbuffers(depthBuffer); 258 | 259 | for ( int i = 0; i < resolveBuffers.length; i++ ) { 260 | glDeleteTextures(resolveBuffers[i]); 261 | resolveBuffers[i] = 0; 262 | } 263 | } 264 | 265 | public void destroy() { 266 | destroyObjects(); 267 | 268 | if ( resolveFBO != 0 ) 269 | fboUtil.deleteFramebuffers(resolveFBO); 270 | fboUtil.deleteFramebuffers(renderFBO); 271 | } 272 | 273 | } -------------------------------------------------------------------------------- /src/org/lwjgl/util/stream/RenderStreamPBO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2012 LWJGL Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of 'LWJGL' nor the names of 17 | * its contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package org.lwjgl.util.stream; 33 | 34 | import org.lwjgl.opengl.ContextCapabilities; 35 | import org.lwjgl.opengl.GLContext; 36 | 37 | import static org.lwjgl.opengl.GL11.*; 38 | import static org.lwjgl.opengl.GL12.*; 39 | import static org.lwjgl.opengl.GL15.*; 40 | import static org.lwjgl.opengl.GL21.*; 41 | import static org.lwjgl.opengl.GL30.*; 42 | 43 | /** Implements streaming PBO updates from a framebuffer object. */ 44 | abstract class RenderStreamPBO extends StreamBufferedPBO implements RenderStream { 45 | 46 | public static enum ReadbackType { 47 | /** RenderBuffers on FBO, ReadPixels to readback. */ 48 | READ_PIXELS, 49 | /** Textures on FBO, GetTexImage to readback. */ 50 | GET_TEX_IMAGE 51 | } 52 | 53 | private final ReadbackType readbackType; 54 | 55 | protected final StreamUtil.FBOUtil fboUtil; 56 | private final int renderFBO; 57 | 58 | private int samples; 59 | 60 | private int rgbaBuffer; 61 | private int depthBuffer; 62 | 63 | private int msaaResolveFBO; 64 | private int msaaResolveBuffer; 65 | 66 | protected int synchronousFrames; 67 | 68 | protected RenderStreamPBO(final StreamHandler handler, final int samples, final int transfersToBuffer, final ReadbackType readbackType) { 69 | super(handler, transfersToBuffer); 70 | 71 | this.readbackType = readbackType; 72 | 73 | final ContextCapabilities caps = GLContext.getCapabilities(); 74 | 75 | fboUtil = StreamUtil.getFBOUtil(caps); 76 | renderFBO = fboUtil.genFramebuffers(); 77 | 78 | this.samples = StreamUtil.checkSamples(samples, caps); 79 | } 80 | 81 | public StreamHandler getHandler() { 82 | return handler; 83 | } 84 | 85 | private void resize(final int width, final int height) { 86 | if ( width < 0 || height < 0 ) 87 | throw new IllegalArgumentException("Invalid dimensions: " + width + " x " + height); 88 | 89 | destroyObjects(); 90 | 91 | this.width = width; 92 | this.height = height; 93 | 94 | this.stride = StreamUtil.getStride(width); 95 | 96 | if ( width == 0 || height == 0 ) 97 | return; 98 | 99 | bufferIndex = synchronousFrames = transfersToBuffer - 1; 100 | 101 | // Setup render FBO 102 | 103 | fboUtil.bindFramebuffer(GL_DRAW_FRAMEBUFFER, renderFBO); 104 | 105 | if ( samples <= 1 && readbackType == ReadbackType.GET_TEX_IMAGE ) 106 | fboUtil.framebufferTexture2D( 107 | GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 108 | rgbaBuffer = StreamUtil.createRenderTexture(width, height), 0 109 | ); 110 | else 111 | fboUtil.framebufferRenderbuffer( 112 | GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 113 | rgbaBuffer = StreamUtil.createRenderBuffer(fboUtil, width, height, samples, GL_RGBA8) 114 | ); 115 | 116 | fboUtil.framebufferRenderbuffer( 117 | GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 118 | depthBuffer = StreamUtil.createRenderBuffer(fboUtil, width, height, samples, GL_DEPTH24_STENCIL8) 119 | ); 120 | 121 | glViewport(0, 0, width, height); 122 | 123 | fboUtil.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); 124 | 125 | if ( 1 < samples ) { 126 | // Setup MSAA resolve FBO 127 | 128 | if ( msaaResolveFBO == 0 ) msaaResolveFBO = fboUtil.genFramebuffers(); 129 | 130 | fboUtil.bindFramebuffer(GL_READ_FRAMEBUFFER, msaaResolveFBO); 131 | 132 | if ( readbackType == ReadbackType.READ_PIXELS ) 133 | fboUtil.framebufferRenderbuffer( 134 | GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 135 | msaaResolveBuffer = StreamUtil.createRenderBuffer(fboUtil, width, height, GL_RGBA8) 136 | ); 137 | else 138 | fboUtil.framebufferTexture2D( 139 | GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 140 | msaaResolveBuffer = StreamUtil.createRenderTexture(width, height), 0 141 | ); 142 | 143 | fboUtil.bindFramebuffer(GL_READ_FRAMEBUFFER, 0); 144 | } else if ( msaaResolveFBO != 0 ) { 145 | if ( readbackType == ReadbackType.READ_PIXELS ) 146 | fboUtil.deleteRenderbuffers(msaaResolveBuffer); 147 | else 148 | glDeleteTextures(msaaResolveBuffer); 149 | msaaResolveBuffer = 0; 150 | 151 | fboUtil.deleteFramebuffers(msaaResolveFBO); 152 | msaaResolveFBO = 0; 153 | } 154 | 155 | // Setup read-back buffers 156 | 157 | resizeBuffers(height, stride); 158 | } 159 | 160 | protected void resizeBuffers(final int height, final int stride) { 161 | super.resizeBuffers(height, stride, GL_PIXEL_PACK_BUFFER, GL_STREAM_READ); 162 | } 163 | 164 | public void bind() { 165 | if ( this.width != handler.getWidth() || this.height != handler.getHeight() ) 166 | resize(handler.getWidth(), handler.getHeight()); 167 | 168 | fboUtil.bindFramebuffer(GL_DRAW_FRAMEBUFFER, renderFBO); 169 | } 170 | 171 | protected void prepareFramebuffer() { 172 | if ( msaaResolveFBO == 0 ) 173 | fboUtil.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); 174 | else { 175 | // Resolve MSAA 176 | fboUtil.bindFramebuffer(GL_READ_FRAMEBUFFER, renderFBO); 177 | fboUtil.bindFramebuffer(GL_DRAW_FRAMEBUFFER, msaaResolveFBO); 178 | fboUtil.blitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); 179 | fboUtil.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); 180 | fboUtil.bindFramebuffer(GL_READ_FRAMEBUFFER, 0); 181 | } 182 | } 183 | 184 | public void swapBuffers() { 185 | if ( width == 0 || height == 0 ) 186 | return; 187 | 188 | final int renderToPBO = (int)(bufferIndex % transfersToBuffer); 189 | final int readFromPBO = (int)((bufferIndex + 1) % transfersToBuffer); // Read from the oldest one we have rendered 190 | 191 | glBindBuffer(GL_PIXEL_PACK_BUFFER, pbos[renderToPBO]); 192 | 193 | // Back-pressure. Make sure we never buffer more than frames ahead. 194 | if ( processingState.get(renderToPBO) ) 195 | waitForProcessingToComplete(renderToPBO); 196 | 197 | prepareFramebuffer(); 198 | readBack(renderToPBO); 199 | // The glFlush is required because it forces the GL to start the readback as soon as possible. Without 200 | // flushing, it may delay the readback until the next action that depends on the PBO. This would effectively 201 | // make double-buffering almost as slow as single-buffering and triple-buffering as slow as double-buffering. 202 | glFlush(); 203 | 204 | // This will be non-zero for the first (transfersToBuffer - 1) frames 205 | // after start-up or a resize. 206 | if ( 0 < synchronousFrames ) { 207 | // The readFromPBO is currently empty. Wait for renderToPBO's ReadPixels to complete and copy the current frame to readFromPBO. 208 | // We do this to avoid sending an empty buffer for processing, which would cause a visible flicker on resize. 209 | copyFrames(renderToPBO, readFromPBO); 210 | synchronousFrames--; 211 | } 212 | 213 | // Time to process the readFromPBO 214 | 215 | pinBuffer(readFromPBO); 216 | 217 | // Send the buffer for processing 218 | 219 | processingState.set(readFromPBO, true); 220 | semaphores[readFromPBO].acquireUninterruptibly(); 221 | 222 | handler.process( 223 | width, height, 224 | pinnedBuffers[readFromPBO], 225 | stride, 226 | semaphores[readFromPBO] 227 | ); 228 | 229 | bufferIndex++; 230 | } 231 | 232 | protected void readBack(final int index) { 233 | // Stride in pixels 234 | glPixelStorei(GL_PACK_ROW_LENGTH, stride >> 2); 235 | 236 | // Asynchronously transfer current frame 237 | if ( readbackType == ReadbackType.READ_PIXELS ) { 238 | fboUtil.bindFramebuffer(GL_READ_FRAMEBUFFER, msaaResolveFBO == 0 ? renderFBO : msaaResolveFBO); 239 | glReadPixels(0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0); 240 | fboUtil.bindFramebuffer(GL_READ_FRAMEBUFFER, 0); 241 | } else { 242 | glBindTexture(GL_TEXTURE_2D, msaaResolveFBO == 0 ? rgbaBuffer : msaaResolveBuffer); 243 | glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0); 244 | glBindTexture(GL_TEXTURE_2D, 0); 245 | } 246 | 247 | // Restore PACK_ROW_LENGTH 248 | glPixelStorei(GL_PACK_ROW_LENGTH, 0); 249 | } 250 | 251 | protected abstract void copyFrames(final int src, final int trg); 252 | 253 | protected abstract void pinBuffer(final int index); 254 | 255 | protected void destroyObjects() { 256 | for ( int i = 0; i < semaphores.length; i++ ) { 257 | if ( processingState.get(i) ) { 258 | glBindBuffer(GL_PIXEL_PACK_BUFFER, pbos[i]); 259 | waitForProcessingToComplete(i); 260 | } 261 | } 262 | 263 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 264 | 265 | for ( int i = 0; i < pbos.length; i++ ) { 266 | if ( pbos[i] != 0 ) 267 | glDeleteBuffers(pbos[i]); 268 | } 269 | 270 | if ( msaaResolveBuffer != 0 ) { 271 | if ( readbackType == ReadbackType.READ_PIXELS ) 272 | fboUtil.deleteRenderbuffers(msaaResolveBuffer); 273 | else 274 | glDeleteTextures(msaaResolveBuffer); 275 | } 276 | if ( depthBuffer != 0 ) fboUtil.deleteRenderbuffers(depthBuffer); 277 | if ( rgbaBuffer != 0 ) { 278 | if ( samples <= 1 && readbackType == ReadbackType.GET_TEX_IMAGE ) 279 | glDeleteTextures(rgbaBuffer); 280 | else 281 | fboUtil.deleteRenderbuffers(rgbaBuffer); 282 | } 283 | } 284 | 285 | public void destroy() { 286 | destroyObjects(); 287 | 288 | if ( msaaResolveFBO != 0 ) 289 | fboUtil.deleteFramebuffers(msaaResolveFBO); 290 | fboUtil.deleteFramebuffers(renderFBO); 291 | } 292 | 293 | } -------------------------------------------------------------------------------- /src/org/lwjgl/util/stream/RenderStreamPBOAMD.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2012 LWJGL Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of 'LWJGL' nor the names of 17 | * its contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package org.lwjgl.util.stream; 33 | 34 | import org.lwjgl.BufferUtils; 35 | import org.lwjgl.MemoryUtil; 36 | import org.lwjgl.opengl.ContextCapabilities; 37 | import org.lwjgl.opengl.GLSync; 38 | import org.lwjgl.util.stream.StreamUtil.PageSizeProvider; 39 | import org.lwjgl.util.stream.StreamUtil.RenderStreamFactory; 40 | 41 | import java.nio.ByteBuffer; 42 | import java.nio.ByteOrder; 43 | 44 | import static org.lwjgl.opengl.AMDPinnedMemory.*; 45 | import static org.lwjgl.opengl.GL15.*; 46 | import static org.lwjgl.opengl.GL32.*; 47 | 48 | /** Optimized StreamPBOReader for AMD GPUs: Asynchronous ReadPixels to AMD_pinned_memory buffers. */ 49 | final class RenderStreamPBOAMD extends RenderStreamPBO { 50 | 51 | public static final RenderStreamFactory FACTORY = new RenderStreamFactory("AMD_pinned_memory") { 52 | public boolean isSupported(final ContextCapabilities caps) { 53 | return TextureStreamPBODefault.FACTORY.isSupported(caps) && caps.GL_AMD_pinned_memory && (caps.OpenGL32 || caps.GL_ARB_sync); 54 | } 55 | 56 | public RenderStream create(final StreamHandler handler, final int samples, final int transfersToBuffer) { 57 | return new RenderStreamPBOAMD(handler, samples, transfersToBuffer); 58 | } 59 | }; 60 | 61 | private final GLSync[] fences; 62 | 63 | RenderStreamPBOAMD(final StreamHandler handler, final int samples, final int transfersToBuffer) { 64 | super(handler, samples, transfersToBuffer, ReadbackType.READ_PIXELS); 65 | 66 | fences = new GLSync[this.transfersToBuffer]; 67 | } 68 | 69 | protected void resizeBuffers(final int height, final int stride) { 70 | final int renderBytes = height * stride; 71 | 72 | for ( int i = 0; i < pbos.length; i++ ) { 73 | pbos[i] = glGenBuffers(); 74 | 75 | glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, pbos[i]); 76 | 77 | // Pre-allocate page-aligned pinned buffers 78 | final int PAGE_SIZE = PageSizeProvider.PAGE_SIZE; 79 | final ByteBuffer buffer = BufferUtils.createByteBuffer(renderBytes + PAGE_SIZE - 1); 80 | final int pageOffset = (int)(MemoryUtil.getAddress(buffer) % PAGE_SIZE); 81 | if ( pageOffset != 0 ) 82 | buffer.position(PAGE_SIZE - pageOffset); // Aligns to page 83 | buffer.limit(buffer.position() + renderBytes); // Caps remaining() to renderBytes 84 | 85 | pinnedBuffers[i] = buffer.slice().order(ByteOrder.nativeOrder()); 86 | glBufferData(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, pinnedBuffers[i], GL_STREAM_READ); 87 | } 88 | 89 | glBindBuffer(GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, 0); 90 | } 91 | 92 | protected void readBack(final int index) { 93 | super.readBack(index); 94 | 95 | // Insert a fence after ReadPixels 96 | fences[index] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); 97 | } 98 | 99 | protected void pinBuffer(final int index) { 100 | if ( fences[index] != null ) // Wait for ReadPixels on the PBO to complete 101 | StreamUtil.waitOnFence(fences, index); 102 | } 103 | 104 | protected void copyFrames(final int src, final int trg) { 105 | StreamUtil.waitOnFence(fences, src); 106 | 107 | final ByteBuffer srcBuffer = pinnedBuffers[src]; 108 | final ByteBuffer trgBuffer = pinnedBuffers[trg]; 109 | 110 | trgBuffer.put(srcBuffer); 111 | 112 | trgBuffer.flip(); 113 | srcBuffer.flip(); 114 | } 115 | 116 | protected void postProcess(final int index) { 117 | } 118 | 119 | protected void destroyObjects() { 120 | for ( int i = 0; i < fences.length; i++ ) { 121 | if ( fences[i] != null ) 122 | StreamUtil.waitOnFence(fences, i); 123 | } 124 | 125 | super.destroyObjects(); 126 | } 127 | 128 | } -------------------------------------------------------------------------------- /src/org/lwjgl/util/stream/RenderStreamPBOCopy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2012 LWJGL Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of 'LWJGL' nor the names of 17 | * its contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package org.lwjgl.util.stream; 33 | 34 | import org.lwjgl.opengl.ContextCapabilities; 35 | import org.lwjgl.util.stream.StreamUtil.RenderStreamFactory; 36 | 37 | import static org.lwjgl.opengl.GL15.*; 38 | import static org.lwjgl.opengl.GL21.*; 39 | import static org.lwjgl.opengl.GL31.*; 40 | 41 | /** Default StreamPBOReader implementation: Asynchronous ReadPixels to PBOs */ 42 | final class RenderStreamPBOCopy extends RenderStreamPBO { 43 | 44 | public static final RenderStreamFactory FACTORY = new RenderStreamFactory("ARB_copy_buffer") { 45 | public boolean isSupported(final ContextCapabilities caps) { 46 | return RenderStreamPBODefault.FACTORY.isSupported(caps) 47 | && caps.GL_ARB_copy_buffer 48 | && caps.GL_NV_gpu_program5 // Nvidia only 49 | && (caps.OpenGL40 || caps.GL_ARB_tessellation_shader) // Fermi+ 50 | ; 51 | } 52 | 53 | public RenderStream create(final StreamHandler handler, final int samples, final int transfersToBuffer) { 54 | return new RenderStreamPBOCopy(handler, samples, transfersToBuffer, ReadbackType.GET_TEX_IMAGE); 55 | } 56 | }; 57 | 58 | private int devicePBO; 59 | 60 | RenderStreamPBOCopy(final StreamHandler handler, final int samples, final int transfersToBuffer, final ReadbackType readbackType) { 61 | super(handler, samples, transfersToBuffer, readbackType); 62 | } 63 | 64 | protected void resizeBuffers(final int height, final int stride) { 65 | super.resizeBuffers(height, stride); 66 | 67 | devicePBO = glGenBuffers(); 68 | 69 | glBindBuffer(GL_PIXEL_PACK_BUFFER, devicePBO); 70 | glBufferData(GL_PIXEL_PACK_BUFFER, height * stride, GL_STREAM_COPY); // Should allocate device memory 71 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 72 | } 73 | 74 | protected void readBack(final int index) { 75 | glBindBuffer(GL_PIXEL_PACK_BUFFER, devicePBO); 76 | 77 | super.readBack(index); 78 | 79 | glBindBuffer(GL_COPY_WRITE_BUFFER, pbos[index]); 80 | 81 | glCopyBufferSubData(GL_PIXEL_PACK_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, height * stride); 82 | 83 | glBindBuffer(GL_COPY_WRITE_BUFFER, 0); 84 | glBindBuffer(GL_COPY_READ_BUFFER, 0); 85 | } 86 | 87 | protected void pinBuffer(final int index) { 88 | glBindBuffer(GL_PIXEL_PACK_BUFFER, pbos[index]); 89 | 90 | // We don't need to manually synchronized here, MapBuffer will block until ReadPixels above has finished. 91 | // The buffer will be unmapped in waitForProcessingToComplete 92 | pinnedBuffers[index] = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY, height * stride, pinnedBuffers[index]); 93 | 94 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 95 | } 96 | 97 | protected void copyFrames(final int src, final int trg) { 98 | glBindBuffer(GL_PIXEL_PACK_BUFFER, pbos[src]); 99 | glBindBuffer(GL_COPY_WRITE_BUFFER, pbos[trg]); 100 | 101 | glCopyBufferSubData(GL_PIXEL_PACK_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, height * stride); 102 | 103 | glBindBuffer(GL_COPY_WRITE_BUFFER, 0); 104 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 105 | } 106 | 107 | protected void postProcess(final int index) { 108 | glUnmapBuffer(GL_PIXEL_PACK_BUFFER); 109 | } 110 | 111 | protected void destroyObjects() { 112 | glDeleteBuffers(devicePBO); 113 | super.destroyObjects(); 114 | } 115 | } -------------------------------------------------------------------------------- /src/org/lwjgl/util/stream/RenderStreamPBODefault.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2012 LWJGL Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of 'LWJGL' nor the names of 17 | * its contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package org.lwjgl.util.stream; 33 | 34 | import org.lwjgl.opengl.ContextCapabilities; 35 | import org.lwjgl.opengl.GLContext; 36 | import org.lwjgl.util.stream.StreamUtil.RenderStreamFactory; 37 | 38 | import static org.lwjgl.opengl.GL15.*; 39 | import static org.lwjgl.opengl.GL21.*; 40 | import static org.lwjgl.opengl.GL31.*; 41 | 42 | /** Default StreamPBOReader implementation: Asynchronous ReadPixels to PBOs */ 43 | final class RenderStreamPBODefault extends RenderStreamPBO { 44 | 45 | public static final RenderStreamFactory FACTORY = new RenderStreamFactory("Asynchronous PBO") { 46 | public boolean isSupported(final ContextCapabilities caps) { 47 | return caps.OpenGL21 || caps.GL_ARB_pixel_buffer_object || caps.GL_EXT_pixel_buffer_object; 48 | } 49 | 50 | public RenderStream create(final StreamHandler handler, final int samples, final int transfersToBuffer) { 51 | final ContextCapabilities caps = GLContext.getCapabilities(); 52 | 53 | return new RenderStreamPBODefault( 54 | handler, samples, transfersToBuffer, 55 | // Detect NVIDIA and use GetTexImage instead of ReadPixels 56 | StreamUtil.isNVIDIA(caps) ? ReadbackType.GET_TEX_IMAGE : ReadbackType.READ_PIXELS 57 | ); 58 | } 59 | }; 60 | 61 | private final boolean USE_COPY_BUFFER_SUB_DATA; 62 | 63 | RenderStreamPBODefault(final StreamHandler handler, final int samples, final int transfersToBuffer, final ReadbackType readbackType) { 64 | super(handler, samples, transfersToBuffer, readbackType); 65 | 66 | final ContextCapabilities caps = GLContext.getCapabilities(); 67 | 68 | USE_COPY_BUFFER_SUB_DATA = (caps.OpenGL31 || caps.GL_ARB_copy_buffer) && 69 | // Disable on ATI/AMD GPUs: ARB_copy_buffer is unoptimized on current 70 | // drivers and kills performance. TODO: Fix? 71 | !StreamUtil.isAMD(caps); 72 | } 73 | 74 | protected void pinBuffer(final int index) { 75 | glBindBuffer(GL_PIXEL_PACK_BUFFER, pbos[index]); 76 | 77 | // We don't need to manually synchronize here, MapBuffer will block until ReadPixels above has finished. 78 | // The buffer will be unmapped in waitForProcessingToComplete 79 | pinnedBuffers[index] = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY, height * stride, pinnedBuffers[index]); 80 | 81 | glBindBuffer(GL_PIXEL_PACK_BUFFER, 0); 82 | } 83 | 84 | protected void copyFrames(final int src, final int trg) { 85 | if ( USE_COPY_BUFFER_SUB_DATA ) { 86 | glBindBuffer(GL_COPY_WRITE_BUFFER, pbos[trg]); 87 | glCopyBufferSubData(GL_PIXEL_PACK_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, height * stride); 88 | glBindBuffer(GL_COPY_WRITE_BUFFER, 0); 89 | } else { 90 | pinnedBuffers[src] = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY, height * stride, pinnedBuffers[src]); 91 | 92 | glBindBuffer(GL_PIXEL_PACK_BUFFER, pbos[trg]); 93 | glBufferSubData(GL_PIXEL_PACK_BUFFER, 0, pinnedBuffers[src]); 94 | 95 | glBindBuffer(GL_PIXEL_PACK_BUFFER, pbos[src]); 96 | glUnmapBuffer(GL_PIXEL_PACK_BUFFER); 97 | } 98 | } 99 | 100 | protected void postProcess(final int index) { 101 | glUnmapBuffer(GL_PIXEL_PACK_BUFFER); 102 | } 103 | 104 | } -------------------------------------------------------------------------------- /src/org/lwjgl/util/stream/StreamBuffered.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2012 LWJGL Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of 'LWJGL' nor the names of 17 | * its contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package org.lwjgl.util.stream; 33 | 34 | import java.nio.ByteBuffer; 35 | import java.util.BitSet; 36 | import java.util.concurrent.Semaphore; 37 | 38 | /** Base functionality for buffered transfers. */ 39 | abstract class StreamBuffered { 40 | 41 | protected final StreamHandler handler; 42 | 43 | // Low: Less memory usage, less concurrency, less transfers behind 44 | // High: More memory usages, more concurrency, more transfers behind 45 | protected final int transfersToBuffer; // 3 provides optimal concurrency in most cases 46 | 47 | protected final ByteBuffer[] pinnedBuffers; 48 | protected final Semaphore[] semaphores; // Required for synchronization with the processing thread 49 | 50 | /** 51 | * A flag per pinned buffer that indicates if it's currently being 52 | * processed by the handler. 53 | */ 54 | protected final BitSet processingState; 55 | 56 | protected int width; 57 | protected int height; 58 | protected int stride; 59 | 60 | protected long bufferIndex; 61 | 62 | protected StreamBuffered(final StreamHandler handler, final int transfersToBuffer) { 63 | this.handler = handler; 64 | this.transfersToBuffer = transfersToBuffer; 65 | 66 | pinnedBuffers = new ByteBuffer[transfersToBuffer]; 67 | semaphores = new Semaphore[transfersToBuffer]; 68 | for ( int i = 0; i < semaphores.length; i++ ) 69 | semaphores[i] = new Semaphore(1, false); 70 | 71 | processingState = new BitSet(transfersToBuffer); 72 | } 73 | 74 | protected void waitForProcessingToComplete(final int index) { 75 | final Semaphore s = semaphores[index]; 76 | // Early-out: start-up or handler has finished processing 77 | if ( s.availablePermits() == 0 ) { 78 | // This will block until handler has finished processing 79 | s.acquireUninterruptibly(); 80 | // Give the permit back 81 | s.release(); 82 | } 83 | 84 | postProcess(index); 85 | processingState.set(index, false); 86 | } 87 | 88 | protected abstract void postProcess(int index); 89 | 90 | } -------------------------------------------------------------------------------- /src/org/lwjgl/util/stream/StreamBufferedPBO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2012 LWJGL Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of 'LWJGL' nor the names of 17 | * its contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package org.lwjgl.util.stream; 33 | 34 | import static org.lwjgl.opengl.GL15.*; 35 | 36 | /** Base functionality for streaming PBO transfers. */ 37 | abstract class StreamBufferedPBO extends StreamBuffered { 38 | 39 | protected final int[] pbos; 40 | 41 | protected StreamBufferedPBO(final StreamHandler handler, final int transfersToBuffer) { 42 | super(handler, transfersToBuffer); 43 | 44 | pbos = new int[transfersToBuffer]; 45 | } 46 | 47 | protected void resizeBuffers(final int height, final int stride, final int pboTarget, final int pboUsage) { 48 | final int renderBytes = height * stride; 49 | 50 | for ( int i = 0; i < pbos.length; i++ ) { 51 | pbos[i] = glGenBuffers(); 52 | 53 | glBindBuffer(pboTarget, pbos[i]); 54 | glBufferData(pboTarget, renderBytes, pboUsage); 55 | 56 | pinnedBuffers[i] = null; 57 | } 58 | 59 | glBindBuffer(pboTarget, 0); 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /src/org/lwjgl/util/stream/StreamHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2012 LWJGL Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of 'LWJGL' nor the names of 17 | * its contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package org.lwjgl.util.stream; 33 | 34 | import java.nio.ByteBuffer; 35 | import java.util.concurrent.Semaphore; 36 | 37 | /** @author Spasi */ 38 | public interface StreamHandler { 39 | 40 | int getWidth(); 41 | 42 | int getHeight(); 43 | 44 | void process(final int width, final int height, ByteBuffer data, final int stride, Semaphore signal); 45 | 46 | } -------------------------------------------------------------------------------- /src/org/lwjgl/util/stream/StreamUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2012 LWJGL Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of 'LWJGL' nor the names of 17 | * its contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package org.lwjgl.util.stream; 33 | 34 | import org.lwjgl.opengl.ContextCapabilities; 35 | import org.lwjgl.opengl.GLContext; 36 | import org.lwjgl.opengl.GLSync; 37 | 38 | import java.lang.reflect.Field; 39 | import java.lang.reflect.Modifier; 40 | import java.nio.ByteBuffer; 41 | import java.util.ArrayList; 42 | import java.util.List; 43 | 44 | import sun.misc.Unsafe; 45 | 46 | import static org.lwjgl.opengl.EXTFramebufferBlit.*; 47 | import static org.lwjgl.opengl.EXTFramebufferMultisample.*; 48 | import static org.lwjgl.opengl.EXTFramebufferObject.*; 49 | import static org.lwjgl.opengl.GL11.*; 50 | import static org.lwjgl.opengl.GL11.glGetInteger; 51 | import static org.lwjgl.opengl.GL12.*; 52 | import static org.lwjgl.opengl.GL30.*; 53 | import static org.lwjgl.opengl.GL32.*; 54 | 55 | /** @author Spasi */ 56 | public final class StreamUtil { 57 | 58 | private static final int TEX_ROW_ALIGNMENT = 16 * 4; // 16 pixels 59 | 60 | private StreamUtil() { 61 | } 62 | 63 | static int createRenderTexture(final int width, final int height) { 64 | return createRenderTexture(width, height, GL_NEAREST); 65 | } 66 | 67 | static int createRenderTexture(final int width, final int height, final int filter) { 68 | final int texID = glGenTextures(); 69 | 70 | glBindTexture(GL_TEXTURE_2D, texID); 71 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); 72 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); 73 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, (ByteBuffer)null); 74 | glBindTexture(GL_TEXTURE_2D, 0); 75 | 76 | return texID; 77 | } 78 | 79 | static int createRenderBuffer(final FBOUtil fboUtil, final int width, final int height, final int internalformat) { 80 | return createRenderBuffer(fboUtil, width, height, 1, internalformat); 81 | } 82 | 83 | static int createRenderBuffer(final FBOUtil fboUtil, final int width, final int height, final int samples, final int internalformat) { 84 | final int bufferID = fboUtil.genRenderbuffers(); 85 | 86 | fboUtil.bindRenderbuffer(GL_RENDERBUFFER, bufferID); 87 | if ( samples <= 1 ) 88 | fboUtil.renderbufferStorage(GL_RENDERBUFFER, internalformat, width, height); 89 | else 90 | fboUtil.renderbufferStorageMultisample(GL_RENDERBUFFER, samples, internalformat, width, height); 91 | fboUtil.bindRenderbuffer(GL_RENDERBUFFER, 0); 92 | 93 | return bufferID; 94 | } 95 | 96 | static void waitOnFence(final GLSync[] fences, final int index) { 97 | glClientWaitSync(fences[index], 0, GL_TIMEOUT_IGNORED); 98 | glDeleteSync(fences[index]); 99 | fences[index] = null; 100 | } 101 | 102 | static boolean isAMD(final ContextCapabilities caps) { 103 | return caps.GL_ATI_fragment_shader || caps.GL_ATI_texture_compression_3dc || caps.GL_AMD_debug_output; 104 | } 105 | 106 | static boolean isNVIDIA(final ContextCapabilities caps) { 107 | return caps.GL_NV_vertex_program || caps.GL_NV_register_combiners || caps.GL_NV_gpu_program4; 108 | } 109 | 110 | static int getStride(final int width) { 111 | // Force a packed format on AMD. Their drivers show unstable 112 | // performance if we mess with (UN)PACK_ROW_LENGTH. 113 | return isAMD(GLContext.getCapabilities()) ? 114 | width * 4 : 115 | getStride(width, TEX_ROW_ALIGNMENT); 116 | } 117 | 118 | /** 119 | * Aligns the row stride. This is beneficial for all the memcpy's we're doing (and also required for INTEL_map_texture). 120 | * 121 | * @param width the row width in pixels 122 | * @param aligment the row aligment in bytes. Must be a power-of-two value. 123 | * 124 | * @return the aligned row stride 125 | */ 126 | static int getStride(final int width, final int aligment) { 127 | int stride = width * 4; 128 | 129 | if ( (stride & (aligment - 1)) != 0 ) 130 | stride += aligment - (stride & (aligment - 1)); 131 | 132 | return stride; 133 | } 134 | 135 | private static void checkCapabilities(final ContextCapabilities caps) { 136 | if ( !caps.OpenGL15 ) 137 | throw new UnsupportedOperationException("Support for OpenGL 1.5 or higher is required."); 138 | 139 | if ( !(caps.OpenGL20 || caps.GL_ARB_texture_non_power_of_two) ) 140 | throw new UnsupportedOperationException("Support for npot textures is required."); 141 | 142 | if ( !(caps.OpenGL30 || caps.GL_ARB_framebuffer_object || caps.GL_EXT_framebuffer_object) ) 143 | throw new UnsupportedOperationException("Framebuffer object support is required."); 144 | } 145 | 146 | public static RenderStreamFactory getRenderStreamImplementation() { 147 | final List list = getRenderStreamImplementations(); 148 | 149 | if ( list.isEmpty() ) 150 | throw new UnsupportedOperationException("A supported TextureStream implementation could not be found."); 151 | 152 | return list.get(0); 153 | } 154 | 155 | public static List getRenderStreamImplementations() { 156 | final ContextCapabilities caps = GLContext.getCapabilities(); 157 | 158 | checkCapabilities(caps); 159 | 160 | final List list = new ArrayList(); 161 | 162 | addIfSupported(caps, list, RenderStreamPBOAMD.FACTORY); 163 | addIfSupported(caps, list, RenderStreamPBOCopy.FACTORY); 164 | addIfSupported(caps, list, RenderStreamINTEL.FACTORY); 165 | addIfSupported(caps, list, RenderStreamPBODefault.FACTORY); 166 | 167 | return list; 168 | } 169 | 170 | public static TextureStreamFactory getTextureStreamImplementation() { 171 | final List list = getTextureStreamImplementations(); 172 | 173 | if ( list.isEmpty() ) 174 | throw new UnsupportedOperationException("A supported TextureStream implementation could not be found."); 175 | 176 | return list.get(0); 177 | } 178 | 179 | public static List getTextureStreamImplementations() { 180 | final ContextCapabilities caps = GLContext.getCapabilities(); 181 | 182 | checkCapabilities(caps); 183 | 184 | final List list = new ArrayList(); 185 | 186 | addIfSupported(caps, list, TextureStreamINTEL.FACTORY); 187 | addIfSupported(caps, list, TextureStreamPBORange.FACTORY); 188 | addIfSupported(caps, list, TextureStreamPBODefault.FACTORY); 189 | 190 | return list; 191 | } 192 | 193 | private static > void addIfSupported(final ContextCapabilities caps, final List list, final T factory) { 194 | if ( factory.isSupported(caps) ) 195 | list.add(factory); 196 | } 197 | 198 | public abstract static class StreamFactory { 199 | 200 | private final String description; 201 | 202 | protected StreamFactory(final String description) { 203 | this.description = description; 204 | } 205 | 206 | public String getDescription() { 207 | return description; 208 | } 209 | 210 | public abstract boolean isSupported(ContextCapabilities caps); 211 | 212 | public String toString() { 213 | return description; 214 | } 215 | 216 | } 217 | 218 | public abstract static class RenderStreamFactory extends StreamFactory { 219 | 220 | protected RenderStreamFactory(final String description) { 221 | super(description); 222 | } 223 | 224 | public abstract RenderStream create(StreamHandler handler, int samples, int transfersToBuffer); 225 | 226 | } 227 | 228 | public abstract static class TextureStreamFactory extends StreamFactory { 229 | 230 | protected TextureStreamFactory(final String description) { 231 | super(description); 232 | } 233 | 234 | public abstract TextureStream create(StreamHandler handler, int transfersToBuffer); 235 | 236 | } 237 | 238 | static int checkSamples(final int samples, final ContextCapabilities caps) { 239 | if ( samples <= 1 ) 240 | return samples; 241 | 242 | if ( !(caps.OpenGL30 || (caps.GL_EXT_framebuffer_multisample && caps.GL_EXT_framebuffer_blit)) ) 243 | throw new UnsupportedOperationException("Multisampled rendering on framebuffer objects is not supported."); 244 | 245 | return Math.min(samples, glGetInteger(GL_MAX_SAMPLES)); 246 | } 247 | 248 | static final class PageSizeProvider { 249 | 250 | static final int PAGE_SIZE; 251 | 252 | static { 253 | int pageSize = 4096; // Assume 4kb if Unsafe is not available 254 | 255 | try { 256 | pageSize = getUnsafeInstance().pageSize(); 257 | } catch (Exception e) { 258 | // ignore 259 | } 260 | 261 | PAGE_SIZE = pageSize; 262 | } 263 | 264 | private static Unsafe getUnsafeInstance() { 265 | final Field[] fields = Unsafe.class.getDeclaredFields(); 266 | 267 | /* 268 | Different runtimes use different names for the Unsafe singleton, 269 | so we cannot use .getDeclaredField and we scan instead. For example: 270 | 271 | Oracle: theUnsafe 272 | PERC : m_unsafe_instance 273 | Android: THE_ONE 274 | */ 275 | for ( Field field : fields ) { 276 | if ( !field.getType().equals(Unsafe.class) ) 277 | continue; 278 | 279 | final int modifiers = field.getModifiers(); 280 | if ( !(Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) ) 281 | continue; 282 | 283 | field.setAccessible(true); 284 | try { 285 | return (Unsafe)field.get(null); 286 | } catch (IllegalAccessException e) { 287 | // ignore 288 | } 289 | break; 290 | } 291 | 292 | throw new UnsupportedOperationException(); 293 | } 294 | } 295 | 296 | interface FBOUtil { 297 | 298 | int genFramebuffers(); 299 | 300 | void bindFramebuffer(int target, int framebuffer); 301 | 302 | void framebufferTexture2D(int target, int attachment, int textarget, int texture, int level); 303 | 304 | void framebufferRenderbuffer(int target, int attachment, int renderbuffertarget, int renderbuffer); 305 | 306 | void deleteFramebuffers(int framebuffer); 307 | 308 | int genRenderbuffers(); 309 | 310 | void bindRenderbuffer(int target, int renderbuffer); 311 | 312 | void renderbufferStorage(int target, int internalformat, int width, int height); 313 | 314 | void renderbufferStorageMultisample(int target, int samples, int internalformat, int width, int height); 315 | 316 | void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter); 317 | 318 | void deleteRenderbuffers(int renderbuffer); 319 | 320 | } 321 | 322 | static FBOUtil getFBOUtil(final ContextCapabilities caps) { 323 | if ( caps.OpenGL30 || caps.GL_ARB_framebuffer_object ) 324 | return new FBOUtil() { 325 | public int genFramebuffers() { 326 | return glGenFramebuffers(); 327 | } 328 | 329 | public void bindFramebuffer(int target, int framebuffer) { 330 | glBindFramebuffer(target, framebuffer); 331 | } 332 | 333 | public void framebufferTexture2D(int target, int attachment, int textarget, int texture, int level) { 334 | glFramebufferTexture2D(target, attachment, textarget, texture, level); 335 | } 336 | 337 | public void framebufferRenderbuffer(int target, int attachment, int renderbuffertarget, int renderbuffer) { 338 | glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); 339 | } 340 | 341 | public void deleteFramebuffers(int framebuffer) { 342 | glDeleteFramebuffers(framebuffer); 343 | } 344 | 345 | public int genRenderbuffers() { 346 | return glGenRenderbuffers(); 347 | } 348 | 349 | public void bindRenderbuffer(int target, int renderbuffer) { 350 | glBindRenderbuffer(target, renderbuffer); 351 | } 352 | 353 | public void renderbufferStorage(int target, int internalformat, int width, int height) { 354 | glRenderbufferStorage(target, internalformat, width, height); 355 | } 356 | 357 | public void renderbufferStorageMultisample(int target, int samples, int internalformat, int width, int height) { 358 | glRenderbufferStorageMultisample(target, samples, internalformat, width, height); 359 | } 360 | 361 | public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) { 362 | glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); 363 | } 364 | 365 | public void deleteRenderbuffers(int renderbuffer) { 366 | glDeleteRenderbuffers(renderbuffer); 367 | } 368 | }; 369 | else if ( caps.GL_EXT_framebuffer_object ) 370 | return new FBOUtil() { 371 | public int genFramebuffers() { 372 | return glGenFramebuffersEXT(); 373 | } 374 | 375 | public void bindFramebuffer(int target, int framebuffer) { 376 | glBindFramebufferEXT(target, framebuffer); 377 | } 378 | 379 | public void framebufferTexture2D(int target, int attachment, int textarget, int texture, int level) { 380 | glFramebufferTexture2DEXT(target, attachment, textarget, texture, level); 381 | } 382 | 383 | public void framebufferRenderbuffer(int target, int attachment, int renderbuffertarget, int renderbuffer) { 384 | glFramebufferRenderbufferEXT(target, attachment, renderbuffertarget, renderbuffer); 385 | } 386 | 387 | public void deleteFramebuffers(int framebuffer) { 388 | glDeleteFramebuffersEXT(framebuffer); 389 | } 390 | 391 | public int genRenderbuffers() { 392 | return glGenRenderbuffersEXT(); 393 | } 394 | 395 | public void bindRenderbuffer(int target, int renderbuffer) { 396 | glBindRenderbufferEXT(target, renderbuffer); 397 | } 398 | 399 | public void renderbufferStorage(int target, int internalformat, int width, int height) { 400 | glRenderbufferStorageEXT(target, internalformat, width, height); 401 | } 402 | 403 | public void renderbufferStorageMultisample(int target, int samples, int internalformat, int width, int height) { 404 | glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height); 405 | } 406 | 407 | public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) { 408 | glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); 409 | } 410 | 411 | public void deleteRenderbuffers(int renderbuffer) { 412 | glDeleteRenderbuffersEXT(renderbuffer); 413 | } 414 | }; 415 | else 416 | throw new UnsupportedOperationException("Framebuffer object is not available."); 417 | } 418 | 419 | } -------------------------------------------------------------------------------- /src/org/lwjgl/util/stream/TextureStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2012 LWJGL Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of 'LWJGL' nor the names of 17 | * its contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package org.lwjgl.util.stream; 33 | 34 | /** @author Spasi */ 35 | public interface TextureStream { 36 | 37 | StreamHandler getHandler(); 38 | 39 | int getWidth(); 40 | 41 | int getHeight(); 42 | 43 | void snapshot(); 44 | 45 | void tick(); 46 | 47 | void bind(); 48 | 49 | void destroy(); 50 | 51 | } -------------------------------------------------------------------------------- /src/org/lwjgl/util/stream/TextureStreamINTEL.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2012 LWJGL Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of 'LWJGL' nor the names of 17 | * its contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package org.lwjgl.util.stream; 33 | 34 | import org.lwjgl.BufferUtils; 35 | import org.lwjgl.opengl.ContextCapabilities; 36 | import org.lwjgl.opengl.GLContext; 37 | import org.lwjgl.util.stream.StreamUtil.TextureStreamFactory; 38 | 39 | import java.nio.ByteBuffer; 40 | import java.nio.IntBuffer; 41 | 42 | import static org.lwjgl.opengl.GL11.*; 43 | import static org.lwjgl.opengl.GL12.*; 44 | import static org.lwjgl.opengl.GL30.*; 45 | import static org.lwjgl.opengl.INTELMapTexture.*; 46 | 47 | /** 48 | * Optimized StreamPBOReader for Intel IGPs: 49 | *

50 | * - We render to a standard FBO. 51 | * - We asynchronously blit to another FBO with INTEL_map_texture attachments (linear layout). 52 | * - We synchronously map the linear textures. 53 | *

54 | */ 55 | final class TextureStreamINTEL extends StreamBuffered implements TextureStream { 56 | 57 | public static final TextureStreamFactory FACTORY = new TextureStreamFactory("INTEL_map_texture") { 58 | public boolean isSupported(final ContextCapabilities caps) { 59 | // TODO: We currently require BlitFramebuffer. Relax and implement manually? 60 | return caps.GL_INTEL_map_texture && (caps.OpenGL30 || caps.GL_ARB_framebuffer_object || caps.GL_EXT_framebuffer_blit); 61 | } 62 | 63 | public TextureStream create(final StreamHandler handler, final int transfersToBuffer) { 64 | return new TextureStreamINTEL(handler, transfersToBuffer); 65 | } 66 | }; 67 | 68 | private final IntBuffer strideBuffer; 69 | private final IntBuffer layoutBuffer; 70 | 71 | private final StreamUtil.FBOUtil fboUtil; 72 | 73 | private final int texFBO; 74 | private final int bufferFBO; 75 | 76 | private int texID; 77 | private int[] buffers; 78 | 79 | private long currentIndex; 80 | 81 | private boolean resetTexture; 82 | 83 | TextureStreamINTEL(final StreamHandler handler, final int transfersToBuffer) { 84 | super(handler, transfersToBuffer); 85 | 86 | this.strideBuffer = BufferUtils.createIntBuffer(1); 87 | this.layoutBuffer = BufferUtils.createIntBuffer(1); 88 | 89 | fboUtil = StreamUtil.getFBOUtil(GLContext.getCapabilities()); 90 | 91 | texFBO = fboUtil.genFramebuffers(); 92 | bufferFBO = fboUtil.genFramebuffers(); 93 | 94 | buffers = new int[transfersToBuffer]; 95 | } 96 | 97 | public StreamHandler getHandler() { 98 | return handler; 99 | } 100 | 101 | public int getWidth() { 102 | return width; 103 | } 104 | 105 | public int getHeight() { 106 | return height; 107 | } 108 | 109 | private void resize(final int width, final int height) { 110 | if ( width < 0 || height < 0 ) 111 | throw new IllegalArgumentException("Invalid dimensions: " + width + " x " + height); 112 | 113 | destroyObjects(); 114 | 115 | this.width = width; 116 | this.height = height; 117 | 118 | this.stride = StreamUtil.getStride(width); 119 | 120 | if ( width == 0 || height == 0 ) 121 | return; 122 | 123 | bufferIndex = 0; 124 | currentIndex = 0; 125 | 126 | resetTexture = true; 127 | 128 | texID = StreamUtil.createRenderTexture(width, height, GL_LINEAR); 129 | 130 | fboUtil.bindFramebuffer(GL_DRAW_FRAMEBUFFER, texFBO); 131 | fboUtil.framebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texID, 0); 132 | fboUtil.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); 133 | 134 | for ( int i = 0; i < buffers.length; i++ ) 135 | buffers[i] = genLayoutLinearTexture(width, height); 136 | 137 | glBindTexture(GL_TEXTURE_2D, 0); 138 | } 139 | 140 | private static int genLayoutLinearTexture(final int width, final int height) { 141 | final int texID = glGenTextures(); 142 | 143 | glBindTexture(GL_TEXTURE_2D, texID); 144 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 145 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 146 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MEMORY_LAYOUT_INTEL, GL_LAYOUT_LINEAR_CPU_CACHED_INTEL); 147 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, (ByteBuffer)null); 148 | 149 | return texID; 150 | } 151 | 152 | public void snapshot() { 153 | if ( width != handler.getWidth() || height != handler.getHeight() ) 154 | resize(handler.getWidth(), handler.getHeight()); 155 | 156 | if ( width == 0 || height == 0 ) 157 | return; 158 | 159 | final int trgPBO = (int)(bufferIndex % transfersToBuffer); 160 | 161 | // Back-pressure. Make sure we never buffer more than frames ahead. 162 | 163 | if ( processingState.get(trgPBO) ) 164 | syncCopy(trgPBO); 165 | 166 | pinnedBuffers[trgPBO] = glMapTexture2DINTEL(buffers[trgPBO], 0, height * stride, GL_MAP_WRITE_BIT, strideBuffer, layoutBuffer, pinnedBuffers[trgPBO]); 167 | 168 | // Send the buffer for processing 169 | 170 | processingState.set(trgPBO, true); 171 | semaphores[trgPBO].acquireUninterruptibly(); 172 | 173 | handler.process( 174 | width, height, 175 | pinnedBuffers[trgPBO], 176 | stride, 177 | semaphores[trgPBO] 178 | ); 179 | 180 | bufferIndex++; 181 | 182 | if ( resetTexture ) { 183 | syncCopy(trgPBO); 184 | resetTexture = true; 185 | } 186 | } 187 | 188 | public void tick() { 189 | final int srcPBO = (int)(currentIndex % transfersToBuffer); 190 | if ( !processingState.get(srcPBO) ) 191 | return; 192 | 193 | // Try again next frame 194 | if ( !semaphores[srcPBO].tryAcquire() ) 195 | return; 196 | 197 | semaphores[srcPBO].release(); // Give it back 198 | 199 | postProcess(srcPBO); 200 | processingState.set(srcPBO, false); 201 | 202 | copyTexture(srcPBO); 203 | } 204 | 205 | private void syncCopy(final int index) { 206 | waitForProcessingToComplete(index); 207 | copyTexture(index); 208 | } 209 | 210 | private void copyTexture(final int index) { 211 | fboUtil.bindFramebuffer(GL_READ_FRAMEBUFFER, bufferFBO); 212 | fboUtil.bindFramebuffer(GL_DRAW_FRAMEBUFFER, texFBO); 213 | 214 | fboUtil.framebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, buffers[index], 0); 215 | fboUtil.blitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); 216 | fboUtil.framebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0); 217 | 218 | fboUtil.bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); 219 | fboUtil.bindFramebuffer(GL_READ_FRAMEBUFFER, 0); 220 | 221 | currentIndex++; 222 | } 223 | 224 | protected void postProcess(final int index) { 225 | glUnmapTexture2DINTEL(buffers[index], 0); 226 | } 227 | 228 | public void bind() { 229 | glBindTexture(GL_TEXTURE_2D, texID); 230 | } 231 | 232 | private void destroyObjects() { 233 | for ( int i = 0; i < semaphores.length; i++ ) { 234 | if ( processingState.get(i) ) 235 | waitForProcessingToComplete(i); 236 | } 237 | 238 | for ( int i = 0; i < buffers.length; i++ ) { 239 | glDeleteTextures(buffers[i]); 240 | buffers[i] = 0; 241 | } 242 | 243 | glDeleteTextures(texID); 244 | } 245 | 246 | public void destroy() { 247 | destroyObjects(); 248 | 249 | fboUtil.deleteFramebuffers(bufferFBO); 250 | fboUtil.deleteFramebuffers(texFBO); 251 | } 252 | 253 | } -------------------------------------------------------------------------------- /src/org/lwjgl/util/stream/TextureStreamPBO.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2012 LWJGL Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of 'LWJGL' nor the names of 17 | * its contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package org.lwjgl.util.stream; 33 | 34 | import static org.lwjgl.opengl.GL11.*; 35 | import static org.lwjgl.opengl.GL12.*; 36 | import static org.lwjgl.opengl.GL15.*; 37 | import static org.lwjgl.opengl.GL21.*; 38 | 39 | /** Implements streaming PBO updates to an OpenGL texture. */ 40 | abstract class TextureStreamPBO extends StreamBufferedPBO implements TextureStream { 41 | 42 | private final int texID; 43 | 44 | private long currentIndex; 45 | 46 | private boolean resetTexture; 47 | 48 | protected TextureStreamPBO(final StreamHandler handler, final int transfersToBuffer) { 49 | super(handler, transfersToBuffer); 50 | 51 | texID = glGenTextures(); 52 | glBindTexture(GL_TEXTURE_2D, texID); 53 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 54 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 55 | glBindTexture(GL_TEXTURE_2D, 0); 56 | } 57 | 58 | public StreamHandler getHandler() { 59 | return handler; 60 | } 61 | 62 | public int getWidth() { 63 | return width; 64 | } 65 | 66 | public int getHeight() { 67 | return height; 68 | } 69 | 70 | private void resize(final int width, final int height) { 71 | if ( width < 0 || height < 0 ) 72 | throw new IllegalArgumentException("Invalid dimensions: " + width + " x " + height); 73 | 74 | destroyObjects(); 75 | 76 | this.width = width; 77 | this.height = height; 78 | 79 | this.stride = StreamUtil.getStride(width); 80 | 81 | if ( width == 0 || height == 0 ) 82 | return; 83 | 84 | bufferIndex = 0; 85 | currentIndex = 0; 86 | 87 | resetTexture = true; 88 | 89 | // Setup upload buffers 90 | 91 | resizeBuffers(height, stride); 92 | } 93 | 94 | protected void resizeBuffers(final int height, final int stride) { 95 | super.resizeBuffers(height, stride, GL_PIXEL_UNPACK_BUFFER, GL_STREAM_DRAW); 96 | } 97 | 98 | public void snapshot() { 99 | if ( width != handler.getWidth() || height != handler.getHeight() ) 100 | resize(handler.getWidth(), handler.getHeight()); 101 | 102 | if ( width == 0 || height == 0 ) 103 | return; 104 | 105 | final int trgPBO = (int)(bufferIndex % transfersToBuffer); 106 | 107 | // Back-pressure. Make sure we never buffer more than frames ahead. 108 | 109 | if ( processingState.get(trgPBO) ) 110 | syncUpload(trgPBO); 111 | 112 | pinBuffer(trgPBO); 113 | 114 | // Send the buffer for processing 115 | 116 | processingState.set(trgPBO, true); 117 | semaphores[trgPBO].acquireUninterruptibly(); 118 | 119 | handler.process( 120 | width, height, 121 | pinnedBuffers[trgPBO], 122 | stride, 123 | semaphores[trgPBO] 124 | ); 125 | 126 | bufferIndex++; 127 | 128 | if ( resetTexture ) // Synchronize to show the first frame immediately 129 | syncUpload(trgPBO); 130 | } 131 | 132 | protected abstract void pinBuffer(final int index); 133 | 134 | public void tick() { 135 | final int srcPBO = (int)(currentIndex % transfersToBuffer); 136 | if ( !processingState.get(srcPBO) ) 137 | return; 138 | 139 | syncUpload(srcPBO); 140 | } 141 | 142 | private void syncUpload(final int index) { 143 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos[index]); 144 | waitForProcessingToComplete(index); 145 | 146 | upload(index); 147 | 148 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); 149 | } 150 | 151 | private void upload(final int srcPBO) { 152 | // Asynchronously upload current update 153 | 154 | glBindTexture(GL_TEXTURE_2D, texID); 155 | glPixelStorei(GL_UNPACK_ROW_LENGTH, stride >> 2); 156 | if ( resetTexture ) { 157 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0); 158 | resetTexture = false; 159 | } else 160 | glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0); 161 | glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); 162 | glBindTexture(GL_TEXTURE_2D, 0); 163 | 164 | postUpload(srcPBO); 165 | 166 | currentIndex++; 167 | } 168 | 169 | protected abstract void postUpload(int index); 170 | 171 | public void bind() { 172 | glBindTexture(GL_TEXTURE_2D, texID); 173 | } 174 | 175 | protected void destroyObjects() { 176 | for ( int i = 0; i < semaphores.length; i++ ) { 177 | if ( processingState.get(i) ) { 178 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos[i]); 179 | waitForProcessingToComplete(i); 180 | } 181 | } 182 | 183 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); 184 | 185 | for ( int i = 0; i < pbos.length; i++ ) { 186 | if ( pbos[i] != 0 ) 187 | glDeleteBuffers(pbos[i]); 188 | } 189 | } 190 | 191 | public void destroy() { 192 | destroyObjects(); 193 | } 194 | 195 | } -------------------------------------------------------------------------------- /src/org/lwjgl/util/stream/TextureStreamPBODefault.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2012 LWJGL Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of 'LWJGL' nor the names of 17 | * its contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package org.lwjgl.util.stream; 33 | 34 | import org.lwjgl.opengl.ContextCapabilities; 35 | import org.lwjgl.util.stream.StreamUtil.TextureStreamFactory; 36 | 37 | import static org.lwjgl.opengl.GL15.*; 38 | import static org.lwjgl.opengl.GL21.*; 39 | 40 | /** Implements streaming PBO updates to an OpenGL texture. */ 41 | public class TextureStreamPBODefault extends TextureStreamPBO { 42 | 43 | public static final TextureStreamFactory FACTORY = new TextureStreamFactory("Asynchronous PBO") { 44 | public boolean isSupported(final ContextCapabilities caps) { 45 | return caps.OpenGL21 || caps.GL_ARB_pixel_buffer_object || caps.GL_EXT_pixel_buffer_object; 46 | } 47 | 48 | public TextureStream create(final StreamHandler handler, final int transfersToBuffer) { 49 | return new TextureStreamPBODefault(handler, transfersToBuffer); 50 | } 51 | }; 52 | 53 | public TextureStreamPBODefault(final StreamHandler handler, final int transfersToBuffer) { 54 | super(handler, transfersToBuffer); 55 | } 56 | 57 | protected void postProcess(final int index) { 58 | glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); 59 | } 60 | 61 | protected void postUpload(final int index) { 62 | } 63 | 64 | public void pinBuffer(final int index) { 65 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos[index]); 66 | glBufferData(GL_PIXEL_UNPACK_BUFFER, height * stride, GL_STREAM_DRAW); // Orphan previous buffer 67 | pinnedBuffers[index] = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY, height * stride, pinnedBuffers[index]); 68 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); 69 | } 70 | 71 | public void destroy() { 72 | destroyObjects(); 73 | } 74 | 75 | } -------------------------------------------------------------------------------- /src/org/lwjgl/util/stream/TextureStreamPBORange.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2002-2012 LWJGL Project 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are 7 | * met: 8 | * 9 | * * Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 12 | * * Redistributions in binary form must reproduce the above copyright 13 | * notice, this list of conditions and the following disclaimer in the 14 | * documentation and/or other materials provided with the distribution. 15 | * 16 | * * Neither the name of 'LWJGL' nor the names of 17 | * its contributors may be used to endorse or promote products derived 18 | * from this software without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | package org.lwjgl.util.stream; 33 | 34 | import org.lwjgl.opengl.ContextCapabilities; 35 | import org.lwjgl.opengl.GLSync; 36 | import org.lwjgl.util.stream.StreamUtil.TextureStreamFactory; 37 | 38 | import static org.lwjgl.opengl.GL15.*; 39 | import static org.lwjgl.opengl.GL21.*; 40 | import static org.lwjgl.opengl.GL30.*; 41 | import static org.lwjgl.opengl.GL32.*; 42 | 43 | /** Implements streaming PBO updates to an OpenGL texture. */ 44 | public class TextureStreamPBORange extends TextureStreamPBO { 45 | 46 | public static final TextureStreamFactory FACTORY = new TextureStreamFactory("ARB_map_buffer_range") { 47 | public boolean isSupported(final ContextCapabilities caps) { 48 | return TextureStreamPBODefault.FACTORY.isSupported(caps) && (caps.OpenGL30 || caps.GL_ARB_map_buffer_range); 49 | } 50 | 51 | public TextureStream create(final StreamHandler handler, final int transfersToBuffer) { 52 | return new TextureStreamPBORange(handler, transfersToBuffer); 53 | } 54 | }; 55 | 56 | private final GLSync[] fences; 57 | 58 | public TextureStreamPBORange(final StreamHandler handler, final int transfersToBuffer) { 59 | super(handler, transfersToBuffer); 60 | 61 | fences = new GLSync[this.transfersToBuffer]; 62 | } 63 | 64 | protected void postUpload(final int index) { 65 | fences[index] = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); 66 | } 67 | 68 | protected void postProcess(final int index) { 69 | glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); 70 | } 71 | 72 | public void pinBuffer(final int index) { 73 | if ( fences[index] != null ) // Wait for TexSubImage to complete 74 | StreamUtil.waitOnFence(fences, index); 75 | 76 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbos[index]); 77 | glBufferData(GL_PIXEL_UNPACK_BUFFER, height * stride, GL_STREAM_DRAW); // Orphan previous buffer 78 | pinnedBuffers[index] = glMapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, height * stride, GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT, pinnedBuffers[index]); 79 | glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); 80 | } 81 | 82 | public void destroy() { 83 | destroyObjects(); 84 | } 85 | 86 | } --------------------------------------------------------------------------------