Codebase list lwjgl / 27e9a56
Simplified the ASM patch Emmanuel Bourg 4 years ago
2 changed file(s) with 15 addition(s) and 2454 deletion(s). Raw diff Collapse all Expand all
00 lwjgl (2.9.3+dfsg-4) UNRELEASED; urgency=medium
11
22 * Team upload.
3 * Simplified the OS X patch
3 * Simplified the OS X and ASM patches
44 * Standards-Version updated to 4.2.1
55 * Use salsa.debian.org Vcs-* URLs
66
00 Description: No asm support. We don't support the optional asm.jar because LWJGL is not compatible with libasm-java.
11 Author: Markus Koschany <apo@debian.org>
22 Forwarded: not-needed
3 --- a/src/java/org/lwjgl/test/mapped/TestMappedObject.java
4 +++ /dev/null
5 @@ -1,87 +0,0 @@
6 -/*
7 - * Copyright (c) 2002-2011 LWJGL Project
8 - * All rights reserved.
9 - *
10 - * Redistribution and use in source and binary forms, with or without
11 - * modification, are permitted provided that the following conditions are
12 - * met:
13 - *
14 - * * Redistributions of source code must retain the above copyright
15 - * notice, this list of conditions and the following disclaimer.
16 - *
17 - * * Redistributions in binary form must reproduce the above copyright
18 - * notice, this list of conditions and the following disclaimer in the
19 - * documentation and/or other materials provided with the distribution.
20 - *
21 - * * Neither the name of 'LWJGL' nor the names of
22 - * its contributors may be used to endorse or promote products derived
23 - * from this software without specific prior written permission.
24 - *
25 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29 - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
30 - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
31 - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
32 - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
34 - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
35 - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 - */
37 -package org.lwjgl.test.mapped;
38 -
39 -import org.lwjgl.util.mapped.MappedObjectClassLoader;
40 -import org.lwjgl.util.mapped.MappedObjectTransformer;
41 -
42 -/** @author Riven */
43 -@SuppressWarnings("static-access")
44 -public class TestMappedObject {
45 -
46 - static {
47 - boolean assertsEnabled = false;
48 - assert assertsEnabled = true; // Intentional side effect!!!
49 - if ( !assertsEnabled )
50 - throw new RuntimeException("Asserts must be enabled for this test.");
51 - }
52 -
53 - public static void main(String[] args) throws Exception {
54 - MappedObjectTransformer.register(MappedFloat.class);
55 - MappedObjectTransformer.register(MappedVec2.class);
56 - MappedObjectTransformer.register(MappedVec3.class);
57 - MappedObjectTransformer.register(MappedSomething.class);
58 - MappedObjectTransformer.register(MappedObjectTests3.Xyz.class);
59 - MappedObjectTransformer.register(MappedObjectTests4.MappedPointer.class);
60 - MappedObjectTransformer.register(MappedObjectTests4.MappedCacheLinePadded.class);
61 - MappedObjectTransformer.register(MappedObjectTests4.MappedFieldCacheLinePadded.class);
62 -
63 - if ( MappedObjectClassLoader.fork(TestMappedObject.class, args) ) {
64 - return;
65 - }
66 -
67 - MappedObjectTests1.testViewField();
68 -
69 - MappedObjectTests2.testFields();
70 -
71 - // MappedObjectBench.benchmarkMapped();
72 - // MappedObjectBench.benchmarkInstances();
73 - // MappedObjectBench.benchmarkIndirectArray();
74 - // MappedObjectBench.benchmarkDirectArray();
75 - // MappedObjectBench.benchmarkUnsafe();
76 -
77 - MappedObjectTests3.testMappedBuffer();
78 - MappedObjectTests3.testForeach();
79 - MappedObjectTests3.testConstructor();
80 - MappedObjectTests3.testMappedSet();
81 -
82 - MappedObjectTests4.testLocalView();
83 - //MappedObjectTests4.testLWJGL();
84 - MappedObjectTests4.testPointer();
85 - MappedObjectTests4.testCacheLineAlignment();
86 - MappedObjectTests4.testCacheLinePadding();
87 - MappedObjectTests4.testCacheLinePaddingPOJO();
88 -
89 - System.out.println("done");
90 - }
91 -
92 -}
93 \ No newline at end of file
94 --- a/src/java/org/lwjgl/test/opengl/sprites/SpriteShootoutMapped.java
95 +++ /dev/null
96 @@ -1,838 +0,0 @@
97 -/*
98 - * Copyright (c) 2002-2011 LWJGL Project
99 - * All rights reserved.
100 - *
101 - * Redistribution and use in source and binary forms, with or without
102 - * modification, are permitted provided that the following conditions are
103 - * met:
104 - *
105 - * * Redistributions of source code must retain the above copyright
106 - * notice, this list of conditions and the following disclaimer.
107 - *
108 - * * Redistributions in binary form must reproduce the above copyright
109 - * notice, this list of conditions and the following disclaimer in the
110 - * documentation and/or other materials provided with the distribution.
111 - *
112 - * * Neither the name of 'LWJGL' nor the names of
113 - * its contributors may be used to endorse or promote products derived
114 - * from this software without specific prior written permission.
115 - *
116 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
117 - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
118 - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
119 - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
120 - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
121 - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
122 - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
123 - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
124 - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
125 - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
126 - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
127 - */
128 -package org.lwjgl.test.opengl.sprites;
129 -
130 -import org.lwjgl.BufferUtils;
131 -import org.lwjgl.LWJGLException;
132 -import org.lwjgl.Sys;
133 -import org.lwjgl.input.Keyboard;
134 -import org.lwjgl.input.Mouse;
135 -import org.lwjgl.opengl.*;
136 -import org.lwjgl.util.mapped.MappedObject;
137 -import org.lwjgl.util.mapped.MappedObjectClassLoader;
138 -import org.lwjgl.util.mapped.MappedObjectTransformer;
139 -import org.lwjgl.util.mapped.MappedType;
140 -
141 -import java.awt.image.BufferedImage;
142 -import java.awt.image.Raster;
143 -import java.io.IOException;
144 -import java.nio.ByteBuffer;
145 -import java.util.Random;
146 -import javax.imageio.ImageIO;
147 -
148 -import static org.lwjgl.opengl.EXTTransformFeedback.*;
149 -import static org.lwjgl.opengl.GL11.*;
150 -import static org.lwjgl.opengl.GL12.*;
151 -import static org.lwjgl.opengl.GL15.*;
152 -import static org.lwjgl.opengl.GL20.*;
153 -import static org.lwjgl.opengl.GL30.*;
154 -
155 -/**
156 - * Sprite rendering demo. Three implementations are supported:
157 - * a) CPU animation + BufferData VBO update.
158 - * b) CPU animation + MapBufferRange VBO update.
159 - * c) GPU animation using transform feedback with a vertex shader.
160 - *
161 - * @author Spasi
162 - * @since 18/3/2011
163 - */
164 -public final class SpriteShootoutMapped {
165 -
166 - static final int SCREEN_WIDTH = 800;
167 - static final int SCREEN_HEIGHT = 600;
168 -
169 - private static final int ANIMATION_TICKS = 60;
170 -
171 - private boolean run = true;
172 - private boolean render = true;
173 - private boolean colorMask = true;
174 - private boolean animate = true;
175 - private boolean smooth;
176 - private boolean vsync;
177 -
178 - int ballSize = 42;
179 - int ballCount = 100 * 1000;
180 -
181 - private SpriteRenderer renderer;
182 -
183 - // OpenGL stuff
184 - private int texID;
185 - private int texBigID;
186 - private int texSmallID;
187 -
188 - long animateTime;
189 -
190 - private SpriteShootoutMapped() {
191 - }
192 -
193 - public static void main(String[] args) {
194 - MappedObjectTransformer.register(Pixel4b.class);
195 - MappedObjectTransformer.register(Pixel3b.class);
196 - MappedObjectTransformer.register(Sprite.class);
197 - MappedObjectTransformer.register(SpriteRender.class);
198 -
199 - if ( MappedObjectClassLoader.fork(SpriteShootoutMapped.class, args) )
200 - return;
201 -
202 - try {
203 - new SpriteShootoutMapped().start();
204 - } catch (LWJGLException e) {
205 - e.printStackTrace();
206 - }
207 - }
208 -
209 - private void start() throws LWJGLException {
210 - try {
211 - initGL();
212 -
213 - final ContextCapabilities caps = GLContext.getCapabilities();
214 - if ( !true && (caps.OpenGL30 || caps.GL_EXT_transform_feedback) )
215 - renderer = new SpriteRendererTF();
216 - else if ( true && caps.GL_ARB_map_buffer_range )
217 - renderer = new SpriteRendererMapped();
218 - else
219 - renderer = new SpriteRendererPlain();
220 -
221 - updateBalls(ballCount);
222 - run();
223 - } catch (Throwable t) {
224 - t.printStackTrace();
225 - } finally {
226 - destroy();
227 - }
228 - }
229 -
230 - private void initGL() throws LWJGLException {
231 - Display.setLocation((Display.getDisplayMode().getWidth() - SCREEN_WIDTH) / 2,
232 - (Display.getDisplayMode().getHeight() - SCREEN_HEIGHT) / 2);
233 - Display.setDisplayMode(new DisplayMode(SCREEN_WIDTH, SCREEN_HEIGHT));
234 - Display.setTitle("Sprite Shootout");
235 - Display.create();
236 - //Display.create(new PixelFormat(), new ContextAttribs(4, 1).withProfileCompatibility(true).withDebug(true));
237 - //AMDDebugOutput.glDebugMessageCallbackAMD(new AMDDebugOutputCallback());
238 -
239 - if ( !GLContext.getCapabilities().OpenGL20 )
240 - throw new RuntimeException("OpenGL 2.0 is required for this demo.");
241 -
242 - // Setup viewport
243 -
244 - glMatrixMode(GL_PROJECTION);
245 - glLoadIdentity();
246 - glOrtho(0, SCREEN_WIDTH, 0, SCREEN_HEIGHT, -1.0, 1.0);
247 -
248 - glMatrixMode(GL_MODELVIEW);
249 - glLoadIdentity();
250 - glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
251 -
252 - glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
253 -
254 - // Create textures
255 -
256 - try {
257 - texSmallID = createTexture("res/ball_sm.png");
258 - texBigID = createTexture("res/ball.png");
259 - } catch (IOException e) {
260 - e.printStackTrace();
261 - System.exit(-1);
262 - }
263 - texID = texBigID;
264 -
265 - // Setup rendering state
266 -
267 - glEnable(GL_BLEND);
268 - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
269 -
270 - glEnable(GL_ALPHA_TEST);
271 - glAlphaFunc(GL_GREATER, 0.0f);
272 -
273 - glColorMask(colorMask, colorMask, colorMask, false);
274 - glDepthMask(false);
275 - glDisable(GL_DEPTH_TEST);
276 -
277 - // Setup geometry
278 -
279 - glEnableClientState(GL_VERTEX_ARRAY);
280 -
281 - Util.checkGLError();
282 - }
283 -
284 - private static int createTexture(final String path) throws IOException {
285 - final BufferedImage img = ImageIO.read(SpriteShootoutMapped.class.getClassLoader().getResource(path));
286 -
287 - final int w = img.getWidth();
288 - final int h = img.getHeight();
289 -
290 - final ByteBuffer buffer = readImage(img);
291 -
292 - final int texID = glGenTextures();
293 -
294 - glBindTexture(GL_TEXTURE_2D, texID);
295 - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
296 - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
297 - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
298 - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
299 - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_BGRA, GL_UNSIGNED_BYTE, buffer);
300 -
301 - return texID;
302 - }
303 -
304 - public static class Pixel4b extends MappedObject {
305 -
306 - public byte r, g, b, a;
307 -
308 - }
309 -
310 - @MappedType(align = 3)
311 - public static class Pixel3b extends MappedObject {
312 -
313 - public byte r, g, b;
314 -
315 - }
316 -
317 - private static ByteBuffer readImage(final BufferedImage img) throws IOException {
318 - final Raster raster = img.getRaster();
319 -
320 - final int bands = raster.getNumBands();
321 -
322 - final int w = img.getWidth();
323 - final int h = img.getHeight();
324 -
325 - final int count = w * h;
326 -
327 - final byte[] pixels = new byte[count * bands];
328 - raster.getDataElements(0, 0, w, h, pixels);
329 -
330 - if ( bands == 4 ) {
331 - Pixel4b p = Pixel4b.malloc(count);
332 -
333 - int b = 0;
334 - for ( int i = 0; i < count; i++, b += 4 ) {
335 - // Pre-multiply alpha
336 - final float a = unpackUByte01(pixels[b + 3]);
337 -
338 - p.view = i;
339 - p.r = packUByte01(unpackUByte01(pixels[b + 2]) * a);
340 - p.g = packUByte01(unpackUByte01(pixels[b + 1]) * a);
341 - p.b = packUByte01(unpackUByte01(pixels[b + 0]) * a);
342 - p.a = pixels[b + 3];
343 - }
344 -
345 - return p.backingByteBuffer();
346 - } else if ( bands == 3 ) {
347 - Pixel3b p = Pixel3b.malloc(count);
348 -
349 - int b = 0;
350 - for ( int i = 0; i < count; i++, b += 3 ) {
351 - p.view = i;
352 - p.r = pixels[b + 2];
353 - p.g = pixels[b + 1];
354 - p.b = pixels[b + 0];
355 - }
356 -
357 - return p.backingByteBuffer();
358 - } else {
359 - ByteBuffer p = BufferUtils.createByteBuffer(count * bands);
360 - p.put(pixels, 0, p.capacity());
361 - p.flip();
362 - return p;
363 - }
364 -
365 - }
366 -
367 - private static float unpackUByte01(final byte x) {
368 - return (x & 0xFF) / 255.0f;
369 - }
370 -
371 - private static byte packUByte01(final float x) {
372 - return (byte)(x * 255.0f);
373 - }
374 -
375 - private void updateBalls(final int count) {
376 - System.out.println("NUMBER OF BALLS: " + count);
377 - renderer.updateBalls(ballCount);
378 - }
379 -
380 - private void run() {
381 - long startTime = System.currentTimeMillis() + 5000;
382 - long fps = 0;
383 -
384 - long time = Sys.getTime();
385 - final int ticksPerUpdate = (int)(Sys.getTimerResolution() / ANIMATION_TICKS);
386 -
387 - renderer.render(false, true, 0);
388 -
389 - while ( run ) {
390 - Display.processMessages();
391 - handleInput();
392 -
393 - glClear(GL_COLOR_BUFFER_BIT);
394 -
395 - final long currTime = Sys.getTime();
396 - final int delta = (int)(currTime - time);
397 - if ( smooth || delta >= ticksPerUpdate ) {
398 - renderer.render(render, animate, delta);
399 - time = currTime;
400 - } else
401 - renderer.render(render, false, 0);
402 -
403 - Display.update(false);
404 - //Display.sync(60);
405 -
406 - if ( startTime > System.currentTimeMillis() ) {
407 - fps++;
408 - } else {
409 - long timeUsed = 5000 + (startTime - System.currentTimeMillis());
410 - startTime = System.currentTimeMillis() + 5000;
411 - System.out.println("FPS: " + (Math.round(fps / (timeUsed / 1000.0) * 10) / 10.0) + ", Balls: " + ballCount);
412 - System.out.println("Animation: " + animateTime / fps);
413 - animateTime = 0;
414 - fps = 0;
415 - }
416 - }
417 - }
418 -
419 - private void handleInput() {
420 - if ( Display.isCloseRequested() )
421 - run = false;
422 -
423 - while ( Keyboard.next() ) {
424 - if ( Keyboard.getEventKeyState() )
425 - continue;
426 -
427 - switch ( Keyboard.getEventKey() ) {
428 - case Keyboard.KEY_1:
429 - case Keyboard.KEY_2:
430 - case Keyboard.KEY_3:
431 - case Keyboard.KEY_4:
432 - case Keyboard.KEY_5:
433 - case Keyboard.KEY_6:
434 - case Keyboard.KEY_7:
435 - case Keyboard.KEY_8:
436 - case Keyboard.KEY_9:
437 - case Keyboard.KEY_0:
438 - ballCount = 1 << (Keyboard.getEventKey() - Keyboard.KEY_1);
439 - updateBalls(ballCount);
440 - break;
441 - case Keyboard.KEY_ADD:
442 - case Keyboard.KEY_SUBTRACT:
443 - int mult;
444 - if ( Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT) )
445 - mult = 1000;
446 - else if ( Keyboard.isKeyDown(Keyboard.KEY_LMENU) || Keyboard.isKeyDown(Keyboard.KEY_RMENU) )
447 - mult = 100;
448 - else if ( Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL) )
449 - mult = 10;
450 - else
451 - mult = 1;
452 - if ( Keyboard.getEventKey() == Keyboard.KEY_SUBTRACT )
453 - mult = -mult;
454 - ballCount += mult * 100;
455 - if ( ballCount <= 0 )
456 - ballCount = 1;
457 - updateBalls(ballCount);
458 - break;
459 - case Keyboard.KEY_ESCAPE:
460 - run = false;
461 - break;
462 - case Keyboard.KEY_A:
463 - animate = !animate;
464 - System.out.println("Animation is now " + (animate ? "on" : "off") + ".");
465 - break;
466 - case Keyboard.KEY_C:
467 - colorMask = !colorMask;
468 - glColorMask(colorMask, colorMask, colorMask, false);
469 - System.out.println("Color mask is now " + (colorMask ? "on" : "off") + ".");
470 - // Disable alpha test when color mask is off, else we get no benefit.
471 - if ( colorMask ) {
472 - glEnable(GL_BLEND);
473 - glEnable(GL_ALPHA_TEST);
474 - } else {
475 - glDisable(GL_BLEND);
476 - glDisable(GL_ALPHA_TEST);
477 - }
478 - break;
479 - case Keyboard.KEY_R:
480 - render = !render;
481 - System.out.println("Rendering is now " + (render ? "on" : "off") + ".");
482 - break;
483 - case Keyboard.KEY_S:
484 - smooth = !smooth;
485 - System.out.println("Smooth animation is now " + (smooth ? "on" : "off") + ".");
486 - break;
487 - case Keyboard.KEY_T:
488 - if ( texID == texBigID ) {
489 - texID = texSmallID;
490 - ballSize = 16;
491 - } else {
492 - texID = texBigID;
493 - ballSize = 42;
494 - }
495 - renderer.updateBallSize();
496 - glBindTexture(GL_TEXTURE_2D, texID);
497 - System.out.println("Now using the " + (texID == texBigID ? "big" : "small") + " texture.");
498 - break;
499 - case Keyboard.KEY_V:
500 - vsync = !vsync;
501 - Display.setVSyncEnabled(vsync);
502 - System.out.println("VSYNC is now " + (vsync ? "enabled" : "disabled") + ".");
503 - break;
504 - }
505 - }
506 -
507 - while ( Mouse.next() ) ;
508 - }
509 -
510 - private void destroy() {
511 - Display.destroy();
512 - }
513 -
514 - public static class Sprite extends MappedObject {
515 -
516 - public float dx, x;
517 - public float dy, y;
518 -
519 - }
520 -
521 - public static class SpriteRender extends MappedObject {
522 -
523 - public float x, y;
524 -
525 - }
526 -
527 - private abstract class SpriteRenderer {
528 -
529 - protected Sprite sprites;
530 -
531 - protected int spriteCount;
532 -
533 - protected int vshID;
534 - protected int progID;
535 -
536 - protected void createProgram() {
537 - final int fshID = glCreateShader(GL_FRAGMENT_SHADER);
538 - glShaderSource(fshID, "uniform sampler2D COLOR_MAP;\n" +
539 - "void main(void) {\n" +
540 - " gl_FragColor = texture2D(COLOR_MAP, gl_PointCoord);\n" +
541 - "}");
542 - glCompileShader(fshID);
543 - if ( glGetShaderi(fshID, GL_COMPILE_STATUS) == GL_FALSE ) {
544 - System.out.println(glGetShaderInfoLog(fshID, glGetShaderi(fshID, GL_INFO_LOG_LENGTH)));
545 - throw new RuntimeException("Failed to compile fragment shader.");
546 - }
547 -
548 - progID = glCreateProgram();
549 - glAttachShader(progID, vshID);
550 - glAttachShader(progID, fshID);
551 - glLinkProgram(progID);
552 - if ( glGetProgrami(progID, GL_LINK_STATUS) == GL_FALSE ) {
553 - System.out.println(glGetProgramInfoLog(progID, glGetProgrami(progID, GL_INFO_LOG_LENGTH)));
554 - throw new RuntimeException("Failed to link shader program.");
555 - }
556 -
557 - glUseProgram(progID);
558 - glUniform1i(glGetUniformLocation(progID, "COLOR_MAP"), 0);
559 -
560 - updateBallSize();
561 -
562 - glEnableClientState(GL_VERTEX_ARRAY);
563 - }
564 -
565 - public void updateBallSize() {
566 - glPointSize(ballSize);
567 - }
568 -
569 - public abstract void updateBalls(int count);
570 -
571 - protected abstract void render(boolean render, boolean animate, int delta);
572 -
573 - }
574 -
575 - private abstract class SpriteRendererBatched extends SpriteRenderer {
576 -
577 - protected static final int BALLS_PER_BATCH = 10 * 1000;
578 -
579 - SpriteRendererBatched() {
580 - vshID = glCreateShader(GL_VERTEX_SHADER);
581 - glShaderSource(vshID, "void main(void) {\n" +
582 - " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" +
583 - "}");
584 - glCompileShader(vshID);
585 - if ( glGetShaderi(vshID, GL_COMPILE_STATUS) == GL_FALSE ) {
586 - System.out.println(glGetShaderInfoLog(vshID, glGetShaderi(vshID, GL_INFO_LOG_LENGTH)));
587 - throw new RuntimeException("Failed to compile vertex shader.");
588 - }
589 -
590 - createProgram();
591 - }
592 -
593 - public void updateBalls(final int count) {
594 - final Random random = new Random();
595 -
596 - final Sprite newSprites = Sprite.malloc(count);
597 - if ( sprites != null ) {
598 - sprites.view = 0;
599 - sprites.copyRange(newSprites, Math.min(count, spriteCount));
600 - }
601 -
602 - if ( count > spriteCount ) {
603 - for ( int i = spriteCount; i < count; i++ ) {
604 - newSprites.view = i;
605 -
606 - newSprites.x = (int)(random.nextFloat() * (SCREEN_WIDTH - ballSize) + ballSize * 0.5f);
607 - newSprites.y = (int)(random.nextFloat() * (SCREEN_HEIGHT - ballSize) + ballSize * 0.5f);
608 - newSprites.dx = random.nextFloat() * 0.4f - 0.2f;
609 - newSprites.dy = random.nextFloat() * 0.4f - 0.2f;
610 - }
611 - }
612 -
613 - sprites = newSprites;
614 - spriteCount = count;
615 - }
616 -
617 - protected void animate(
618 - final Sprite sprite,
619 - final SpriteRender spriteRender,
620 - final int ballSize, final int ballIndex, final int batchSize, final int delta
621 - ) {
622 - final float ballRadius = ballSize * 0.5f;
623 - final float boundW = SCREEN_WIDTH - ballRadius;
624 - final float boundH = SCREEN_HEIGHT - ballRadius;
625 -
626 - final Sprite[] sprites = sprite.asArray();
627 - final SpriteRender[] spritesRender = spriteRender.asArray();
628 - for ( int b = ballIndex, r = 0, len = (ballIndex + batchSize); b < len; b++, r++ ) {
629 - float dx = sprites[b].dx;
630 - float x = sprites[b].x;
631 -
632 - x += dx * delta;
633 - if ( x < ballRadius ) {
634 - x = ballRadius;
635 - dx = -dx;
636 - } else if ( x > boundW ) {
637 - x = boundW;
638 - dx = -dx;
639 - }
640 -
641 - sprites[b].dx = dx;
642 - sprites[b].x = x;
643 - spritesRender[r].x = x;
644 -
645 - float dy = sprites[b].dy;
646 - float y = sprites[b].y;
647 -
648 - y += dy * delta;
649 - if ( y < ballRadius ) {
650 - y = ballRadius;
651 - dy = -dy;
652 - } else if ( y > boundH ) {
653 - y = boundH;
654 - dy = -dy;
655 - }
656 -
657 - sprites[b].dy = dy;
658 - sprites[b].y = y;
659 - spritesRender[r].y = y;
660 - }
661 - }
662 -
663 - }
664 -
665 - private class SpriteRendererPlain extends SpriteRendererBatched {
666 -
667 - private final int DATA_PER_BATCH = BALLS_PER_BATCH * 2 * 4; // balls * 2 floats * 4 bytes
668 -
669 - protected int[] animVBO;
670 -
671 - private SpriteRender spritesRender;
672 -
673 - SpriteRendererPlain() {
674 - System.out.println("Shootout Implementation: CPU animation & BufferData");
675 - spritesRender = SpriteRender.malloc(BALLS_PER_BATCH);
676 - }
677 -
678 - public void updateBalls(final int count) {
679 - super.updateBalls(count);
680 -
681 - final int batchCount = count / BALLS_PER_BATCH + (count % BALLS_PER_BATCH == 0 ? 0 : 1);
682 - if ( animVBO != null && batchCount == animVBO.length )
683 - return;
684 -
685 - final int[] newAnimVBO = new int[batchCount];
686 - if ( animVBO != null ) {
687 - System.arraycopy(animVBO, 0, newAnimVBO, 0, Math.min(animVBO.length, newAnimVBO.length));
688 - for ( int i = newAnimVBO.length; i < animVBO.length; i++ )
689 - glDeleteBuffers(animVBO[i]);
690 - }
691 - for ( int i = animVBO == null ? 0 : animVBO.length; i < newAnimVBO.length; i++ ) {
692 - newAnimVBO[i] = glGenBuffers();
693 - glBindBuffer(GL_ARRAY_BUFFER, newAnimVBO[i]);
694 - }
695 -
696 - animVBO = newAnimVBO;
697 - }
698 -
699 - public void render(final boolean render, final boolean animate, final int delta) {
700 - int batchSize = Math.min(ballCount, BALLS_PER_BATCH);
701 - int ballIndex = 0;
702 - int batchIndex = 0;
703 - while ( ballIndex < ballCount ) {
704 - glBindBuffer(GL_ARRAY_BUFFER, animVBO[batchIndex]);
705 -
706 - if ( animate )
707 - animate(ballIndex, batchSize, delta);
708 -
709 - if ( render ) {
710 - glVertexPointer(2, GL_FLOAT, 0, 0);
711 - glDrawArrays(GL_POINTS, 0, batchSize);
712 - }
713 -
714 - ballIndex += batchSize;
715 - batchSize = Math.min(ballCount - ballIndex, BALLS_PER_BATCH);
716 - batchIndex++;
717 - }
718 - }
719 -
720 - private void animate(final int ballIndex, final int batchSize, final int delta) {
721 - animate(
722 - sprites, spritesRender,
723 - ballSize, ballIndex, batchSize, delta
724 - );
725 -
726 - glBufferData(GL_ARRAY_BUFFER, DATA_PER_BATCH, GL_STREAM_DRAW);
727 - glBufferSubData(GL_ARRAY_BUFFER, 0, spritesRender.backingByteBuffer());
728 - }
729 -
730 - }
731 -
732 - private class SpriteRendererMapped extends SpriteRendererBatched {
733 -
734 - private StreamVBO animVBO;
735 -
736 - SpriteRendererMapped() {
737 - System.out.println("Shootout Implementation: CPU animation & MapBufferRange");
738 - }
739 -
740 - public void updateBalls(final int count) {
741 - super.updateBalls(count);
742 -
743 - if ( animVBO != null )
744 - animVBO.destroy();
745 -
746 - animVBO = new StreamVBO(GL_ARRAY_BUFFER, ballCount * (2 * 4));
747 - }
748 -
749 - public void render(final boolean render, final boolean animate, final int delta) {
750 - int batchSize = Math.min(ballCount, BALLS_PER_BATCH);
751 - int ballIndex = 0;
752 - while ( ballIndex < ballCount ) {
753 - if ( animate ) {
754 - final ByteBuffer buffer = animVBO.map(batchSize * (2 * 4));
755 -
756 - long t0 = System.nanoTime();
757 - animate(sprites, SpriteRender.<SpriteRender>map(buffer), ballSize, ballIndex, batchSize, delta);
758 - long t1 = System.nanoTime();
759 -
760 - animateTime += t1 - t0;
761 -
762 - animVBO.unmap();
763 - }
764 -
765 - if ( render ) {
766 - glVertexPointer(2, GL_FLOAT, 0, ballIndex * (2 * 4));
767 - glDrawArrays(GL_POINTS, 0, batchSize);
768 - }
769 -
770 - ballIndex += batchSize;
771 - batchSize = Math.min(ballCount - ballIndex, BALLS_PER_BATCH);
772 - }
773 - }
774 -
775 - }
776 -
777 - private class SpriteRendererTF extends SpriteRenderer {
778 -
779 - private int progIDTF;
780 - private int ballSizeLoc;
781 - private int deltaLoc;
782 -
783 - private int[] tfVBO = new int[2];
784 - private int currVBO;
785 -
786 - SpriteRendererTF() {
787 - System.out.println("Shootout Implementation: TF GPU animation");
788 -
789 - // Transform-feedback program
790 -
791 - final int vshID = glCreateShader(GL_VERTEX_SHADER);
792 - glShaderSource(vshID, "#version 130\n" +
793 - "const float WIDTH = " + SCREEN_WIDTH + ";\n" +
794 - "const float HEIGHT = " + SCREEN_HEIGHT + ";\n" +
795 - "uniform float ballSize;\n" + // ballSize / 2
796 - "uniform float delta;\n" +
797 - "void main(void) {\n" +
798 - " vec4 anim = gl_Vertex;\n" +
799 - " anim.xy = anim.xy + anim.zw * delta;\n" +
800 - " vec2 animC = clamp(anim.xy, vec2(ballSize), vec2(WIDTH - ballSize, HEIGHT - ballSize));\n" +
801 - " if ( anim.x != animC.x ) anim.z = -anim.z;\n" +
802 - " if ( anim.y != animC.y ) anim.w = -anim.w;\n" +
803 - " gl_Position = vec4(animC, anim.zw);\n" +
804 - "}");
805 - glCompileShader(vshID);
806 - if ( glGetShaderi(vshID, GL_COMPILE_STATUS) == GL_FALSE ) {
807 - System.out.println(glGetShaderInfoLog(vshID, glGetShaderi(vshID, GL_INFO_LOG_LENGTH)));
808 - throw new RuntimeException("Failed to compile vertex shader.");
809 - }
810 -
811 - progIDTF = glCreateProgram();
812 - glAttachShader(progIDTF, vshID);
813 - glTransformFeedbackVaryings(progIDTF, new CharSequence[] { "gl_Position" }, GL_SEPARATE_ATTRIBS);
814 - glLinkProgram(progIDTF);
815 - if ( glGetProgrami(progIDTF, GL_LINK_STATUS) == GL_FALSE ) {
816 - System.out.println(glGetProgramInfoLog(progIDTF, glGetProgrami(progIDTF, GL_INFO_LOG_LENGTH)));
817 - throw new RuntimeException("Failed to link shader program.");
818 - }
819 -
820 - glUseProgram(progIDTF);
821 -
822 - ballSizeLoc = glGetUniformLocation(progIDTF, "ballSize");
823 - deltaLoc = glGetUniformLocation(progIDTF, "delta");
824 -
825 - glUniform1f(ballSizeLoc, ballSize * 0.5f);
826 -
827 - // -----------------
828 -
829 - this.vshID = glCreateShader(GL_VERTEX_SHADER);
830 - glShaderSource(this.vshID, "void main(void) {\n" +
831 - " gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" +
832 - "}");
833 - glCompileShader(this.vshID);
834 - if ( glGetShaderi(this.vshID, GL_COMPILE_STATUS) == GL_FALSE ) {
835 - System.out.println(glGetShaderInfoLog(this.vshID, glGetShaderi(this.vshID, GL_INFO_LOG_LENGTH)));
836 - throw new RuntimeException("Failed to compile vertex shader.");
837 - }
838 -
839 - createProgram();
840 - }
841 -
842 - public void updateBallSize() {
843 - glUseProgram(progIDTF);
844 - glUniform1f(ballSizeLoc, ballSize * 0.5f);
845 -
846 - glUseProgram(progID);
847 - super.updateBallSize();
848 - }
849 -
850 - private void doUpdateBalls(final int count) {
851 - final Random random = new Random();
852 -
853 - final Sprite newSprites = Sprite.malloc(count);
854 - if ( sprites != null ) {
855 - sprites.view = 0;
856 - sprites.copyRange(newSprites, Math.min(count, spriteCount));
857 - }
858 -
859 - if ( count > spriteCount ) {
860 - for ( int i = spriteCount; i < count; i++ ) {
861 - newSprites.view = i;
862 -
863 - newSprites.x = (int)(random.nextFloat() * (SCREEN_WIDTH - ballSize) + ballSize * 0.5f);
864 - newSprites.y = (int)(random.nextFloat() * (SCREEN_HEIGHT - ballSize) + ballSize * 0.5f);
865 - newSprites.dx = random.nextFloat() * 0.4f - 0.2f;
866 - newSprites.dy = random.nextFloat() * 0.4f - 0.2f;
867 - }
868 - }
869 -
870 - sprites = newSprites;
871 - spriteCount = count;
872 - }
873 -
874 - public void updateBalls(final int count) {
875 - if ( tfVBO[0] != 0 ) {
876 - // Fetch current animation state
877 - glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, sprites.backingByteBuffer());
878 - }
879 -
880 - doUpdateBalls(count);
881 -
882 - if ( tfVBO[0] != 0 ) {
883 - for ( int i = 0; i < tfVBO.length; i++ )
884 - glDeleteBuffers(tfVBO[i]);
885 - }
886 -
887 - for ( int i = 0; i < tfVBO.length; i++ ) {
888 - tfVBO[i] = glGenBuffers();
889 - glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, tfVBO[i]);
890 - glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sprites.backingByteBuffer(), GL_STATIC_DRAW);
891 - }
892 -
893 - glBindBuffer(GL_ARRAY_BUFFER, tfVBO[0]);
894 - glVertexPointer(2, GL_FLOAT, (4 * 4), 0);
895 - }
896 -
897 - public void render(final boolean render, final boolean animate, final int delta) {
898 - if ( animate ) {
899 - glUseProgram(progIDTF);
900 - glUniform1f(deltaLoc, delta);
901 -
902 - final int vbo = currVBO;
903 - currVBO = 1 - currVBO;
904 -
905 - glBindBuffer(GL_ARRAY_BUFFER, tfVBO[vbo]);
906 - glVertexPointer(4, GL_FLOAT, 0, 0);
907 -
908 - glEnable(GL_RASTERIZER_DISCARD);
909 - if ( GLContext.getCapabilities().OpenGL30 ) {
910 - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, tfVBO[1 - vbo]);
911 -
912 - glBeginTransformFeedback(GL_POINTS);
913 - glDrawArrays(GL_POINTS, 0, ballCount);
914 - glEndTransformFeedback();
915 - } else {
916 - glBindBufferBaseEXT(GL_TRANSFORM_FEEDBACK_BUFFER_EXT, 0, tfVBO[1 - vbo]);
917 -
918 - glBeginTransformFeedbackEXT(GL_POINTS);
919 - glDrawArrays(GL_POINTS, 0, ballCount);
920 - glEndTransformFeedbackEXT();
921 - }
922 - glDisable(GL_RASTERIZER_DISCARD);
923 -
924 - glUseProgram(progID);
925 - glVertexPointer(2, GL_FLOAT, (4 * 4), 0);
926 - }
927 -
928 - if ( render )
929 - glDrawArrays(GL_POINTS, 0, ballCount);
930 - }
931 -
932 - }
933 -
934 -}
935 \ No newline at end of file
936 --- a/src/java/org/lwjgl/util/mapped/MappedObjectClassLoader.java
937 +++ /dev/null
938 @@ -1,193 +0,0 @@
939 -/*
940 - * Copyright (c) 2002-2011 LWJGL Project
941 - * All rights reserved.
942 - *
943 - * Redistribution and use in source and binary forms, with or without
944 - * modification, are permitted provided that the following conditions are
945 - * met:
946 - *
947 - * * Redistributions of source code must retain the above copyright
948 - * notice, this list of conditions and the following disclaimer.
949 - *
950 - * * Redistributions in binary form must reproduce the above copyright
951 - * notice, this list of conditions and the following disclaimer in the
952 - * documentation and/or other materials provided with the distribution.
953 - *
954 - * * Neither the name of 'LWJGL' nor the names of
955 - * its contributors may be used to endorse or promote products derived
956 - * from this software without specific prior written permission.
957 - *
958 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
959 - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
960 - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
961 - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
962 - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
963 - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
964 - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
965 - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
966 - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
967 - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
968 - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
969 - */
970 -package org.lwjgl.util.mapped;
971 -
972 -import org.lwjgl.LWJGLUtil;
973 -
974 -import java.io.IOException;
975 -import java.io.InputStream;
976 -import java.lang.reflect.InvocationTargetException;
977 -import java.lang.reflect.Method;
978 -import java.net.URLClassLoader;
979 -
980 -/**
981 - * This classloader is responsible for applying the bytecode transformation to mapped objects.
982 - * The transformation can either be applied using a Java agent, or with the convenient {@link #fork} method.
983 - *
984 - * @author Riven
985 - */
986 -public class MappedObjectClassLoader extends URLClassLoader {
987 -
988 - static final String MAPPEDOBJECT_PACKAGE_PREFIX = MappedObjectClassLoader.class.getPackage().getName() + ".";
989 -
990 - static boolean FORKED;
991 -
992 - /**
993 - * Forks the specified class containing a main method, passing the specified arguments. See
994 - * {@link org.lwjgl.test.mapped.TestMappedObject} for example usage.
995 - *
996 - * @param mainClass the class containing the main method
997 - * @param args the arguments to pass
998 - *
999 - * @return true if the fork was successful.
1000 - */
1001 - public static boolean fork(Class<?> mainClass, String[] args) {
1002 - if ( FORKED ) {
1003 - return false;
1004 - }
1005 -
1006 - FORKED = true;
1007 -
1008 - try {
1009 - MappedObjectClassLoader loader = new MappedObjectClassLoader(mainClass);
1010 - loader.loadMappedObject();
1011 -
1012 - Class<?> replacedMainClass = loader.loadClass(mainClass.getName());
1013 - Method mainMethod = replacedMainClass.getMethod("main", String[].class);
1014 - mainMethod.invoke(null, new Object[] { args });
1015 - } catch (InvocationTargetException exc) {
1016 - Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), exc.getCause());
1017 - } catch (Throwable cause) {
1018 - throw new Error("failed to fork", cause);
1019 - }
1020 -
1021 - return true;
1022 - }
1023 -
1024 - private MappedObjectClassLoader(Class<?> mainClass) {
1025 - super(((URLClassLoader)mainClass.getClassLoader()).getURLs());
1026 - }
1027 -
1028 - protected synchronized Class<?> loadMappedObject() throws ClassNotFoundException {
1029 - final String name = MappedObject.class.getName();
1030 - String className = name.replace('.', '/');
1031 -
1032 - byte[] bytecode = readStream(this.getResourceAsStream(className.concat(".class")));
1033 -
1034 - long t0 = System.nanoTime();
1035 - bytecode = MappedObjectTransformer.transformMappedObject(bytecode);
1036 - long t1 = System.nanoTime();
1037 - total_time_transforming += (t1 - t0);
1038 -
1039 - if ( MappedObjectTransformer.PRINT_ACTIVITY )
1040 - printActivity(className, t0, t1);
1041 -
1042 - Class<?> clazz = super.defineClass(name, bytecode, 0, bytecode.length);
1043 - resolveClass(clazz);
1044 - return clazz;
1045 - }
1046 -
1047 - private static long total_time_transforming;
1048 -
1049 - @Override
1050 - protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
1051 - if ( name.startsWith("java.")
1052 - || name.startsWith("javax.")
1053 - || name.startsWith("sun.")
1054 - || name.startsWith("sunw.")
1055 - || name.startsWith("org.objectweb.asm.")
1056 - )
1057 - return super.loadClass(name, resolve);
1058 -
1059 - final String className = name.replace('.', '/');
1060 - final boolean inThisPackage = name.startsWith(MAPPEDOBJECT_PACKAGE_PREFIX);
1061 -
1062 - if ( inThisPackage && (
1063 - name.equals(MappedObjectClassLoader.class.getName())
1064 - || name.equals((MappedObjectTransformer.class.getName()))
1065 - || name.equals((CacheUtil.class.getName()))
1066 - ) )
1067 - return super.loadClass(name, resolve);
1068 -
1069 - byte[] bytecode = readStream(this.getResourceAsStream(className.concat(".class")));
1070 -
1071 - // Classes in this package do not get transformed, but need to go through here because we have transformed MappedObject.
1072 - if ( !(inThisPackage && name.substring(MAPPEDOBJECT_PACKAGE_PREFIX.length()).indexOf('.') == -1) ) {
1073 - long t0 = System.nanoTime();
1074 - final byte[] newBytecode = MappedObjectTransformer.transformMappedAPI(className, bytecode);
1075 - long t1 = System.nanoTime();
1076 -
1077 - total_time_transforming += (t1 - t0);
1078 -
1079 - if ( bytecode != newBytecode ) {
1080 - bytecode = newBytecode;
1081 - if ( MappedObjectTransformer.PRINT_ACTIVITY )
1082 - printActivity(className, t0, t1);
1083 - }
1084 - }
1085 -
1086 - Class<?> clazz = super.defineClass(name, bytecode, 0, bytecode.length);
1087 - if ( resolve )
1088 - resolveClass(clazz);
1089 - return clazz;
1090 - }
1091 -
1092 - private static void printActivity(final String className, final long t0, final long t1) {
1093 - final StringBuilder msg = new StringBuilder(MappedObjectClassLoader.class.getSimpleName() + ": " + className);
1094 -
1095 - if ( MappedObjectTransformer.PRINT_TIMING )
1096 - msg.append("\n\ttransforming took " + (t1 - t0) / 1000 + " micros (total: " + (total_time_transforming / 1000 / 1000) + "ms)");
1097 -
1098 - LWJGLUtil.log(msg);
1099 - }
1100 -
1101 - private static byte[] readStream(InputStream in) {
1102 - byte[] bytecode = new byte[256];
1103 - int len = 0;
1104 - try {
1105 - while ( true ) {
1106 - if ( bytecode.length == len )
1107 - bytecode = copyOf(bytecode, len * 2);
1108 - int got = in.read(bytecode, len, bytecode.length - len);
1109 - if ( got == -1 )
1110 - break;
1111 - len += got;
1112 - }
1113 - } catch (IOException exc) {
1114 - // stop!
1115 - } finally {
1116 - try {
1117 - in.close();
1118 - } catch (IOException exc) {
1119 - // ignore...
1120 - }
1121 - }
1122 - return copyOf(bytecode, len);
1123 - }
1124 -
1125 - private static byte[] copyOf(byte[] original, int newLength) {
1126 - byte[] copy = new byte[newLength];
1127 - System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
1128 - return copy;
1129 - }
1130 -
1131 -}
1132 \ No newline at end of file
1133 --- a/src/java/org/lwjgl/util/mapped/MappedObjectTransformer.java
1134 +++ /dev/null
1135 @@ -1,1319 +0,0 @@
1136 -/*
1137 - * Copyright (c) 2002-2011 LWJGL Project
1138 - * All rights reserved.
1139 - *
1140 - * Redistribution and use in source and binary forms, with or without
1141 - * modification, are permitted provided that the following conditions are
1142 - * met:
1143 - *
1144 - * * Redistributions of source code must retain the above copyright
1145 - * notice, this list of conditions and the following disclaimer.
1146 - *
1147 - * * Redistributions in binary form must reproduce the above copyright
1148 - * notice, this list of conditions and the following disclaimer in the
1149 - * documentation and/or other materials provided with the distribution.
1150 - *
1151 - * * Neither the name of 'LWJGL' nor the names of
1152 - * its contributors may be used to endorse or promote products derived
1153 - * from this software without specific prior written permission.
1154 - *
1155 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1156 - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1157 - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1158 - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
1159 - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
1160 - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
1161 - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1162 - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
1163 - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
1164 - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
1165 - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1166 - */
1167 -package org.lwjgl.util.mapped;
1168 -
1169 -import org.lwjgl.BufferUtils;
1170 -import org.lwjgl.LWJGLUtil;
1171 -import org.lwjgl.MemoryUtil;
1172 -import org.objectweb.asm.*;
1173 -import org.objectweb.asm.tree.*;
1174 -import org.objectweb.asm.tree.analysis.*;
1175 -import org.objectweb.asm.tree.analysis.Frame;
1176 -import org.objectweb.asm.util.TraceClassVisitor;
1177 -
1178 -import java.io.PrintWriter;
1179 -import java.io.StringWriter;
1180 -import java.lang.reflect.Field;
1181 -import java.lang.reflect.Modifier;
1182 -import java.nio.Buffer;
1183 -import java.nio.ByteBuffer;
1184 -import java.util.HashMap;
1185 -import java.util.Map;
1186 -
1187 -import static org.objectweb.asm.ClassWriter.*;
1188 -import static org.objectweb.asm.Opcodes.*;
1189 -
1190 -/**
1191 - * This class implements the bytecode transformation that mapped object go through.
1192 - * Mapped object classes need to first be registered with the transformer, see {@link #register(Class)}.
1193 - * <p/>
1194 - * The transformer supports some debugging tools, enabled through JVM system properties:<br/>
1195 - * org.lwjgl.util.mapped.PrintTiming=true, prints timing information for the transformation step.<br/>
1196 - * org.lwjgl.util.mapped.PrintActivity=true, prints activity information.<br/>
1197 - * org.lwjgl.util.mapped.PrintBytecode=true, prints the transformed bytecode.<br/>
1198 - * org.lwjgl.util.Debug must also be set to true for the above to work.
1199 - *
1200 - * @author Riven
1201 - */
1202 -public class MappedObjectTransformer {
1203 -
1204 - static final boolean PRINT_ACTIVITY = LWJGLUtil.DEBUG && LWJGLUtil.getPrivilegedBoolean("org.lwjgl.util.mapped.PrintActivity");
1205 - static final boolean PRINT_TIMING = PRINT_ACTIVITY && LWJGLUtil.getPrivilegedBoolean("org.lwjgl.util.mapped.PrintTiming");
1206 - static final boolean PRINT_BYTECODE = LWJGLUtil.DEBUG && LWJGLUtil.getPrivilegedBoolean("org.lwjgl.util.mapped.PrintBytecode");
1207 -
1208 - static final Map<String, MappedSubtypeInfo> className_to_subtype;
1209 -
1210 - static final String MAPPED_OBJECT_JVM = jvmClassName(MappedObject.class);
1211 - static final String MAPPED_HELPER_JVM = jvmClassName(MappedHelper.class);
1212 -
1213 - static final String MAPPEDSET_PREFIX = jvmClassName(MappedSet.class);
1214 - static final String MAPPED_SET2_JVM = jvmClassName(MappedSet2.class);
1215 - static final String MAPPED_SET3_JVM = jvmClassName(MappedSet3.class);
1216 - static final String MAPPED_SET4_JVM = jvmClassName(MappedSet4.class);
1217 -
1218 - static final String CACHE_LINE_PAD_JVM = "L" + jvmClassName(CacheLinePad.class) + ";";
1219 -
1220 - // Public methods
1221 - static final String VIEWADDRESS_METHOD_NAME = "getViewAddress";
1222 - static final String NEXT_METHOD_NAME = "next";
1223 - static final String ALIGN_METHOD_NAME = "getAlign";
1224 - static final String SIZEOF_METHOD_NAME = "getSizeof";
1225 - static final String CAPACITY_METHOD_NAME = "capacity"; // Used for .asArray().length
1226 -
1227 - // Internal methods
1228 - static final String VIEW_CONSTRUCTOR_NAME = "constructView$LWJGL"; // Used by runViewConstructor
1229 -
1230 - static final Map<Integer, String> OPCODE_TO_NAME = new HashMap<Integer, String>();
1231 - static final Map<Integer, String> INSNTYPE_TO_NAME = new HashMap<Integer, String>();
1232 -
1233 - static boolean is_currently_computing_frames;
1234 -
1235 - static {
1236 - getClassEnums(Opcodes.class, OPCODE_TO_NAME, "V1_", "ACC_", "T_", "F_", "MH_");
1237 - getClassEnums(AbstractInsnNode.class, INSNTYPE_TO_NAME);
1238 -
1239 - className_to_subtype = new HashMap<String, MappedSubtypeInfo>();
1240 -
1241 - {
1242 - // HACK: required for mapped.view++
1243 - //
1244 - // because the compiler generates:
1245 - // => GETFIELD MappedObject.view
1246 - // => ICONST_1
1247 - // => IADD
1248 - // => PUTFIELD MyMappedType.view
1249 - //
1250 - // instead of:
1251 - // => GETFIELD MyMappedType.view
1252 - // => ICONST_1
1253 - // => IADD
1254 - // => PUTFIELD MyMappedType.view
1255 - //
1256 - className_to_subtype.put(MAPPED_OBJECT_JVM, new MappedSubtypeInfo(MAPPED_OBJECT_JVM, null, -1, -1, -1, false));
1257 - }
1258 -
1259 - final String vmName = System.getProperty("java.vm.name");
1260 - if ( vmName != null && !vmName.contains("Server") ) {
1261 - System.err.println("Warning: " + MappedObject.class.getSimpleName() + "s have inferiour performance on Client VMs, please consider switching to a Server VM.");
1262 - }
1263 - }
1264 -
1265 - /**
1266 - * Registers a class as a mapped object.
1267 - * The class must extend {@link org.lwjgl.util.mapped.MappedObject} and be annotated with {@link org.lwjgl.util.mapped.MappedField}.
1268 - *
1269 - * @param type the mapped object class.
1270 - */
1271 - public static void register(Class<? extends MappedObject> type) {
1272 - if ( MappedObjectClassLoader.FORKED )
1273 - return;
1274 -
1275 - final MappedType mapped = type.getAnnotation(MappedType.class);
1276 -
1277 - if ( mapped != null && mapped.padding() < 0 )
1278 - throw new ClassFormatError("Invalid mapped type padding: " + mapped.padding());
1279 -
1280 - if ( type.getEnclosingClass() != null && !Modifier.isStatic(type.getModifiers()) )
1281 - throw new InternalError("only top-level or static inner classes are allowed");
1282 -
1283 - final String className = jvmClassName(type);
1284 - final Map<String, FieldInfo> fields = new HashMap<String, FieldInfo>();
1285 -
1286 - long sizeof = 0;
1287 - for ( Field field : type.getDeclaredFields() ) {
1288 - FieldInfo fieldInfo = registerField(mapped == null || mapped.autoGenerateOffsets(), className, sizeof, field);
1289 - if ( fieldInfo == null )
1290 - continue;
1291 -
1292 - fields.put(field.getName(), fieldInfo);
1293 -
1294 - sizeof = Math.max(sizeof, fieldInfo.offset + fieldInfo.lengthPadded);
1295 - }
1296 -
1297 - int align = 4;
1298 - int padding = 0;
1299 - boolean cacheLinePadded = false;
1300 -
1301 - if ( mapped != null ) {
1302 - align = mapped.align();
1303 - if ( mapped.cacheLinePadding() ) {
1304 - if ( mapped.padding() != 0 )
1305 - throw new ClassFormatError("Mapped type padding cannot be specified together with cacheLinePadding.");
1306 -
1307 - final int cacheLineMod = (int)(sizeof % CacheUtil.getCacheLineSize());
1308 - if ( cacheLineMod != 0 )
1309 - padding = CacheUtil.getCacheLineSize() - cacheLineMod;
1310 -
1311 - cacheLinePadded = true;
1312 - } else
1313 - padding = mapped.padding();
1314 - }
1315 -
1316 - sizeof += padding;
1317 -
1318 - final MappedSubtypeInfo mappedType = new MappedSubtypeInfo(className, fields, (int)sizeof, align, padding, cacheLinePadded);
1319 - if ( className_to_subtype.put(className, mappedType) != null )
1320 - throw new InternalError("duplicate mapped type: " + mappedType.className);
1321 - }
1322 -
1323 - private static FieldInfo registerField(final boolean autoGenerateOffsets, final String className, long advancingOffset, final Field field) {
1324 - if ( Modifier.isStatic(field.getModifiers()) ) // static fields are never mapped
1325 - return null;
1326 -
1327 - // we only support primitives and ByteBuffers
1328 - if ( !field.getType().isPrimitive() && field.getType() != ByteBuffer.class )
1329 - throw new ClassFormatError("field '" + className + "." + field.getName() + "' not supported: " + field.getType());
1330 -
1331 - MappedField meta = field.getAnnotation(MappedField.class);
1332 - if ( meta == null && !autoGenerateOffsets )
1333 - throw new ClassFormatError("field '" + className + "." + field.getName() + "' missing annotation " + MappedField.class.getName() + ": " + className);
1334 -
1335 - Pointer pointer = field.getAnnotation(Pointer.class);
1336 - if ( pointer != null && field.getType() != long.class )
1337 - throw new ClassFormatError("The @Pointer annotation can only be used on long fields. @Pointer field found: " + className + "." + field.getName() + ": " + field.getType());
1338 -
1339 - if ( Modifier.isVolatile(field.getModifiers()) && (pointer != null || field.getType() == ByteBuffer.class) )
1340 - throw new ClassFormatError("The volatile keyword is not supported for @Pointer or ByteBuffer fields. Volatile field found: " + className + "." + field.getName() + ": " + field.getType());
1341 -
1342 - // quick hack
1343 - long byteLength;
1344 - if ( field.getType() == long.class || field.getType() == double.class ) {
1345 - if ( pointer == null )
1346 - byteLength = 8;
1347 - else
1348 - byteLength = MappedObjectUnsafe.INSTANCE.addressSize();
1349 - } else if ( field.getType() == double.class )
1350 - byteLength = 8;
1351 - else if ( field.getType() == int.class || field.getType() == float.class )
1352 - byteLength = 4;
1353 - else if ( field.getType() == char.class || field.getType() == short.class )
1354 - byteLength = 2;
1355 - else if ( field.getType() == byte.class )
1356 - byteLength = 1;
1357 - else if ( field.getType() == ByteBuffer.class ) {
1358 - byteLength = meta.byteLength();
1359 - if ( byteLength < 0 )
1360 - throw new IllegalStateException("invalid byte length for mapped ByteBuffer field: " + className + "." + field.getName() + " [length=" + byteLength + "]");
1361 - } else
1362 - throw new ClassFormatError(field.getType().getName());
1363 -
1364 - if ( field.getType() != ByteBuffer.class && (advancingOffset % byteLength) != 0 )
1365 - throw new IllegalStateException("misaligned mapped type: " + className + "." + field.getName());
1366 -
1367 - CacheLinePad pad = field.getAnnotation(CacheLinePad.class);
1368 -
1369 - long byteOffset = advancingOffset;
1370 - if ( meta != null && meta.byteOffset() != -1 ) {
1371 - if ( meta.byteOffset() < 0 )
1372 - throw new ClassFormatError("Invalid field byte offset: " + className + "." + field.getName() + " [byteOffset=" + meta.byteOffset() + "]");
1373 - if ( pad != null )
1374 - throw new ClassFormatError("A field byte offset cannot be specified together with cache-line padding: " + className + "." + field.getName());
1375 -
1376 - byteOffset = meta.byteOffset();
1377 - }
1378 -
1379 - long byteLengthPadded = byteLength;
1380 - if ( pad != null ) {
1381 - // Pad before
1382 - if ( pad.before() && byteOffset % CacheUtil.getCacheLineSize() != 0 )
1383 - byteOffset += CacheUtil.getCacheLineSize() - (byteOffset & (CacheUtil.getCacheLineSize() - 1));
1384 -
1385 - // Pad after
1386 - if ( pad.after() && (byteOffset + byteLength) % CacheUtil.getCacheLineSize() != 0 )
1387 - byteLengthPadded += CacheUtil.getCacheLineSize() - (byteOffset + byteLength) % CacheUtil.getCacheLineSize();
1388 -
1389 - assert !pad.before() || (byteOffset % CacheUtil.getCacheLineSize() == 0);
1390 - assert !pad.after() || ((byteOffset + byteLengthPadded) % CacheUtil.getCacheLineSize() == 0);
1391 - }
1392 -
1393 - if ( PRINT_ACTIVITY )
1394 - LWJGLUtil.log(MappedObjectTransformer.class.getSimpleName() + ": " + className + "." + field.getName() + " [type=" + field.getType().getSimpleName() + ", offset=" + byteOffset + "]");
1395 -
1396 - return new FieldInfo(byteOffset, byteLength, byteLengthPadded, Type.getType(field.getType()), Modifier.isVolatile(field.getModifiers()), pointer != null);
1397 - }
1398 -
1399 - /** Removes final from methods that will be overriden by subclasses. */
1400 - static byte[] transformMappedObject(byte[] bytecode) {
1401 - final ClassWriter cw = new ClassWriter(0);
1402 -
1403 - ClassVisitor cv = new ClassAdapter(cw) {
1404 -
1405 - private final String[] DEFINALIZE_LIST = {
1406 - VIEWADDRESS_METHOD_NAME,
1407 - NEXT_METHOD_NAME,
1408 - ALIGN_METHOD_NAME,
1409 - SIZEOF_METHOD_NAME,
1410 - CAPACITY_METHOD_NAME,
1411 - };
1412 -
1413 - public MethodVisitor visitMethod(int access, final String name, final String desc, final String signature, final String[] exceptions) {
1414 - for ( String method : DEFINALIZE_LIST ) {
1415 - if ( name.equals(method) ) {
1416 - access &= ~ACC_FINAL;
1417 - break;
1418 - }
1419 - }
1420 - return super.visitMethod(access, name, desc, signature, exceptions);
1421 - }
1422 - };
1423 -
1424 - new ClassReader(bytecode).accept(cv, 0);
1425 - return cw.toByteArray();
1426 - }
1427 -
1428 - static byte[] transformMappedAPI(final String className, byte[] bytecode) {
1429 - final ClassWriter cw = new ClassWriter(COMPUTE_FRAMES) {
1430 -
1431 - @Override
1432 - protected String getCommonSuperClass(String a, String b) {
1433 - // HACK: prevent user-code static-initialization-blocks to be executed
1434 - if ( is_currently_computing_frames && !a.startsWith("java/") || !b.startsWith("java/") )
1435 - return "java/lang/Object";
1436 -
1437 - return super.getCommonSuperClass(a, b);
1438 - }
1439 -
1440 - };
1441 -
1442 - final TransformationAdapter ta = new TransformationAdapter(cw, className);
1443 -
1444 - ClassVisitor cv = ta;
1445 - if ( className_to_subtype.containsKey(className) ) // Do a first pass to generate address getters
1446 - cv = getMethodGenAdapter(className, cv);
1447 -
1448 - new ClassReader(bytecode).accept(cv, ClassReader.SKIP_FRAMES);
1449 -
1450 - if ( !ta.transformed )
1451 - return bytecode;
1452 -
1453 - bytecode = cw.toByteArray();
1454 - if ( PRINT_BYTECODE )
1455 - printBytecode(bytecode);
1456 -
1457 - return bytecode;
1458 - }
1459 -
1460 - private static ClassAdapter getMethodGenAdapter(final String className, final ClassVisitor cv) {
1461 - return new ClassAdapter(cv) {
1462 -
1463 - @Override
1464 - public void visitEnd() {
1465 - final MappedSubtypeInfo mappedSubtype = className_to_subtype.get(className);
1466 -
1467 - generateViewAddressGetter();
1468 - generateCapacity();
1469 - generateAlignGetter(mappedSubtype);
1470 - generateSizeofGetter();
1471 - generateNext();
1472 -
1473 - for ( String fieldName : mappedSubtype.fields.keySet() ) {
1474 - final FieldInfo field = mappedSubtype.fields.get(fieldName);
1475 -
1476 - if ( field.type.getDescriptor().length() > 1 ) { // ByteBuffer, getter only
1477 - generateByteBufferGetter(fieldName, field);
1478 - } else {
1479 - generateFieldGetter(fieldName, field);
1480 - generateFieldSetter(fieldName, field);
1481 - }
1482 - }
1483 -
1484 - super.visitEnd();
1485 - }
1486 -
1487 - private void generateViewAddressGetter() {
1488 - MethodVisitor mv = super.visitMethod(ACC_PUBLIC, VIEWADDRESS_METHOD_NAME, "(I)J", null, null);
1489 - mv.visitCode();
1490 - mv.visitVarInsn(ALOAD, 0);
1491 - mv.visitFieldInsn(GETFIELD, MAPPED_OBJECT_JVM, "baseAddress", "J");
1492 - mv.visitVarInsn(ILOAD, 1);
1493 - mv.visitFieldInsn(GETSTATIC, className, "SIZEOF", "I");
1494 - mv.visitInsn(IMUL);
1495 - mv.visitInsn(I2L);
1496 - mv.visitInsn(LADD);
1497 - if ( MappedObject.CHECKS ) {
1498 - mv.visitInsn(DUP2);
1499 - mv.visitVarInsn(ALOAD, 0);
1500 - mv.visitMethodInsn(INVOKESTATIC, MAPPED_HELPER_JVM, "checkAddress", "(JL" + MAPPED_OBJECT_JVM + ";)V");
1501 - }
1502 - mv.visitInsn(LRETURN);
1503 - mv.visitMaxs(3, 2);
1504 - mv.visitEnd();
1505 - }
1506 -
1507 - private void generateCapacity() {
1508 - // return (backingByteBuffer().capacity() + (int)(MemoryUtil.getAddress0(backingByteBuffer()) - baseAddress)) / SIZEOF;
1509 - MethodVisitor mv = super.visitMethod(ACC_PUBLIC, CAPACITY_METHOD_NAME, "()I", null, null);
1510 - mv.visitCode();
1511 - mv.visitVarInsn(ALOAD, 0);
1512 - mv.visitMethodInsn(INVOKEVIRTUAL, MAPPED_OBJECT_JVM, "backingByteBuffer", "()L" + jvmClassName(ByteBuffer.class) + ";");
1513 - mv.visitInsn(DUP);
1514 - mv.visitMethodInsn(INVOKEVIRTUAL, jvmClassName(ByteBuffer.class), "capacity", "()I");
1515 - mv.visitInsn(SWAP);
1516 - mv.visitMethodInsn(INVOKESTATIC, jvmClassName(MemoryUtil.class), "getAddress0", "(L" + jvmClassName(Buffer.class) + ";)J");
1517 - mv.visitVarInsn(ALOAD, 0);
1518 - mv.visitFieldInsn(GETFIELD, MAPPED_OBJECT_JVM, "baseAddress", "J");
1519 - mv.visitInsn(LSUB);
1520 - mv.visitInsn(L2I);
1521 - mv.visitInsn(IADD);
1522 - mv.visitFieldInsn(GETSTATIC, className, "SIZEOF", "I");
1523 - mv.visitInsn(IDIV);
1524 - mv.visitInsn(IRETURN);
1525 - mv.visitMaxs(3, 1);
1526 - mv.visitEnd();
1527 - }
1528 -
1529 - private void generateAlignGetter(final MappedSubtypeInfo mappedSubtype) {
1530 - MethodVisitor mv = super.visitMethod(ACC_PUBLIC, ALIGN_METHOD_NAME, "()I", null, null);
1531 - mv.visitCode();
1532 - visitIntNode(mv, mappedSubtype.sizeof);
1533 - mv.visitInsn(IRETURN);
1534 - mv.visitMaxs(1, 1);
1535 - mv.visitEnd();
1536 - }
1537 -
1538 - private void generateSizeofGetter() {
1539 - MethodVisitor mv = super.visitMethod(ACC_PUBLIC, SIZEOF_METHOD_NAME, "()I", null, null);
1540 - mv.visitCode();
1541 - mv.visitFieldInsn(GETSTATIC, className, "SIZEOF", "I");
1542 - mv.visitInsn(IRETURN);
1543 - mv.visitMaxs(1, 1);
1544 - mv.visitEnd();
1545 - }
1546 -
1547 - private void generateNext() {
1548 - MethodVisitor mv = super.visitMethod(ACC_PUBLIC, NEXT_METHOD_NAME, "()V", null, null);
1549 - mv.visitCode();
1550 - mv.visitVarInsn(ALOAD, 0);
1551 - mv.visitInsn(DUP);
1552 - mv.visitFieldInsn(GETFIELD, MAPPED_OBJECT_JVM, "viewAddress", "J");
1553 - mv.visitFieldInsn(GETSTATIC, className, "SIZEOF", "I");
1554 - mv.visitInsn(I2L);
1555 - mv.visitInsn(LADD);
1556 - mv.visitMethodInsn(INVOKEVIRTUAL, className, "setViewAddress", "(J)V");
1557 - mv.visitInsn(RETURN);
1558 - mv.visitMaxs(3, 1);
1559 - mv.visitEnd();
1560 - }
1561 -
1562 - private void generateByteBufferGetter(final String fieldName, final FieldInfo field) {
1563 - MethodVisitor mv = super.visitMethod(ACC_PUBLIC | ACC_STATIC, getterName(fieldName), "(L" + className + ";I)" + field.type.getDescriptor(), null, null);
1564 - mv.visitCode();
1565 - mv.visitVarInsn(ALOAD, 0);
1566 - mv.visitVarInsn(ILOAD, 1);
1567 - mv.visitMethodInsn(INVOKEVIRTUAL, className, VIEWADDRESS_METHOD_NAME, "(I)J");
1568 - visitIntNode(mv, (int)field.offset);
1569 - mv.visitInsn(I2L);
1570 - mv.visitInsn(LADD);
1571 - visitIntNode(mv, (int)field.length);
1572 - mv.visitMethodInsn(INVOKESTATIC, MAPPED_HELPER_JVM, "newBuffer", "(JI)L" + jvmClassName(ByteBuffer.class) + ";");
1573 - mv.visitInsn(ARETURN);
1574 - mv.visitMaxs(3, 2);
1575 - mv.visitEnd();
1576 - }
1577 -
1578 - private void generateFieldGetter(final String fieldName, final FieldInfo field) {
1579 - MethodVisitor mv = super.visitMethod(ACC_PUBLIC | ACC_STATIC, getterName(fieldName), "(L" + className + ";I)" + field.type.getDescriptor(), null, null);
1580 - mv.visitCode();
1581 - mv.visitVarInsn(ALOAD, 0);
1582 - mv.visitVarInsn(ILOAD, 1);
1583 - mv.visitMethodInsn(INVOKEVIRTUAL, className, VIEWADDRESS_METHOD_NAME, "(I)J");
1584 - visitIntNode(mv, (int)field.offset);
1585 - mv.visitInsn(I2L);
1586 - mv.visitInsn(LADD);
1587 - mv.visitMethodInsn(INVOKESTATIC, MAPPED_HELPER_JVM, field.getAccessType() + "get", "(J)" + field.type.getDescriptor());
1588 - mv.visitInsn(field.type.getOpcode(IRETURN));
1589 - mv.visitMaxs(3, 2);
1590 - mv.visitEnd();
1591 - }
1592 -
1593 - private void generateFieldSetter(final String fieldName, final FieldInfo field) {
1594 - MethodVisitor mv = super.visitMethod(ACC_PUBLIC | ACC_STATIC, setterName(fieldName), "(L" + className + ";I" + field.type.getDescriptor() + ")V", null, null);
1595 - mv.visitCode();
1596 - int load = 0;
1597 - switch ( field.type.getSort() ) {
1598 - case Type.BOOLEAN:
1599 - case Type.CHAR:
1600 - case Type.BYTE:
1601 - case Type.SHORT:
1602 - case Type.INT:
1603 - load = ILOAD;
1604 - break;
1605 - case Type.FLOAT:
1606 - load = FLOAD;
1607 - break;
1608 - case Type.LONG:
1609 - load = LLOAD;
1610 - break;
1611 - case Type.DOUBLE:
1612 - load = DLOAD;
1613 - break;
1614 - }
1615 - mv.visitVarInsn(load, 2);
1616 - mv.visitVarInsn(ALOAD, 0);
1617 - mv.visitVarInsn(ILOAD, 1);
1618 - mv.visitMethodInsn(INVOKEVIRTUAL, className, VIEWADDRESS_METHOD_NAME, "(I)J");
1619 - visitIntNode(mv, (int)field.offset);
1620 - mv.visitInsn(I2L);
1621 - mv.visitInsn(LADD);
1622 - mv.visitMethodInsn(INVOKESTATIC, MAPPED_HELPER_JVM, field.getAccessType() + "put", "(" + field.type.getDescriptor() + "J)V");
1623 - mv.visitInsn(RETURN);
1624 - mv.visitMaxs(4, 4);
1625 - mv.visitEnd();
1626 - }
1627 -
1628 - };
1629 - }
1630 -
1631 - private static class TransformationAdapter extends ClassAdapter {
1632 -
1633 - final String className;
1634 -
1635 - boolean transformed;
1636 -
1637 - TransformationAdapter(final ClassVisitor cv, final String className) {
1638 - super(cv);
1639 - this.className = className;
1640 - }
1641 -
1642 - @Override
1643 - public FieldVisitor visitField(final int access, final String name, final String desc, final String signature, final Object value) {
1644 - // remove redirected fields
1645 - final MappedSubtypeInfo mappedSubtype = className_to_subtype.get(className);
1646 - if ( mappedSubtype != null && mappedSubtype.fields.containsKey(name) ) {
1647 - if ( PRINT_ACTIVITY )
1648 - LWJGLUtil.log(MappedObjectTransformer.class.getSimpleName() + ": discarding field: " + className + "." + name + ":" + desc);
1649 - return null;
1650 - }
1651 -
1652 - if ( (access & ACC_STATIC) == 0 ) {
1653 - return new FieldNode(access, name, desc, signature, value) {
1654 - public void visitEnd() {
1655 - if ( visibleAnnotations == null ) { // early-out
1656 - accept(cv);
1657 - return;
1658 - }
1659 -
1660 - boolean before = false;
1661 - boolean after = false;
1662 - int byteLength = 0;
1663 - for ( AnnotationNode pad : visibleAnnotations ) {
1664 - if ( CACHE_LINE_PAD_JVM.equals(pad.desc) ) {
1665 - if ( "J".equals(desc) || "D".equals(desc) )
1666 - byteLength = 8;
1667 - else if ( "I".equals(desc) || "F".equals(desc) )
1668 - byteLength = 4;
1669 - else if ( "S".equals(desc) || "C".equals(desc) )
1670 - byteLength = 2;
1671 - else if ( "B".equals(desc) || "Z".equals(desc) )
1672 - byteLength = 1;
1673 - else
1674 - throw new ClassFormatError("The @CacheLinePad annotation cannot be used on non-primitive fields: " + className + "." + name);
1675 -
1676 - transformed = true;
1677 -
1678 - after = true;
1679 - if ( pad.values != null ) {
1680 - for ( int i = 0; i < pad.values.size(); i += 2 ) {
1681 - final boolean value = pad.values.get(i + 1).equals(Boolean.TRUE);
1682 - if ( "before".equals(pad.values.get(i)) )
1683 - before = value;
1684 - else
1685 - after = value;
1686 - }
1687 - }
1688 - break;
1689 - }
1690 - }
1691 -
1692 - /*
1693 - We make the fields public to force the JVM to keep the fields in the object.
1694 - Instead of using only longs or integers, we use the same type as the original
1695 - field. That's because modern JVMs usually reorder fields by type:
1696 - longs, then doubles, then integers, then booleans, etc. This way it's more
1697 - likely that the padding will work as expected.
1698 - */
1699 -
1700 - if ( before ) {
1701 - final int count = CacheUtil.getCacheLineSize() / byteLength - 1;
1702 - for ( int i = count; i >= 1; i-- )
1703 - cv.visitField(access | ACC_PUBLIC | ACC_SYNTHETIC, name + "$PAD_" + i, desc, signature, null);
1704 - }
1705 -
1706 - accept(cv);
1707 -
1708 - if ( after ) {
1709 - final int count = CacheUtil.getCacheLineSize() / byteLength - 1;
1710 - for ( int i = 1; i <= count; i++ )
1711 - cv.visitField(access | ACC_PUBLIC | ACC_SYNTHETIC, name + "$PAD" + i, desc, signature, null);
1712 - }
1713 - }
1714 - };
1715 - } else
1716 - return super.visitField(access, name, desc, signature, value);
1717 - }
1718 -
1719 - @Override
1720 - public MethodVisitor visitMethod(final int access, String name, final String desc, final String signature, final String[] exceptions) {
1721 - // Move MappedSubtype constructors to another method
1722 - if ( "<init>".equals(name) ) {
1723 - final MappedSubtypeInfo mappedSubtype = className_to_subtype.get(className);
1724 - if ( mappedSubtype != null ) {
1725 - if ( !"()V".equals(desc) )
1726 - throw new ClassFormatError(className + " can only have a default constructor, found: " + desc);
1727 -
1728 - final MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
1729 - mv.visitVarInsn(ALOAD, 0);
1730 - mv.visitMethodInsn(INVOKESPECIAL, MAPPED_OBJECT_JVM, "<init>", "()V");
1731 - mv.visitInsn(RETURN);
1732 - mv.visitMaxs(0, 0);
1733 -
1734 - // put the method body in another method
1735 - name = VIEW_CONSTRUCTOR_NAME;
1736 - }
1737 - }
1738 -
1739 - final MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
1740 - return new MethodNode(access, name, desc, signature, exceptions) {
1741 -
1742 - /** When true, the method has touched a mapped object and needs to be transformed. We track this
1743 - * so we can skip the expensive frame analysis and tree API usage. */
1744 - boolean needsTransformation;
1745 -
1746 - @Override
1747 - public void visitMaxs(int a, int b) {
1748 - try {
1749 - is_currently_computing_frames = true;
1750 - super.visitMaxs(a, b);
1751 - } finally {
1752 - is_currently_computing_frames = false;
1753 - }
1754 - }
1755 -
1756 - @Override
1757 - public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) {
1758 - if ( className_to_subtype.containsKey(owner) || owner.startsWith(MAPPEDSET_PREFIX) )
1759 - needsTransformation = true;
1760 -
1761 - super.visitFieldInsn(opcode, owner, name, desc);
1762 - }
1763 -
1764 - @Override
1765 - public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc) {
1766 - if ( className_to_subtype.containsKey(owner) )
1767 - needsTransformation = true;
1768 -
1769 - super.visitMethodInsn(opcode, owner, name, desc);
1770 - }
1771 -
1772 - @Override
1773 - public void visitEnd() {
1774 - if ( needsTransformation ) { // Early-out for methods that do not touch a mapped object.
1775 - //System.err.println("\nTRANSFORMING: " + className + "." + name + desc);
1776 - transformed = true;
1777 - try {
1778 - transformMethod(analyse());
1779 - } catch (Exception e) {
1780 - throw new RuntimeException(e);
1781 - }
1782 - }
1783 -
1784 - // Pass the instruction stream to the adapter's MethodVisitor
1785 - accept(mv);
1786 - }
1787 -
1788 - private Frame<BasicValue>[] analyse() throws AnalyzerException {
1789 - final Analyzer<BasicValue> a = new Analyzer<BasicValue>(new SimpleVerifier());
1790 - a.analyze(className, this);
1791 - return a.getFrames();
1792 - }
1793 -
1794 - private void transformMethod(final Frame<BasicValue>[] frames) {
1795 - final InsnList instructions = this.instructions;
1796 -
1797 - final Map<Integer, MappedSubtypeInfo> arrayVars = new HashMap<Integer, MappedSubtypeInfo>();
1798 -
1799 - /*
1800 - We need this map because we insert/remove instructions from the stream and we need a way
1801 - to match each original instruction with the corresponding frame.
1802 - TODO: Can we keep track of everything more efficiently without a map?
1803 - */
1804 - final Map<AbstractInsnNode, Frame<BasicValue>> frameMap = new HashMap<AbstractInsnNode, Frame<BasicValue>>();
1805 - for ( int i = 0; i < frames.length; i++ )
1806 - frameMap.put(instructions.get(i), frames[i]);
1807 -
1808 - for ( int i = 0; i < instructions.size(); i++ ) { // f is a separate cursor for frames
1809 - final AbstractInsnNode instruction = instructions.get(i);
1810 -
1811 - //System.out.println("MAIN LOOP #" + i + " - " + getOpcodeName(instruction));
1812 -
1813 - switch ( instruction.getType() ) {
1814 - case AbstractInsnNode.VAR_INSN:
1815 - if ( instruction.getOpcode() == ALOAD ) {
1816 - VarInsnNode varInsn = (VarInsnNode)instruction;
1817 - final MappedSubtypeInfo mappedSubtype = arrayVars.get(varInsn.var);
1818 - if ( mappedSubtype != null )
1819 - i = transformArrayAccess(instructions, i, frameMap, varInsn, mappedSubtype, varInsn.var);
1820 - }
1821 - break;
1822 - case AbstractInsnNode.FIELD_INSN:
1823 - FieldInsnNode fieldInsn = (FieldInsnNode)instruction;
1824 -
1825 - final InsnList list = transformFieldAccess(fieldInsn);
1826 - if ( list != null )
1827 - i = replace(instructions, i, instruction, list);
1828 -
1829 - break;
1830 - case AbstractInsnNode.METHOD_INSN:
1831 - MethodInsnNode methodInsn = (MethodInsnNode)instruction;
1832 - final MappedSubtypeInfo mappedType = className_to_subtype.get(methodInsn.owner);
1833 - if ( mappedType != null )
1834 - i = transformMethodCall(instructions, i, frameMap, methodInsn, mappedType, arrayVars);
1835 - break;
1836 - }
1837 - }
1838 - }
1839 - };
1840 - }
1841 - }
1842 -
1843 - static int transformMethodCall(final InsnList instructions, int i, final Map<AbstractInsnNode, Frame<BasicValue>> frameMap, final MethodInsnNode methodInsn, final MappedSubtypeInfo mappedType, final Map<Integer, MappedSubtypeInfo> arrayVars) {
1844 - switch ( methodInsn.getOpcode() ) {
1845 - case INVOKEVIRTUAL:
1846 - if ( "asArray".equals(methodInsn.name) && methodInsn.desc.equals("()[L" + MAPPED_OBJECT_JVM + ";") ) {
1847 - // Go forward and store the local variable index.
1848 - // We only allow this pattern: INVOKEVIRTUAL -> CHECKCAST -> ASTORE.
1849 - // We remove the first two and store the target MappedSubtype in the ASTORE variable
1850 - AbstractInsnNode nextInstruction;
1851 - checkInsnAfterIsArray(nextInstruction = methodInsn.getNext(), CHECKCAST);
1852 - checkInsnAfterIsArray(nextInstruction = nextInstruction.getNext(), ASTORE);
1853 -
1854 - final Frame<BasicValue> frame = frameMap.get(nextInstruction);
1855 - final String targetType = frame.getStack(frame.getStackSize() - 1).getType().getElementType().getInternalName();
1856 - if ( !methodInsn.owner.equals(targetType) ) {
1857 - /*
1858 - This may happen with the current API, like so:
1859 - MappedA foo = MappedA.malloc(...);
1860 - MappedB[] cursor = foo.asArray();
1861 - We have to parameterize MappedObject to avoid this.
1862 - */
1863 - throw new ClassCastException("Source: " + methodInsn.owner + " - Target: " + targetType);
1864 - }
1865 -
1866 - final VarInsnNode varInstruction = (VarInsnNode)nextInstruction;
1867 -
1868 - arrayVars.put(varInstruction.var, mappedType);
1869 -
1870 - instructions.remove(methodInsn.getNext()); // Remove CHECKCAST
1871 - instructions.remove(methodInsn); // Remove INVOKEVIRTUAL
1872 - }
1873 -
1874 - if ( "dup".equals(methodInsn.name) && methodInsn.desc.equals("()L" + MAPPED_OBJECT_JVM + ";") ) {
1875 - i = replace(instructions, i, methodInsn, generateDupInstructions(methodInsn));
1876 - break;
1877 - }
1878 -
1879 - if ( "slice".equals(methodInsn.name) && methodInsn.desc.equals("()L" + MAPPED_OBJECT_JVM + ";") ) {
1880 - i = replace(instructions, i, methodInsn, generateSliceInstructions(methodInsn));
1881 - break;
1882 - }
1883 -
1884 - if ( "runViewConstructor".equals(methodInsn.name) && "()V".equals(methodInsn.desc) ) {
1885 - i = replace(instructions, i, methodInsn, generateRunViewConstructorInstructions(methodInsn));
1886 - break;
1887 - }
1888 -
1889 - if ( "copyTo".equals(methodInsn.name) && methodInsn.desc.equals("(L" + MAPPED_OBJECT_JVM + ";)V") ) {
1890 - i = replace(instructions, i, methodInsn, generateCopyToInstructions(mappedType));
1891 - break;
1892 - }
1893 -
1894 - if ( "copyRange".equals(methodInsn.name) && methodInsn.desc.equals("(L" + MAPPED_OBJECT_JVM + ";I)V") ) {
1895 - i = replace(instructions, i, methodInsn, generateCopyRangeInstructions(mappedType));
1896 - break;
1897 - }
1898 -
1899 - break;
1900 - case INVOKESPECIAL:
1901 - // super() in VIEW_CONSTRUCTOR_NAME, remove
1902 - if ( methodInsn.owner.equals(MAPPED_OBJECT_JVM) && "<init>".equals(methodInsn.name) && "()V".equals(methodInsn.desc) ) {
1903 - instructions.remove(methodInsn.getPrevious()); // ALOAD
1904 - instructions.remove(methodInsn); // INVOKESPECIAL
1905 -
1906 - i -= 2;
1907 - }
1908 - break;
1909 - case INVOKESTATIC:
1910 - boolean isMapDirectMethod = "map".equals(methodInsn.name) && methodInsn.desc.equals("(JI)L" + MAPPED_OBJECT_JVM + ";");
1911 - boolean isMapBufferMethod = "map".equals(methodInsn.name) && methodInsn.desc.equals("(Ljava/nio/ByteBuffer;)L" + MAPPED_OBJECT_JVM + ";");
1912 - boolean isMallocMethod = "malloc".equals(methodInsn.name) && methodInsn.desc.equals("(I)L" + MAPPED_OBJECT_JVM + ";");
1913 -
1914 - if ( (isMapDirectMethod || isMapBufferMethod) || isMallocMethod )
1915 - i = replace(instructions, i, methodInsn, generateMapInstructions(mappedType, methodInsn.owner, isMapDirectMethod, isMallocMethod));
1916 - break;
1917 - }
1918 -
1919 - return i;
1920 - }
1921 -
1922 - private static InsnList generateCopyRangeInstructions(final MappedSubtypeInfo mappedType) {
1923 - final InsnList list = new InsnList();
1924 -
1925 - // stack: instances, target, this
1926 - list.add(getIntNode(mappedType.sizeof));
1927 - // stack: sizeof, instances, target, this
1928 - list.add(new InsnNode(IMUL));
1929 - // stack: bytes, target, this
1930 - list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "copy", "(L" + MAPPED_OBJECT_JVM + ";L" + MAPPED_OBJECT_JVM + ";I)V"));
1931 - // stack: -
1932 -
1933 - return list;
1934 - }
1935 -
1936 - private static InsnList generateCopyToInstructions(final MappedSubtypeInfo mappedType) {
1937 - final InsnList list = new InsnList();
1938 -
1939 - // stack: target, this
1940 - list.add(getIntNode(mappedType.sizeof - mappedType.padding));
1941 - // stack: sizeof, target, this
1942 - list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "copy", "(L" + MAPPED_OBJECT_JVM + ";L" + MAPPED_OBJECT_JVM + ";I)V"));
1943 - // stack: -
1944 -
1945 - return list;
1946 - }
1947 -
1948 - private static InsnList generateRunViewConstructorInstructions(final MethodInsnNode methodInsn) {
1949 - final InsnList list = new InsnList();
1950 -
1951 - // stack: this
1952 - list.add(new InsnNode(DUP));
1953 - // stack: this, this
1954 - list.add(new MethodInsnNode(INVOKEVIRTUAL, methodInsn.owner, VIEW_CONSTRUCTOR_NAME, "()V"));
1955 - // stack: this
1956 -
1957 - return list;
1958 - }
1959 -
1960 - private static InsnList generateSliceInstructions(final MethodInsnNode methodInsn) {
1961 - final InsnList list = new InsnList();
1962 -
1963 - // stack: this
1964 - list.add(new TypeInsnNode(NEW, methodInsn.owner));
1965 - // stack: new, this
1966 - list.add(new InsnNode(DUP));
1967 - // stack: new, new, this
1968 - list.add(new MethodInsnNode(INVOKESPECIAL, methodInsn.owner, "<init>", "()V"));
1969 - // stack: new, this
1970 - list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "slice", "(L" + MAPPED_OBJECT_JVM + ";L" + MAPPED_OBJECT_JVM + ";)L" + MAPPED_OBJECT_JVM + ";"));
1971 - // stack: new
1972 -
1973 - return list;
1974 - }
1975 -
1976 - private static InsnList generateDupInstructions(final MethodInsnNode methodInsn) {
1977 - final InsnList list = new InsnList();
1978 -
1979 - // stack: this
1980 - list.add(new TypeInsnNode(NEW, methodInsn.owner));
1981 - // stack: new, this
1982 - list.add(new InsnNode(DUP));
1983 - // stack: new, new, this
1984 - list.add(new MethodInsnNode(INVOKESPECIAL, methodInsn.owner, "<init>", "()V"));
1985 - // stack: new, this
1986 - list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "dup", "(L" + MAPPED_OBJECT_JVM + ";L" + MAPPED_OBJECT_JVM + ";)L" + MAPPED_OBJECT_JVM + ";"));
1987 - // stack: new
1988 -
1989 - return list;
1990 - }
1991 -
1992 - private static InsnList generateMapInstructions(final MappedSubtypeInfo mappedType, final String className, final boolean mapDirectMethod, final boolean mallocMethod) {
1993 - final InsnList trg = new InsnList();
1994 -
1995 - if ( mallocMethod ) {
1996 - // stack: count
1997 - trg.add(getIntNode(mappedType.sizeof));
1998 - // stack: sizeof, count
1999 - trg.add(new InsnNode(IMUL));
2000 - // stack: bytes
2001 - trg.add(new MethodInsnNode(INVOKESTATIC, mappedType.cacheLinePadded ? jvmClassName(CacheUtil.class) : jvmClassName(BufferUtils.class), "createByteBuffer", "(I)L" + jvmClassName(ByteBuffer.class) + ";"));
2002 - // stack: buffer
2003 - } else if ( mapDirectMethod ) {
2004 - // stack: capacity, address
2005 - trg.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "newBuffer", "(JI)L" + jvmClassName(ByteBuffer.class) + ";"));
2006 - // stack: buffer
2007 - }
2008 -
2009 - // stack: buffer
2010 - trg.add(new TypeInsnNode(NEW, className));
2011 - // stack: new, buffer
2012 - trg.add(new InsnNode(DUP));
2013 - // stack: new, new, buffer
2014 - trg.add(new MethodInsnNode(INVOKESPECIAL, className, "<init>", "()V"));
2015 - // stack: new, buffer
2016 - trg.add(new InsnNode(DUP_X1));
2017 - // stack: new, buffer, new
2018 - trg.add(new InsnNode(SWAP));
2019 - // stack: buffer, new, new
2020 - trg.add(getIntNode(mappedType.align));
2021 - // stack: int, buffer, new, new
2022 - trg.add(getIntNode(mappedType.sizeof));
2023 - // stack: int, int, buffer, new, new
2024 - trg.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "setup", "(L" + MAPPED_OBJECT_JVM + ";Ljava/nio/ByteBuffer;II)V"));
2025 - // stack: new
2026 -
2027 - return trg;
2028 - }
2029 -
2030 - static InsnList transformFieldAccess(final FieldInsnNode fieldInsn) {
2031 - final MappedSubtypeInfo mappedSubtype;
2032 - mappedSubtype = className_to_subtype.get(fieldInsn.owner);
2033 - if ( mappedSubtype == null ) { // early out
2034 - // MappedSet.view
2035 - outer:
2036 - if ( "view".equals(fieldInsn.name) && fieldInsn.owner.startsWith(MAPPEDSET_PREFIX) )
2037 - return generateSetViewInstructions(fieldInsn);
2038 -
2039 - return null; // early out
2040 - }
2041 -
2042 - if ( "SIZEOF".equals(fieldInsn.name) )
2043 - return generateSIZEOFInstructions(fieldInsn, mappedSubtype);
2044 -
2045 - if ( "view".equals(fieldInsn.name) )
2046 - return generateViewInstructions(fieldInsn, mappedSubtype);
2047 -
2048 - if ( "baseAddress".equals(fieldInsn.name) || "viewAddress".equals(fieldInsn.name) ) {
2049 - return generateAddressInstructions(fieldInsn);
2050 - }
2051 -
2052 - final FieldInfo field = mappedSubtype.fields.get(fieldInsn.name);
2053 - if ( field == null ) // early out
2054 - return null;
2055 -
2056 - // now we're going to transform ByteBuffer-typed field access
2057 - if ( fieldInsn.desc.equals("L" + jvmClassName(ByteBuffer.class) + ";") )
2058 - return generateByteBufferInstructions(fieldInsn, mappedSubtype, field.offset);
2059 -
2060 - // we're now going to transform the field access
2061 - return generateFieldInstructions(fieldInsn, field);
2062 - }
2063 -
2064 - private static InsnList generateSetViewInstructions(final FieldInsnNode fieldInsn) {
2065 - if ( fieldInsn.getOpcode() == GETFIELD )
2066 - throwAccessErrorOnReadOnlyField(fieldInsn.owner, fieldInsn.name);
2067 - if ( fieldInsn.getOpcode() != PUTFIELD )
2068 - throw new InternalError();
2069 -
2070 - final InsnList list = new InsnList();
2071 -
2072 - // stack: index, this
2073 - if ( MAPPED_SET2_JVM.equals(fieldInsn.owner) )
2074 - list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "put_views", "(L" + MAPPED_SET2_JVM + ";I)V"));
2075 - else if ( MAPPED_SET3_JVM.equals(fieldInsn.owner) )
2076 - list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "put_views", "(L" + MAPPED_SET3_JVM + ";I)V"));
2077 - else if ( MAPPED_SET4_JVM.equals(fieldInsn.owner) )
2078 - list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "put_views", "(L" + MAPPED_SET4_JVM + ";I)V"));
2079 - else
2080 - throw new InternalError();
2081 - // stack: -
2082 -
2083 - return list;
2084 - }
2085 -
2086 - private static InsnList generateSIZEOFInstructions(final FieldInsnNode fieldInsn, final MappedSubtypeInfo mappedSubtype) {
2087 - if ( !"I".equals(fieldInsn.desc) )
2088 - throw new InternalError();
2089 -
2090 - final InsnList list = new InsnList();
2091 -
2092 - if ( fieldInsn.getOpcode() == GETSTATIC ) {
2093 - list.add(getIntNode(mappedSubtype.sizeof));
2094 - return list;
2095 - }
2096 -
2097 - if ( fieldInsn.getOpcode() == PUTSTATIC )
2098 - throwAccessErrorOnReadOnlyField(fieldInsn.owner, fieldInsn.name);
2099 -
2100 - throw new InternalError();
2101 - }
2102 -
2103 - private static InsnList generateViewInstructions(final FieldInsnNode fieldInsn, final MappedSubtypeInfo mappedSubtype) {
2104 - if ( !"I".equals(fieldInsn.desc) )
2105 - throw new InternalError();
2106 -
2107 - final InsnList list = new InsnList();
2108 -
2109 - if ( fieldInsn.getOpcode() == GETFIELD ) {
2110 - if ( mappedSubtype.sizeof_shift != 0 ) {
2111 - // stack: instance
2112 - list.add(getIntNode(mappedSubtype.sizeof_shift));
2113 - // stack: sizeof, instance
2114 - list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "get_view_shift", "(L" + MAPPED_OBJECT_JVM + ";I)I"));
2115 - // stack: view
2116 - } else {
2117 - // stack: instance
2118 - list.add(getIntNode(mappedSubtype.sizeof));
2119 - // stack: sizeof, instance
2120 - list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "get_view", "(L" + MAPPED_OBJECT_JVM + ";I)I"));
2121 - // stack: view
2122 - }
2123 - return list;
2124 - }
2125 -
2126 - if ( fieldInsn.getOpcode() == PUTFIELD ) {
2127 - if ( mappedSubtype.sizeof_shift != 0 ) {
2128 - // stack: view, instance
2129 - list.add(getIntNode(mappedSubtype.sizeof_shift));
2130 - // stack: sizeof, view, instance
2131 - list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "put_view_shift", "(L" + MAPPED_OBJECT_JVM + ";II)V"));
2132 - // stack: -
2133 - } else {
2134 - // stack: view, instance
2135 - list.add(getIntNode(mappedSubtype.sizeof));
2136 - // stack: sizeof, view, instance
2137 - list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "put_view", "(L" + MAPPED_OBJECT_JVM + ";II)V"));
2138 - // stack: -
2139 - }
2140 - return list;
2141 - }
2142 -
2143 - throw new InternalError();
2144 - }
2145 -
2146 - private static InsnList generateAddressInstructions(final FieldInsnNode fieldInsn) {
2147 - if ( !"J".equals(fieldInsn.desc) )
2148 - throw new IllegalStateException();
2149 -
2150 - if ( fieldInsn.getOpcode() == GETFIELD ) // do not change a thing
2151 - return null;
2152 -
2153 - if ( fieldInsn.getOpcode() == PUTFIELD )
2154 - throwAccessErrorOnReadOnlyField(fieldInsn.owner, fieldInsn.name);
2155 -
2156 - throw new InternalError();
2157 - }
2158 -
2159 - private static InsnList generateByteBufferInstructions(final FieldInsnNode fieldInsn, final MappedSubtypeInfo mappedSubtype, final long fieldOffset) {
2160 - if ( fieldInsn.getOpcode() == PUTFIELD )
2161 - throwAccessErrorOnReadOnlyField(fieldInsn.owner, fieldInsn.name);
2162 -
2163 - if ( fieldInsn.getOpcode() == GETFIELD ) {
2164 - final InsnList list = new InsnList();
2165 -
2166 - // stack: ref
2167 - list.add(new FieldInsnNode(GETFIELD, mappedSubtype.className, "viewAddress", "J"));
2168 - // stack: long
2169 - list.add(new LdcInsnNode(fieldOffset));
2170 - // stack: long, long
2171 - list.add(new InsnNode(LADD));
2172 - // stack: long
2173 - list.add(new LdcInsnNode(mappedSubtype.fields.get(fieldInsn.name).length));
2174 - // stack: long, long
2175 - list.add(new InsnNode(L2I));
2176 - // stack: int, long
2177 - list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, "newBuffer", "(JI)L" + jvmClassName(ByteBuffer.class) + ";"));
2178 - // stack: buffer
2179 -
2180 - return list;
2181 - }
2182 -
2183 - throw new InternalError();
2184 - }
2185 -
2186 - private static InsnList generateFieldInstructions(final FieldInsnNode fieldInsn, final FieldInfo field) {
2187 - final InsnList list = new InsnList();
2188 -
2189 - if ( fieldInsn.getOpcode() == PUTFIELD ) {
2190 - // stack: value, ref
2191 - list.add(getIntNode((int)field.offset));
2192 - // stack: fieldOffset, value, ref
2193 - list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, field.getAccessType() + "put", "(L" + MAPPED_OBJECT_JVM + ";" + fieldInsn.desc + "I)V"));
2194 - // stack -
2195 - return list;
2196 - }
2197 -
2198 - if ( fieldInsn.getOpcode() == GETFIELD ) {
2199 - // stack: ref
2200 - list.add(getIntNode((int)field.offset));
2201 - // stack: fieldOffset, ref
2202 - list.add(new MethodInsnNode(INVOKESTATIC, MAPPED_HELPER_JVM, field.getAccessType() + "get", "(L" + MAPPED_OBJECT_JVM + ";I)" + fieldInsn.desc));
2203 - // stack: -
2204 - return list;
2205 - }
2206 -
2207 - throw new InternalError();
2208 - }
2209 -
2210 - static int transformArrayAccess(final InsnList instructions, int i, final Map<AbstractInsnNode, Frame<BasicValue>> frameMap, final VarInsnNode loadInsn, final MappedSubtypeInfo mappedSubtype, final int var) {
2211 - // We need to go forward in time to find how we use the array var
2212 - final int loadStackSize = frameMap.get(loadInsn).getStackSize() + 1;
2213 -
2214 - AbstractInsnNode nextInsn = loadInsn;
2215 -
2216 - while ( true ) {
2217 - nextInsn = nextInsn.getNext();
2218 - if ( nextInsn == null )
2219 - throw new InternalError();
2220 -
2221 - Frame<BasicValue> frame = frameMap.get(nextInsn);
2222 - if ( frame == null )
2223 - continue;
2224 -
2225 - int stackSize = frame.getStackSize();
2226 -
2227 - if ( stackSize == loadStackSize + 1 && nextInsn.getOpcode() == AALOAD ) {
2228 - final AbstractInsnNode aaLoadInsn = nextInsn;
2229 -
2230 - while ( true ) {
2231 - nextInsn = nextInsn.getNext();
2232 - if ( nextInsn == null )
2233 - break;
2234 -
2235 - frame = frameMap.get(nextInsn);
2236 - if ( frame == null )
2237 - continue;
2238 - stackSize = frame.getStackSize();
2239 -
2240 - if ( stackSize == loadStackSize + 1 && nextInsn.getOpcode() == PUTFIELD ) {
2241 - final FieldInsnNode fieldInsn = (FieldInsnNode)nextInsn;
2242 -
2243 - // stack: value, view, ref
2244 - instructions.insert(nextInsn, new MethodInsnNode(INVOKESTATIC, mappedSubtype.className, setterName(fieldInsn.name), "(L" + mappedSubtype.className + ";I" + fieldInsn.desc + ")V"));
2245 - // stack: -
2246 - instructions.remove(nextInsn);
2247 -
2248 - break;
2249 - } else if ( stackSize == loadStackSize && nextInsn.getOpcode() == GETFIELD ) {
2250 - final FieldInsnNode fieldInsn = (FieldInsnNode)nextInsn;
2251 -
2252 - // stack: view, ref
2253 - instructions.insert(nextInsn, new MethodInsnNode(INVOKESTATIC, mappedSubtype.className, getterName(fieldInsn.name), "(L" + mappedSubtype.className + ";I)" + fieldInsn.desc));
2254 - // stack: value
2255 - instructions.remove(nextInsn);
2256 -
2257 - break;
2258 - } else if ( stackSize == loadStackSize && nextInsn.getOpcode() == DUP && nextInsn.getNext().getOpcode() == GETFIELD ) {
2259 - // May happen with operator+assignment (e.g. cursor[i].value += 10)
2260 - final FieldInsnNode fieldInsn = (FieldInsnNode)nextInsn.getNext();
2261 -
2262 - final MethodInsnNode getter = new MethodInsnNode(INVOKESTATIC, mappedSubtype.className, getterName(fieldInsn.name), "(L" + mappedSubtype.className + ";I)" + fieldInsn.desc);
2263 -
2264 - // stack: view, ref
2265 - instructions.insert(nextInsn, new InsnNode(DUP2));
2266 - // stack: view, ref, view, ref
2267 - instructions.insert(nextInsn.getNext(), getter);
2268 - // stack: value, view, ref
2269 -
2270 - instructions.remove(nextInsn);
2271 - instructions.remove(fieldInsn);
2272 -
2273 - nextInsn = getter;
2274 - continue;
2275 - } else if ( stackSize < loadStackSize )
2276 - throw new ClassFormatError("Invalid " + mappedSubtype.className + " view array usage detected: " + getOpcodeName(nextInsn));
2277 - }
2278 -
2279 - instructions.remove(aaLoadInsn);
2280 -
2281 - return i;
2282 - } else if ( stackSize == loadStackSize && nextInsn.getOpcode() == ARRAYLENGTH ) {
2283 - if ( LWJGLUtil.DEBUG && loadInsn.getNext() != nextInsn )
2284 - throw new InternalError();
2285 -
2286 - instructions.remove(nextInsn);
2287 - loadInsn.var = var;
2288 - instructions.insert(loadInsn, new MethodInsnNode(INVOKEVIRTUAL, mappedSubtype.className, CAPACITY_METHOD_NAME, "()I"));
2289 -
2290 - return i + 1;
2291 - } else if ( stackSize < loadStackSize ) // Consumed by something other than AALOAD or ARRAYLENGTH
2292 - throw new ClassFormatError("Invalid " + mappedSubtype.className + " view array usage detected: " + getOpcodeName(nextInsn));
2293 - }
2294 - }
2295 -
2296 - private static class FieldInfo {
2297 -
2298 - final long offset;
2299 - final long length;
2300 - final long lengthPadded;
2301 - final Type type;
2302 - final boolean isVolatile;
2303 - final boolean isPointer;
2304 -
2305 - FieldInfo(final long offset, final long length, final long lengthPadded, final Type type, final boolean isVolatile, final boolean isPointer) {
2306 - this.offset = offset;
2307 - this.length = length;
2308 - this.lengthPadded = lengthPadded;
2309 - this.type = type;
2310 - this.isVolatile = isVolatile;
2311 - this.isPointer = isPointer;
2312 - }
2313 -
2314 - String getAccessType() {
2315 - return isPointer ? "a" : type.getDescriptor().toLowerCase() + (isVolatile ? "v" : "");
2316 - }
2317 -
2318 - }
2319 -
2320 - private static class MappedSubtypeInfo {
2321 -
2322 - final String className;
2323 -
2324 - final int sizeof;
2325 - final int sizeof_shift;
2326 - final int align;
2327 - final int padding;
2328 - final boolean cacheLinePadded;
2329 -
2330 - final Map<String, FieldInfo> fields;
2331 -
2332 - MappedSubtypeInfo(String className, Map<String, FieldInfo> fields, int sizeof, int align, int padding, final boolean cacheLinePadded) {
2333 - this.className = className;
2334 -
2335 - this.sizeof = sizeof;
2336 - if ( ((sizeof - 1) & sizeof) == 0 )
2337 - this.sizeof_shift = getPoT(sizeof);
2338 - else
2339 - this.sizeof_shift = 0;
2340 - this.align = align;
2341 - this.padding = padding;
2342 - this.cacheLinePadded = cacheLinePadded;
2343 -
2344 - this.fields = fields;
2345 - }
2346 -
2347 - private static int getPoT(int value) {
2348 - int pot = -1;
2349 - while ( value > 0 ) {
2350 - pot++;
2351 - value >>= 1;
2352 - }
2353 - return pot;
2354 - }
2355 -
2356 - }
2357 -
2358 - // -------------------------------------------------------
2359 - // -------------------[ MACROS & UTILS ]------------------
2360 - // -------------------------------------------------------
2361 -
2362 - private static void getClassEnums(final Class clazz, final Map<Integer, String> map, final String... prefixFilters) {
2363 - try {
2364 - OUTER:
2365 - for ( Field field : clazz.getFields() ) {
2366 - if ( !Modifier.isStatic(field.getModifiers()) || field.getType() != int.class )
2367 - continue;
2368 -
2369 - for ( String filter : prefixFilters ) {
2370 - if ( field.getName().startsWith(filter) )
2371 - continue OUTER;
2372 - }
2373 -
2374 - if ( map.put((Integer)field.get(null), field.getName()) != null )
2375 - throw new IllegalStateException();
2376 - }
2377 - } catch (Exception e) {
2378 - e.printStackTrace();
2379 - }
2380 - }
2381 -
2382 - static String getOpcodeName(final AbstractInsnNode insn) {
2383 - final String op = OPCODE_TO_NAME.get(insn.getOpcode());
2384 - return INSNTYPE_TO_NAME.get(insn.getType()) + ": " + insn.getOpcode() + (op == null ? "" : " [" + OPCODE_TO_NAME.get(insn.getOpcode()) + "]");
2385 - }
2386 -
2387 - static String jvmClassName(Class<?> type) {
2388 - return type.getName().replace('.', '/');
2389 - }
2390 -
2391 - static String getterName(final String fieldName) {
2392 - return "get$" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1) + "$LWJGL";
2393 - }
2394 -
2395 - static String setterName(final String fieldName) {
2396 - return "set$" + Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1) + "$LWJGL";
2397 - }
2398 -
2399 - private static void checkInsnAfterIsArray(final AbstractInsnNode instruction, final int opcode) {
2400 - if ( instruction == null )
2401 - throw new ClassFormatError("Unexpected end of instructions after .asArray() method.");
2402 -
2403 - if ( instruction.getOpcode() != opcode )
2404 - throw new ClassFormatError("The result of .asArray() must be stored to a local variable. Found: " + getOpcodeName(instruction));
2405 - }
2406 -
2407 - static AbstractInsnNode getIntNode(final int value) {
2408 - if ( value <= 5 && -1 <= value )
2409 - return new InsnNode(ICONST_M1 + value + 1);
2410 -
2411 - if ( value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE )
2412 - return new IntInsnNode(BIPUSH, value);
2413 -
2414 - if ( value >= Short.MIN_VALUE && value <= Short.MAX_VALUE )
2415 - return new IntInsnNode(SIPUSH, value);
2416 -
2417 - return new LdcInsnNode(value);
2418 - }
2419 -
2420 - static void visitIntNode(final MethodVisitor mv, final int value) {
2421 - if ( value <= 5 && -1 <= value )
2422 - mv.visitInsn(ICONST_M1 + value + 1);
2423 - else if ( value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE )
2424 - mv.visitIntInsn(BIPUSH, value);
2425 - else if ( value >= Short.MIN_VALUE && value <= Short.MAX_VALUE )
2426 - mv.visitIntInsn(SIPUSH, value);
2427 - else
2428 - mv.visitLdcInsn(value);
2429 - }
2430 -
2431 - /** Replace an instruction with a list of instructions. */
2432 - static int replace(final InsnList instructions, final int i, final AbstractInsnNode location, final InsnList list) {
2433 - final int size = list.size();
2434 -
2435 - instructions.insert(location, list);
2436 - instructions.remove(location);
2437 -
2438 - return i + (size - 1);
2439 - }
2440 -
2441 - private static void throwAccessErrorOnReadOnlyField(String className, String fieldName) {
2442 - throw new IllegalAccessError("The " + className + "." + fieldName + " field is final.");
2443 - }
2444 -
2445 - private static void printBytecode(byte[] bytecode) {
2446 - StringWriter sw = new StringWriter();
2447 - ClassVisitor tracer = new TraceClassVisitor(new ClassWriter(0), new PrintWriter(sw));
2448 - new ClassReader(bytecode).accept(tracer, 0);
2449 - String dump = sw.toString();
2450 -
2451 - LWJGLUtil.log(dump);
2452 - }
2453 -
2454 -}
2455 \ No newline at end of file
3 --- a/build.xml
4 +++ b/build.xml
5 @@ -476,8 +476,10 @@
6 <include name="org/lwjgl/opencl/**"/>
7 <include name="org/lwjgl/util/**"/>
8 <exclude name="org/lwjgl/util/generator/**"/>
9 + <exclude name="org/lwjgl/util/mapped/MappedObjectClassLoader.java"/>
10 + <exclude name="org/lwjgl/util/mapped/MappedObjectTransformer.java"/>
11 </javac>
12 - <javac debug="yes" srcdir="${lwjgl.src}/java/" destdir="${lwjgl.bin}" includes="org/lwjgl/test/**" source="1.5" target="1.5" taskname="test" />
13 + <javac debug="yes" srcdir="${lwjgl.src}/java/" destdir="${lwjgl.bin}" includes="org/lwjgl/test/**" source="1.5" target="1.5" taskname="test" excludes="**/TestMappedObject.java,**/SpriteShootoutMapped.java"/>
14 <javac debug="yes" srcdir="${lwjgl.src}/java/" destdir="${lwjgl.bin}" includes="org/lwjgl/examples/**" source="1.5" target="1.5" taskname="examples" />
15 </target>
16