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);
			}
		}
	}
Example #2
0
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;
}