package org.lwjgl.util.stream; import org.lwjgl.opengl.ContextCapabilities; import org.lwjgl.opengl.GLContext; import org.lwjgl.opengl.GLSync; import sun.misc.Unsafe; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.List; import static org.lwjgl.opengl.EXTFramebufferBlit.glBlitFramebufferEXT; import static org.lwjgl.opengl.EXTFramebufferMultisample.glRenderbufferStorageMultisampleEXT; import static org.lwjgl.opengl.EXTFramebufferObject.*; import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL11.glGetInteger; import static org.lwjgl.opengl.GL12.GL_BGRA; import static org.lwjgl.opengl.GL12.GL_UNSIGNED_INT_8_8_8_8_REV; import static org.lwjgl.opengl.GL30.*; import static org.lwjgl.opengl.GL32.*; /** @author Spasi */ public final class StreamUtil { private static final int TEX_ROW_ALIGNMENT = 16 * 4; // 16 pixels private StreamUtil() { } static int createRenderTexture(final int width, final int height) { return createRenderTexture(width, height, GL_NEAREST); } static int createRenderTexture(final int width, final int height, final int filter) { final int texID = glGenTextures(); glBindTexture(GL_TEXTURE_2D, texID); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, (ByteBuffer)null); glBindTexture(GL_TEXTURE_2D, 0); return texID; } static int createRenderBuffer(final FBOUtil fboUtil, final int width, final int height, final int internalformat) { return createRenderBuffer(fboUtil, width, height, 1, internalformat); } static int createRenderBuffer(final FBOUtil fboUtil, final int width, final int height, final int samples, final int internalformat) { final int bufferID = fboUtil.genRenderbuffers(); fboUtil.bindRenderbuffer(GL_RENDERBUFFER, bufferID); if ( samples <= 1 ) fboUtil.renderbufferStorage(GL_RENDERBUFFER, internalformat, width, height); else fboUtil.renderbufferStorageMultisample(GL_RENDERBUFFER, samples, internalformat, width, height); fboUtil.bindRenderbuffer(GL_RENDERBUFFER, 0); return bufferID; } static void waitOnFence(final GLSync[] fences, final int index) { glWaitSync(fences[index], 0, GL_TIMEOUT_IGNORED); glDeleteSync(fences[index]); fences[index] = null; } static boolean isAMD(final ContextCapabilities caps) { return caps.GL_ATI_fragment_shader || caps.GL_ATI_texture_compression_3dc || caps.GL_AMD_debug_output; } static boolean isNVIDIA(final ContextCapabilities caps) { return caps.GL_NV_vertex_program || caps.GL_NV_register_combiners || caps.GL_NV_gpu_program4; } static int getStride(final int width) { // Force a packed format on AMD. Their drivers show unstable // performance if we mess with (UN)PACK_ROW_LENGTH. return isAMD(GLContext.getCapabilities()) ? width * 4 : getStride(width, TEX_ROW_ALIGNMENT); } /** * Aligns the row stride. This is beneficial for all the memcpy's we're doing (and also required for INTEL_map_texture). * * @param width the row width in pixels * @param aligment the row aligment in bytes. Must be a power-of-two value. * * @return the aligned row stride */ static int getStride(final int width, final int aligment) { int stride = width * 4; if ( (stride & (aligment - 1)) != 0 ) stride += aligment - (stride & (aligment - 1)); return stride; } private static void checkCapabilities(final ContextCapabilities caps) { if ( !caps.OpenGL15 ) throw new UnsupportedOperationException("Support for OpenGL 1.5 or higher is required."); if ( !(caps.OpenGL20 || caps.GL_ARB_texture_non_power_of_two) ) throw new UnsupportedOperationException("Support for npot textures is required."); if ( !(caps.OpenGL30 || caps.GL_ARB_framebuffer_object || caps.GL_EXT_framebuffer_object) ) throw new UnsupportedOperationException("Framebuffer object support is required."); } public static RenderStreamFactory getRenderStreamImplementation() { final List<RenderStreamFactory> list = getRenderStreamImplementations(); if ( list.isEmpty() ) throw new UnsupportedOperationException("A supported TextureStream implementation could not be found."); return list.get(0); } public static List<RenderStreamFactory> getRenderStreamImplementations() { final ContextCapabilities caps = GLContext.getCapabilities(); checkCapabilities(caps); final List<RenderStreamFactory> list = new ArrayList<RenderStreamFactory>(); addIfSupported(caps, list, RenderStreamPBOAMD.FACTORY); addIfSupported(caps, list, RenderStreamPBOCopy.FACTORY); addIfSupported(caps, list, RenderStreamINTEL.FACTORY); addIfSupported(caps, list, RenderStreamPBODefault.FACTORY); return list; } public static TextureStreamFactory getTextureStreamImplementation() { final List<TextureStreamFactory> list = getTextureStreamImplementations(); if ( list.isEmpty() ) throw new UnsupportedOperationException("A supported TextureStream implementation could not be found."); return list.get(0); } public static List<TextureStreamFactory> getTextureStreamImplementations() { final ContextCapabilities caps = GLContext.getCapabilities(); checkCapabilities(caps); final List<TextureStreamFactory> list = new ArrayList<TextureStreamFactory>(); addIfSupported(caps, list, TextureStreamINTEL.FACTORY); addIfSupported(caps, list, TextureStreamPBORange.FACTORY); addIfSupported(caps, list, TextureStreamPBODefault.FACTORY); return list; } private static <T extends StreamFactory<?>> void addIfSupported(final ContextCapabilities caps, final List<T> list, final T factory) { if ( factory.isSupported(caps) ) list.add(factory); } public abstract static class StreamFactory<S> { private final String description; protected StreamFactory(final String description) { this.description = description; } public String getDescription() { return description; } public abstract boolean isSupported(ContextCapabilities caps); public String toString() { return description; } } public abstract static class RenderStreamFactory extends StreamFactory<RenderStream> { protected RenderStreamFactory(final String description) { super(description); } public abstract RenderStream create(StreamHandler handler, int samples, int transfersToBuffer); } public abstract static class TextureStreamFactory extends StreamFactory<TextureStream> { protected TextureStreamFactory(final String description) { super(description); } public abstract TextureStream create(StreamHandler handler, int transfersToBuffer); } static int checkSamples(final int samples, final ContextCapabilities caps) { if ( samples <= 1 ) return samples; if ( !(caps.OpenGL30 || (caps.GL_EXT_framebuffer_multisample && caps.GL_EXT_framebuffer_blit)) ) throw new UnsupportedOperationException("Multisampled rendering on framebuffer objects is not supported."); return Math.min(samples, glGetInteger(GL_MAX_SAMPLES)); } static final class PageSizeProvider { static final int PAGE_SIZE; static { int pageSize = 4096; // Assume 4kb if Unsafe is not available try { pageSize = getUnsafeInstance().pageSize(); } catch (Exception e) { // ignore } PAGE_SIZE = pageSize; } private static Unsafe getUnsafeInstance() { final Field[] fields = Unsafe.class.getDeclaredFields(); /* Different runtimes use different names for the Unsafe singleton, so we cannot use .getDeclaredField and we scan instead. For example: Oracle: theUnsafe PERC : m_unsafe_instance Android: THE_ONE */ for ( Field field : fields ) { if ( !field.getType().equals(Unsafe.class) ) continue; final int modifiers = field.getModifiers(); if ( !(Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) ) continue; field.setAccessible(true); try { return (Unsafe)field.get(null); } catch (IllegalAccessException e) { // ignore } break; } throw new UnsupportedOperationException(); } } interface FBOUtil { int genFramebuffers(); void bindFramebuffer(int target, int framebuffer); void framebufferTexture2D(int target, int attachment, int textarget, int texture, int level); void framebufferRenderbuffer(int target, int attachment, int renderbuffertarget, int renderbuffer); void deleteFramebuffers(int framebuffer); int genRenderbuffers(); void bindRenderbuffer(int target, int renderbuffer); void renderbufferStorage(int target, int internalformat, int width, int height); void renderbufferStorageMultisample(int target, int samples, int internalformat, int width, int height); void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter); void deleteRenderbuffers(int renderbuffer); } static FBOUtil getFBOUtil(final ContextCapabilities caps) { if ( caps.OpenGL30 || caps.GL_ARB_framebuffer_object ) return new FBOUtil() { public int genFramebuffers() { return glGenFramebuffers(); } public void bindFramebuffer(int target, int framebuffer) { glBindFramebuffer(target, framebuffer); } public void framebufferTexture2D(int target, int attachment, int textarget, int texture, int level) { glFramebufferTexture2D(target, attachment, textarget, texture, level); } public void framebufferRenderbuffer(int target, int attachment, int renderbuffertarget, int renderbuffer) { glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); } public void deleteFramebuffers(int framebuffer) { glDeleteFramebuffers(framebuffer); } public int genRenderbuffers() { return glGenRenderbuffers(); } public void bindRenderbuffer(int target, int renderbuffer) { glBindRenderbuffer(target, renderbuffer); } public void renderbufferStorage(int target, int internalformat, int width, int height) { glRenderbufferStorage(target, internalformat, width, height); } public void renderbufferStorageMultisample(int target, int samples, int internalformat, int width, int height) { glRenderbufferStorageMultisample(target, samples, internalformat, width, height); } public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) { glBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); } public void deleteRenderbuffers(int renderbuffer) { glDeleteRenderbuffers(renderbuffer); } }; else if ( caps.GL_EXT_framebuffer_object ) return new FBOUtil() { public int genFramebuffers() { return glGenFramebuffersEXT(); } public void bindFramebuffer(int target, int framebuffer) { glBindFramebufferEXT(target, framebuffer); } public void framebufferTexture2D(int target, int attachment, int textarget, int texture, int level) { glFramebufferTexture2DEXT(target, attachment, textarget, texture, level); } public void framebufferRenderbuffer(int target, int attachment, int renderbuffertarget, int renderbuffer) { glFramebufferRenderbufferEXT(target, attachment, renderbuffertarget, renderbuffer); } public void deleteFramebuffers(int framebuffer) { glDeleteFramebuffersEXT(framebuffer); } public int genRenderbuffers() { return glGenRenderbuffersEXT(); } public void bindRenderbuffer(int target, int renderbuffer) { glBindRenderbufferEXT(target, renderbuffer); } public void renderbufferStorage(int target, int internalformat, int width, int height) { glRenderbufferStorageEXT(target, internalformat, width, height); } public void renderbufferStorageMultisample(int target, int samples, int internalformat, int width, int height) { glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height); } public void blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, int mask, int filter) { glBlitFramebufferEXT(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); } public void deleteRenderbuffers(int renderbuffer) { glDeleteRenderbuffersEXT(renderbuffer); } }; else throw new UnsupportedOperationException("Framebuffer object is not available."); } }