From 0ecac31abd7ce84357cc94c025ac06f4f6712e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 8 Oct 2013 17:18:59 +0200 Subject: [PATCH] Use glBindAttribLocation to simplify shader usage a little. --- GPU/GLES/ShaderManager.cpp | 74 +++++++++++++++++++++------------- GPU/GLES/ShaderManager.h | 28 ++++++++----- GPU/GLES/TransformPipeline.cpp | 45 +++++++++++---------- 3 files changed, 87 insertions(+), 60 deletions(-) diff --git a/GPU/GLES/ShaderManager.cpp b/GPU/GLES/ShaderManager.cpp index 8da8927389..fbbfcf14dc 100644 --- a/GPU/GLES/ShaderManager.cpp +++ b/GPU/GLES/ShaderManager.cpp @@ -44,7 +44,7 @@ Shader::Shader(const char *code, uint32_t shaderType, bool useHWTransform) : fai OutputDebugStringUTF8(code); #endif shader = glCreateShader(shaderType); - glShaderSource(shader, 1, &code, 0); + glShaderSource(shader, 1, &code, 0); glCompileShader(shader); GLint success; glGetShaderiv(shader, GL_COMPILE_STATUS, &success); @@ -73,13 +73,35 @@ Shader::~Shader() { glDeleteShader(shader); } +static int glGetAttribLocationL(int program, const char *name) { + int attrLoc = glGetAttribLocation(program, name); + ERROR_LOG(HLE, "Attr Loc: %i %i %s", program, attrLoc, name); + return attrLoc; +} + LinkedShader::LinkedShader(Shader *vs, Shader *fs, u32 vertType, bool useHWTransform) : useHWTransform_(useHWTransform), program(0), dirtyUniforms(0) { program = glCreateProgram(); + glAttachShader(program, vs->shader); glAttachShader(program, fs->shader); + + // Bind attribute locations to fixed locations so that they're + // the same in all shaders. We can use this later to minimize the calls to + // glEnableVertexAttribArray and glDisableVertexAttribArray. + glBindAttribLocation(program, ATTR_POSITION, "a_position"); + glBindAttribLocation(program, ATTR_TEXCOORD, "a_texcoord"); + glBindAttribLocation(program, ATTR_NORMAL, "a_normal"); + glBindAttribLocation(program, ATTR_W1, "a_w1"); + glBindAttribLocation(program, ATTR_W2, "a_w2"); + glBindAttribLocation(program, ATTR_COLOR0, "a_color0"); + glBindAttribLocation(program, ATTR_COLOR1, "a_color1"); + glLinkProgram(program); + glDetachShader(program, vs->shader); + glDetachShader(program, fs->shader); + GLint linkStatus = GL_FALSE; glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); if (linkStatus != GL_TRUE) { @@ -162,13 +184,16 @@ LinkedShader::LinkedShader(Shader *vs, Shader *fs, u32 vertType, bool useHWTrans u_lightspecular[i] = glGetUniformLocation(program, temp); } - a_position = glGetAttribLocation(program, "a_position"); - a_color0 = glGetAttribLocation(program, "a_color0"); - a_color1 = glGetAttribLocation(program, "a_color1"); - a_texcoord = glGetAttribLocation(program, "a_texcoord"); - a_normal = glGetAttribLocation(program, "a_normal"); - a_weight0123 = glGetAttribLocation(program, "a_w1"); - a_weight4567 = glGetAttribLocation(program, "a_w2"); + attrMask = 0; + if (-1 != glGetAttribLocationL(program, "a_position")) attrMask |= 1 << ATTR_POSITION; + if (-1 != glGetAttribLocationL(program, "a_texcoord")) attrMask |= 1 << ATTR_TEXCOORD; + if (-1 != glGetAttribLocationL(program, "a_normal")) attrMask |= 1 << ATTR_NORMAL; + if (-1 != glGetAttribLocationL(program, "a_w1")) attrMask |= 1 << ATTR_W1; + if (-1 != glGetAttribLocationL(program, "a_w2")) attrMask |= 1 << ATTR_W2; + if (-1 != glGetAttribLocationL(program, "a_color0")) attrMask |= 1 << ATTR_COLOR0; + if (-1 != glGetAttribLocationL(program, "a_color1")) attrMask |= 1 << ATTR_COLOR1; + + ELOG("AttrMask: %02x", attrMask); glUseProgram(program); @@ -262,23 +287,19 @@ static void SetMatrix4x3(int uniform, const float *m4x3) { void LinkedShader::use(u32 vertType) { glUseProgram(program); updateUniforms(vertType); - glEnableVertexAttribArray(a_position); - if (a_texcoord != -1) glEnableVertexAttribArray(a_texcoord); - if (a_color0 != -1) glEnableVertexAttribArray(a_color0); - if (a_color1 != -1) glEnableVertexAttribArray(a_color1); - if (a_normal != -1) glEnableVertexAttribArray(a_normal); - if (a_weight0123 != -1) glEnableVertexAttribArray(a_weight0123); - if (a_weight4567 != -1) glEnableVertexAttribArray(a_weight4567); + glEnableVertexAttribArray(0); + for (int i = 1; i < ATTR_COUNT; i++) { + if (attrMask & (1 << i)) + glEnableVertexAttribArray(i); + } } void LinkedShader::stop() { - glDisableVertexAttribArray(a_position); - if (a_texcoord != -1) glDisableVertexAttribArray(a_texcoord); - if (a_color0 != -1) glDisableVertexAttribArray(a_color0); - if (a_color1 != -1) glDisableVertexAttribArray(a_color1); - if (a_normal != -1) glDisableVertexAttribArray(a_normal); - if (a_weight0123 != -1) glDisableVertexAttribArray(a_weight0123); - if (a_weight4567 != -1) glDisableVertexAttribArray(a_weight4567); + glDisableVertexAttribArray(0); + for (int i = 1; i < ATTR_COUNT; i++) { + if (attrMask & (1 << i)) + glDisableVertexAttribArray(i); + } } void LinkedShader::updateUniforms(u32 vertType) { @@ -331,10 +352,10 @@ void LinkedShader::updateUniforms(u32 vertType) { if (gstate.isModeThrough()) { // We never get here because we don't use HW transform with through mode. // Although - why don't we? - uvscaleoff[0] = gstate_c.uv.uScale / gstate_c.curTextureWidth; - uvscaleoff[1] = gstate_c.uv.vScale / gstate_c.curTextureHeight; - uvscaleoff[2] = gstate_c.uv.uOff / gstate_c.curTextureWidth; - uvscaleoff[3] = gstate_c.uv.vOff / gstate_c.curTextureHeight; + uvscaleoff[0] = gstate_c.uv.uScale / (float)gstate_c.curTextureWidth; + uvscaleoff[1] = gstate_c.uv.vScale / (float)gstate_c.curTextureHeight; + uvscaleoff[2] = gstate_c.uv.uOff / (float)gstate_c.curTextureWidth; + uvscaleoff[3] = gstate_c.uv.vOff / (float)gstate_c.curTextureHeight; glUniform4fv(u_uvscaleoffset, 1, uvscaleoff); } else { int w = gstate.getTextureWidth(0); @@ -372,7 +393,6 @@ void LinkedShader::updateUniforms(u32 vertType) { // TODO: Could even set all bones in one go if they're all dirty. #ifdef USE_BONE_ARRAY - if (u_bone != -1) { float allBones[8 * 16]; diff --git a/GPU/GLES/ShaderManager.h b/GPU/GLES/ShaderManager.h index f7f53df26f..8e63b56b38 100644 --- a/GPU/GLES/ShaderManager.h +++ b/GPU/GLES/ShaderManager.h @@ -25,8 +25,20 @@ class Shader; -class LinkedShader -{ +// Pre-fetched attrs and uniforms +enum { + ATTR_POSITION = 0, + ATTR_TEXCOORD = 1, + ATTR_NORMAL = 2, + ATTR_W1 = 3, + ATTR_W2 = 4, + ATTR_COLOR0 = 5, + ATTR_COLOR1 = 6, + + ATTR_COUNT, +}; + +class LinkedShader { public: LinkedShader(Shader *vs, Shader *fs, u32 vertType, bool useHWTransform); ~LinkedShader(); @@ -41,14 +53,8 @@ public: uint32_t program; u32 dirtyUniforms; - // Pre-fetched attrs and uniforms - int a_position; - int a_color0; - int a_color1; - int a_texcoord; - int a_normal; - int a_weight0123; - int a_weight4567; + // Present attributes in the shader. + int attrMask; // 1 << ATTR_ ... or-ed together. int u_tex; int u_proj; @@ -63,7 +69,7 @@ public: int u_bone[8]; #endif int numBones; - + // Fragment processing inputs int u_alphacolorref; int u_colormask; diff --git a/GPU/GLES/TransformPipeline.cpp b/GPU/GLES/TransformPipeline.cpp index 45bdbbee45..4a151586b5 100644 --- a/GPU/GLES/TransformPipeline.cpp +++ b/GPU/GLES/TransformPipeline.cpp @@ -329,13 +329,13 @@ static inline void VertexAttribSetup(int attrib, int fmt, int stride, u8 *ptr) { // TODO: Use VBO and get rid of the vertexData pointers - with that, we will supply only offsets static void SetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt, u8 *vertexData) { - VertexAttribSetup(program->a_weight0123, decFmt.w0fmt, decFmt.stride, vertexData + decFmt.w0off); - VertexAttribSetup(program->a_weight4567, decFmt.w1fmt, decFmt.stride, vertexData + decFmt.w1off); - VertexAttribSetup(program->a_texcoord, decFmt.uvfmt, decFmt.stride, vertexData + decFmt.uvoff); - VertexAttribSetup(program->a_color0, decFmt.c0fmt, decFmt.stride, vertexData + decFmt.c0off); - VertexAttribSetup(program->a_color1, decFmt.c1fmt, decFmt.stride, vertexData + decFmt.c1off); - VertexAttribSetup(program->a_normal, decFmt.nrmfmt, decFmt.stride, vertexData + decFmt.nrmoff); - VertexAttribSetup(program->a_position, decFmt.posfmt, decFmt.stride, vertexData + decFmt.posoff); + VertexAttribSetup(ATTR_W1, decFmt.w0fmt, decFmt.stride, vertexData + decFmt.w0off); + VertexAttribSetup(ATTR_W2, decFmt.w1fmt, decFmt.stride, vertexData + decFmt.w1off); + VertexAttribSetup(ATTR_TEXCOORD, decFmt.uvfmt, decFmt.stride, vertexData + decFmt.uvoff); + VertexAttribSetup(ATTR_COLOR0, decFmt.c0fmt, decFmt.stride, vertexData + decFmt.c0off); + VertexAttribSetup(ATTR_COLOR1, decFmt.c1fmt, decFmt.stride, vertexData + decFmt.c1off); + VertexAttribSetup(ATTR_NORMAL, decFmt.nrmfmt, decFmt.stride, vertexData + decFmt.nrmoff); + VertexAttribSetup(ATTR_POSITION, decFmt.posfmt, decFmt.stride, vertexData + decFmt.posoff); } // The verts are in the order: BR BL TL TR @@ -375,7 +375,6 @@ static void RotateUVThrough(TransformedVertex v[4]) { SwapUVs(v[1], v[3]); } - // Clears on the PSP are best done by drawing a series of vertical strips // in clear mode. This tries to detect that. bool TransformDrawEngine::IsReallyAClear(int numVerts) const { @@ -395,7 +394,7 @@ bool TransformDrawEngine::IsReallyAClear(int numVerts) const { memcpy(&vcolor, transformed[i].color0, 4); if (vcolor != matchcolor || transformed[i].z != matchz) return false; - + if ((i & 1) == 0) { // Top left of a rectangle if (transformed[i].y != 0) @@ -598,7 +597,7 @@ void TransformDrawEngine::SoftwareTransformAndDraw( uv[1] = vscale * (ruv[1]*gstate_c.uv.vScale + gstate_c.uv.vOff); uv[2] = 1.0f; break; - + case GE_TEXMAP_TEXTURE_MATRIX: { // Projection mapping @@ -607,11 +606,11 @@ void TransformDrawEngine::SoftwareTransformAndDraw( case GE_PROJMAP_POSITION: // Use model space XYZ as source source = pos; break; - + case GE_PROJMAP_UV: // Use unscaled UV as source source = Vec3f(ruv[0], ruv[1], 0.0f); break; - + case GE_PROJMAP_NORMALIZED_NORMAL: // Use normalized normal as source if (reader.hasNormal()) { source = Vec3f(norm).Normalized(); @@ -620,7 +619,7 @@ void TransformDrawEngine::SoftwareTransformAndDraw( source = Vec3f(0.0f, 0.0f, 1.0f); } break; - + case GE_PROJMAP_NORMAL: // Use non-normalized normal as source! if (reader.hasNormal()) { source = Vec3f(norm); @@ -638,7 +637,7 @@ void TransformDrawEngine::SoftwareTransformAndDraw( uv[2] = uvw[2]; } break; - + case GE_TEXMAP_ENVIRONMENT_MAP: // Shade mapping - use two light sources to generate U and V. { @@ -650,15 +649,16 @@ void TransformDrawEngine::SoftwareTransformAndDraw( uv[2] = 1.0f; } break; - + default: // Illegal ERROR_LOG_REPORT(G3D, "Impossible UV gen mode? %d", gstate.getUVGenMode()); break; } + uv[0] = uv[0] * widthFactor; uv[1] = uv[1] * heightFactor; - + // Transform the coord by the view matrix. Vec3ByMatrix43(v, out, gstate.viewMatrix); fogCoef = (v[2] + fog_end) * fog_slope; @@ -765,6 +765,7 @@ void TransformDrawEngine::SoftwareTransformAndDraw( // Apparently, non-through RotateUV just breaks things. // If we find a game where it helps, we'll just have to figure out how they differ. // Possibly, it has something to do with flipped viewport Y axis, which a few games use. + // One game might be one of the Metal Gear ones, can't find the issue right now though. // else // RotateUV(trans); @@ -779,19 +780,19 @@ void TransformDrawEngine::SoftwareTransformAndDraw( } } - // TODO: Add a post-transform cache here for multi-RECTANGLES only. // Might help for text drawing. // these spam the gDebugger log. const int vertexSize = sizeof(transformed[0]); - + bool doTextureProjection = gstate.getUVGenMode() == GE_TEXMAP_TEXTURE_MATRIX; glBindBuffer(GL_ARRAY_BUFFER, 0); - glVertexAttribPointer(program->a_position, 4, GL_FLOAT, GL_FALSE, vertexSize, drawBuffer); - if (program->a_texcoord != -1) glVertexAttribPointer(program->a_texcoord, doTextureProjection ? 3 : 2, GL_FLOAT, GL_FALSE, vertexSize, ((uint8_t*)drawBuffer) + 4 * 4); - if (program->a_color0 != -1) glVertexAttribPointer(program->a_color0, 4, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, ((uint8_t*)drawBuffer) + 7 * 4); - if (program->a_color1 != -1) glVertexAttribPointer(program->a_color1, 3, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, ((uint8_t*)drawBuffer) + 8 * 4); + glVertexAttribPointer(ATTR_POSITION, 4, GL_FLOAT, GL_FALSE, vertexSize, drawBuffer); + int attrMask = program->attrMask; + if (attrMask & (1 << ATTR_TEXCOORD)) glVertexAttribPointer(ATTR_TEXCOORD, doTextureProjection ? 3 : 2, GL_FLOAT, GL_FALSE, vertexSize, ((uint8_t*)drawBuffer) + 4 * 4); + if (attrMask & (1 << ATTR_COLOR0)) glVertexAttribPointer(ATTR_COLOR0, 4, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, ((uint8_t*)drawBuffer) + 7 * 4); + if (attrMask & (1 << ATTR_COLOR1)) glVertexAttribPointer(ATTR_COLOR1, 3, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, ((uint8_t*)drawBuffer) + 8 * 4); if (drawIndexed) { //#ifdef USING_GLES2 glDrawElements(glprim[prim], numTrans, GL_UNSIGNED_SHORT, inds);