/*
This file is part of jpcsp.
Jpcsp is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Jpcsp 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 for more details.
You should have received a copy of the GNU General Public License
along with Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.graphics.RE;
import jpcsp.graphics.VideoEngine;
/**
* @author gid15
*
*/
public class ShaderProgram {
private int programId = -1;
private ShaderProgramKey key;
private int shaderAttribPosition;
private int shaderAttribNormal;
private int shaderAttribColor;
private int shaderAttribTexture;
private int shaderAttribWeights1;
private int shaderAttribWeights2;
private boolean hasGeometryShader;
private int[] lightType = new int[VideoEngine.NUM_LIGHTS]; // values: [0..2]
private int[] lightKind = new int[VideoEngine.NUM_LIGHTS]; // values: [0..2]
private int[] lightEnabled = new int[VideoEngine.NUM_LIGHTS]; // values: [0..1]
private int[] matFlags = new int[3]; // values: [0..1]
private int[] texShade = new int[2]; // values: [0..3]
private int[] texEnvMode = new int[2]; // values: #0:[0..4], #1:[0..1]
private int ctestFunc; // values: [0..3]
private int texMapMode; // values: [0..2]
private int texMapProj; // values: [0..3]
private int vinfoColor; // values: [0..8]
private int vinfoPosition; // values: [0..3]
private int vinfoTexture; // values: [0..3]
private int vinfoNormal; // values: [0..3]
private float colorDoubling; // values: [1..2]
private int texEnable; // values: [0..1]
private int lightingEnable; // values: [0..1]
private int vinfoTransform2D; // values: [0..1]
private int ctestEnable; // values: [0..1]
private int lightMode; // values: [0..1]
private int texPixelFormat; // values: [0..14]
private int numberBones; // values: [0..8]
private int clutIndexHint; // values: [0..4]
private int alphaTestEnable; // values: [0..1]
private int alphaTestFunc; // values: [0..7]
private int stencilTestEnable; // values: [0..1]
private int stencilFunc; // values: [0..7]
private int stencilOpFail; // values: [0..5]
private int stencilOpZFail; // values: [0..5]
private int stencilOpZPass; // values: [0..5]
private int depthTestEnable; // values: [0..1]
private int depthFunc; // values: [0..7]
private int blendTestEnable; // values: [0..1]
private int blendEquation; // values: [0..5]
private int blendSrc; // values: [0..10];
private int blendDst; // values: [0..10];
private int colorMaskEnable; // values: [0..1]
private int copyRedToAlpha; // values: [0..1]
private int fogEnable; // values: [0..1]
public static class ShaderProgramKey {
private long key1;
private long key2;
public ShaderProgramKey(long key1, long key2) {
this.key1 = key1;
this.key2 = key2;
}
@Override
public int hashCode() {
int hashCode = (int) key1;
hashCode ^= (int) (key1 >> 32);
hashCode ^= (int) key2;
hashCode ^= (int) (key2 >> 32);
return hashCode;
}
public boolean equals(ShaderProgramKey that) {
return key1 == that.key1 && key2 == that.key2;
}
@Override
public boolean equals(Object that) {
if (that instanceof ShaderProgramKey) {
return equals((ShaderProgramKey) that);
}
return super.equals(that);
}
}
public ShaderProgram() {
}
public ShaderProgram(ShaderContext shaderContext, boolean hasGeometryShader) {
this.hasGeometryShader = hasGeometryShader;
for (int i = 0; i < lightType.length; i++) {
lightType[i] = shaderContext.getLightType(i);
lightKind[i] = shaderContext.getLightKind(i);
lightEnabled[i] = shaderContext.getLightEnabled(i);
}
matFlags[0] = shaderContext.getMatFlags(0);
matFlags[1] = shaderContext.getMatFlags(1);
matFlags[2] = shaderContext.getMatFlags(2);
texShade[0] = shaderContext.getTexShade(0);
texShade[1] = shaderContext.getTexShade(1);
texEnvMode[0] = shaderContext.getTexEnvMode(0);
texEnvMode[1] = shaderContext.getTexEnvMode(1);
ctestFunc = shaderContext.getCtestFunc();
texMapMode = shaderContext.getTexMapMode();
texMapProj = shaderContext.getTexMapProj();
vinfoColor = shaderContext.getVinfoColor();
vinfoPosition = shaderContext.getVinfoPosition();
vinfoTexture = shaderContext.getVinfoTexture();
vinfoNormal = shaderContext.getVinfoNormal();
colorDoubling = shaderContext.getColorDoubling();
texEnable = shaderContext.getTexEnable();
lightingEnable = shaderContext.getLightingEnable();
vinfoTransform2D = shaderContext.getVinfoTransform2D();
ctestEnable = shaderContext.getCtestEnable();
lightMode = shaderContext.getLightMode();
texPixelFormat = shaderContext.getTexPixelFormat();
numberBones = shaderContext.getNumberBones();
clutIndexHint = shaderContext.getClutIndexHint();
alphaTestEnable = shaderContext.getAlphaTestEnable();
alphaTestFunc = shaderContext.getAlphaTestFunc();
stencilTestEnable = shaderContext.getStencilTestEnable();
stencilFunc = shaderContext.getStencilFunc();
stencilOpFail = shaderContext.getStencilOpFail();
stencilOpZFail = shaderContext.getStencilOpZFail();
stencilOpZPass = shaderContext.getStencilOpZPass();
depthTestEnable = shaderContext.getDepthTestEnable();
depthFunc = shaderContext.getDepthFunc();
blendTestEnable = shaderContext.getBlendTestEnable();
blendEquation = shaderContext.getBlendEquation();
blendSrc = shaderContext.getBlendSrc();
blendDst = shaderContext.getBlendDst();
colorMaskEnable = shaderContext.getColorMaskEnable();
copyRedToAlpha = shaderContext.getCopyRedToAlpha();
fogEnable = shaderContext.getFogEnable();
key = getKey(shaderContext, hasGeometryShader);
}
public static String getDummyDynamicDefines() {
StringBuilder defines = new StringBuilder();
int dummyValue = -1;
for (int i = 0; i < VideoEngine.NUM_LIGHTS; i++) {
// LightType and LightKind are currently not used as defines in the shaders
//REShader.addDefine(defines, "LIGHT_TYPE" + i, dummyValue);
//REShader.addDefine(defines, "LIGHT_KIND" + i, dummyValue);
REShader.addDefine(defines, "LIGHT_ENABLED" + i, dummyValue);
}
REShader.addDefine(defines, "MAT_FLAGS0", dummyValue);
REShader.addDefine(defines, "MAT_FLAGS1", dummyValue);
REShader.addDefine(defines, "MAT_FLAGS2", dummyValue);
REShader.addDefine(defines, "TEX_SHADE0", dummyValue);
REShader.addDefine(defines, "TEX_SHADE1", dummyValue);
REShader.addDefine(defines, "TEX_ENV_MODE0", dummyValue);
REShader.addDefine(defines, "TEX_ENV_MODE1", dummyValue);
REShader.addDefine(defines, "CTEST_FUNC", dummyValue);
REShader.addDefine(defines, "TEX_MAP_MODE", dummyValue);
REShader.addDefine(defines, "TEX_MAP_PROJ", dummyValue);
REShader.addDefine(defines, "VINFO_COLOR", dummyValue);
REShader.addDefine(defines, "VINFO_POSITION", dummyValue);
REShader.addDefine(defines, "VINFO_TEXTURE", dummyValue);
REShader.addDefine(defines, "VINFO_NORMAL", dummyValue);
REShader.addDefine(defines, "COLOR_DOUBLING", dummyValue);
REShader.addDefine(defines, "TEX_ENABLE", dummyValue);
REShader.addDefine(defines, "LIGHTING_ENABLE", dummyValue);
REShader.addDefine(defines, "VINFO_TRANSFORM_2D", dummyValue);
REShader.addDefine(defines, "CTEST_ENABLE", dummyValue);
REShader.addDefine(defines, "LIGHT_MODE", dummyValue);
REShader.addDefine(defines, "TEX_PIXEL_FORMAT", dummyValue);
REShader.addDefine(defines, "NUMBER_BONES", dummyValue);
REShader.addDefine(defines, "CLUT_INDEX_HINT", dummyValue);
REShader.addDefine(defines, "ALPHA_TEST_ENABLE", dummyValue);
REShader.addDefine(defines, "ALPHA_TEST_FUNC", dummyValue);
REShader.addDefine(defines, "STENCIL_TEST_ENABLE", dummyValue);
REShader.addDefine(defines, "STENCIL_FUNC", dummyValue);
REShader.addDefine(defines, "STENCIL_OP_FAIL", dummyValue);
REShader.addDefine(defines, "STENCIL_OP_ZFAIL", dummyValue);
REShader.addDefine(defines, "STENCIL_OP_ZPASS", dummyValue);
REShader.addDefine(defines, "DEPTH_TEST_ENABLE", dummyValue);
REShader.addDefine(defines, "DEPTH_FUNC", dummyValue);
REShader.addDefine(defines, "BLEND_TEST_ENABLE", dummyValue);
REShader.addDefine(defines, "BLEND_EQUATION", dummyValue);
REShader.addDefine(defines, "BLEND_SRC", dummyValue);
REShader.addDefine(defines, "BLEND_DST", dummyValue);
REShader.addDefine(defines, "COLOR_MASK_ENABLE", dummyValue);
REShader.addDefine(defines, "COPY_RED_TO_ALPHA", dummyValue);
REShader.addDefine(defines, "FOG_ENABLE", dummyValue);
return defines.toString();
}
public String getDynamicDefines() {
StringBuilder defines = new StringBuilder();
for (int i = 0; i < lightType.length; i++) {
// LightType and LightKind are currently not used as defines in the shaders
//REShader.addDefine(defines, "LIGHT_TYPE" + i, lightType[i]);
//REShader.addDefine(defines, "LIGHT_KIND" + i, lightKind[i]);
REShader.addDefine(defines, "LIGHT_ENABLED" + i, lightEnabled[i]);
}
REShader.addDefine(defines, "MAT_FLAGS0", matFlags[0]);
REShader.addDefine(defines, "MAT_FLAGS1", matFlags[1]);
REShader.addDefine(defines, "MAT_FLAGS2", matFlags[2]);
REShader.addDefine(defines, "TEX_SHADE0", texShade[0]);
REShader.addDefine(defines, "TEX_SHADE1", texShade[1]);
REShader.addDefine(defines, "TEX_ENV_MODE0", texEnvMode[0]);
REShader.addDefine(defines, "TEX_ENV_MODE1", texEnvMode[1]);
REShader.addDefine(defines, "CTEST_FUNC", ctestFunc);
REShader.addDefine(defines, "TEX_MAP_MODE", texMapMode);
REShader.addDefine(defines, "TEX_MAP_PROJ", texMapProj);
REShader.addDefine(defines, "VINFO_COLOR", vinfoColor);
REShader.addDefine(defines, "VINFO_POSITION", vinfoPosition);
REShader.addDefine(defines, "VINFO_TEXTURE", vinfoTexture);
REShader.addDefine(defines, "VINFO_NORMAL", vinfoNormal);
REShader.addDefine(defines, "COLOR_DOUBLING", (int) colorDoubling);
REShader.addDefine(defines, "TEX_ENABLE", texEnable);
REShader.addDefine(defines, "LIGHTING_ENABLE", lightingEnable);
REShader.addDefine(defines, "VINFO_TRANSFORM_2D", vinfoTransform2D);
REShader.addDefine(defines, "CTEST_ENABLE", ctestEnable);
REShader.addDefine(defines, "LIGHT_MODE", lightMode);
REShader.addDefine(defines, "TEX_PIXEL_FORMAT", texPixelFormat);
REShader.addDefine(defines, "NUMBER_BONES", numberBones);
REShader.addDefine(defines, "CLUT_INDEX_HINT", clutIndexHint);
REShader.addDefine(defines, "ALPHA_TEST_ENABLE", alphaTestEnable);
REShader.addDefine(defines, "ALPHA_TEST_FUNC", alphaTestFunc);
REShader.addDefine(defines, "STENCIL_TEST_ENABLE", stencilTestEnable);
REShader.addDefine(defines, "STENCIL_FUNC", stencilFunc);
REShader.addDefine(defines, "STENCIL_OP_FAIL", stencilOpFail);
REShader.addDefine(defines, "STENCIL_OP_ZFAIL", stencilOpZFail);
REShader.addDefine(defines, "STENCIL_OP_ZPASS", stencilOpZPass);
REShader.addDefine(defines, "DEPTH_TEST_ENABLE", depthTestEnable);
REShader.addDefine(defines, "DEPTH_FUNC", depthFunc);
REShader.addDefine(defines, "BLEND_TEST_ENABLE", blendTestEnable);
REShader.addDefine(defines, "BLEND_EQUATION", blendEquation);
REShader.addDefine(defines, "BLEND_SRC", blendSrc);
REShader.addDefine(defines, "BLEND_DST", blendDst);
REShader.addDefine(defines, "COLOR_MASK_ENABLE", colorMaskEnable);
REShader.addDefine(defines, "COPY_RED_TO_ALPHA", copyRedToAlpha);
REShader.addDefine(defines, "FOG_ENABLE", fogEnable);
return defines.toString();
}
public static ShaderProgramKey getKey(ShaderContext shaderContext, boolean hasGeometryShader) {
long key = 0;
long key1;
long key2;
int shift = 0;
key += hasGeometryShader ? 1 : 0;
shift++;
for (int i = 0; i < VideoEngine.NUM_LIGHTS; i++) {
// LightType and LightKind are currently not used as defines in the shaders
//key += shaderContext.getLightType(i) << shift;
//shift += 2;
//key += shaderContext.getLightKind(i) << shift;
//shift += 2;
key += shaderContext.getLightEnabled(i) << shift;
shift++;
}
key += ((long) shaderContext.getMatFlags(0)) << shift;
shift++;
key += ((long) shaderContext.getMatFlags(1)) << shift;
shift++;
key += ((long) shaderContext.getMatFlags(2)) << shift;
shift++;
key += ((long) shaderContext.getTexShade(0)) << shift;
shift += 2;
key += ((long) shaderContext.getTexShade(1)) << shift;
shift += 2;
key += ((long) shaderContext.getTexEnvMode(0)) << shift;
shift += 3;
key += ((long) shaderContext.getTexEnvMode(1)) << shift;
shift++;
key += ((long) shaderContext.getCtestFunc()) << shift;
shift += 2;
key += ((long) shaderContext.getTexMapMode()) << shift;
shift += 2;
key += ((long) shaderContext.getTexMapProj()) << shift;
shift += 2;
key += ((long) shaderContext.getVinfoColor()) << shift;
shift += 4;
key += ((long) shaderContext.getVinfoPosition()) << shift;
shift += 2;
key += ((long) shaderContext.getVinfoTexture()) << shift;
shift += 2;
key += ((long) shaderContext.getVinfoNormal()) << shift;
shift += 2;
key += (shaderContext.getColorDoubling() == 2.f ? 1L : 0L) << shift;
shift++;
key += ((long) shaderContext.getTexEnable()) << shift;
shift++;
key += ((long) shaderContext.getLightingEnable()) << shift;
shift++;
key += ((long) shaderContext.getVinfoTransform2D()) << shift;
shift++;
key += ((long) shaderContext.getCtestEnable()) << shift;
shift++;
key += ((long) shaderContext.getLightMode()) << shift;
shift++;
key += ((long) shaderContext.getTexPixelFormat()) << shift;
shift += 4;
key += ((long) shaderContext.getNumberBones()) << shift;
shift += 4;
key += ((long) shaderContext.getClutIndexHint()) << shift;
shift += 3;
key += ((long) shaderContext.getAlphaTestEnable()) << shift;
shift++;
key += ((long) shaderContext.getAlphaTestFunc()) << shift;
shift += 3;
key += ((long) shaderContext.getStencilTestEnable()) << shift;
shift++;
key += ((long) shaderContext.getStencilFunc()) << shift;
shift += 3;
key += ((long) shaderContext.getStencilOpFail()) << shift;
shift += 3;
key += ((long) shaderContext.getStencilOpZFail()) << shift;
shift += 3;
if (shift > Long.SIZE) {
VideoEngine.log.error(String.format("ShaderProgram: too long key1: %d bits", shift));
}
key1 = key;
key = 0;
shift = 0;
key += ((long) shaderContext.getStencilOpZPass()) << shift;
shift += 3;
key += ((long) shaderContext.getDepthTestEnable()) << shift;
shift += 3;
key += ((long) shaderContext.getDepthFunc()) << shift;
shift += 3;
key += ((long) shaderContext.getBlendTestEnable()) << shift;
shift++;
key += ((long) shaderContext.getBlendEquation()) << shift;
shift += 3;
key += ((long) shaderContext.getBlendSrc()) << shift;
shift += 4;
key += ((long) shaderContext.getBlendDst()) << shift;
shift += 4;
key += ((long) shaderContext.getColorMaskEnable()) << shift;
shift++;
key += ((long) shaderContext.getCopyRedToAlpha()) << shift;
shift++;
key += ((long) shaderContext.getFogEnable()) << shift;
shift++;
if (shift > Long.SIZE) {
VideoEngine.log.error(String.format("ShaderProgram: too long key2: %d bits", shift));
}
key2 = key;
return new ShaderProgramKey(key1, key2);
}
public boolean matches(ShaderContext shaderContext, boolean hasGeometryShader) {
ShaderProgramKey key = getKey(shaderContext, hasGeometryShader);
return key.equals(this.key);
}
public void use(IRenderingEngine re) {
re.useProgram(programId);
}
public int getProgramId() {
return programId;
}
public void setProgramId(IRenderingEngine re, int programId) {
this.programId = programId;
shaderAttribWeights1 = re.getAttribLocation(programId, REShader.attributeNameWeights1);
shaderAttribWeights2 = re.getAttribLocation(programId, REShader.attributeNameWeights2);
shaderAttribPosition = re.getAttribLocation(programId, REShader.attributeNamePosition);
shaderAttribNormal = re.getAttribLocation(programId, REShader.attributeNameNormal);
shaderAttribColor = re.getAttribLocation(programId, REShader.attributeNameColor);
shaderAttribTexture = re.getAttribLocation(programId, REShader.attributeNameTexture);
}
public int getShaderAttribPosition() {
return shaderAttribPosition;
}
public int getShaderAttribNormal() {
return shaderAttribNormal;
}
public int getShaderAttribColor() {
return shaderAttribColor;
}
public int getShaderAttribTexture() {
return shaderAttribTexture;
}
public int getShaderAttribWeights1() {
return shaderAttribWeights1;
}
public int getShaderAttribWeights2() {
return shaderAttribWeights2;
}
public ShaderProgramKey getKey() {
return key;
}
@Override
public String toString() {
return String.format("ShaderProgram[%d, geometryShader=%b, %s]", programId, hasGeometryShader, getDynamicDefines().replace(System.getProperty("line.separator"), ", "));
}
}