├── 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 | 
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 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
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 extends RenderStreamFactory> 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 extends TextureStreamFactory> 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 extends BufferingChoice> 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 extends Boolean> 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 extends Number> 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 extends Number> 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 extends Boolean> 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 | }
--------------------------------------------------------------------------------