1
0
mirror of https://github.com/blawar/GLideN64.git synced 2024-07-07 03:13:49 +00:00
GLideN64/GLSLCombiner.cpp
2015-05-13 10:20:09 +06:00

1191 lines
38 KiB
C++

#include <assert.h>
#include <stdio.h>
#include <string>
#include "N64.h"
#include "OpenGL.h"
#include "Config.h"
#include "GLSLCombiner.h"
#include "FrameBuffer.h"
#include "DepthBuffer.h"
#include "RSP.h"
#include "VI.h"
#include "Log.h"
#define SHADER_PRECISION
#include "Shaders.h"
static GLuint g_vertex_shader_object;
static GLuint g_vertex_shader_object_notex;
static GLuint g_calc_light_shader_object;
static GLuint g_calc_mipmap_shader_object;
static GLuint g_calc_noise_shader_object;
static GLuint g_calc_depth_shader_object;
static GLuint g_test_alpha_shader_object;
static GLuint g_readtex_shader_object;
static GLuint g_dither_shader_object;
GLuint g_monochrome_image_program = 0;
#ifdef GL_IMAGE_TEXTURES_SUPPORT
GLuint g_draw_shadow_map_program = 0;
static GLuint g_zlut_tex = 0;
GLuint g_tlut_tex = 0;
static u32 g_paletteCRC256 = 0;
#endif // GL_IMAGE_TEXTURES_SUPPORT
static std::string strFragmentShader;
static const GLsizei nShaderLogSize = 1024;
bool checkShaderCompileStatus(GLuint obj)
{
GLint status;
glGetShaderiv(obj, GL_COMPILE_STATUS, &status);
if(status == GL_FALSE)
{
GLchar shader_log[nShaderLogSize];
GLsizei nLogSize = nShaderLogSize;
glGetShaderInfoLog(obj, nShaderLogSize, &nLogSize, shader_log);
shader_log[nLogSize] = 0;
LOG(LOG_ERROR, "shader_compile error: %s\n", shader_log);
return false;
}
return true;
}
bool checkProgramLinkStatus(GLuint obj)
{
GLint status;
glGetProgramiv(obj, GL_LINK_STATUS, &status);
if(status == GL_FALSE)
{
GLsizei nLogSize = nShaderLogSize;
GLchar shader_log[nShaderLogSize];
glGetProgramInfoLog(obj, nShaderLogSize, &nLogSize, shader_log);
LOG(LOG_ERROR, "shader_link error: %s\n", shader_log);
return false;
}
return true;
}
static const GLuint noiseTexIndex = 2;
class NoiseTexture
{
public:
NoiseTexture() : m_pTexture(NULL), m_PBO(0), m_DList(0) {}
void init();
void destroy();
void update();
private:
CachedTexture * m_pTexture;
#ifndef GLES2
GLuint m_PBO;
#else
GLubyte* m_PBO;
#endif
u32 m_DList;
} noiseTex;
void NoiseTexture::init()
{
m_pTexture = textureCache().addFrameBufferTexture();
m_pTexture->format = G_IM_FMT_RGBA;
m_pTexture->clampS = 1;
m_pTexture->clampT = 1;
m_pTexture->frameBufferTexture = TRUE;
m_pTexture->maskS = 0;
m_pTexture->maskT = 0;
m_pTexture->mirrorS = 0;
m_pTexture->mirrorT = 0;
m_pTexture->realWidth = 640;
m_pTexture->realHeight = 480;
m_pTexture->textureBytes = m_pTexture->realWidth * m_pTexture->realHeight;
textureCache().addFrameBufferTextureSize(m_pTexture->textureBytes);
glBindTexture(GL_TEXTURE_2D, m_pTexture->glName);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, m_pTexture->realWidth, m_pTexture->realHeight, 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
// Generate Pixel Buffer Object. Initialize it later
#ifndef GLES2
glGenBuffers(1, &m_PBO);
#endif
}
void NoiseTexture::destroy()
{
if (m_pTexture != NULL) {
textureCache().removeFrameBufferTexture(m_pTexture);
m_pTexture = NULL;
}
#ifndef GLES2
glDeleteBuffers(1, &m_PBO);
m_PBO = 0;
#endif
}
void NoiseTexture::update()
{
if (m_DList == RSP.DList)
return;
const u32 dataSize = VI.width*VI.height;
if (dataSize == 0)
return;
#ifndef GLES2
PBOBinder binder(GL_PIXEL_UNPACK_BUFFER, m_PBO);
glBufferData(GL_PIXEL_UNPACK_BUFFER, dataSize, NULL, GL_DYNAMIC_DRAW);
GLubyte* ptr = (GLubyte*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
#else
m_PBO = (GLubyte*)malloc(dataSize);
GLubyte* ptr = m_PBO;
PBOBinder binder(m_PBO);
#endif // GLES2
if (ptr == NULL)
return;
for (u32 y = 0; y < VI.height; ++y) {
for (u32 x = 0; x < VI.width; ++x)
ptr[x + y*VI.width] = rand()&0xFF;
}
#ifndef GLES2
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); // release the mapped buffer
#endif
glActiveTexture(GL_TEXTURE0 + noiseTexIndex);
glBindTexture(GL_TEXTURE_2D, m_pTexture->glName);
#ifndef GLES2
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, VI.width, VI.height, GL_RED, GL_UNSIGNED_BYTE, 0);
#else
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, VI.width, VI.height, GL_RED, GL_UNSIGNED_BYTE, m_PBO);
#endif
m_DList = RSP.DList;
}
#ifdef GL_IMAGE_TEXTURES_SUPPORT
static
void InitZlutTexture()
{
if (!video().getRender().isImageTexturesSupported())
return;
const u16 * const zLUT = depthBufferList().getZLUT();
glGenTextures(1, &g_zlut_tex);
glBindTexture(GL_TEXTURE_2D, g_zlut_tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R16,
512, 512, 0, GL_RED, GL_UNSIGNED_SHORT,
zLUT);
glBindImageTexture(ZlutImageUnit, g_zlut_tex, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R16UI);
}
static
void DestroyZlutTexture()
{
if (!video().getRender().isImageTexturesSupported())
return;
glBindImageTexture(ZlutImageUnit, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R16UI);
if (g_zlut_tex > 0) {
glBindTexture(GL_TEXTURE_2D, 0);
glDeleteTextures(1, &g_zlut_tex);
g_zlut_tex = 0;
}
}
GLuint createShaderProgram(const char * _strVertex, const char * _strFragment)
{
GLuint vertex_shader_object = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader_object, 1, &_strVertex, NULL);
glCompileShader(vertex_shader_object);
assert(checkShaderCompileStatus(vertex_shader_object));
GLuint fragment_shader_object = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader_object, 1, &_strFragment, NULL);
glCompileShader(fragment_shader_object);
assert(checkShaderCompileStatus(fragment_shader_object));
GLuint program = glCreateProgram();
glBindAttribLocation(program, SC_POSITION, "aPosition");
glAttachShader(program, vertex_shader_object);
glAttachShader(program, fragment_shader_object);
glLinkProgram(program);
glDeleteShader(vertex_shader_object);
glDeleteShader(fragment_shader_object);
assert(checkProgramLinkStatus(program));
return program;
}
static
void InitShadowMapShader()
{
if (!video().getRender().isImageTexturesSupported())
return;
g_paletteCRC256 = 0;
glGenTextures(1, &g_tlut_tex);
glBindTexture(GL_TEXTURE_1D, g_tlut_tex);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexImage1D(GL_TEXTURE_1D, 0, GL_R16, 256, 0, GL_RED, GL_UNSIGNED_SHORT, NULL);
g_draw_shadow_map_program = createShaderProgram(default_vertex_shader, shadow_map_fragment_shader_float);
g_monochrome_image_program = createShaderProgram(default_vertex_shader, zelda_monochrome_fragment_shader);
}
static
void DestroyShadowMapShader()
{
if (!video().getRender().isImageTexturesSupported())
return;
glBindImageTexture(TlutImageUnit, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R16UI);
if (g_tlut_tex > 0) {
glBindTexture(GL_TEXTURE_1D, 0);
glDeleteTextures(1, &g_tlut_tex);
g_tlut_tex = 0;
}
glDeleteProgram(g_draw_shadow_map_program);
g_draw_shadow_map_program = 0;
glDeleteProgram(g_monochrome_image_program);
g_monochrome_image_program = 0;
}
#endif // GL_IMAGE_TEXTURES_SUPPORT
static
GLuint _createShader(GLenum _type, const char * _strShader)
{
GLuint shader_object = glCreateShader(_type);
glShaderSource(shader_object, 1, &_strShader, NULL);
glCompileShader(shader_object);
assert(checkShaderCompileStatus(shader_object));
return shader_object;
}
void InitShaderCombiner()
{
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
g_vertex_shader_object = _createShader(GL_VERTEX_SHADER, vertex_shader);
g_vertex_shader_object_notex = _createShader(GL_VERTEX_SHADER, vertex_shader_notex);
strFragmentShader.reserve(1024*5);
#ifndef GLES2
g_calc_light_shader_object = _createShader(GL_FRAGMENT_SHADER, fragment_shader_calc_light);
g_calc_mipmap_shader_object = _createShader(GL_FRAGMENT_SHADER, fragment_shader_mipmap);
g_calc_noise_shader_object = _createShader(GL_FRAGMENT_SHADER, fragment_shader_noise);
g_test_alpha_shader_object = _createShader(GL_FRAGMENT_SHADER, alpha_test_fragment_shader);
g_readtex_shader_object = _createShader(GL_FRAGMENT_SHADER, fragment_shader_readtex);
g_dither_shader_object = _createShader(GL_FRAGMENT_SHADER, fragment_shader_dither);
#ifdef GL_IMAGE_TEXTURES_SUPPORT
if (video().getRender().isImageTexturesSupported() && config.frameBufferEmulation.N64DepthCompare != 0)
g_calc_depth_shader_object = _createShader(GL_FRAGMENT_SHADER, depth_compare_shader_float);
InitZlutTexture();
InitShadowMapShader();
noiseTex.init();
#endif // GL_IMAGE_TEXTURES_SUPPORT
#endif // GLES2
}
void DestroyShaderCombiner() {
strFragmentShader.clear();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glDeleteShader(g_vertex_shader_object);
g_vertex_shader_object = 0;
glDeleteShader(g_vertex_shader_object_notex);
g_vertex_shader_object_notex = 0;
#ifndef GLES2
glDeleteShader(g_calc_light_shader_object);
g_calc_light_shader_object = 0;
glDeleteShader(g_calc_mipmap_shader_object);
g_calc_mipmap_shader_object = 0;
glDeleteShader(g_readtex_shader_object);
g_readtex_shader_object = 0;
glDeleteShader(g_calc_noise_shader_object);
g_calc_noise_shader_object = 0;
glDeleteShader(g_test_alpha_shader_object);
g_test_alpha_shader_object = 0;
glDeleteShader(g_dither_shader_object);
g_dither_shader_object = 0;
glDeleteShader(g_calc_depth_shader_object);
g_calc_depth_shader_object = 0;
#ifdef GL_IMAGE_TEXTURES_SUPPORT
noiseTex.destroy();
DestroyZlutTexture();
DestroyShadowMapShader();
#endif // GL_IMAGE_TEXTURES_SUPPORT
#endif // GLES2
}
const char *ColorInput[] = {
"combined_color.rgb",
"readtex0.rgb",
"readtex1.rgb",
"uPrimColor.rgb",
"vec_color.rgb",
"uEnvColor.rgb",
"uCenterColor.rgb",
"uScaleColor.rgb",
"combined_color.a",
"vec3(readtex0.a)",
"vec3(readtex1.a)",
"vec3(uPrimColor.a)",
"vec3(vec_color.a)",
"vec3(uEnvColor.a)",
"vec3(lod_frac)", // TODO: emulate lod_fraction
"vec3(uPrimLod)",
"vec3(0.5 + 0.5*snoise())",
"vec3(uK4)",
"vec3(uK5)",
"vec3(1.0)",
"vec3(0.0)"
};
const char *AlphaInput[] = {
"combined_color.a",
"readtex0.a",
"readtex1.a",
"uPrimColor.a",
"vec_color.a",
"uEnvColor.a",
"uCenterColor.a",
"uScaleColor.a",
"combined_color.a",
"readtex0.a",
"readtex1.a",
"uPrimColor.a",
"vec_color.a",
"uEnvColor.a",
"lod_frac",
"uPrimLod",
"0.5 + 0.5*snoise()",
"uK4",
"uK5",
"1.0",
"0.0"
};
inline
int CorrectFirstStageParam(int _param)
{
switch (_param) {
case TEXEL1:
return TEXEL0;
case TEXEL1_ALPHA:
return TEXEL0_ALPHA;
}
return _param;
}
static
void CorrectFirstStageParams(CombinerStage & _stage)
{
for (int i = 0; i < _stage.numOps; ++i) {
_stage.op[i].param1 = CorrectFirstStageParam(_stage.op[i].param1);
_stage.op[i].param2 = CorrectFirstStageParam(_stage.op[i].param2);
_stage.op[i].param3 = CorrectFirstStageParam(_stage.op[i].param3);
}
}
inline
int CorrectSecondStageParam(int _param)
{
switch (_param) {
case TEXEL0:
return TEXEL1;
case TEXEL1:
return TEXEL0;
case TEXEL0_ALPHA:
return TEXEL1_ALPHA;
case TEXEL1_ALPHA:
return TEXEL0_ALPHA;
}
return _param;
}
static
void CorrectSecondStageParams(CombinerStage & _stage) {
for (int i = 0; i < _stage.numOps; ++i) {
_stage.op[i].param1 = CorrectSecondStageParam(_stage.op[i].param1);
_stage.op[i].param2 = CorrectSecondStageParam(_stage.op[i].param2);
_stage.op[i].param3 = CorrectSecondStageParam(_stage.op[i].param3);
}
}
static
int CompileCombiner(const CombinerStage & _stage, const char** _Input, char * _strCombiner) {
char buf[128];
bool bBracketOpen = false;
int nRes = 0;
for (int i = 0; i < _stage.numOps; ++i) {
switch(_stage.op[i].op) {
case LOAD:
sprintf(buf, "(%s ", _Input[_stage.op[i].param1]);
strcat(_strCombiner, buf);
bBracketOpen = true;
nRes |= 1 << _stage.op[i].param1;
break;
case SUB:
if (bBracketOpen) {
sprintf(buf, "- %s)", _Input[_stage.op[i].param1]);
bBracketOpen = false;
} else
sprintf(buf, "- %s", _Input[_stage.op[i].param1]);
strcat(_strCombiner, buf);
nRes |= 1 << _stage.op[i].param1;
break;
case ADD:
if (bBracketOpen) {
sprintf(buf, "+ %s)", _Input[_stage.op[i].param1]);
bBracketOpen = false;
} else
sprintf(buf, "+ %s", _Input[_stage.op[i].param1]);
strcat(_strCombiner, buf);
nRes |= 1 << _stage.op[i].param1;
break;
case MUL:
if (bBracketOpen) {
sprintf(buf, ")*%s", _Input[_stage.op[i].param1]);
bBracketOpen = false;
} else
sprintf(buf, "*%s", _Input[_stage.op[i].param1]);
strcat(_strCombiner, buf);
nRes |= 1 << _stage.op[i].param1;
break;
case INTER:
sprintf(buf, "mix(%s, %s, %s)", _Input[_stage.op[0].param2], _Input[_stage.op[0].param1], _Input[_stage.op[0].param3]);
strcat(_strCombiner, buf);
nRes |= 1 << _stage.op[i].param1;
nRes |= 1 << _stage.op[i].param2;
nRes |= 1 << _stage.op[i].param3;
break;
// default:
// assert(false);
}
}
if (bBracketOpen)
strcat(_strCombiner, ")");
strcat(_strCombiner, "; \n");
return nRes;
}
ShaderCombiner::ShaderCombiner(Combiner & _color, Combiner & _alpha, const gDPCombine & _combine) : m_combine(_combine)
{
if (gDP.otherMode.cycleType == G_CYC_1CYCLE) {
CorrectFirstStageParams(_alpha.stage[0]);
CorrectFirstStageParams(_color.stage[0]);
}
char strCombiner[1024];
strcpy(strCombiner, " alpha1 = ");
m_nInputs = CompileCombiner(_alpha.stage[0], AlphaInput, strCombiner);
strcat(strCombiner, " color1 = ");
m_nInputs |= CompileCombiner(_color.stage[0], ColorInput, strCombiner);
strcat(strCombiner, fragment_shader_blender);
strcat(strCombiner, " combined_color = vec4(color1, alpha1); \n");
if (_alpha.numStages == 2) {
strcat(strCombiner, " alpha2 = ");
if (gDP.otherMode.cycleType == G_CYC_2CYCLE)
CorrectSecondStageParams(_alpha.stage[1]);
m_nInputs |= CompileCombiner(_alpha.stage[1], AlphaInput, strCombiner);
} else
strcat(strCombiner, " alpha2 = alpha1; \n");
if (_color.numStages == 2) {
strcat(strCombiner, " color2 = ");
if (gDP.otherMode.cycleType == G_CYC_2CYCLE)
CorrectSecondStageParams(_color.stage[1]);
m_nInputs |= CompileCombiner(_color.stage[1], ColorInput, strCombiner);
} else
strcat(strCombiner, " color2 = color1; \n");
if (usesTex()) {
strFragmentShader.assign(fragment_shader_header_common_variables);
strFragmentShader.append(fragment_shader_header_common_functions);
}
else {
strFragmentShader.assign(fragment_shader_header_common_variables_notex);
strFragmentShader.append(fragment_shader_header_common_functions_notex);
}
strFragmentShader.append(fragment_shader_header_main);
const bool bUseLod = (m_nInputs & (1<<LOD_FRACTION)) > 0;
if (bUseLod) {
#ifdef SHADER_PRECISION
strFragmentShader.append(" lowp vec4 readtex0, readtex1; \n");
strFragmentShader.append(" lowp float lod_frac = mipmap(readtex0, readtex1); \n");
#else
strFragmentShader.append(" vec4 readtex0, readtex1; \n");
strFragmentShader.append(" float lod_frac = mipmap(readtex0, readtex1); \n");
#endif
} else {
if (usesT0())
strFragmentShader.append(" lowp vec4 readtex0 = readTex(uTex0, vTexCoord0, uFb8Bit == 1 || uFb8Bit == 3, uFbFixedAlpha == 1 || uFbFixedAlpha == 3); \n");
if (usesT1())
strFragmentShader.append(" lowp vec4 readtex1 = readTex(uTex1, vTexCoord1, uFb8Bit == 2 || uFb8Bit == 3, uFbFixedAlpha == 2 || uFbFixedAlpha == 3); \n");
}
const bool bUseHWLight = config.generalEmulation.enableHWLighting != 0 && GBI.isHWLSupported() && usesShadeColor();
if (bUseHWLight)
#ifdef SHADER_PRECISION
strFragmentShader.append(" calc_light(vNumLights, vShadeColor.rgb, input_color); \n");
#else
strFragmentShader.append(" calc_light(vNumLights, vShadeColor.rgb, input_color); \n");
#endif
else
strFragmentShader.append(" input_color = vShadeColor.rgb;\n");
strFragmentShader.append(" vec_color = vec4(input_color, vShadeColor.a); \n");
strFragmentShader.append(strCombiner);
strFragmentShader.append(" if (!alpha_test(alpha2)) discard;\n");
if (config.generalEmulation.enableNoise != 0) {
strFragmentShader.append(
" if (uColorDitherMode == 2) colorNoiseDither(clamp(snoise(), 0.0, 1.0), color2); \n"
" if (uAlphaDitherMode == 2) alphaNoiseDither(clamp(snoise(), 0.0, 1.0), alpha2); \n"
);
}
strFragmentShader.append(
" switch (uFogUsage&255) { \n"
" case 2: \n"
" fragColor = vec4(color2, uFogColor.a); \n"
" break; \n"
" case 3: \n"
" fragColor = uFogColor; \n"
" break; \n"
" case 4: \n"
" fragColor = vec4(color2, uFogColor.a*alpha2); \n"
" break; \n"
" default: \n"
" fragColor = vec4(color2, alpha2); \n"
" break; \n"
" } \n"
);
if (video().getRender().isImageTexturesSupported() && config.frameBufferEmulation.N64DepthCompare != 0)
strFragmentShader.append(" if (!depth_compare()) discard; \n");
#ifdef USE_TOONIFY
strFragmentShader.append(" toonify(intensity); \n");
#endif
strFragmentShader.append(
" if (uFogUsage == 257) \n"
" fragColor = vec4(mix(fragColor.rgb, uFogColor.rgb, vFogFragCoord), fragColor.a); \n"
" if (uGammaCorrectionEnabled != 0) \n"
" fragColor = vec4(sqrt(fragColor.rgb), fragColor.a); \n"
);
strFragmentShader.append(fragment_shader_end);
if (config.generalEmulation.enableNoise == 0)
strFragmentShader.append(fragment_shader_dummy_noise);
#ifdef USE_TOONIFY
strFragmentShader.append(fragment_shader_toonify);
#endif
#ifdef GLES2
strFragmentShader.append(alpha_test_fragment_shader);
strFragmentShader.append(noise_fragment_shader);
if (bUseLod)
strFragmentShader.append(fragment_shader_mipmap);
if (config.generalEmulation.enableHWLighting)
strFragmentShader.append(fragment_shader_calc_light);
#endif
GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
const GLchar * strShaderData = strFragmentShader.data();
glShaderSource(fragmentShader, 1, &strShaderData, NULL);
glCompileShader(fragmentShader);
if (!checkShaderCompileStatus(fragmentShader))
LOG(LOG_ERROR, "Error in fragment shader:\n%s\n", strFragmentShader.data());
m_program = glCreateProgram();
_locate_attributes();
if (usesTex())
glAttachShader(m_program, g_vertex_shader_object);
else
glAttachShader(m_program, g_vertex_shader_object_notex);
glAttachShader(m_program, fragmentShader);
#ifndef GLES2
if (bUseHWLight)
glAttachShader(m_program, g_calc_light_shader_object);
if (bUseLod)
glAttachShader(m_program, g_calc_mipmap_shader_object);
else if (usesTex())
glAttachShader(m_program, g_readtex_shader_object);
glAttachShader(m_program, g_test_alpha_shader_object);
if (video().getRender().isImageTexturesSupported() && config.frameBufferEmulation.N64DepthCompare != 0)
glAttachShader(m_program, g_calc_depth_shader_object);
if (config.generalEmulation.enableNoise != 0) {
glAttachShader(m_program, g_calc_noise_shader_object);
glAttachShader(m_program, g_dither_shader_object);
}
#endif
glLinkProgram(m_program);
assert(checkProgramLinkStatus(m_program));
glDeleteShader(fragmentShader);
_locateUniforms();
}
ShaderCombiner::~ShaderCombiner() {
glDeleteProgram(m_program);
m_program = 0;
}
#define LocateUniform(A) \
m_uniforms.A.loc = glGetUniformLocation(m_program, #A);
void ShaderCombiner::_locateUniforms() {
LocateUniform(uTex0);
LocateUniform(uTex1);
LocateUniform(uTexNoise);
LocateUniform(uTlutImage);
LocateUniform(uZlutImage);
LocateUniform(uDepthImage);
LocateUniform(uFogMode);
LocateUniform(uFogUsage);
LocateUniform(uAlphaCompareMode);
LocateUniform(uAlphaDitherMode);
LocateUniform(uColorDitherMode);
LocateUniform(uGammaCorrectionEnabled);
LocateUniform(uEnableLod);
LocateUniform(uEnableAlphaTest);
LocateUniform(uEnableDepth);
LocateUniform(uEnableDepthCompare)
LocateUniform(uEnableDepthUpdate);
LocateUniform(uDepthMode);
LocateUniform(uDepthSource);
LocateUniform(uFb8Bit);
LocateUniform(uFbFixedAlpha);
LocateUniform(uMaxTile)
LocateUniform(uTextureDetail);
LocateUniform(uTexturePersp);
LocateUniform(uTextureFilterMode);
LocateUniform(uSpecialBlendMode);
LocateUniform(uFogAlpha);
LocateUniform(uPrimitiveLod);
LocateUniform(uMinLod);
LocateUniform(uDeltaZ);
LocateUniform(uAlphaTestValue);
LocateUniform(uRenderState);
LocateUniform(uScreenScale);
LocateUniform(uDepthScale);
LocateUniform(uFogScale);
}
void ShaderCombiner::_locate_attributes() const {
glBindAttribLocation(m_program, SC_POSITION, "aPosition");
glBindAttribLocation(m_program, SC_COLOR, "aColor");
glBindAttribLocation(m_program, SC_TEXCOORD0, "aTexCoord0");
glBindAttribLocation(m_program, SC_TEXCOORD1, "aTexCoord1");
glBindAttribLocation(m_program, SC_NUMLIGHTS, "aNumLights");
}
void ShaderCombiner::update(bool _bForce) {
glUseProgram(m_program);
if (_bForce) {
_setIUniform(m_uniforms.uTex0, 0, _bForce);
_setIUniform(m_uniforms.uTex1, 1, _bForce);
_setIUniform(m_uniforms.uTexNoise, noiseTexIndex, _bForce);
}
updateRenderState(_bForce);
updateGammaCorrection(_bForce);
updateFogMode(_bForce);
updateDitherMode(_bForce);
updateLOD(_bForce);
updateTextureInfo(_bForce);
updateAlphaTestInfo(_bForce);
updateFBInfo(_bForce);
updateDepthInfo(_bForce);
}
void ShaderCombiner::updateRenderState(bool _bForce)
{
_setIUniform(m_uniforms.uRenderState, video().getRender().getRenderState(), _bForce);
}
void ShaderCombiner::updateGammaCorrection(bool _bForce)
{
_setIUniform(m_uniforms.uGammaCorrectionEnabled, *REG.VI_STATUS & 8, _bForce);
}
void ShaderCombiner::updateFogMode(bool _bForce)
{
const u32 blender = (gDP.otherMode.l >> 16);
const int nFogBlendEnabled = (gDP.otherMode.c1_m1a == 3 || gDP.otherMode.c1_m2a == 3 || gDP.otherMode.c2_m1a == 3 || gDP.otherMode.c2_m2a == 3) ? 256 : 0;
int nFogUsage = (config.generalEmulation.enableFog != 0 && (gSP.geometryMode & G_FOG) != 0) ? 1 : 0;
int nSpecialBlendMode = 0;
switch (blender) {
case 0x0150:
case 0x0D18:
nFogUsage = gDP.otherMode.cycleType == G_CYC_2CYCLE ? 2 : 0;
break;
case 0x0440:
nFogUsage = gDP.otherMode.cycleType == G_CYC_1CYCLE ? 2 : 0;
break;
case 0xC912:
nFogUsage = 2;
break;
case 0xF550:
nFogUsage = 3;
break;
case 0x0550:
nFogUsage = 4;
break;
case 0x0382:
case 0x0091:
// Mace
// CLR_IN * A_IN + CLR_BL * 1MA
if (gDP.otherMode.cycleType == G_CYC_2CYCLE)
nSpecialBlendMode = 1;
break;
case 0xA500:
// Bomberman 2
// CLR_BL * A_FOG + CLR_IN * 1MA
if (gDP.otherMode.cycleType == G_CYC_1CYCLE) {
nSpecialBlendMode = 2;
nFogUsage = 5;
}
break;
case 0x07C2:
// Conker BFD shadow
// CLR_IN * A_FOG + CLR_FOG * 1MA
if (gDP.otherMode.cycleType == G_CYC_2CYCLE) {
nSpecialBlendMode = 3;
nFogUsage = 5;
}
break;
/* Brings troubles with Roadsters sky
case 0xc702:
// Donald Duck
// clr_fog*a_fog + clr_in*1ma
nFogUsage = 5;
nSpecialBlendMode = 2;
break;
*/
}
int nFogMode = 0; // Normal
if (nFogUsage == 0) {
switch (blender) {
case 0xC410:
case 0xC411:
case 0xF500:
nFogMode = 1; // fog blend
nFogUsage = 1;
break;
case 0x04D1:
nFogMode = 2; // inverse fog blend
nFogUsage = 1;
break;
}
}
_setIUniform(m_uniforms.uSpecialBlendMode, nSpecialBlendMode, _bForce);
_setIUniform(m_uniforms.uFogUsage, nFogUsage | nFogBlendEnabled, _bForce);
_setIUniform(m_uniforms.uFogMode, nFogMode, _bForce);
if (nFogUsage + nFogMode != 0) {
_setFV2Uniform(m_uniforms.uFogScale, (float)gSP.fog.multiplier / 256.0f, (float)gSP.fog.offset / 256.0f, _bForce);
_setFUniform(m_uniforms.uFogAlpha, gDP.fogColor.a, _bForce);
}
}
void ShaderCombiner::updateDitherMode(bool _bForce)
{
if (gDP.otherMode.cycleType < G_CYC_COPY) {
_setIUniform(m_uniforms.uAlphaCompareMode, gDP.otherMode.alphaCompare, _bForce);
_setIUniform(m_uniforms.uAlphaDitherMode, gDP.otherMode.alphaDither, _bForce);
_setIUniform(m_uniforms.uColorDitherMode, gDP.otherMode.colorDither, _bForce);
} else {
_setIUniform(m_uniforms.uAlphaCompareMode, 0, _bForce);
_setIUniform(m_uniforms.uAlphaDitherMode, 0, _bForce);
_setIUniform(m_uniforms.uColorDitherMode, 0, _bForce);
}
const int nDither = (gDP.otherMode.cycleType < G_CYC_COPY) && (gDP.otherMode.colorDither == G_CD_NOISE || gDP.otherMode.alphaDither == G_AD_NOISE || gDP.otherMode.alphaCompare == G_AC_DITHER) ? 1 : 0;
if ((m_nInputs & (1 << NOISE)) + nDither != 0) {
_setFV2Uniform(m_uniforms.uScreenScale, video().getScaleX(), video().getScaleY(), _bForce);
noiseTex.update();
}
}
void ShaderCombiner::updateLOD(bool _bForce)
{
if (usesLOD()) {
int uCalcLOD = (config.generalEmulation.enableLOD && gDP.otherMode.textureLOD == G_TL_LOD) ? 1 : 0;
_setIUniform(m_uniforms.uEnableLod, uCalcLOD, _bForce);
if (uCalcLOD) {
_setFV2Uniform(m_uniforms.uScreenScale, video().getScaleX(), video().getScaleY(), _bForce);
_setFUniform(m_uniforms.uPrimitiveLod, gDP.primColor.l, _bForce);
_setFUniform(m_uniforms.uMinLod, gDP.primColor.m, _bForce);
_setIUniform(m_uniforms.uMaxTile, gSP.texture.level, _bForce);
_setIUniform(m_uniforms.uTextureDetail, gDP.otherMode.textureDetail, _bForce);
}
}
}
void ShaderCombiner::updateTextureInfo(bool _bForce) {
_setIUniform(m_uniforms.uTexturePersp, gDP.otherMode.texturePersp, _bForce);
_setIUniform(m_uniforms.uTextureFilterMode, config.texture.bilinearMode == BILINEAR_3POINT ? gDP.otherMode.textureFilter | (gSP.objRendermode&G_OBJRM_BILERP) : 0, _bForce);
}
void ShaderCombiner::updateFBInfo(bool _bForce) {
if (!usesTex())
return;
int nFb8bitMode = 0, nFbFixedAlpha = 0;
TextureCache & cache = textureCache();
if (cache.current[0] != NULL && cache.current[0]->frameBufferTexture == TRUE) {
if (cache.current[0]->size == G_IM_SIZ_8b) {
nFb8bitMode |= 1;
if (gDP.otherMode.imageRead == 0)
nFbFixedAlpha |= 1;
}
}
if (cache.current[1] != NULL && cache.current[1]->frameBufferTexture == TRUE) {
if (cache.current[1]->size == G_IM_SIZ_8b) {
nFb8bitMode |= 2;
if (gDP.otherMode.imageRead == 0)
nFbFixedAlpha |= 2;
}
}
_setIUniform(m_uniforms.uFb8Bit, nFb8bitMode, _bForce);
_setIUniform(m_uniforms.uFbFixedAlpha, nFbFixedAlpha, _bForce);
gDP.changed &= ~CHANGED_FB_TEXTURE;
}
void ShaderCombiner::updateDepthInfo(bool _bForce) {
if (RSP.bLLE)
_setFV2Uniform(m_uniforms.uDepthScale, 0.5f, 0.5f, _bForce);
else
_setFV2Uniform(m_uniforms.uDepthScale, gSP.viewport.vscale[2], gSP.viewport.vtrans[2], _bForce);
if (config.frameBufferEmulation.N64DepthCompare == 0 || !video().getRender().isImageTexturesSupported())
return;
FrameBuffer * pBuffer = frameBufferList().getCurrent();
if (pBuffer == NULL || pBuffer->m_pDepthBuffer == NULL)
return;
const int nDepthEnabled = (gSP.geometryMode & G_ZBUFFER) > 0 ? 1 : 0;
_setIUniform(m_uniforms.uEnableDepth, nDepthEnabled, _bForce);
if (nDepthEnabled == 0) {
_setIUniform(m_uniforms.uEnableDepthCompare, 0, _bForce);
_setIUniform(m_uniforms.uEnableDepthUpdate, 0, _bForce);
} else {
_setIUniform(m_uniforms.uEnableDepthCompare, gDP.otherMode.depthCompare, _bForce);
_setIUniform(m_uniforms.uEnableDepthUpdate, gDP.otherMode.depthUpdate, _bForce);
}
_setIUniform(m_uniforms.uDepthMode, gDP.otherMode.depthMode, _bForce);
_setIUniform(m_uniforms.uDepthSource, gDP.otherMode.depthSource, _bForce);
if (gDP.otherMode.depthSource == G_ZS_PRIM)
_setFUniform(m_uniforms.uDeltaZ, gDP.primDepth.deltaZ, _bForce);
}
void ShaderCombiner::updateAlphaTestInfo(bool _bForce) {
if (gDP.otherMode.cycleType == G_CYC_FILL) {
_setIUniform(m_uniforms.uEnableAlphaTest, 0, _bForce);
_setFUniform(m_uniforms.uAlphaTestValue, 0.0f, _bForce);
} else if (gDP.otherMode.cycleType == G_CYC_COPY) {
if (gDP.otherMode.alphaCompare & 1) {
_setIUniform(m_uniforms.uEnableAlphaTest, 1, _bForce);
_setFUniform(m_uniforms.uAlphaTestValue, 0.5f, _bForce);
} else {
_setIUniform(m_uniforms.uEnableAlphaTest, 0, _bForce);
_setFUniform(m_uniforms.uAlphaTestValue, 0.0f, _bForce);
}
} else if ((gDP.otherMode.alphaCompare == G_AC_THRESHOLD) && (gDP.otherMode.alphaCvgSel == 0) && (gDP.otherMode.forceBlender == 0 || gDP.blendColor.a > 0)) {
_setIUniform(m_uniforms.uEnableAlphaTest, 1, _bForce);
_setFUniform(m_uniforms.uAlphaTestValue, gDP.blendColor.a, _bForce);
} else if (gDP.otherMode.cvgXAlpha != 0) {
_setIUniform(m_uniforms.uEnableAlphaTest, 1, _bForce);
_setFUniform(m_uniforms.uAlphaTestValue, 0.125f, _bForce);
} else {
_setIUniform(m_uniforms.uEnableAlphaTest, 0, _bForce);
_setFUniform(m_uniforms.uAlphaTestValue, 0.0f, _bForce);
}
}
#ifdef GL_IMAGE_TEXTURES_SUPPORT
void SetDepthFogCombiner()
{
if (!video().getRender().isImageTexturesSupported())
return;
if (g_paletteCRC256 != gDP.paletteCRC256) {
g_paletteCRC256 = gDP.paletteCRC256;
u16 palette[256];
u16 *src = (u16*)&TMEM[256];
for (int i = 0; i < 256; ++i)
palette[i] = swapword(src[i*4]);
glBindImageTexture(TlutImageUnit, 0, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R16UI);
glBindTexture(GL_TEXTURE_1D, g_tlut_tex);
glTexSubImage1D(GL_TEXTURE_1D, 0, 0, 256, GL_RED, GL_UNSIGNED_SHORT, palette);
glBindTexture(GL_TEXTURE_1D, 0);
glBindImageTexture(TlutImageUnit, g_tlut_tex, 0, GL_FALSE, 0, GL_READ_ONLY, GL_R16UI);
}
glUseProgram(g_draw_shadow_map_program);
int loc = glGetUniformLocation(g_draw_shadow_map_program, "uFogColor");
if (loc >= 0)
glUniform4fv(loc, 1, &gDP.fogColor.r);
gDP.changed |= CHANGED_COMBINE;
}
#endif // GL_IMAGE_TEXTURES_SUPPORT
void SetMonochromeCombiner() {
glUseProgram(g_monochrome_image_program);
gDP.changed |= CHANGED_COMBINE;
}
/*======================UniformBlock==========================*/
static
const char * strTextureUniforms[UniformBlock::tuTotal] = {
"uTexScale",
"uTexMask",
"uTexOffset",
"uCacheScale",
"uCacheOffset",
"uCacheShiftScale",
"uCacheFrameBuffer"
};
static
const char * strColorUniforms[UniformBlock::cuTotal] = {
"uFogColor",
"uCenterColor",
"uScaleColor",
"uBlendColor",
"uEnvColor",
"uPrimColor",
"uPrimLod",
"uK4",
"uK5"
};
static
const char * strLightUniforms[UniformBlock::luTotal] = {
"uLightDirection",
"uLightColor"
};
UniformBlock::UniformBlock() : m_currentBuffer(0)
{
}
UniformBlock::~UniformBlock()
{
}
void UniformBlock::_initTextureBuffer(GLuint _program)
{
const GLint blockSize = m_textureBlock.initBuffer(_program, "TextureBlock", strTextureUniforms);
if (blockSize == 0)
return;
m_textureBlockData.resize(blockSize);
GLbyte * pData = m_textureBlockData.data();
memset(pData, 0, blockSize);
glBindBuffer(GL_UNIFORM_BUFFER, m_textureBlock.m_buffer);
glBufferData(GL_UNIFORM_BUFFER, blockSize, 0, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, m_textureBlock.m_blockBindingPoint, m_textureBlock.m_buffer);
updateTextureParameters();
}
void UniformBlock::_initColorsBuffer(GLuint _program)
{
const GLint blockSize = m_colorsBlock.initBuffer(_program, "ColorsBlock", strColorUniforms);
if (blockSize == 0)
return;
m_colorsBlockData.resize(blockSize);
GLbyte * pData = m_colorsBlockData.data();
memset(pData, 0, blockSize);
memcpy(pData + m_colorsBlock.m_offsets[cuFogColor], &gDP.fogColor.r, sizeof(f32)* 4);
memcpy(pData + m_colorsBlock.m_offsets[cuCenterColor], &gDP.key.center.r, sizeof(f32)* 4);
memcpy(pData + m_colorsBlock.m_offsets[cuScaleColor], &gDP.key.scale.r, sizeof(f32)* 4);
memcpy(pData + m_colorsBlock.m_offsets[cuEnvColor], &gDP.envColor.r, sizeof(f32)* 4);
memcpy(pData + m_colorsBlock.m_offsets[cuPrimColor], &gDP.primColor.r, sizeof(f32)* 4);
*(f32*)(pData + m_colorsBlock.m_offsets[cuPrimLod]) = gDP.primColor.l;
*(f32*)(pData + m_colorsBlock.m_offsets[cuK4]) = gDP.convert.k4*0.0039215689f;
*(f32*)(pData + m_colorsBlock.m_offsets[cuK5]) = gDP.convert.k5*0.0039215689f;
glBindBuffer(GL_UNIFORM_BUFFER, m_colorsBlock.m_buffer);
glBufferData(GL_UNIFORM_BUFFER, blockSize, pData, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, m_colorsBlock.m_blockBindingPoint, m_colorsBlock.m_buffer);
m_currentBuffer = m_colorsBlock.m_buffer;
}
void UniformBlock::_initLightBuffer(GLuint _program)
{
const GLint blockSize = m_lightBlock.initBuffer(_program, "LightBlock", strLightUniforms);
if (blockSize == 0)
return;
m_lightBlockData.resize(blockSize);
GLbyte * pData = m_lightBlockData.data();
memset(pData, 0, blockSize);
glBindBuffer(GL_UNIFORM_BUFFER, m_lightBlock.m_buffer);
glBufferData(GL_UNIFORM_BUFFER, blockSize, 0, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, m_lightBlock.m_blockBindingPoint, m_lightBlock.m_buffer);
updateLightParameters();
}
bool UniformBlock::_isDataChanged(void * _pBuffer, const void * _pData, u32 _dataSize)
{
u32 * pSrc = (u32*)_pData;
u32 * pDst = (u32*)_pBuffer;
u32 cnt = _dataSize / 4;
for (u32 i = 0; i < cnt; ++i) {
if (pSrc[i] != pDst[i]) {
memcpy(_pBuffer, _pData, _dataSize);
return true;
}
}
return false;
}
void UniformBlock::bindWithShaderCombiner(ShaderCombiner * _pCombiner)
{
const GLuint program = _pCombiner->m_program;
if (_pCombiner->usesTex()) {
if (m_textureBlock.m_buffer == 0)
_initTextureBuffer(program);
else {
const GLint blockIndex = glGetUniformBlockIndex(program, "TextureBlock");
if (blockIndex != GL_INVALID_INDEX)
glUniformBlockBinding(program, blockIndex, m_textureBlock.m_blockBindingPoint);
}
}
if (m_colorsBlock.m_buffer == 0)
_initColorsBuffer(program);
else {
const GLint blockIndex = glGetUniformBlockIndex(program, "ColorsBlock");
if (blockIndex != GL_INVALID_INDEX)
glUniformBlockBinding(program, blockIndex, m_colorsBlock.m_blockBindingPoint);
}
if (_pCombiner->usesShadeColor() && config.generalEmulation.enableHWLighting != 0) {
if (m_lightBlock.m_buffer == 0)
_initLightBuffer(program);
else {
const GLint blockIndex = glGetUniformBlockIndex(program, "LightBlock");
if (blockIndex != GL_INVALID_INDEX)
glUniformBlockBinding(program, blockIndex, m_lightBlock.m_blockBindingPoint);
}
}
}
void UniformBlock::setColorData(ColorUniforms _index, u32 _dataSize, const void * _data)
{
if (m_colorsBlock.m_buffer == 0)
return;
if (!_isDataChanged(m_colorsBlockData.data() + m_colorsBlock.m_offsets[_index], _data, _dataSize))
return;
if (m_currentBuffer != m_colorsBlock.m_buffer) {
m_currentBuffer = m_colorsBlock.m_buffer;
glBindBuffer(GL_UNIFORM_BUFFER, m_colorsBlock.m_buffer);
}
glBufferSubData(GL_UNIFORM_BUFFER, m_colorsBlock.m_offsets[_index], _dataSize, _data);
}
void UniformBlock::updateTextureParameters()
{
if (m_textureBlock.m_buffer == 0)
return;
GLbyte * pData = m_textureBlockData.data();
f32 texScale[4] = { gSP.texture.scales, gSP.texture.scalet, 0, 0 };
memcpy(pData + m_textureBlock.m_offsets[tuTexScale], texScale, m_textureBlock.m_offsets[tuTexMask] - m_textureBlock.m_offsets[tuTexScale]);
f32 texOffset[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
f32 texMask[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
if (gSP.textureTile[0] != NULL) {
if (gSP.textureTile[0]->textureMode != TEXTUREMODE_BGIMAGE && gSP.textureTile[0]->textureMode != TEXTUREMODE_FRAMEBUFFER_BG) {
texOffset[0] = gSP.textureTile[0]->fuls;
texOffset[1] = gSP.textureTile[0]->fult;
texMask[0] = gSP.textureTile[0]->masks > 0 ? (float)(1 << gSP.textureTile[0]->masks) : 0.0f;
texMask[1] = gSP.textureTile[0]->maskt > 0 ? (float)(1 << gSP.textureTile[0]->maskt) : 0.0f;
}
}
if (gSP.textureTile[1] != 0) {
texOffset[4] = gSP.textureTile[1]->fuls;
texOffset[5] = gSP.textureTile[1]->fult;
texMask[4] = gSP.textureTile[1]->masks > 0 ? (float)(1 << gSP.textureTile[1]->masks) : 0.0f;
texMask[5] = gSP.textureTile[1]->maskt > 0 ? (float)(1 << gSP.textureTile[1]->maskt) : 0.0f;
}
memcpy(pData + m_textureBlock.m_offsets[tuTexMask], texMask, m_textureBlock.m_offsets[tuTexOffset] - m_textureBlock.m_offsets[tuTexMask]);
memcpy(pData + m_textureBlock.m_offsets[tuTexOffset], texOffset, m_textureBlock.m_offsets[tuCacheScale] - m_textureBlock.m_offsets[tuTexOffset]);
f32 texCacheScale[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
f32 texCacheOffset[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
f32 texCacheShiftScale[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
GLint texCacheFrameBuffer[4] = { 0, 0, 0, 0 };
TextureCache & cache = textureCache();
if (cache.current[0]) {
texCacheScale[0] = cache.current[0]->scaleS;
texCacheScale[1] = cache.current[0]->scaleT;
texCacheOffset[0] = cache.current[0]->offsetS;
texCacheOffset[1] = cache.current[0]->offsetT;
texCacheShiftScale[0] = cache.current[0]->shiftScaleS;
texCacheShiftScale[1] = cache.current[0]->shiftScaleT;
texCacheFrameBuffer[0] = cache.current[0]->frameBufferTexture;
}
if (cache.current[1]) {
texCacheScale[4] = cache.current[1]->scaleS;
texCacheScale[5] = cache.current[1]->scaleT;
texCacheOffset[4] = cache.current[1]->offsetS;
texCacheOffset[5] = cache.current[1]->offsetT;
texCacheShiftScale[4] = cache.current[1]->shiftScaleS;
texCacheShiftScale[5] = cache.current[1]->shiftScaleT;
texCacheFrameBuffer[1] = cache.current[1]->frameBufferTexture;
}
memcpy(pData + m_textureBlock.m_offsets[tuCacheScale], texCacheScale, m_textureBlock.m_offsets[tuCacheOffset] - m_textureBlock.m_offsets[tuCacheScale]);
memcpy(pData + m_textureBlock.m_offsets[tuCacheOffset], texCacheOffset, m_textureBlock.m_offsets[tuCacheShiftScale] - m_textureBlock.m_offsets[tuCacheOffset]);
memcpy(pData + m_textureBlock.m_offsets[tuCacheShiftScale], texCacheShiftScale, m_textureBlock.m_offsets[tuCacheFrameBuffer] - m_textureBlock.m_offsets[tuCacheShiftScale]);
memcpy(pData + m_textureBlock.m_offsets[tuCacheFrameBuffer], texCacheFrameBuffer, m_textureBlockData.size() - m_textureBlock.m_offsets[tuCacheFrameBuffer]);
if (m_currentBuffer != m_textureBlock.m_buffer) {
m_currentBuffer = m_textureBlock.m_buffer;
glBindBuffer(GL_UNIFORM_BUFFER, m_textureBlock.m_buffer);
}
glBufferSubData(GL_UNIFORM_BUFFER, m_textureBlock.m_offsets[tuTexScale], m_textureBlockData.size(), pData);
}
void UniformBlock::updateLightParameters()
{
if (m_lightBlock.m_buffer == 0)
return;
GLbyte * pData = m_lightBlockData.data();
const u32 arraySize = m_lightBlock.m_offsets[luLightColor] / 8;
for (s32 i = 0; i <= gSP.numLights; ++i) {
memcpy(pData + m_lightBlock.m_offsets[luLightDirection] + arraySize*i, &gSP.lights[i].x, arraySize);
memcpy(pData + m_lightBlock.m_offsets[luLightColor] + arraySize*i, &gSP.lights[i].r, arraySize);
}
if (m_currentBuffer != m_lightBlock.m_buffer) {
m_currentBuffer = m_lightBlock.m_buffer;
glBindBuffer(GL_UNIFORM_BUFFER, m_lightBlock.m_buffer);
}
glBufferSubData(GL_UNIFORM_BUFFER, m_lightBlock.m_offsets[luLightDirection], m_lightBlockData.size(), pData);
}