/*
* JaamSim Discrete Event Simulation
* Copyright (C) 2012 Ausenco Engineering Canada Inc.
*
* 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 com.jaamsim.render;
import com.jogamp.opengl.GL2GL3;
/**
* An easy to use wrapper around a shader, is created with filenames for vertex and fragment shader
* source files, keeps shader handles around. Needs a cache later, but for now this will work
* @author Matt.Chudleigh
*
*/
public class Shader {
public enum ShaderStatus {
GOOD, BAD_INPUT, VERT_FAILED, FRAG_FAILED, LINK_FAILED, VALIDATE_FAILED
}
private int _vsHandle;
private int _fsHandle;
private int _progHandle;
private ShaderStatus _status;
private String _vertCompLog;
private String _fragCompLog;
private String _progLinkLog;
private String _progValidateLog;
public Shader(String vertString, String fragString, GL2GL3 gl) {
_vsHandle = gl.glCreateShader(GL2GL3.GL_VERTEX_SHADER);
_fsHandle = gl.glCreateShader(GL2GL3.GL_FRAGMENT_SHADER);
String[] sa = new String[1];
int[] ia = new int[1];
sa[0] = vertString;
ia[0] = vertString.length();
gl.glShaderSource(_vsHandle, 1, sa, ia, 0);
sa[0] = fragString;
ia[0] = fragString.length();
gl.glShaderSource(_fsHandle, 1, sa, ia, 0);
gl.glCompileShader(_vsHandle);
_vertCompLog = getShaderLog(_vsHandle, gl);
if (!checkCompileStatus(_vsHandle, gl)) {
_status = ShaderStatus.VERT_FAILED;
return;
}
gl.glCompileShader(_fsHandle);
_fragCompLog = getShaderLog(_fsHandle, gl);
if (!checkCompileStatus(_fsHandle, gl)) {
_status = ShaderStatus.FRAG_FAILED;
return;
}
_progHandle = gl.glCreateProgram();
gl.glAttachShader(_progHandle, _vsHandle);
gl.glAttachShader(_progHandle, _fsHandle);
gl.glLinkProgram(_progHandle);
_progLinkLog = getProgramLog(_progHandle, gl);
if (!checkLinkStatus(gl)) {
_status = ShaderStatus.LINK_FAILED;
return;
}
gl.glValidateProgram(_progHandle);
_progValidateLog = getProgramLog(_progHandle, gl);
if (!checkValidateStatus(gl)) {
_status = ShaderStatus.VALIDATE_FAILED;
return;
}
_status = ShaderStatus.GOOD;
}
public boolean isGood() {
return _status == ShaderStatus.GOOD;
}
public void useShader(GL2GL3 gl) {
if (_status != ShaderStatus.GOOD) {
assert(false);
}
gl.glUseProgram(_progHandle);
}
private boolean checkCompileStatus(int shaderHandle, GL2GL3 gl) {
int[] res = new int[1];
gl.glGetShaderiv(shaderHandle, GL2GL3.GL_COMPILE_STATUS, res, 0);
return (res[0] == GL2GL3.GL_TRUE);
}
private boolean checkLinkStatus(GL2GL3 gl) {
int[] res = new int[1];
gl.glGetProgramiv(_progHandle, GL2GL3.GL_LINK_STATUS, res, 0);
return (res[0] == GL2GL3.GL_TRUE);
}
private boolean checkValidateStatus(GL2GL3 gl) {
int[] res = new int[1];
gl.glGetProgramiv(_progHandle, GL2GL3.GL_VALIDATE_STATUS, res, 0);
return (res[0] == GL2GL3.GL_TRUE);
}
public int getProgramHandle() {
return _progHandle;
}
/**
* Free the OpenGL resources used by this shader, can not be used again after
*/
public void clearProgram(GL2GL3 gl) {
if (_status != ShaderStatus.GOOD) {
return;
}
gl.glDeleteProgram(_progHandle);
}
private String getShaderLog(int shaderHandle, GL2GL3 gl) {
int[] is = new int[1];
gl.glGetShaderiv(shaderHandle, GL2GL3.GL_INFO_LOG_LENGTH, is, 0);
int logLength = is[0];
if (logLength == 0) {
return "";
}
byte[] bs = new byte[logLength];
gl.glGetShaderInfoLog(shaderHandle, logLength, is, 0, bs, 0);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < is[0]; ++i) {
sb.append((char)bs[i]);
}
return sb.toString();
}
private String getProgramLog(int progHandle, GL2GL3 gl) {
int[] is = new int[1];
gl.glGetProgramiv(progHandle, GL2GL3.GL_INFO_LOG_LENGTH, is, 0);
int logLength = is[0];
if (logLength == 0) {
return "";
}
byte[] bs = new byte[logLength];
gl.glGetProgramInfoLog(progHandle, logLength, is, 0, bs, 0);
StringBuilder sb = new StringBuilder();
for(int i = 0; i < is[0]; ++i) {
sb.append((char)bs[i]);
}
return sb.toString();
}
public String getVertShaderLog() {
return _vertCompLog;
}
public String getFragShaderLog() {
return _fragCompLog;
}
public String getProgramLinkLog() {
return _progLinkLog;
}
public String getProgramValidateLog() {
return _progValidateLog;
}
/**
* Utility to get the most recent failure information, this could be implemented by an outside class
* with the other public information
* @return - a human readable string to help debug the shader issues
*/
public String getFailureLog() {
switch (_status) {
case GOOD:
return "No problems";
case BAD_INPUT:
return "Could not find an shader source";
case VERT_FAILED:
return "Vertex Shader compile failure: \n" + _vertCompLog;
case FRAG_FAILED:
return "Fragment Shader compile failure: \n" + _fragCompLog;
case LINK_FAILED:
return "Program link failure: \n" + _progLinkLog;
case VALIDATE_FAILED:
return "Program validation failure: \n" + _progValidateLog;
}
assert(false);
return "";
}
} // class Shader