parent
9a3dce2fb3
commit
44899098e2
@ -0,0 +1,185 @@ |
||||
//This is a port of java.util.Random to OpenCL |
||||
|
||||
//Because not all devices support doubles, the double returning functions |
||||
//must be explicit activated with the following preprocessor macro: |
||||
//#define RANDOM_DOUBLES |
||||
|
||||
#ifdef RANDOM_DOUBLES |
||||
#ifdef cl_khr_fp64 |
||||
#pragma OPENCL EXTENSION cl_khr_fp64 : enable |
||||
#elif defined(cl_amd_fp64) |
||||
#pragma OPENCL EXTENSION cl_amd_fp64 : enable |
||||
#else |
||||
#error "Double precision floating point not supported by OpenCL implementation." |
||||
#endif |
||||
#endif |
||||
|
||||
inline int randNext(int bits, __global ulong* seed) |
||||
{ |
||||
*seed = (*seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1); |
||||
return (int)(*seed >> (48 - bits)); |
||||
} |
||||
|
||||
/** |
||||
* Retrieves the next random integer value. |
||||
* The buffer used as seed must be read-write. |
||||
* Usage: |
||||
* <code> |
||||
* __kernel void TestRandom(__global ulong* seeds) { |
||||
* // ... |
||||
* int i = randInt(seeds + get_global_id(0)); |
||||
* // --- |
||||
* } |
||||
* </code> |
||||
*/ |
||||
inline int randInt(__global ulong* seed) { |
||||
return randNext(32, seed); |
||||
} |
||||
|
||||
/** |
||||
* Retrieves the next random integer value between 0 (inclusive) and n (exclusive). |
||||
* The buffer used as seed must be read-write. |
||||
* Usage: |
||||
* <code> |
||||
* __kernel void TestRandom(__global ulong* seeds) { |
||||
* // ... |
||||
* int i = randIntN(n, seeds + get_global_id(0)); |
||||
* // --- |
||||
* } |
||||
* </code> |
||||
*/ |
||||
inline int randIntN(int n, __global ulong* seed) { |
||||
if (n <= 0) |
||||
return 0; |
||||
|
||||
if ((n & -n) == n) // i.e., n is a power of 2 |
||||
return (int)((n * (long)randNext(31, seed)) >> 31); |
||||
|
||||
int bits, val; |
||||
do { |
||||
bits = randNext(31, seed); |
||||
val = bits % n; |
||||
} while (bits - val + (n-1) < 0); |
||||
return val; |
||||
} |
||||
|
||||
/** |
||||
* Retrieves the next random long value. |
||||
* The buffer used as seed must be read-write. |
||||
* Usage: |
||||
* <code> |
||||
* __kernel void TestRandom(__global ulong* seeds) { |
||||
* // ... |
||||
* long l = randLong(seeds + get_global_id(0)); |
||||
* // --- |
||||
* } |
||||
* </code> |
||||
*/ |
||||
inline long randLong(__global ulong* seed) { |
||||
// it's okay that the bottom word remains signed. |
||||
return ((long)(randNext(32, seed)) << 32) + randNext(32, seed); |
||||
} |
||||
|
||||
/** |
||||
* Retrieves the next random boolean value. |
||||
* The buffer used as seed must be read-write. |
||||
* Usage: |
||||
* <code> |
||||
* __kernel void TestRandom(__global ulong* seeds) { |
||||
* // ... |
||||
* bool b = randBool(seeds + get_global_id(0)); |
||||
* // --- |
||||
* } |
||||
* </code> |
||||
*/ |
||||
inline bool randBool(__global ulong* seed) { |
||||
return randNext(1, seed) != 0; |
||||
} |
||||
|
||||
#ifdef RANDOM_DOUBLES |
||||
/** |
||||
* Retrieves the next random double value. |
||||
* The buffer used as seed must be read-write. |
||||
* To use this function, the preprocessor define RANDOM_DOUBLES must be set. |
||||
* Usage: |
||||
* <code> |
||||
* __kernel void TestRandom(__global ulong* seeds) { |
||||
* // ... |
||||
* double d = randDouble(seeds + get_global_id(0)); |
||||
* // --- |
||||
* } |
||||
* </code> |
||||
*/ |
||||
inline double randDouble(__global ulong* seed) { |
||||
return (((long)(randNext(26, seed)) << 27) + randNext(27, seed)) |
||||
/ (double)(1L << 53); |
||||
} |
||||
#endif |
||||
|
||||
/** |
||||
* Retrieves the next random float value. |
||||
* The buffer used as seed must be read-write. |
||||
* Usage: |
||||
* <code> |
||||
* __kernel void TestRandom(__global ulong* seeds) { |
||||
* // ... |
||||
* float f = randFloat(seeds + get_global_id(0)); |
||||
* // --- |
||||
* } |
||||
* </code> |
||||
*/ |
||||
inline float randFloat(__global ulong* seed) |
||||
{ |
||||
return randNext(24, seed) / ((float)(1 << 24)); |
||||
} |
||||
|
||||
/** |
||||
* Retrieves the next random float values with a gaussian distribution of mean 0 |
||||
* and derivation 1. |
||||
* The buffer used as seed must be read-write. |
||||
* Usage: |
||||
* <code> |
||||
* __kernel void TestRandom(__global ulong* seeds) { |
||||
* // ... |
||||
* float2 f2 = randGausianf(seeds + get_global_id(0)); |
||||
* // --- |
||||
* } |
||||
* </code> |
||||
*/ |
||||
inline float2 randGaussianf(__global ulong* seed) { |
||||
float v1, v2, s; |
||||
do { |
||||
v1 = 2 * randFloat(seed) - 1; // between -1 and 1 |
||||
v2 = 2 * randFloat(seed) - 1; // between -1 and 1 |
||||
s = v1 * v1 + v2 * v2; |
||||
} while (s >= 1 || s == 0); |
||||
float multiplier = sqrt(-2 * log(s)/s); |
||||
return (float2) (v1 * multiplier, v2 * multiplier); |
||||
} |
||||
|
||||
#ifdef RANDOM_DOUBLES |
||||
/** |
||||
* Retrieves the next random double values with a gaussian distribution of mean 0 |
||||
* and derivation 1. |
||||
* The buffer used as seed must be read-write. |
||||
* To use this function, the preprocessor define RANDOM_DOUBLES must be set. |
||||
* Usage: |
||||
* <code> |
||||
* __kernel void TestRandom(__global ulong* seeds) { |
||||
* // ... |
||||
* double2 f2 = randGausian(seeds + get_global_id(0)); |
||||
* // --- |
||||
* } |
||||
* </code> |
||||
*/ |
||||
inline double2 randGaussian(__global ulong* seed) { |
||||
double v1, v2, s; |
||||
do { |
||||
v1 = 2 * randDouble(seed) - 1; // between -1 and 1 |
||||
v2 = 2 * randDouble(seed) - 1; // between -1 and 1 |
||||
s = v1 * v1 + v2 * v2; |
||||
} while (s >= 1 || s == 0); |
||||
double multiplier = sqrt(-2 * log(s)/s); |
||||
return (double2) (v1 * multiplier, v2 * multiplier); |
||||
} |
||||
#endif |
@ -0,0 +1,257 @@ |
||||
/* |
||||
* Copyright (c) 2009-2012 jMonkeyEngine |
||||
* All rights reserved. |
||||
* |
||||
* Redistribution and use in source and binary forms, with or without |
||||
* modification, are permitted provided that the following conditions are |
||||
* met: |
||||
* |
||||
* * Redistributions of source code must retain the above copyright |
||||
* notice, this list of conditions and the following disclaimer. |
||||
* |
||||
* * Redistributions in binary form must reproduce the above copyright |
||||
* notice, this list of conditions and the following disclaimer in the |
||||
* documentation and/or other materials provided with the distribution. |
||||
* |
||||
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||
* may be used to endorse or promote products derived from this software |
||||
* without specific prior written permission. |
||||
* |
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
*/ |
||||
|
||||
package jme3test.opencl; |
||||
|
||||
import com.jme3.app.SimpleApplication; |
||||
import com.jme3.font.BitmapFont; |
||||
import com.jme3.font.BitmapText; |
||||
import com.jme3.math.ColorRGBA; |
||||
import com.jme3.opencl.*; |
||||
import com.jme3.system.AppSettings; |
||||
import com.jme3.util.BufferUtils; |
||||
import java.nio.*; |
||||
import java.util.Arrays; |
||||
import java.util.Objects; |
||||
import java.util.Random; |
||||
import java.util.logging.Level; |
||||
import java.util.logging.Logger; |
||||
|
||||
/** |
||||
* Test class for the build in libraries |
||||
* @author shaman |
||||
*/ |
||||
public class TestOpenCLLibraries extends SimpleApplication { |
||||
private static final Logger LOG = Logger.getLogger(TestOpenCLLibraries.class.getName()); |
||||
|
||||
public static void main(String[] args){ |
||||
TestOpenCLLibraries app = new TestOpenCLLibraries(); |
||||
AppSettings settings = new AppSettings(true); |
||||
settings.setOpenCLSupport(true); |
||||
settings.setVSync(true); |
||||
// settings.setRenderer(AppSettings.JOGL_OPENGL_FORWARD_COMPATIBLE);
|
||||
app.setSettings(settings); |
||||
app.start(); // start the game
|
||||
} |
||||
|
||||
@Override |
||||
public void simpleInitApp() { |
||||
BitmapFont fnt = assetManager.loadFont("Interface/Fonts/Default.fnt"); |
||||
Context clContext = context.getOpenCLContext(); |
||||
if (clContext == null) { |
||||
BitmapText txt = new BitmapText(fnt); |
||||
txt.setText("No OpenCL Context created!\nSee output log for details."); |
||||
txt.setLocalTranslation(5, settings.getHeight() - 5, 0); |
||||
guiNode.attachChild(txt); |
||||
return; |
||||
} |
||||
CommandQueue clQueue = clContext.createQueue(clContext.getDevices().get(0)); |
||||
|
||||
StringBuilder str = new StringBuilder(); |
||||
str.append("OpenCL Context created:\n Platform: ") |
||||
.append(clContext.getDevices().get(0).getPlatform().getName()) |
||||
.append("\n Devices: ").append(clContext.getDevices()); |
||||
str.append("\nTests:"); |
||||
str.append("\n Random numbers: ").append(testRandom(clContext, clQueue)); |
||||
|
||||
clQueue.release(); |
||||
|
||||
BitmapText txt1 = new BitmapText(fnt); |
||||
txt1.setText(str.toString()); |
||||
txt1.setLocalTranslation(5, settings.getHeight() - 5, 0); |
||||
guiNode.attachChild(txt1); |
||||
|
||||
flyCam.setEnabled(false); |
||||
inputManager.setCursorVisible(true); |
||||
} |
||||
|
||||
private static void assertEquals(byte expected, byte actual, String message) { |
||||
if (expected != actual) { |
||||
System.err.println(message+": expected="+expected+", actual="+actual); |
||||
throw new AssertionError(); |
||||
} |
||||
} |
||||
private static void assertEquals(long expected, long actual, String message) { |
||||
if (expected != actual) { |
||||
System.err.println(message+": expected="+expected+", actual="+actual); |
||||
throw new AssertionError(); |
||||
} |
||||
} |
||||
private static void assertEquals(double expected, double actual, String message) { |
||||
if (Math.abs(expected - actual) >= 0.00001) { |
||||
System.err.println(message+": expected="+expected+", actual="+actual); |
||||
throw new AssertionError(); |
||||
} |
||||
} |
||||
private static void assertEquals(Object expected, Object actual, String message) { |
||||
if (!Objects.equals(expected, actual)) { |
||||
System.err.println(message+": expected="+expected+", actual="+actual); |
||||
throw new AssertionError(); |
||||
} |
||||
} |
||||
|
||||
private boolean testRandom(Context clContext, CommandQueue clQueue) { |
||||
try { |
||||
//test for doubles
|
||||
boolean supportsDoubles = clContext.getDevices().get(0).hasDouble(); |
||||
|
||||
//create code
|
||||
String code = "" |
||||
+ "#import \"Common/OpenCL/Random.clh\"\n" |
||||
+ "__kernel void TestBool(__global ulong* seeds, __global uchar* results) {\n" |
||||
+ " results[get_global_id(0)] = randBool(seeds + get_global_id(0)) ? 1 : 0;\n" |
||||
+ "}\n" |
||||
+ "__kernel void TestInt(__global ulong* seeds, __global int* results) {\n" |
||||
+ " results[get_global_id(0)] = randInt(seeds + get_global_id(0));\n" |
||||
+ "}\n" |
||||
+ "__kernel void TestIntN(__global ulong* seeds, int n, __global int* results) {\n" |
||||
+ " results[get_global_id(0)] = randIntN(n, seeds + get_global_id(0));\n" |
||||
+ "}\n" |
||||
+ "__kernel void TestLong(__global ulong* seeds, __global long* results) {\n" |
||||
+ " results[get_global_id(0)] = randLong(seeds + get_global_id(0));\n" |
||||
+ "}\n" |
||||
+ "__kernel void TestFloat(__global ulong* seeds, __global float* results) {\n" |
||||
+ " results[get_global_id(0)] = randFloat(seeds + get_global_id(0));\n" |
||||
+ "}\n" |
||||
+ "#ifdef RANDOM_DOUBLES\n" |
||||
+ "__kernel void TestDouble(__global ulong* seeds, __global double* results) {\n" |
||||
+ " results[get_global_id(0)] = randDouble(seeds + get_global_id(0));\n" |
||||
+ "}\n" |
||||
+ "#endif\n"; |
||||
if (supportsDoubles) { |
||||
code = "#define RANDOM_DOUBLES\n" + code; |
||||
} |
||||
Program program = clContext.createProgramFromSourceCodeWithDependencies(code, assetManager); |
||||
program.build(); |
||||
|
||||
int count = 256; |
||||
Kernel.WorkSize ws = new Kernel.WorkSize(count); |
||||
|
||||
//create seeds
|
||||
Random initRandom = new Random(); |
||||
long[] seeds = new long[count]; |
||||
Random[] randoms = new Random[count]; |
||||
for (int i=0; i<count; ++i) { |
||||
seeds[i] = initRandom.nextLong(); |
||||
randoms[i] = new Random(seeds[i]); |
||||
seeds[i] = (seeds[i] ^ 0x5DEECE66DL) & ((1L << 48) - 1); //needed because the Random constructor scrambles the initial seed
|
||||
} |
||||
com.jme3.opencl.Buffer seedsBuffer = clContext.createBuffer(8 * count); |
||||
ByteBuffer tmpByteBuffer = BufferUtils.createByteBuffer(8 * count); |
||||
tmpByteBuffer.asLongBuffer().put(seeds); |
||||
seedsBuffer.write(clQueue, tmpByteBuffer); |
||||
|
||||
//test it
|
||||
ByteBuffer resultByteBuffer = BufferUtils.createByteBuffer(8 * count); |
||||
IntBuffer resultIntBuffer = resultByteBuffer.asIntBuffer(); |
||||
LongBuffer resultLongBuffer = resultByteBuffer.asLongBuffer(); |
||||
FloatBuffer resultFloatBuffer = resultByteBuffer.asFloatBuffer(); |
||||
DoubleBuffer resultDoubleBuffer = resultByteBuffer.asDoubleBuffer(); |
||||
com.jme3.opencl.Buffer resultBuffer = clContext.createBuffer(8 * count); |
||||
//boolean
|
||||
Kernel testBoolKernel = program.createKernel("TestBool"); |
||||
testBoolKernel.Run1NoEvent(clQueue, ws, seedsBuffer, resultBuffer); |
||||
resultByteBuffer.rewind(); |
||||
resultBuffer.read(clQueue, resultByteBuffer); |
||||
for (int i=0; i<count; ++i) { |
||||
assertEquals(randoms[i].nextBoolean() ? 1 : 0, resultByteBuffer.get(i), "randBool at i="+i); |
||||
} |
||||
testBoolKernel.release(); |
||||
//int
|
||||
Kernel testIntKernel = program.createKernel("TestInt"); |
||||
testIntKernel.Run1NoEvent(clQueue, ws, seedsBuffer, resultBuffer); |
||||
resultByteBuffer.rewind(); |
||||
resultBuffer.read(clQueue, resultByteBuffer); |
||||
for (int i=0; i<count; ++i) { |
||||
assertEquals(randoms[i].nextInt(), resultIntBuffer.get(i), "randInt at i="+i); |
||||
} |
||||
testIntKernel.release(); |
||||
//int n
|
||||
Kernel testIntNKernel = program.createKernel("TestIntN"); |
||||
testIntNKernel.Run1NoEvent(clQueue, ws, seedsBuffer, 186, resultBuffer); |
||||
resultByteBuffer.rewind(); |
||||
resultBuffer.read(clQueue, resultByteBuffer); |
||||
for (int i=0; i<count; ++i) { |
||||
assertEquals(randoms[i].nextInt(186), resultIntBuffer.get(i), "randInt at i="+i+" with n="+186); |
||||
} |
||||
testIntNKernel.Run1NoEvent(clQueue, ws, seedsBuffer, 97357, resultBuffer); |
||||
resultByteBuffer.rewind(); |
||||
resultBuffer.read(clQueue, resultByteBuffer); |
||||
for (int i=0; i<count; ++i) { |
||||
assertEquals(randoms[i].nextInt(97357), resultIntBuffer.get(i), "randInt at i="+i+" with n="+97357); |
||||
} |
||||
testIntNKernel.release(); |
||||
//long
|
||||
Kernel testLongKernel = program.createKernel("TestLong"); |
||||
testLongKernel.Run1NoEvent(clQueue, ws, seedsBuffer, resultBuffer); |
||||
resultByteBuffer.rewind(); |
||||
resultBuffer.read(clQueue, resultByteBuffer); |
||||
for (int i=0; i<count; ++i) { |
||||
assertEquals(randoms[i].nextLong(), resultLongBuffer.get(i), "randLong at i="+i); |
||||
} |
||||
testLongKernel.release(); |
||||
//float
|
||||
Kernel testFloatKernel = program.createKernel("TestFloat"); |
||||
testFloatKernel.Run1NoEvent(clQueue, ws, seedsBuffer, resultBuffer); |
||||
resultByteBuffer.rewind(); |
||||
resultBuffer.read(clQueue, resultByteBuffer); |
||||
for (int i=0; i<count; ++i) { |
||||
assertEquals(randoms[i].nextFloat(), resultFloatBuffer.get(i), "randFloat at i="+i); |
||||
} |
||||
testFloatKernel.release(); |
||||
//double
|
||||
if (supportsDoubles) { |
||||
Kernel testDoubleKernel = program.createKernel("TestDouble"); |
||||
testDoubleKernel.Run1NoEvent(clQueue, ws, seedsBuffer, resultBuffer); |
||||
resultByteBuffer.rewind(); |
||||
resultBuffer.read(clQueue, resultByteBuffer); |
||||
for (int i=0; i<count; ++i) { |
||||
assertEquals(randoms[i].nextDouble(), resultDoubleBuffer.get(i), "randLong at i="+i); |
||||
} |
||||
testDoubleKernel.release(); |
||||
} |
||||
|
||||
seedsBuffer.release(); |
||||
resultBuffer.release(); |
||||
program.release(); |
||||
|
||||
} catch (AssertionError ex) { |
||||
LOG.log(Level.SEVERE, "kernel test failed with an assertion error"); |
||||
return false; |
||||
} catch (Exception ex) { |
||||
LOG.log(Level.SEVERE, "kernel test failed with:", ex); |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue