void GenerateVertexShader(int prim, char *buffer) { char *p = buffer; #if defined(USING_GLES2) WRITE(p, "precision highp float;\n"); #elif !defined(FORCE_OPENGL_2_0) WRITE(p, "#version 110\n"); // Remove lowp/mediump in non-mobile implementations WRITE(p, "#define lowp\n"); WRITE(p, "#define mediump\n"); #else // Need to remove lowp/mediump for Mac WRITE(p, "#define lowp\n"); WRITE(p, "#define mediump\n"); #endif const u32 vertType = gstate.vertType; int lmode = (gstate.lmode & 1) && gstate.isLightingEnabled(); int doTexture = gstate.isTextureMapEnabled() && !gstate.isModeClear(); bool hwXForm = CanUseHardwareTransform(prim); bool hasColor = (vertType & GE_VTYPE_COL_MASK) != 0 || !hwXForm; bool hasNormal = (vertType & GE_VTYPE_NRM_MASK) != 0 && hwXForm; bool enableFog = gstate.isFogEnabled() && !gstate.isModeThrough() && !gstate.isModeClear(); bool throughmode = (vertType & GE_VTYPE_THROUGH_MASK) != 0; bool flipV = gstate_c.flipTexture; bool doTextureProjection = gstate.getUVGenMode() == 1; DoLightComputation doLight[4] = {LIGHT_OFF, LIGHT_OFF, LIGHT_OFF, LIGHT_OFF}; if (hwXForm) { int shadeLight0 = gstate.getUVGenMode() == 2 ? gstate.getUVLS0() : -1; int shadeLight1 = gstate.getUVGenMode() == 2 ? gstate.getUVLS1() : -1; for (int i = 0; i < 4; i++) { if (!hasNormal) continue; if (i == shadeLight0 || i == shadeLight1) doLight[i] = LIGHT_SHADE; if ((gstate.lightingEnable & 1) && (gstate.lightEnable[i] & 1)) doLight[i] = LIGHT_FULL; } } if ((vertType & GE_VTYPE_WEIGHT_MASK) != GE_VTYPE_WEIGHT_NONE) { WRITE(p, "%s", boneWeightAttrDecl[gstate.getNumBoneWeights() - 1]); } if (hwXForm) WRITE(p, "attribute vec3 a_position;\n"); else WRITE(p, "attribute vec4 a_position;\n"); // need to pass the fog coord in w if (doTexture) { if (!hwXForm && doTextureProjection) WRITE(p, "attribute vec3 a_texcoord;\n"); else WRITE(p, "attribute vec2 a_texcoord;\n"); } if (hasColor) { WRITE(p, "attribute lowp vec4 a_color0;\n"); if (lmode && !hwXForm) // only software transform supplies color1 as vertex data WRITE(p, "attribute lowp vec3 a_color1;\n"); } if (hwXForm && hasNormal) WRITE(p, "attribute mediump vec3 a_normal;\n"); if (gstate.isModeThrough()) { WRITE(p, "uniform mat4 u_proj_through;\n"); } else { WRITE(p, "uniform mat4 u_proj;\n"); // Add all the uniforms we'll need to transform properly. } if (hwXForm || !hasColor) WRITE(p, "uniform lowp vec4 u_matambientalpha;\n"); // matambient + matalpha if (enableFog) { WRITE(p, "uniform vec2 u_fogcoef;\n"); } if (hwXForm) { // When transforming by hardware, we need a great deal more uniforms... WRITE(p, "uniform mat4 u_world;\n"); WRITE(p, "uniform mat4 u_view;\n"); if (gstate.getUVGenMode() == 0) WRITE(p, "uniform vec4 u_uvscaleoffset;\n"); else if (gstate.getUVGenMode() == 1) WRITE(p, "uniform mat4 u_texmtx;\n"); if ((vertType & GE_VTYPE_WEIGHT_MASK) != GE_VTYPE_WEIGHT_NONE) { int numBones = 1 + ((vertType & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT); for (int i = 0; i < numBones; i++) { WRITE(p, "uniform mat4 u_bone%i;\n", i); } } if (gstate.isLightingEnabled()) { WRITE(p, "uniform lowp vec4 u_ambient;\n"); if ((gstate.materialupdate & 2) == 0) WRITE(p, "uniform lowp vec3 u_matdiffuse;\n"); // if ((gstate.materialupdate & 4) == 0) WRITE(p, "uniform lowp vec4 u_matspecular;\n"); // Specular coef is contained in alpha WRITE(p, "uniform lowp vec3 u_matemissive;\n"); } for (int i = 0; i < 4; i++) { if (doLight[i] != LIGHT_OFF) { // This is needed for shade mapping WRITE(p, "uniform vec3 u_lightpos%i;\n", i); } if (doLight[i] == LIGHT_FULL) { // These are needed for the full thing WRITE(p, "uniform vec3 u_lightdir%i;\n", i); WRITE(p, "uniform vec3 u_lightatt%i;\n", i); WRITE(p, "uniform float u_lightangle%i;\n", i); WRITE(p, "uniform float u_lightspotCoef%i;\n", i); WRITE(p, "uniform lowp vec3 u_lightambient%i;\n", i); WRITE(p, "uniform lowp vec3 u_lightdiffuse%i;\n", i); WRITE(p, "uniform lowp vec3 u_lightspecular%i;\n", i); } } }
VSShader *ShaderManagerDX9::ApplyShader(int prim, u32 vertType) { bool useHWTransform = CanUseHardwareTransform(prim); ShaderID VSID; ComputeVertexShaderID(&VSID, vertType, useHWTransform); ShaderID FSID; ComputeFragmentShaderID(&FSID); // Just update uniforms if this is the same shader as last time. if (lastVShader_ != nullptr && lastPShader_ != nullptr && VSID == lastVSID_ && FSID == lastFSID_) { if (globalDirty_) { PSUpdateUniforms(globalDirty_); VSUpdateUniforms(globalDirty_); globalDirty_ = 0; } return lastVShader_; // Already all set. } VSCache::iterator vsIter = vsCache_.find(VSID); VSShader *vs; if (vsIter == vsCache_.end()) { // Vertex shader not in cache. Let's compile it. GenerateVertexShaderDX9(VSID, codeBuffer_); vs = new VSShader(VSID, codeBuffer_, useHWTransform); if (vs->Failed()) { ERROR_LOG(HLE, "Shader compilation failed, falling back to software transform"); osm.Show("hardware transform error - falling back to software", 2.5f, 0xFF3030FF, -1, true); delete vs; ComputeVertexShaderID(&VSID, vertType, false); // TODO: Look for existing shader with the appropriate ID, use that instead of generating a new one - however, need to make sure // that that shader ID is not used when computing the linked shader ID below, because then IDs won't match // next time and we'll do this over and over... // Can still work with software transform. GenerateVertexShaderDX9(VSID, codeBuffer_); vs = new VSShader(VSID, codeBuffer_, false); } vsCache_[VSID] = vs; } else { vs = vsIter->second; } lastVSID_ = VSID; FSCache::iterator fsIter = fsCache_.find(FSID); PSShader *fs; if (fsIter == fsCache_.end()) { // Fragment shader not in cache. Let's compile it. GenerateFragmentShaderDX9(FSID, codeBuffer_); fs = new PSShader(FSID, codeBuffer_); fsCache_[FSID] = fs; } else { fs = fsIter->second; } lastFSID_ = FSID; if (globalDirty_) { PSUpdateUniforms(globalDirty_); VSUpdateUniforms(globalDirty_); globalDirty_ = 0; } pD3Ddevice->SetPixelShader(fs->shader); pD3Ddevice->SetVertexShader(vs->shader); lastPShader_ = fs; lastVShader_ = vs; return vs; }