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

playn.core.gl.GLContext Maven / Gradle / Ivy

/**
 * Copyright 2010 The PlayN Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
package playn.core.gl;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.List;

import pythagoras.i.Rectangle;

import playn.core.AbstractPlatform;
import playn.core.CanvasImage;
import playn.core.Image;
import playn.core.InternalTransform;
import playn.core.StockInternalTransform;

public abstract class GLContext {

  /** Used to configure texture image scaling. */
  public static enum Filter { LINEAR, NEAREST }

  /** Used to track and report rendering statistics. */
  public static class Stats {
    public int frames;

    public int shaderCreates;
    public int frameBufferCreates;
    public int texCreates;

    public int shaderBinds;
    public int frameBufferBinds;
    public int texBinds;

    public int quadsRendered;
    public int trisRendered;
    public int shaderFlushes;

    /** Resets all counters. */
    public void reset() {
      frames = 0;
      shaderCreates = 0;
      frameBufferCreates = 0;
      texCreates = 0;
      shaderBinds = 0;
      frameBufferBinds = 0;
      texBinds = 0;
      quadsRendered = 0;
      trisRendered = 0;
      shaderFlushes = 0;
    }
  }

  protected static final boolean STATS_ENABLED = true;
  protected final Stats stats = new Stats();

  protected final AbstractPlatform platform;
  private GLShader curShader;
  private int lastFramebuffer, epoch;
  private int pushedFramebuffer = -1, pushedWidth, pushedHeight;
  private List scissors = new ArrayList();
  private int scissorDepth;
  private Image fillImage;

  /** The (actual screen pixel) width and height of our default frame buffer. */
  protected int defaultFbufWidth, defaultFbufHeight;

  /** The (actual screen pixel) width and height of our current frame buffer. */
  protected int curFbufWidth, curFbufHeight;

  /** The (logical pixel) width and height of our view. */
  public int viewWidth, viewHeight;

  /** The scale factor for HiDPI mode, or 1 if HDPI mode is not enabled. */
  public final Scale scale;

  /**
   * Sets the frame buffer to the specified width and height (in pixels). The view will potentially
   * be smaller than this size if a HiDPI scale factor is in effect.
   */
  public final void setSize(int pixelWidth, int pixelHeight) {
    viewWidth = scale.invScaledFloor(pixelWidth);
    viewHeight = scale.invScaledFloor(pixelHeight);
    curFbufWidth = defaultFbufWidth = pixelWidth;
    curFbufHeight = defaultFbufHeight = pixelHeight;
    viewConfigChanged();
  }

  /**
   * Configures the filter function used when rendering scaled textures.
   *
   * @param minFilter the scaling to use when rendering textures that are scaled down.
   * @param magFilter the scaling to use when rendering textures that are scaled up.
   */
  public abstract void setTextureFilter(Filter minFilter, Filter magFilter);

  /** Returns the specified GL string parameter. */
  public abstract String getString(int param);

  /** Returns the specified GL integer parameter. */
  public abstract int getInteger(int param);

  /** Returns the specified GL float parameter. */
  public abstract float getFloat(int param);

  /** Returns the specified GL boolean parameter. */
  public abstract boolean getBoolean(int param);

  /**
   * See http://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexImage2D.xml
   *
   * 

The default implementation is based on {@link Image#getRgb} and will hand over an RGBA byte * array. Please set the (internal)format and type parameters accordingly; they are mainly * present for future support of different formats. The WebGL implementation will pass through * all parameters.

*/ public void texImage2D(Image image, int target, int level, int internalformat, int format, int type) { throw new UnsupportedOperationException(); } /** * See http://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexSubImage2D.xml * *

The default implementation is based on {@link Image#getRgb} and will hand over a RGBA byte * array. Please set the (internal)format and type parameters accordingly; they are mainly * present for future support of different formats. The WebGL implementation will pass through * all parameters.

*/ public void texSubImage2D(Image image, int target, int level, int xOffset, int yOffset, int format, int type) { throw new UnsupportedOperationException(); } /** * Creates a shader program, for use by a single {@link GLShader}. * @param vertShader the source code for the vertex shader. * @param fragShader the source code for the fragment shader. */ public abstract GLProgram createProgram(String vertShader, String fragShader); /** * Creates a float buffer with the specified initial capacity. */ public abstract GLBuffer.Float createFloatBuffer(int capacity); /** * Creates a short buffer with the specified initial capacity. */ public abstract GLBuffer.Short createShortBuffer(int capacity); /** Creates a framebuffer that will render into the supplied texture. NOTE: this must be * followed immediately by a call to {@link #bindFramebuffer(int,int,int)} or {@link * #pushFramebuffer}. */ public int createFramebuffer(int tex) { flush(true); // flush any pending rendering calls, because createFramebufferImpl (necessarily) // binds the new framebuffer in order to bind it to the specified texture (meh) return createFramebufferImpl(tex); } /** Deletes the supplied frame buffer (which will have come from {@link #createFramebuffer}). */ public abstract void deleteFramebuffer(int fbuf); /** Creates a texture with the specified repeat behavior. */ public abstract int createTexture(boolean repeatX, boolean repeatY, boolean mipmaps); /** Creates a texture of the specified size, with the specified repeat behavior, into which we * can subsequently render. */ public abstract int createTexture(int width, int height, boolean repeatX, boolean repeatY, boolean mipmaps); /** Generates mipmaps for the specified texture. */ public abstract void generateMipmap(int tex); /** Activates the specified texture unit. * @param glTextureN the texture unit to active (e.g. {@link GL20#GL_TEXTURE0}). */ public abstract void activeTexture(int glTextureN); /** Binds the specified texture. */ public abstract void bindTexture(int tex); /** Destroys the supplied texture. */ public abstract void destroyTexture(int tex); /** Starts a series of drawing commands that are clipped to the specified rectangle (in view * coordinates, not OpenGL coordinates). Thus must be followed by a call to {@link #endClipped} * when the clipped drawing commands are done. * @return whether the resulting clip rectangle is not empty */ public abstract boolean startClipped(int x, int y, int width, int height); /** Ends a series of drawing commands that were clipped per a call to {@link #startClipped}. */ public abstract void endClipped(); /** Clears the bound framebuffer with the specified color. */ public abstract void clear(float r, float g, float b, float a); /** NOOP except when debugging, checks and logs whether any GL errors have occurred. */ public abstract void checkGLError(String op); /** Queues a texture to be destroyed on the GL thread. */ public void queueDestroyTexture(final int tex) { platform.invokeLater(new Runnable() { public void run() { destroyTexture(tex); } }); } /** Queues a framebuffer to be destroyed on the GL thread. */ public void queueDeleteFramebuffer(final int fbuf) { platform.invokeLater(new Runnable() { public void run() { deleteFramebuffer(fbuf); } }); } /** Queues a custom shader to be cleaned up on the GL thread. */ public void queueClearShader(final GLShader shader) { platform.invokeLater(new Runnable() { public void run() { shader.clearProgram(); } }); } /** Creates an identity transform, which may subsequently be mutated. */ public InternalTransform createTransform() { return new StockInternalTransform(); } /** Returns the root transform which converts scale-independent coordinates into pixels. On some * platforms this may also handle screen rotation. Do not modify! */ public abstract InternalTransform rootTransform(); public void bindFramebuffer(int fbuf, int width, int height) { if (fbuf != lastFramebuffer) { flush(true); // flush and deactivate any shader rendering to the old framebuffer checkGLError("bindFramebuffer"); bindFramebufferImpl(lastFramebuffer = fbuf, curFbufWidth = width, curFbufHeight = height); } } public void bindFramebuffer() { bindFramebuffer(defaultFramebuffer(), defaultFbufWidth, defaultFbufHeight); } /** Stores the metadata for the currently bound frame buffer, and binds the supplied framebuffer. * This must be followed by a call to {@link #popFramebuffer}. Also, it is not allowed to push a * framebuffer if a framebuffer is already pushed. Only one level of nesting is supported. */ public void pushFramebuffer(int fbuf, int width, int height) { assert pushedFramebuffer == -1 : "Already have a pushed framebuffer"; pushedFramebuffer = lastFramebuffer; pushedWidth = curFbufWidth; pushedHeight = curFbufHeight; bindFramebuffer(fbuf, width, height); } /** Pops the framebuffer pushed by a previous call to {@link #pushFramebuffer} and restores the * framebuffer that was active prior to that call. */ public void popFramebuffer() { assert pushedFramebuffer != -1 : "Have no pushed framebuffer"; bindFramebuffer(pushedFramebuffer, pushedWidth, pushedHeight); pushedFramebuffer = -1; } /** Returns the supplied shader if non-null, or the default quad shader if null. */ public GLShader quadShader (GLShader custom) { return custom == null ? quadShader() : custom; } /** Returns the supplied shader if non-null, or the default triangles shader if null. */ public GLShader trisShader (GLShader custom) { return custom == null ? trisShader() : custom; } public void flush() { flush(false); } public void flush(boolean deactivate) { if (curShader != null) { checkGLError("flush()"); curShader.flush(); if (deactivate) { curShader.deactivate(); curShader = null; } } } /** * Makes the supplied shader the current shader, flushing any previous shader. */ public boolean useShader(GLShader shader) { if (curShader == shader) return false; checkGLError("useShader"); flush(true); curShader = shader; return true; } /** * Returns the current rendering stats. These will be all zeros unless the library was compiled * with stats enabled (which is not the default). */ public Stats stats() { return stats; } /** * Returns debugging info on the quad shader. Useful for performance analysis. */ public String quadShaderInfo() { return String.valueOf(quadShader()); } /** * Returns debugging info on the triangles shader. Useful for performance analysis. */ public String trisShaderInfo() { return String.valueOf(trisShader()); } /** * Returns a (created on demand, then cached) image used when filling solid color quads or * triangles. */ Image fillImage() { if (fillImage == null) { CanvasImage image = platform.graphics().createImage(1, 1); image.canvas().setFillColor(0xFFFFFFFF).fillRect(0, 0, image.width(), image.height()); fillImage = image; } return fillImage; } /** * Adds the given rectangle to the scissors stack, intersecting with the previous one if it * exists. Intended for use by subclasses to implement {@link #startClipped} and {@link * #endClipped}. * *

NOTE: calls to this method must be matched by a corresponding call {@link * #popScissorState}, or all hell will break loose.

* * @return the new clipping rectangle to use */ protected Rectangle pushScissorState (int x, int y, int width, int height) { // grow the scissors buffer if necessary if (scissorDepth == scissors.size()) { scissors.add(new Rectangle()); } Rectangle r = scissors.get(scissorDepth); if (scissorDepth == 0) { r.setBounds(x, y, width, height); } else { // intersect current with previous Rectangle pr = scissors.get(scissorDepth - 1); r.setLocation(Math.max(pr.x, x), Math.max(pr.y, y)); r.setSize(Math.min(pr.maxX(), x + width - 1) - r.x, Math.min(pr.maxY(), y + height - 1) - r.y); } scissorDepth++; return r; } /** * Removes the most recently pushed scissor state and returns the rectangle that should now * be used for clipping, or null if clipping should be disabled. */ protected Rectangle popScissorState () { scissorDepth--; return scissorDepth == 0 ? null : scissors.get(scissorDepth - 1); } /** * Returns the current scissor stack size. Zero means no scissors are currently pushed. */ protected int getScissorDepth () { return scissorDepth; } protected GLContext(AbstractPlatform platform, float scaleFactor) { this.scale = new Scale(scaleFactor); this.platform = platform; } protected void viewConfigChanged () { bindFramebuffer(); } /** * Increments our GL context epoch. This should be called by platform backends when the GL * context has been lost and a new one created. */ protected void incrementEpoch () { ++epoch; } /** * Returns the current GL context epoch. This is used to invalidate shaders when we lose and * regain our GL context. */ protected int epoch () { return epoch; } /** * Returns the default framebuffer. */ protected abstract int defaultFramebuffer(); /** * Creates a framebuffer that will render into the supplied texture. */ protected abstract int createFramebufferImpl(int tex); /** * Binds the specified framebuffer and sets the viewport to the specified dimensions. */ protected abstract void bindFramebufferImpl(int fbuf, int width, int height); protected boolean shouldTryQuadShader() { return QuadShader.isLikelyToPerform(this); } protected GLShader createQuadShader() { if (shouldTryQuadShader()) { try { GLShader quadShader = new QuadShader(this); quadShader.createCore(); // force core creation to test whether it fails return quadShader; } catch (Throwable t) { platform.reportError("Failed to create QuadShader", t); } } return new IndexedTrisShader(this); } // used by GLContext.tex(Sub)Image2D impls protected static ByteBuffer getRgba(Image image) { int w = (int) image.width(), h = (int) image.height(), size = w * h; int[] rawPixels = new int[size]; ByteBuffer pixels = ByteBuffer.allocateDirect(size * 4); pixels.order(ByteOrder.nativeOrder()); IntBuffer rgba = pixels.asIntBuffer(); image.getRgb(0, 0, w, h, rawPixels, 0, w); for (int i = 0; i < size; i++) { int argb = rawPixels[i]; // Order is inverted because this is read as a byte array, and we store intel ints. rgba.put(i, ((argb >> 16) & 0x0ff) | (argb & 0x0ff00ff00) | ((argb & 0xff) << 16)); } return pixels; } protected abstract GLShader quadShader(); protected abstract GLShader trisShader(); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy