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

com.sun.prism.es2.ES2Shader Maven / Gradle / Ivy

There is a newer version: 24-ea+15
Show newest version
/*
 * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package com.sun.prism.es2;

import com.sun.prism.impl.BaseGraphicsResource;
import com.sun.prism.impl.Disposer;
import com.sun.prism.ps.Shader;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

/**
 * Represents an OpenGL shader program object, which can be constructed from
 * the source code for a vertex shader, a fragment shader, or both.
 * Contains convenience methods for enabling/disabling shader state.
 * 

* Usage example: *

 *     String source =
 *         "uniform sampler2D myTex;" +
 *         "void main(void)" +
 *         "{" +
 *         "    vec4 src = texture2D(myTex, gl_TexCoord[0].st);" +
 *         "    gl_FragColor = src.bgra;" + // swizzle!
 *         "}";
 *     ES2Shader shader = new ES2Shader(source);
 *     shader.setConstant("myTex", 0); // myTex will be on texture unit 0
 *     ...
 *     shader.enable();
 *     texture.enable();
 *     texture.bind();
 *     ...
 *     texture.disable();
 *     shader.disable();
 * };
 * 
*/ public class ES2Shader extends BaseGraphicsResource implements Shader { private static class Uniform { private int location; private Object values; } /** * The handle to the OpenGL shader program object. */ private int programID; private final ES2Context context; private final Map uniforms = new HashMap<>(); private final int maxTexCoordIndex; private final boolean isPixcoordUsed; private boolean valid; private float[] currentMatrix; private ES2Shader(ES2Context context, int programID, int vertexShaderID, int[] fragmentShaderID, Map samplers, int maxTexCoordIndex, boolean isPixcoordUsed) throws RuntimeException { super(new ES2ShaderDisposerRecord(context, vertexShaderID, fragmentShaderID, programID)); this.context = context; this.programID = programID; this.maxTexCoordIndex = maxTexCoordIndex; this.isPixcoordUsed = isPixcoordUsed; this.valid = (programID != 0); if (valid && samplers != null) { // save/restore the current program (creating an ES2Shader // should not affect context state) int currentProgram = context.getShaderProgram(); context.setShaderProgram(programID); for (String key : samplers.keySet()) { setConstant(key, samplers.get(key)); } context.setShaderProgram(currentProgram); } } static ES2Shader createFromSource(ES2Context context, String vert, String[] frag, Map samplers, Map attributes, int maxTexCoordIndex, boolean isPixcoordUsed) { GLContext glCtx = context.getGLContext(); if (!glCtx.isShaderCompilerSupported()) { throw new RuntimeException("Shader compiler not available on this device"); } if (vert == null || frag == null || frag.length == 0) { throw new RuntimeException( "Both vertexShaderSource and fragmentShaderSource " + "must be specified"); } int vertexShaderID = glCtx.compileShader(vert, true); if (vertexShaderID == 0) { throw new RuntimeException("Error creating vertex shader"); } int[] fragmentShaderID = new int[frag.length]; for (int i = 0; i < frag.length; i++) { fragmentShaderID[i] = glCtx.compileShader(frag[i], false); if (fragmentShaderID[i] == 0) { glCtx.deleteShader(vertexShaderID); //TODO: delete any fragment shaders already created throw new RuntimeException("Error creating fragment shader"); } } String[] attrs = new String[attributes.size()]; int[] indexs = new int[attrs.length]; int i = 0; for (String attr : attributes.keySet()) { attrs[i] = attr; indexs[i] = attributes.get(attr); i++; } int programID = glCtx.createProgram(vertexShaderID, fragmentShaderID, attrs, indexs); if (programID == 0) { // createProgram() will have already detached/deleted // vertexShader and fragmentShader resources throw new RuntimeException("Error creating shader program"); } return new ES2Shader(context, programID, vertexShaderID, fragmentShaderID, samplers, maxTexCoordIndex, isPixcoordUsed); } static ES2Shader createFromSource(ES2Context context, String vert, InputStream frag, Map samplers, Map attributes, int maxTexCoordIndex, boolean isPixcoordUsed) { String[] fragmentShaderSource = new String[] {readStreamIntoString(frag)}; return createFromSource(context, vert, fragmentShaderSource, samplers, attributes, maxTexCoordIndex, isPixcoordUsed); } static String readStreamIntoString(InputStream in) { StringBuffer sb = new StringBuffer(1024); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); try { char[] chars = new char[1024]; int numRead = 0; while ((numRead = reader.read(chars)) > -1) { sb.append(String.valueOf(chars, 0, numRead)); } } catch (IOException e) { throw new RuntimeException("Error reading shader stream"); } finally { try { reader.close(); } catch (IOException e) { throw new RuntimeException("Error closing reader"); } } return sb.toString(); } /** * Returns the underlying OpenGL program object handle for this fragment * shader. Most applications will not need to access this, since it is * handled automatically by the enable() and dispose() methods. * * @return the OpenGL program object handle for this fragment shader */ public int getProgramObject() { return programID; } /** * Returns the maximum texcoord index referenced by this shader program. * * @return the maximum texcoord index referenced by this shader program */ public int getMaxTexCoordIndex() { return maxTexCoordIndex; } /** * Returns true if this shader uses the special pixcoord variable, * otherwise returns false * * @return true if this shader uses the special pixcoord variable */ public boolean isPixcoordUsed() { return isPixcoordUsed; } private Uniform getUniform(String name) { Uniform uniform = uniforms.get(name); if (uniform == null) { // cache native uniform locations in a hashmap for quicker access int loc = context.getGLContext().getUniformLocation(programID, name); uniform = new Uniform(); uniform.location = loc; uniforms.put(name, uniform); } return uniform; } /** * Enables this shader program in the current GL context's state. * * @throws RuntimeException if no OpenGL context was current or if any * OpenGL-related errors occurred */ @Override public void enable() throws RuntimeException { context.updateShaderProgram(programID); } /** * Disables this shader program in the current GL context's state. * * @throws RuntimeException if no OpenGL context was current or if any * OpenGL-related errors occurred */ @Override public void disable() throws RuntimeException { // TODO: remove disable() method from Shader interface... (RT-27442) context.updateShaderProgram(0); } @Override public boolean isValid() { return valid; } /** * Sets the uniform variable of the given name with the provided * integer value. * * @param name the name of the uniform variable to be set * @param i0 the first uniform parameter * @throws RuntimeException if no OpenGL context was current or if any * OpenGL-related errors occurred */ @Override public void setConstant(String name, int i0) throws RuntimeException { Uniform uniform = getUniform(name); if (uniform.location == -1) { return; } if (uniform.values == null) { uniform.values = new int[1]; } int[] values = (int[]) uniform.values; if (values[0] != i0) { values[0] = i0; context.getGLContext().uniform1i(uniform.location, i0); } } /** * Sets the uniform variable of the given name with the provided * integer values. * * @param name the name of the uniform variable to be set * @param i0 the first uniform parameter * @param i1 the second uniform parameter * @throws RuntimeException if no OpenGL context was current or if any * OpenGL-related errors occurred */ @Override public void setConstant(String name, int i0, int i1) throws RuntimeException { Uniform uniform = getUniform(name); if (uniform.location == -1) { return; } if (uniform.values == null) { uniform.values = new int[2]; } int[] values = (int[]) uniform.values; if (values[0] != i0 || values[1] != i1) { values[0] = i0; values[1] = i1; context.getGLContext().uniform2i(uniform.location, i0, i1); } } /** * Sets the uniform variable of the given name with the provided * integer values. * * @param name the name of the uniform variable to be set * @param i0 the first uniform parameter * @param i1 the second uniform parameter * @param i2 the third uniform parameter * @throws RuntimeException if no OpenGL context was current or if any * OpenGL-related errors occurred */ @Override public void setConstant(String name, int i0, int i1, int i2) throws RuntimeException { Uniform uniform = getUniform(name); if (uniform.location == -1) { return; } if (uniform.values == null) { uniform.values = new int[3]; } int[] values = (int[]) uniform.values; if (values[0] != i0 || values[1] != i1 || values[2] != i2) { values[0] = i0; values[1] = i1; values[2] = i2; context.getGLContext().uniform3i(uniform.location, i0, i1, i2); } } /** * Sets the uniform variable of the given name with the provided * integer values. * * @param name the name of the uniform variable to be set * @param i0 the first uniform parameter * @param i1 the second uniform parameter * @param i2 the third uniform parameter * @param i3 the fourth uniform parameter * @throws RuntimeException if no OpenGL context was current or if any * OpenGL-related errors occurred */ @Override public void setConstant(String name, int i0, int i1, int i2, int i3) throws RuntimeException { Uniform uniform = getUniform(name); if (uniform.location == -1) { return; } if (uniform.values == null) { uniform.values = new int[4]; } int[] values = (int[]) uniform.values; if (values[0] != i0 || values[1] != i1 || values[2] != i2 || values[3] != i3) { values[0] = i0; values[1] = i1; values[2] = i2; values[3] = i3; context.getGLContext().uniform4i(uniform.location, i0, i1, i2, i3); } } /** * Sets the uniform variable of the given name with the provided * float value. * * @param name the name of the uniform variable to be set * @param f0 the first uniform parameter * @throws RuntimeException if no OpenGL context was current or if any * OpenGL-related errors occurred */ @Override public void setConstant(String name, float f0) throws RuntimeException { Uniform uniform = getUniform(name); if (uniform.location == -1) { return; } if (uniform.values == null) { uniform.values = new float[1]; } float[] values = (float[]) uniform.values; if (values[0] != f0) { values[0] = f0; context.getGLContext().uniform1f(uniform.location, f0); } } /** * Sets the uniform variable of the given name with the provided * float values. * * @param name the name of the uniform variable to be set * @param f0 the first uniform parameter * @param f1 the second uniform parameter * @throws RuntimeException if no OpenGL context was current or if any * OpenGL-related errors occurred */ @Override public void setConstant(String name, float f0, float f1) throws RuntimeException { Uniform uniform = getUniform(name); if (uniform.location == -1) { return; } if (uniform.values == null) { uniform.values = new float[2]; } float[] values = (float[]) uniform.values; if (values[0] != f0 || values[1] != f1) { values[0] = f0; values[1] = f1; context.getGLContext().uniform2f(uniform.location, f0, f1); } } /** * Sets the uniform variable of the given name with the provided * float values. * * @param name the name of the uniform variable to be set * @param f0 the first uniform parameter * @param f1 the second uniform parameter * @param f2 the third uniform parameter * @throws RuntimeException if no OpenGL context was current or if any * OpenGL-related errors occurred */ @Override public void setConstant(String name, float f0, float f1, float f2) throws RuntimeException { Uniform uniform = getUniform(name); if (uniform.location == -1) { return; } if (uniform.values == null) { uniform.values = new float[3]; } float[] values = (float[]) uniform.values; if (values[0] != f0 || values[1] != f1 || values[2] != f2) { values[0] = f0; values[1] = f1; values[2] = f2; context.getGLContext().uniform3f(uniform.location, f0, f1, f2); } } /** * Sets the uniform variable of the given name with the provided * float values. * * @param name the name of the uniform variable to be set * @param f0 the first uniform parameter * @param f1 the second uniform parameter * @param f2 the third uniform parameter * @param f3 the fourth uniform parameter * @throws RuntimeException if no OpenGL context was current or if any * OpenGL-related errors occurred */ @Override public void setConstant(String name, float f0, float f1, float f2, float f3) throws RuntimeException { Uniform uniform = getUniform(name); if (uniform.location == -1) { return; } if (uniform.values == null) { uniform.values = new float[4]; } float[] values = (float[]) uniform.values; if (values[0] != f0 || values[1] != f1 || values[2] != f2 || values[3] != f3) { values[0] = f0; values[1] = f1; values[2] = f2; values[3] = f3; context.getGLContext().uniform4f(uniform.location, f0, f1, f2, f3); } } /** * Sets the uniform array variable of the given name with the provided * int array values. * * @param name the name of the uniform variable to be set * @param buf the array values to be set * @param off the offset into the vals array * @param count the number of ivec4 elements in the array * @throws RuntimeException if no OpenGL context was current or if any * OpenGL-related errors occurred */ @Override public void setConstants(String name, IntBuffer buf, int off, int count) throws RuntimeException { // TODO: remove off param in favor of IntBuffer.position() (RT-27442) int loc = getUniform(name).location; if (loc == -1) { return; } context.getGLContext().uniform4iv(loc, count, buf); } /** * Sets the uniform array variable of the given name with the provided * float array values. * * @param name the name of the uniform variable to be set * @param buf the array values to be set * @param count the number of vec4 elements in the array * @param off the offset into the vals array * @throws RuntimeException if no OpenGL context was current or if any * OpenGL-related errors occurred */ @Override public void setConstants(String name, FloatBuffer buf, int off, int count) throws RuntimeException { int loc = getUniform(name).location; if (loc == -1) { return; } context.getGLContext().uniform4fv(loc, count, buf); } /** * Sets the uniform matrix variable of the given name with the provided * float array values. * * @param name the name of the uniform variable to be set * @param buf the matrix values to be set * @throws RuntimeException if no OpenGL context was current or if any * OpenGL-related errors occurred */ public void setMatrix(String name, float buf[]) throws RuntimeException { int loc = getUniform(name).location; if (loc == -1) { return; } if (currentMatrix == null) { currentMatrix = new float[GLContext.NUM_MATRIX_ELEMENTS]; } if (!Arrays.equals(currentMatrix, buf)) { context.getGLContext().uniformMatrix4fv(loc, false, buf); System.arraycopy(buf, 0, currentMatrix, 0, buf.length); } } /** * Disposes the native resources used by this program object. * * @throws RuntimeException if no OpenGL context was current or if any * OpenGL-related errors occurred */ @Override public void dispose() throws RuntimeException { if (programID != 0) { disposerRecord.dispose(); programID = 0; } valid = false; } private static class ES2ShaderDisposerRecord implements Disposer.Record { private final ES2Context context; private int vertexShaderID; private int[] fragmentShaderID; private int programID; private ES2ShaderDisposerRecord(ES2Context context, int vertexShaderID, int[] fragmentShaderID, int programID) { this.context = context; this.vertexShaderID = vertexShaderID; this.fragmentShaderID = fragmentShaderID; this.programID = programID; } @Override public void dispose() { if (programID != 0) { context.getGLContext().disposeShaders(programID, vertexShaderID, fragmentShaderID); programID = vertexShaderID = 0; fragmentShaderID = null; } } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy