All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jme3.opencl.Context Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2009-2021 jMonkeyEngine
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * * Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the distribution.
 *
 * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
 *   may be used to endorse or promote products derived from this software
 *   without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.jme3.opencl;

import com.jme3.asset.AssetInfo;
import com.jme3.asset.AssetKey;
import com.jme3.asset.AssetManager;
import com.jme3.asset.AssetNotFoundException;
import com.jme3.opencl.Image.ImageDescriptor;
import com.jme3.opencl.Image.ImageFormat;
import com.jme3.opencl.Image.ImageType;
import com.jme3.scene.VertexBuffer;
import com.jme3.texture.FrameBuffer;
import com.jme3.texture.Texture;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * The central OpenCL context. Every action starts from here.
 * The context can be obtained by {@link com.jme3.system.JmeContext#getOpenCLContext() }.
 * 

* The context is used to: *

    *
  • Query the available devices
  • *
  • Create a command queue
  • *
  • Create buffers and images
  • *
  • Created buffers and images shared with OpenGL vertex buffers, textures and renderbuffers
  • *
  • Create program objects from source code and source files
  • *
* * @author shaman */ public abstract class Context extends AbstractOpenCLObject { private static final Logger LOG = Logger.getLogger(Context.class.getName()); protected Context(ObjectReleaser releaser) { super(releaser); } @Override public Context register() { super.register(); return this; } /** * Returns all available devices for this context. * These devices all belong to the same {@link Platform}. * They are used to create a command queue sending commands to a particular * device, see {@link #createQueue(com.jme3.opencl.Device) }. * Also, device capabilities, like the supported OpenCL version, extensions, * memory size and so on, are queried over the Device instances. *
* The available devices were specified by a {@link PlatformChooser}. * * @return a list of devices */ public abstract List getDevices(); /** * Alternative version of {@link #createQueue(com.jme3.opencl.Device) }, * just uses the first device returned by {@link #getDevices() }. * * @return the command queue */ public CommandQueue createQueue() { return createQueue(getDevices().get(0)); } /** * Creates a command queue sending commands to the specified device. * The device must be an entry of {@link #getDevices() }. * * @param device the target device * @return the command queue */ public abstract CommandQueue createQueue(Device device); /** * Allocates a new buffer of the specific size and access type on the device. * * @param size the size of the buffer in bytes * @param access the allowed access of this buffer from kernel code * @return the new buffer */ public abstract Buffer createBuffer(long size, MemoryAccess access); /** * Alternative version of {@link #createBuffer(long, com.jme3.opencl.MemoryAccess) }, * creates a buffer with read and write access. * * @param size the size of the buffer in bytes * @return the new buffer */ public Buffer createBuffer(long size) { return createBuffer(size, MemoryAccess.READ_WRITE); } /** * Creates a new buffer wrapping the specific host memory. This host memory * specified by a ByteBuffer can then be used directly by kernel code, * although the access might be slower than with native buffers * created by {@link #createBuffer(long, com.jme3.opencl.MemoryAccess) }. * * @param data the host buffer to use * @param access the allowed access of this buffer from kernel code * @return the new buffer */ public abstract Buffer createBufferFromHost(ByteBuffer data, MemoryAccess access); /** * Alternative version of {@link #createBufferFromHost(java.nio.ByteBuffer, com.jme3.opencl.MemoryAccess) }, * creates a buffer with read and write access. * * @param data the host buffer to use * @return the new buffer */ public Buffer createBufferFromHost(ByteBuffer data) { return createBufferFromHost(data, MemoryAccess.READ_WRITE); } /** * Creates a new 1D, 2D, 3D image.
* {@code ImageFormat} specifies the element type and order, like RGBA of floats.
* {@code ImageDescriptor} specifies the dimension of the image.
* Furthermore, a ByteBuffer can be specified in the ImageDescriptor together * with row and slice pitches. This buffer is then used to store the image. * If no ByteBuffer is specified, a new buffer is allocated (this is the * normal behaviour). * * @param access the allowed access of this image from kernel code * @param format the image format * @param descr the image descriptor * @return the new image object */ public abstract Image createImage(MemoryAccess access, ImageFormat format, ImageDescriptor descr); //TODO: add simplified methods for 1D, 2D, 3D textures /** * Queries all supported image formats for a specified memory access and * image type. *
* Note that the returned array may contain {@code ImageFormat} objects * where {@code ImageChannelType} or {@code ImageChannelOrder} are {@code null} * (or both). This is the case when the device supports new formats that * are not included in this wrapper yet. * * @param access the memory access type * @param type the image type (1D, 2D, 3D, ...) * @return an array of all supported image formats */ public abstract ImageFormat[] querySupportedFormats(MemoryAccess access, ImageType type); //Interop /** * Creates a shared buffer from a VertexBuffer. * The returned buffer and the vertex buffer operate on the same memory, * changes in one view are visible in the other view. * This can be used to modify meshes directly from OpenCL (e.g. for particle systems). *
* Note: The vertex buffer must already been uploaded to the GPU, * i.e. it must be used at least once for drawing. *

* Before the returned buffer can be used, it must be acquired explicitly * by {@link Buffer#acquireBufferForSharingAsync(com.jme3.opencl.CommandQueue) } * and after modifying it, released by {@link Buffer#releaseBufferForSharingAsync(com.jme3.opencl.CommandQueue) }. * This is needed so that OpenGL and OpenCL operations do not interfere with each other. * * @param vb the vertex buffer to share * @param access the memory access for the kernel * @return the new buffer */ public abstract Buffer bindVertexBuffer(VertexBuffer vb, MemoryAccess access); /** * Creates a shared image object from a jME3-image. * The returned image shares the same memory with the jME3-image, changes * in one view are visible in the other view. * This can be used to modify textures and images directly from OpenCL * (e.g. for post-processing effects and other texture effects). *
* Note: The image must already been uploaded to the GPU, * i.e. it must be used at least once for drawing. *

* Before the returned image can be used, it must be acquired explicitly * by {@link Image#acquireImageForSharingAsync(com.jme3.opencl.CommandQueue) } * and after modifying it, released by {@link Image#releaseImageForSharingAsync(com.jme3.opencl.CommandQueue) } * This is needed so that OpenGL and OpenCL operations do not interfere with each other. * * @param image the jME3 image object * @param textureType the texture type (1D, 2D, 3D), since this is not stored in the image * @param miplevel the mipmap level that should be shared * @param access the allowed memory access for kernels * @return the OpenCL image */ public abstract Image bindImage(com.jme3.texture.Image image, Texture.Type textureType, int miplevel, MemoryAccess access); /** * Creates a shared image object from a jME3 texture. * The returned image shares the same memory with the jME3 texture, changes * in one view are visible in the other view. * This can be used to modify textures and images directly from OpenCL * (e.g. for post-processing effects and other texture effects). *
* Note: The image must already been uploaded to the GPU, * i.e. it must be used at least once for drawing. *

* Before the returned image can be used, it must be acquired explicitly * by {@link Image#acquireImageForSharingAsync(com.jme3.opencl.CommandQueue) } * and after modifying it, released by {@link Image#releaseImageForSharingAsync(com.jme3.opencl.CommandQueue) } * This is needed so that OpenGL and OpenCL operations do not interfere with each other. *

* This method is equivalent to calling * {@code bindImage(texture.getImage(), texture.getType(), miplevel, access)}. * * @param texture the jME3 texture * @param miplevel the mipmap level that should be shared * @param access the allowed memory access for kernels * @return the OpenCL image */ public Image bindImage(Texture texture, int miplevel, MemoryAccess access) { return bindImage(texture.getImage(), texture.getType(), miplevel, access); } /** * Alternative version to {@link #bindImage(com.jme3.texture.Texture, int, com.jme3.opencl.MemoryAccess) }, * uses {@code miplevel=0}. * * @param texture the jME3 texture * @param access the allowed memory access for kernels * @return the OpenCL image */ public Image bindImage(Texture texture, MemoryAccess access) { return bindImage(texture, 0, access); } /** * Creates a shared image object from a jME3 render buffer. * The returned image shares the same memory with the jME3 render buffer, changes * in one view are visible in the other view. *
* This can be used as an alternative to post-processing effects * (e.g. reduce sum operations, needed e.g. for tone mapping). *
* Note: The renderbuffer must already been uploaded to the GPU, * i.e. it must be used at least once for drawing. *

* Before the returned image can be used, it must be acquired explicitly * by {@link Image#acquireImageForSharingAsync(com.jme3.opencl.CommandQueue) } * and after modifying it, released by {@link Image#releaseImageForSharingAsync(com.jme3.opencl.CommandQueue) } * This is needed so that OpenGL and OpenCL operations do not interfere with each other. * * @param buffer the buffer to bind * @param access the kernel access permissions * @return an image */ public Image bindRenderBuffer(FrameBuffer.RenderBuffer buffer, MemoryAccess access) { if (buffer.getTexture() == null) { return bindPureRenderBuffer(buffer, access); } else { return bindImage(buffer.getTexture(), access); } } protected abstract Image bindPureRenderBuffer(FrameBuffer.RenderBuffer buffer, MemoryAccess access); /** * Creates a program object from the provided source code. * The program still needs to be compiled using {@link Program#build() }. * * @param sourceCode the source code * @return the program object */ public abstract Program createProgramFromSourceCode(String sourceCode); /** * Resolves dependencies (using {@code #include } in the source code) * and delegates the combined source code to * {@link #createProgramFromSourceCode(java.lang.String) }. * Important: only absolute paths are allowed. * * @param sourceCode the original source code * @param assetManager the asset manager to load the files * @return the created program object * @throws AssetNotFoundException if a dependency could not be loaded */ public Program createProgramFromSourceCodeWithDependencies(String sourceCode, AssetManager assetManager) { StringBuilder builder = new StringBuilder(sourceCode.length()); BufferedReader reader = new BufferedReader(new StringReader(sourceCode)); try { buildSourcesRec(reader, builder, assetManager); } catch (IOException ex) { throw new AssetNotFoundException("Unable to read a dependency file", ex); } return createProgramFromSourceCode(builder.toString()); } private void buildSourcesRec(BufferedReader reader, StringBuilder builder, AssetManager assetManager) throws IOException { String ln; while ((ln = reader.readLine()) != null) { if (ln.trim().startsWith("#import ")) { ln = ln.trim().substring(8).trim(); if (ln.startsWith("\"")) { ln = ln.substring(1); } if (ln.endsWith("\"")) { ln = ln.substring(0, ln.length() - 1); } AssetInfo info = assetManager.locateAsset(new AssetKey(ln)); if (info == null) { throw new AssetNotFoundException("Unable to load source file \"" + ln + "\""); } try (BufferedReader r = new BufferedReader(new InputStreamReader(info.openStream()))) { builder.append("//-- begin import ").append(ln).append(" --\n"); buildSourcesRec(r, builder, assetManager); builder.append("//-- end import ").append(ln).append(" --\n"); } } else { builder.append(ln).append('\n'); } } } /** * Creates a program object from the provided source code and files. * The source code is made up from the specified include string first, * then all files specified by the resource array (array of asset paths) * are loaded by the provided asset manager and appended to the source code. *

* The typical use case is: *

    *
  • The include string contains some compiler constants like the grid size
  • *
  • Some common OpenCL files used as libraries (Convention: file names end with {@code .clh}
  • *
  • One main OpenCL file containing the actual kernels (Convention: file name ends with {@code .cl})
  • *
* * After the files were combined, additional include statements are resolved * by {@link #createProgramFromSourceCodeWithDependencies(java.lang.String, com.jme3.asset.AssetManager) }. * * @param assetManager the asset manager used to load the files * @param include an additional include string * @param resources an array of asset paths pointing to OpenCL source files * @return the new program objects * @throws AssetNotFoundException if a file could not be loaded */ public Program createProgramFromSourceFilesWithInclude(AssetManager assetManager, String include, String... resources) { return createProgramFromSourceFilesWithInclude(assetManager, include, Arrays.asList(resources)); } /** * Creates a program object from the provided source code and files. * The source code is made up from the specified include string first, * then all files specified by the resource array (array of asset paths) * are loaded by the provided asset manager and appended to the source code. *

* The typical use case is: *

    *
  • The include string contains some compiler constants like the grid size
  • *
  • Some common OpenCL files used as libraries (Convention: file names end with {@code .clh}
  • *
  • One main OpenCL file containing the actual kernels (Convention: file name ends with {@code .cl})
  • *
* * After the files were combined, additional include statements are resolved * by {@link #createProgramFromSourceCodeWithDependencies(java.lang.String, com.jme3.asset.AssetManager) }. * * @param assetManager the asset manager used to load the files * @param include an additional include string * @param resources an array of asset paths pointing to OpenCL source files * @return the new program objects * @throws AssetNotFoundException if a file could not be loaded */ public Program createProgramFromSourceFilesWithInclude(AssetManager assetManager, String include, List resources) { StringBuilder str = new StringBuilder(); str.append(include); for (String res : resources) { AssetInfo info = assetManager.locateAsset(new AssetKey(res)); if (info == null) { throw new AssetNotFoundException("Unable to load source file \"" + res + "\""); } try (BufferedReader reader = new BufferedReader(new InputStreamReader(info.openStream()))) { while (true) { String line = reader.readLine(); if (line == null) { break; } str.append(line).append('\n'); } } catch (IOException ex) { LOG.log(Level.WARNING, "unable to load source file '" + res + "'", ex); } } return createProgramFromSourceCodeWithDependencies(str.toString(), assetManager); } /** * Alternative version of {@link #createProgramFromSourceFilesWithInclude(com.jme3.asset.AssetManager, java.lang.String, java.lang.String...) } * with an empty include string * * @param assetManager for loading assets * @param resources asset paths pointing to OpenCL source files * @return a new instance * @throws AssetNotFoundException if a file could not be loaded */ public Program createProgramFromSourceFiles(AssetManager assetManager, String... resources) { return createProgramFromSourceFilesWithInclude(assetManager, "", resources); } /** * Alternative version of {@link #createProgramFromSourceFilesWithInclude(com.jme3.asset.AssetManager, java.lang.String, java.util.List) } * with an empty include string * * @param assetManager for loading assets * @param resources a list of asset paths pointing to OpenCL source files * @return a new instance * @throws AssetNotFoundException if a file could not be loaded */ public Program createProgramFromSourceFiles(AssetManager assetManager, List resources) { return createProgramFromSourceFilesWithInclude(assetManager, "", resources); } /** * Creates a program from the specified binaries. * The binaries are created by {@link Program#getBinary(com.jme3.opencl.Device) }. * The returned program still needs to be build using * {@link Program#build(java.lang.String, com.jme3.opencl.Device...) }. * Important:The device passed to {@code Program.getBinary(..)}, * this method and {@code Program#build(..)} must be the same. * * The binaries are used to build a program cache across multiple launches * of the application. The programs build much faster from binaries than * from sources. * * @param binaries the binaries * @param device the device to use * @return the new program */ public abstract Program createProgramFromBinary(ByteBuffer binaries, Device device); @Override public String toString() { return "Context (" + getDevices() + ')'; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy